From a8599b66f4d16611159c88fcce3fb88c85b2a683 Mon Sep 17 00:00:00 2001 From: Masaki Yatsu Date: Wed, 10 Dec 2025 13:26:41 +0900 Subject: [PATCH] fix(minio): fix OIDC and add public access recipes --- minio/README.md | 77 +++++++++++++++ minio/justfile | 95 +++++++++++++++++++ .../minio-oidc-external-secret.gomplate.yaml | 22 +++++ minio/minio-values.gomplate.yaml | 5 +- 4 files changed, 197 insertions(+), 2 deletions(-) create mode 100644 minio/minio-oidc-external-secret.gomplate.yaml diff --git a/minio/README.md b/minio/README.md index dda1a09..4f0c017 100644 --- a/minio/README.md +++ b/minio/README.md @@ -221,6 +221,83 @@ just minio::bucket-exists mybucket This returns exit code 0 if the bucket exists, 1 otherwise. +## Public Access + +MinIO allows you to configure anonymous (public) access to buckets or specific prefixes for serving static content like images. + +### Set Public Download Access + +Enable public read access for a bucket or prefix: + +```bash +# Set public access for entire bucket +just minio::set-public-download mybucket + +# Set public access for specific prefix only +just minio::set-public-download mybucket/public +``` + +After setting public access, files can be accessed without authentication: + +```text +https://your-minio-host/mybucket/public/image.png +``` + +### Check Public Access Status + +View current anonymous access policy: + +```bash +just minio::show-public-access mybucket +``` + +Possible values: + +- `private`: No anonymous access (default) +- `download`: Public read access +- `upload`: Public write access +- `public`: Public read and write access +- `custom`: Custom policy applied + +### Remove Public Access + +Revoke anonymous access: + +```bash +just minio::remove-public-access mybucket/public +``` + +### Using mc Commands + +```bash +# Set public download (read-only) +mc anonymous set download myminio/mybucket/public + +# Set public upload (write-only) +mc anonymous set upload myminio/mybucket/uploads + +# Set full public access (read and write) +mc anonymous set public myminio/mybucket + +# Remove public access +mc anonymous set none myminio/mybucket + +# Check current policy +mc anonymous get myminio/mybucket +``` + +### Presigned URLs (Temporary Access) + +For temporary access to private objects, use presigned URLs: + +```bash +# Generate URL valid for 7 days +mc share download myminio/mybucket/private-file.pdf --expire=168h + +# Generate upload URL +mc share upload myminio/mybucket/uploads/ --expire=1h +``` + ## User Management ### Create MinIO User diff --git a/minio/justfile b/minio/justfile index 5473047..fbb6668 100644 --- a/minio/justfile +++ b/minio/justfile @@ -47,6 +47,7 @@ create-root-credentials: gomplate -f minio-root-external-secret.gomplate.yaml | kubectl apply -f - echo "Waiting for ExternalSecret to sync..." + sleep 2 kubectl wait --for=condition=Ready externalsecret/minio \ -n ${MINIO_NAMESPACE} --timeout=60s else @@ -96,7 +97,12 @@ install: --placeholder="e.g., minio-console.example.com" ) fi + + # Generate OIDC client secret for confidential client + OIDC_CLIENT_SECRET=$(just utils::random-password) + just keycloak::create-client realm=${KEYCLOAK_REALM} client_id=${MINIO_OIDC_CLIENT_ID} \ + client_secret="${OIDC_CLIENT_SECRET}" \ redirect_url="https://${MINIO_HOST}/oauth_callback,https://${MINIO_CONSOLE_HOST}/oauth_callback" just add-keycloak-minio-policy just create-namespace @@ -105,6 +111,28 @@ install: pod-security.kubernetes.io/enforce=restricted --overwrite just create-root-credentials + + # Store OIDC client secret + if helm status external-secrets -n ${EXTERNAL_SECRETS_NAMESPACE} &>/dev/null; then + echo "Storing OIDC client secret in Vault..." + just vault::put minio/oidc client_id="${MINIO_OIDC_CLIENT_ID}" client_secret="${OIDC_CLIENT_SECRET}" + kubectl delete externalsecret minio-oidc -n ${MINIO_NAMESPACE} --ignore-not-found + gomplate -f minio-oidc-external-secret.gomplate.yaml | kubectl apply -f - + echo "Waiting for ExternalSecret to sync..." + sleep 2 + kubectl wait --for=condition=Ready externalsecret/minio-oidc \ + -n ${MINIO_NAMESPACE} --timeout=60s + else + echo "Creating OIDC client secret directly..." + kubectl delete secret minio-oidc -n ${MINIO_NAMESPACE} --ignore-not-found + kubectl create secret generic minio-oidc -n ${MINIO_NAMESPACE} \ + --from-literal=clientId="${MINIO_OIDC_CLIENT_ID}" \ + --from-literal=clientSecret="${OIDC_CLIENT_SECRET}" + if helm status vault -n ${K8S_VAULT_NAMESPACE} &>/dev/null; then + just vault::put minio/oidc client_id="${MINIO_OIDC_CLIENT_ID}" client_secret="${OIDC_CLIENT_SECRET}" + fi + fi + just add-helm-repo gomplate -f minio-values.gomplate.yaml -o minio-values.yaml helm upgrade --install minio minio/minio \ @@ -260,3 +288,70 @@ grant-policy user='' policy='': mc admin policy attach local ${POLICY} --user=${USER}" echo "✅ Policy ${POLICY} granted to user ${USER}" + +# Set public download access for a bucket or prefix +set-public-download path='': + #!/bin/bash + set -euo pipefail + PATH_ARG="{{ path }}" + while [ -z "${PATH_ARG}" ]; do + PATH_ARG=$( + gum input --prompt="Bucket/prefix path: " --width=100 \ + --placeholder="e.g., my-bucket/public" + ) + done + + ROOT_USER=$(just root-username) + ROOT_PASSWORD=$(just root-password) + + kubectl -n ${MINIO_NAMESPACE} exec deploy/minio -- \ + mc alias set local http://localhost:9000 ${ROOT_USER} ${ROOT_PASSWORD} + + kubectl -n ${MINIO_NAMESPACE} exec deploy/minio -- \ + mc anonymous set download local/${PATH_ARG} + + echo "✅ Public download access enabled for ${PATH_ARG}" + +# Remove public access from a bucket or prefix +remove-public-access path='': + #!/bin/bash + set -euo pipefail + PATH_ARG="{{ path }}" + while [ -z "${PATH_ARG}" ]; do + PATH_ARG=$( + gum input --prompt="Bucket/prefix path: " --width=100 \ + --placeholder="e.g., my-bucket/public" + ) + done + + ROOT_USER=$(just root-username) + ROOT_PASSWORD=$(just root-password) + + kubectl -n ${MINIO_NAMESPACE} exec deploy/minio -- \ + mc alias set local http://localhost:9000 ${ROOT_USER} ${ROOT_PASSWORD} + + kubectl -n ${MINIO_NAMESPACE} exec deploy/minio -- \ + mc anonymous set none local/${PATH_ARG} + + echo "✅ Public access removed from ${PATH_ARG}" + +# Show anonymous access policy for a bucket or prefix +show-public-access path='': + #!/bin/bash + set -euo pipefail + PATH_ARG="{{ path }}" + while [ -z "${PATH_ARG}" ]; do + PATH_ARG=$( + gum input --prompt="Bucket/prefix path: " --width=100 \ + --placeholder="e.g., my-bucket" + ) + done + + ROOT_USER=$(just root-username) + ROOT_PASSWORD=$(just root-password) + + kubectl -n ${MINIO_NAMESPACE} exec deploy/minio -- \ + mc alias set local http://localhost:9000 ${ROOT_USER} ${ROOT_PASSWORD} + + kubectl -n ${MINIO_NAMESPACE} exec deploy/minio -- \ + mc anonymous get local/${PATH_ARG} diff --git a/minio/minio-oidc-external-secret.gomplate.yaml b/minio/minio-oidc-external-secret.gomplate.yaml new file mode 100644 index 0000000..cad4440 --- /dev/null +++ b/minio/minio-oidc-external-secret.gomplate.yaml @@ -0,0 +1,22 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: minio-oidc + namespace: {{ .Env.MINIO_NAMESPACE }} +spec: + refreshInterval: 1h + secretStoreRef: + name: vault-secret-store + kind: ClusterSecretStore + target: + name: minio-oidc + creationPolicy: Owner + data: + - secretKey: clientId + remoteRef: + key: minio/oidc + property: client_id + - secretKey: clientSecret + remoteRef: + key: minio/oidc + property: client_secret diff --git a/minio/minio-values.gomplate.yaml b/minio/minio-values.gomplate.yaml index 445f986..6db5249 100644 --- a/minio/minio-values.gomplate.yaml +++ b/minio/minio-values.gomplate.yaml @@ -7,8 +7,9 @@ existingSecret: "minio" oidc: enabled: true configUrl: "https://{{ .Env.KEYCLOAK_HOST }}/realms/{{ .Env.KEYCLOAK_REALM }}/.well-known/openid-configuration" - clientId: "{{ .Env.MINIO_OIDC_CLIENT_ID }}" - clientSecret: "" + existingClientSecretName: "minio-oidc" + existingClientIdKey: "clientId" + existingClientSecretKey: "clientSecret" claimName: "minioPolicy" scopes: "openid,profile,email" redirectUri: "https://{{ .Env.MINIO_CONSOLE_HOST }}/oauth_callback"