zot: add oauth proxy and user sync scripts
This commit is contained in:
parent
828f66d18c
commit
b87f06f6ff
@ -372,8 +372,8 @@ function xmrwallet_bootstrap --description "Interactive setup of monero-wallet-r
|
||||
end
|
||||
|
||||
# Use your private image by default (in Zot)
|
||||
read -P "Container image for wallet RPC [registry.bstein.dev/infra/monero-wallet-rpc:0.18.4.1]: " image
|
||||
if test -z "$image"; set image registry.bstein.dev/infra/monero-wallet-rpc:0.18.4.1; end
|
||||
read -P "Container image for wallet RPC [cli.registry.bstein.dev/infra/monero-wallet-rpc:0.18.4.1]: " image
|
||||
if test -z "$image"; set image cli.registry.bstein.dev/infra/monero-wallet-rpc:0.18.4.1; end
|
||||
_require "Container image" $image; or return 1
|
||||
|
||||
# --- Secrets (defaults: RPC user=wallet name, passwords auto if missing)
|
||||
@ -1375,4 +1375,3 @@ function xmrwallet_help_detailed
|
||||
echo " Probes it via a temporary port-forward so it works from your workstation."
|
||||
echo " Set xmrwallet_SKIP_DAEMON_CHECK=1 to bypass the daemon probe (not recommended)."
|
||||
end
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ end
|
||||
|
||||
# Default image chooser (you should override with your own multi-arch image)
|
||||
function _sui_default_image -a NET
|
||||
echo registry.bstein.dev/infra/sui-tools:1.53.2
|
||||
echo cli.registry.bstein.dev/infra/sui-tools:1.53.2
|
||||
end
|
||||
|
||||
# Convert any string to a k8s-safe name (RFC-1123 label-ish)
|
||||
|
||||
92
scripts/gitea_cred_sync.sh
Executable file
92
scripts/gitea_cred_sync.sh
Executable file
@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env bash
|
||||
# Sync Keycloak users into Gitea local accounts (for CLI + tokens).
|
||||
# Requires: curl, jq, kubectl. Expects a Keycloak client with realm-management
|
||||
# permissions (manage-users) and a Gitea admin token stored in a secret.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
require() { command -v "$1" >/dev/null 2>&1 || { echo "missing required binary: $1" >&2; exit 1; }; }
|
||||
require curl; require jq; require kubectl
|
||||
|
||||
: "${KEYCLOAK_URL:=https://sso.bstein.dev}"
|
||||
: "${KEYCLOAK_REALM:=atlas}"
|
||||
: "${KEYCLOAK_CLIENT_ID:?set KEYCLOAK_CLIENT_ID or export via secret}"
|
||||
: "${KEYCLOAK_CLIENT_SECRET:?set KEYCLOAK_CLIENT_SECRET or export via secret}"
|
||||
: "${GITEA_BASE_URL:=https://scm.bstein.dev}"
|
||||
: "${GITEA_NAMESPACE:=gitea}"
|
||||
: "${GITEA_TOKEN_SECRET_NAME:=gitea-admin-token}"
|
||||
: "${GITEA_TOKEN_SECRET_KEY:=token}"
|
||||
: "${DEFAULT_PASSWORD:=TempSsoPass!2025}"
|
||||
|
||||
fetch_token() {
|
||||
curl -fsS -X POST \
|
||||
-d "grant_type=client_credentials" \
|
||||
-d "client_id=${KEYCLOAK_CLIENT_ID}" \
|
||||
-d "client_secret=${KEYCLOAK_CLIENT_SECRET}" \
|
||||
"${KEYCLOAK_URL}/realms/${KEYCLOAK_REALM}/protocol/openid-connect/token" \
|
||||
| jq -r '.access_token'
|
||||
}
|
||||
|
||||
pull_users() {
|
||||
local token="$1"
|
||||
curl -fsS -H "Authorization: Bearer ${token}" \
|
||||
"${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users?max=500" \
|
||||
| jq -r '.[] | select(.enabled == true) | select(.username | startswith("service-account-") | not) | [.username, (.email // ""), (.firstName // ""), (.lastName // "")] | @tsv'
|
||||
}
|
||||
|
||||
get_gitea_token() {
|
||||
if [[ -n "${GITEA_ADMIN_TOKEN:-}" ]]; then
|
||||
echo "${GITEA_ADMIN_TOKEN}"
|
||||
return
|
||||
fi
|
||||
kubectl -n "${GITEA_NAMESPACE}" get secret "${GITEA_TOKEN_SECRET_NAME}" -o "jsonpath={.data.${GITEA_TOKEN_SECRET_KEY}}" \
|
||||
| base64 -d
|
||||
}
|
||||
|
||||
user_exists() {
|
||||
local token="$1" username="$2"
|
||||
local code
|
||||
code=$(curl -s -o /dev/null -w '%{http_code}' \
|
||||
-H "Authorization: token ${token}" \
|
||||
"${GITEA_BASE_URL}/api/v1/admin/users/${username}")
|
||||
[[ "${code}" == "200" ]]
|
||||
}
|
||||
|
||||
create_user() {
|
||||
local token="$1" username="$2" email="$3" fname="$4" lname="$5"
|
||||
local body status fullname
|
||||
fullname="$(echo "${fname} ${lname}" | xargs)"
|
||||
if [[ -z "${email}" ]]; then
|
||||
email="${username}@example.local"
|
||||
fi
|
||||
body=$(jq -n --arg u "${username}" --arg e "${email}" --arg p "${DEFAULT_PASSWORD}" \
|
||||
--arg fn "${fullname}" '{username:$u, email:$e, password:$p, must_change_password:false, full_name:$fn}')
|
||||
status=$(curl -s -o /dev/null -w '%{http_code}' \
|
||||
-H "Authorization: token ${token}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-X POST \
|
||||
-d "${body}" \
|
||||
"${GITEA_BASE_URL}/api/v1/admin/users")
|
||||
if [[ "${status}" == "201" ]]; then
|
||||
echo "created gitea user ${username}"
|
||||
elif [[ "${status}" == "409" ]]; then
|
||||
echo "gitea user ${username} already exists (409)" >&2
|
||||
else
|
||||
echo "failed to create gitea user ${username} (status ${status})" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
local kc_token gitea_token
|
||||
kc_token="$(fetch_token)"
|
||||
gitea_token="$(get_gitea_token)"
|
||||
|
||||
while IFS=$'\t' read -r username email fname lname; do
|
||||
if user_exists "${gitea_token}" "${username}"; then
|
||||
continue
|
||||
fi
|
||||
create_user "${gitea_token}" "${username}" "${email}" "${fname}" "${lname}"
|
||||
done < <(pull_users "${kc_token}")
|
||||
}
|
||||
|
||||
main "$@"
|
||||
87
scripts/gitops_cred_sync.sh
Executable file
87
scripts/gitops_cred_sync.sh
Executable file
@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env bash
|
||||
# Ensure Keycloak users are in the GitOps admin group used by weave-gitops (cd.bstein.dev).
|
||||
# Weave GitOps relies on OIDC; membership in the "admin" group maps to cluster-admin via RBAC.
|
||||
# Requires: curl, jq. Needs a Keycloak client with realm-management (manage-users/groups).
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
require() { command -v "$1" >/dev/null 2>&1 || { echo "missing required binary: $1" >&2; exit 1; }; }
|
||||
require curl; require jq
|
||||
|
||||
: "${KEYCLOAK_URL:=https://sso.bstein.dev}"
|
||||
: "${KEYCLOAK_REALM:=atlas}"
|
||||
: "${KEYCLOAK_CLIENT_ID:?set KEYCLOAK_CLIENT_ID or export via secret}"
|
||||
: "${KEYCLOAK_CLIENT_SECRET:?set KEYCLOAK_CLIENT_SECRET or export via secret}"
|
||||
: "${GITOPS_GROUP:=admin}"
|
||||
# Comma-separated usernames to sync; set SYNC_ALL_USERS=true to include all Keycloak users.
|
||||
: "${TARGET_USERNAMES:=bstein}"
|
||||
: "${SYNC_ALL_USERS:=false}"
|
||||
|
||||
fetch_token() {
|
||||
curl -fsS -X POST \
|
||||
-d "grant_type=client_credentials" \
|
||||
-d "client_id=${KEYCLOAK_CLIENT_ID}" \
|
||||
-d "client_secret=${KEYCLOAK_CLIENT_SECRET}" \
|
||||
"${KEYCLOAK_URL}/realms/${KEYCLOAK_REALM}/protocol/openid-connect/token" \
|
||||
| jq -r '.access_token'
|
||||
}
|
||||
|
||||
ensure_group() {
|
||||
local token="$1" group="$2" id
|
||||
id=$(curl -fsS -H "Authorization: Bearer ${token}" \
|
||||
"${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/groups?search=${group}" \
|
||||
| jq -r --arg g "${group}" '.[] | select(.name==$g) | .id' | head -n1)
|
||||
if [[ -n "${id}" ]]; then
|
||||
echo "${id}"
|
||||
return
|
||||
fi
|
||||
curl -fsS -H "Authorization: Bearer ${token}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"name\":\"${group}\"}" \
|
||||
-X POST "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/groups"
|
||||
# Fetch again to get id
|
||||
curl -fsS -H "Authorization: Bearer ${token}" \
|
||||
"${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/groups?search=${group}" \
|
||||
| jq -r --arg g "${group}" '.[] | select(.name==$g) | .id' | head -n1
|
||||
}
|
||||
|
||||
user_id_by_name() {
|
||||
local token="$1" username="$2"
|
||||
curl -fsS -H "Authorization: Bearer ${token}" \
|
||||
"${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users?username=${username}" \
|
||||
| jq -r '.[0].id'
|
||||
}
|
||||
|
||||
add_user_to_group() {
|
||||
local token="$1" user_id="$2" group_id="$3" username="$4"
|
||||
if [[ -z "${user_id}" ]]; then
|
||||
echo "user ${username} not found in Keycloak; skip" >&2
|
||||
return
|
||||
fi
|
||||
curl -fsS -o /dev/null -w '%{http_code}' \
|
||||
-H "Authorization: Bearer ${token}" \
|
||||
-X PUT "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users/${user_id}/groups/${group_id}" \
|
||||
| grep -qE '^(204|409)$' || echo "failed adding ${username} to group" >&2
|
||||
}
|
||||
|
||||
main() {
|
||||
local token group_id users=()
|
||||
token="$(fetch_token)"
|
||||
group_id="$(ensure_group "${token}" "${GITOPS_GROUP}")"
|
||||
|
||||
if [[ "${SYNC_ALL_USERS}" == "true" ]]; then
|
||||
readarray -t users < <(curl -fsS -H "Authorization: Bearer ${token}" \
|
||||
"${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users?max=500" \
|
||||
| jq -r '.[] | select(.enabled==true) | .username')
|
||||
else
|
||||
IFS=',' read -ra users <<< "${TARGET_USERNAMES}"
|
||||
fi
|
||||
|
||||
for user in "${users[@]}"; do
|
||||
user="$(echo "${user}" | xargs)"
|
||||
[[ -z "${user}" ]] && continue
|
||||
add_user_to_group "${token}" "$(user_id_by_name "${token}" "${user}")" "${group_id}" "${user}"
|
||||
done
|
||||
}
|
||||
|
||||
main "$@"
|
||||
94
scripts/jenkins_cred_sync.sh
Executable file
94
scripts/jenkins_cred_sync.sh
Executable file
@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env bash
|
||||
# Sync Keycloak users into Jenkins local accounts (for CLI/API use).
|
||||
# Jenkins is OIDC-enabled, but local users can still be provisioned for tokens.
|
||||
# Requires: curl, jq, kubectl. Needs Jenkins admin user+API token.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
require() { command -v "$1" >/dev/null 2>&1 || { echo "missing required binary: $1" >&2; exit 1; }; }
|
||||
require curl; require jq; require kubectl
|
||||
|
||||
: "${KEYCLOAK_URL:=https://sso.bstein.dev}"
|
||||
: "${KEYCLOAK_REALM:=atlas}"
|
||||
: "${KEYCLOAK_CLIENT_ID:?set KEYCLOAK_CLIENT_ID or export via secret}"
|
||||
: "${KEYCLOAK_CLIENT_SECRET:?set KEYCLOAK_CLIENT_SECRET or export via secret}"
|
||||
: "${JENKINS_URL:=https://ci.bstein.dev}"
|
||||
: "${JENKINS_NAMESPACE:=jenkins}"
|
||||
: "${JENKINS_ADMIN_SECRET_NAME:=jenkins-admin-token}"
|
||||
: "${JENKINS_ADMIN_USER_KEY:=username}"
|
||||
: "${JENKINS_ADMIN_TOKEN_KEY:=token}"
|
||||
: "${DEFAULT_PASSWORD:=TempSsoPass!2025}"
|
||||
|
||||
fetch_token() {
|
||||
curl -fsS -X POST \
|
||||
-d "grant_type=client_credentials" \
|
||||
-d "client_id=${KEYCLOAK_CLIENT_ID}" \
|
||||
-d "client_secret=${KEYCLOAK_CLIENT_SECRET}" \
|
||||
"${KEYCLOAK_URL}/realms/${KEYCLOAK_REALM}/protocol/openid-connect/token" \
|
||||
| jq -r '.access_token'
|
||||
}
|
||||
|
||||
pull_users() {
|
||||
local token="$1"
|
||||
curl -fsS -H "Authorization: Bearer ${token}" \
|
||||
"${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users?max=500" \
|
||||
| jq -r '.[] | select(.enabled == true) | select(.username | startswith("service-account-") | not) | [.id, .username, (.email // "")] | @tsv'
|
||||
}
|
||||
|
||||
get_admin_auth() {
|
||||
local user token
|
||||
if [[ -n "${JENKINS_ADMIN_USER:-}" && -n "${JENKINS_ADMIN_TOKEN:-}" ]]; then
|
||||
echo "${JENKINS_ADMIN_USER}:${JENKINS_ADMIN_TOKEN}"
|
||||
return
|
||||
fi
|
||||
user=$(kubectl -n "${JENKINS_NAMESPACE}" get secret "${JENKINS_ADMIN_SECRET_NAME}" -o "jsonpath={.data.${JENKINS_ADMIN_USER_KEY}}" | base64 -d)
|
||||
token=$(kubectl -n "${JENKINS_NAMESPACE}" get secret "${JENKINS_ADMIN_SECRET_NAME}" -o "jsonpath={.data.${JENKINS_ADMIN_TOKEN_KEY}}" | base64 -d)
|
||||
echo "${user}:${token}"
|
||||
}
|
||||
|
||||
get_crumb() {
|
||||
local auth="$1"
|
||||
curl -fsS -u "${auth}" "${JENKINS_URL}/crumbIssuer/api/json" | jq -r .crumb
|
||||
}
|
||||
|
||||
user_exists() {
|
||||
local auth="$1" user="$2"
|
||||
local code
|
||||
code=$(curl -s -o /dev/null -w '%{http_code}' -u "${auth}" "${JENKINS_URL}/user/${user}/api/json")
|
||||
[[ "${code}" == "200" ]]
|
||||
}
|
||||
|
||||
create_user() {
|
||||
local auth="$1" crumb="$2" username="$3" email="$4"
|
||||
local status
|
||||
status=$(curl -s -o /dev/null -w '%{http_code}' \
|
||||
-u "${auth}" \
|
||||
-H "Jenkins-Crumb: ${crumb}" \
|
||||
-X POST \
|
||||
--data "username=${username}&password1=${DEFAULT_PASSWORD}&password2=${DEFAULT_PASSWORD}&fullname=${username}&email=${email}" \
|
||||
"${JENKINS_URL}/securityRealm/createAccountByAdmin")
|
||||
|
||||
if [[ "${status}" == "200" || "${status}" == "302" ]]; then
|
||||
echo "created jenkins user ${username}"
|
||||
elif [[ "${status}" == "400" ]]; then
|
||||
echo "jenkins user ${username} already exists (400)" >&2
|
||||
else
|
||||
echo "failed to create jenkins user ${username} (status ${status})" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
local kc_token auth crumb
|
||||
kc_token="$(fetch_token)"
|
||||
auth="$(get_admin_auth)"
|
||||
crumb="$(get_crumb "${auth}")"
|
||||
|
||||
while IFS=$'\t' read -r _ uid email; do
|
||||
if user_exists "${auth}" "${uid}"; then
|
||||
continue
|
||||
fi
|
||||
create_user "${auth}" "${crumb}" "${uid}" "${email}"
|
||||
done < <(pull_users "${kc_token}")
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env fish
|
||||
|
||||
function pvc-usage --description "Show Longhorn PVC usage (human-readable) mapped to namespace/name"
|
||||
function pvc-usage --description "Show Longhorn PVC usage mapped to namespace/name"
|
||||
begin
|
||||
kubectl -n longhorn-system get volumes.longhorn.io -o json \
|
||||
| jq -r '.items[] | "\(.metadata.name)\t\(.status.actualSize)\t\(.spec.size)"' \
|
||||
|
||||
@ -39,7 +39,7 @@ SITES=(
|
||||
"Jellyfin|https://stream.bstein.dev"
|
||||
"Gitea|https://scm.bstein.dev"
|
||||
"Jenkins|https://ci.bstein.dev"
|
||||
"Zot|https://registry.bstein.dev"
|
||||
"Zot|https://web.registry.bstein.dev"
|
||||
"Vault|https://secret.bstein.dev"
|
||||
"Jitsi|https://meet.bstein.dev"
|
||||
"Grafana|https://metrics.bstein.dev"
|
||||
|
||||
76
scripts/zot_cred_sync.sh
Executable file
76
scripts/zot_cred_sync.sh
Executable file
@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env bash
|
||||
# Sync Keycloak users into Zot htpasswd.
|
||||
# Intended to be rendered into a ConfigMap/Job; keep real secrets out of git.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
require() { command -v "$1" >/dev/null 2>&1 || { echo "missing required binary: $1" >&2; exit 1; }; }
|
||||
require curl; require jq; require kubectl; require htpasswd
|
||||
|
||||
: "${KEYCLOAK_URL:=https://sso.bstein.dev}"
|
||||
: "${KEYCLOAK_REALM:=atlas}"
|
||||
: "${KEYCLOAK_CLIENT_ID:?set KEYCLOAK_CLIENT_ID or export via secret}"
|
||||
: "${KEYCLOAK_CLIENT_SECRET:?set KEYCLOAK_CLIENT_SECRET or export via secret}"
|
||||
: "${ZOT_NAMESPACE:=zot}"
|
||||
: "${HTPASSWD_SECRET_NAME:=zot-htpasswd}"
|
||||
: "${DEFAULT_PASSWORD:=TempSsoPass!2025}"
|
||||
: "${UI_PROXY_USER:=zot-ui-proxy}"
|
||||
: "${UI_PROXY_PASSWORD:=TempSsoUiPass!2025}"
|
||||
|
||||
fetch_token() {
|
||||
curl -fsS -X POST \
|
||||
-d "grant_type=client_credentials" \
|
||||
-d "client_id=${KEYCLOAK_CLIENT_ID}" \
|
||||
-d "client_secret=${KEYCLOAK_CLIENT_SECRET}" \
|
||||
"${KEYCLOAK_URL}/realms/${KEYCLOAK_REALM}/protocol/openid-connect/token" \
|
||||
| jq -r '.access_token'
|
||||
}
|
||||
|
||||
pull_users() {
|
||||
local token="$1"
|
||||
curl -fsS -H "Authorization: Bearer ${token}" \
|
||||
"${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users?max=500" \
|
||||
| jq -r '.[] | select(.enabled == true) | select(.username | startswith("service-account-") | not) | .username'
|
||||
}
|
||||
|
||||
decode_secret_file() {
|
||||
local ns="$1" name="$2" key="$3" out="$4"
|
||||
if kubectl -n "${ns}" get secret "${name}" >/dev/null 2>&1; then
|
||||
kubectl -n "${ns}" get secret "${name}" -o "jsonpath={.data.${key}}" | base64 -d > "${out}" || true
|
||||
else
|
||||
: > "${out}"
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_htpasswd_line() {
|
||||
local user="$1" password="$2" file="$3"
|
||||
if ! grep -q "^${user}:" "${file}" 2>/dev/null; then
|
||||
htpasswd -nbB "${user}" "${password}" >> "${file}"
|
||||
echo "added user ${user} to htpasswd"
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
local token tmp existing
|
||||
tmp="$(mktemp)"
|
||||
existing="$(mktemp)"
|
||||
trap 'rm -f "${tmp}" "${existing}"' EXIT
|
||||
|
||||
decode_secret_file "${ZOT_NAMESPACE}" "${HTPASSWD_SECRET_NAME}" htpasswd "${existing}"
|
||||
cp "${existing}" "${tmp}"
|
||||
|
||||
ensure_htpasswd_line "${UI_PROXY_USER}" "${UI_PROXY_PASSWORD}" "${tmp}"
|
||||
|
||||
token="$(fetch_token)"
|
||||
readarray -t users < <(pull_users "${token}")
|
||||
for user in "${users[@]}"; do
|
||||
ensure_htpasswd_line "${user}" "${DEFAULT_PASSWORD}" "${tmp}"
|
||||
done
|
||||
|
||||
kubectl create secret generic "${HTPASSWD_SECRET_NAME}" \
|
||||
--namespace "${ZOT_NAMESPACE}" \
|
||||
--from-file=htpasswd="${tmp}" \
|
||||
--dry-run=client -o yaml | kubectl apply -f -
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@ -35,7 +35,7 @@ spec:
|
||||
values: ["rpi4"]
|
||||
containers:
|
||||
- name: monerod
|
||||
image: registry.bstein.dev/infra/monerod:0.18.4.1
|
||||
image: cli.registry.bstein.dev/infra/monerod:0.18.4.1
|
||||
command: ["/opt/monero/monerod"]
|
||||
args:
|
||||
- --data-dir=/data
|
||||
|
||||
@ -32,7 +32,7 @@ spec:
|
||||
values: ["rpi4"]
|
||||
containers:
|
||||
- name: monero-p2pool
|
||||
image: registry.bstein.dev/infra/monero-p2pool:4.9
|
||||
image: cli.registry.bstein.dev/infra/monero-p2pool:4.9
|
||||
imagePullPolicy: Always
|
||||
command: ["p2pool"]
|
||||
args:
|
||||
|
||||
@ -63,7 +63,7 @@ spec:
|
||||
--key "$CLIENT_ID" \
|
||||
--secret "$CLIENT_SECRET" \
|
||||
--auto-discover-url "$DISCOVERY_URL" \
|
||||
--scopes "openid profile email" \
|
||||
--scopes "openid profile email groups" \
|
||||
--required-claim-name "" \
|
||||
--required-claim-value "" \
|
||||
--group-claim-name groups \
|
||||
@ -77,7 +77,7 @@ spec:
|
||||
--key "$CLIENT_ID" \
|
||||
--secret "$CLIENT_SECRET" \
|
||||
--auto-discover-url "$DISCOVERY_URL" \
|
||||
--scopes "openid profile email" \
|
||||
--scopes "openid profile email groups" \
|
||||
--required-claim-name "" \
|
||||
--required-claim-value "" \
|
||||
--group-claim-name groups \
|
||||
|
||||
@ -52,7 +52,7 @@ spec:
|
||||
- name: zot-regcred
|
||||
initContainers:
|
||||
- name: mailu-http-listener
|
||||
image: registry.bstein.dev/sso/mailu-http-listener:0.1.0
|
||||
image: cli.registry.bstein.dev/sso/mailu-http-listener:0.1.0
|
||||
imagePullPolicy: IfNotPresent
|
||||
command: ["/bin/sh", "-c"]
|
||||
args:
|
||||
|
||||
@ -39,7 +39,7 @@ spec:
|
||||
- operator: Exists
|
||||
containers:
|
||||
- name: dcgm-exporter
|
||||
image: registry.bstein.dev/monitoring/dcgm-exporter:4.4.2-4.7.0-ubuntu22.04
|
||||
image: cli.registry.bstein.dev/monitoring/dcgm-exporter:4.4.2-4.7.0-ubuntu22.04
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- name: metrics
|
||||
|
||||
@ -6,7 +6,7 @@ metadata:
|
||||
namespace: sso
|
||||
spec:
|
||||
forwardAuth:
|
||||
address: http://oauth2-proxy.sso.svc.cluster.local:4180/oauth2/auth
|
||||
address: http://oauth2-proxy.sso.svc.cluster.local/oauth2/auth
|
||||
trustForwardHeader: true
|
||||
authResponseHeaders:
|
||||
- Authorization
|
||||
|
||||
@ -58,7 +58,7 @@ spec:
|
||||
|
||||
containers:
|
||||
- name: pegasus
|
||||
image: registry.bstein.dev/pegasus:1.2.32 # {"$imagepolicy": "jellyfin:pegasus"}
|
||||
image: cli.registry.bstein.dev/pegasus:1.2.32 # {"$imagepolicy": "jellyfin:pegasus"}
|
||||
imagePullPolicy: Always
|
||||
command: ["/pegasus"]
|
||||
env:
|
||||
|
||||
@ -5,7 +5,7 @@ metadata:
|
||||
name: pegasus
|
||||
namespace: jellyfin
|
||||
spec:
|
||||
image: registry.bstein.dev/pegasus
|
||||
image: cli.registry.bstein.dev/pegasus
|
||||
interval: 1m0s
|
||||
secretRef:
|
||||
name: zot-regcred
|
||||
|
||||
@ -26,14 +26,14 @@ data:
|
||||
"repositories": {
|
||||
"**": {
|
||||
"policies": [
|
||||
{ "users": ["bstein"], "actions": ["read", "create", "update", "delete"] }
|
||||
{ "users": ["bstein", "zot-ui-proxy"], "actions": ["read", "create", "update", "delete"] }
|
||||
],
|
||||
"defaultPolicy": [],
|
||||
"anonymousPolicy": []
|
||||
}
|
||||
},
|
||||
"adminPolicy": {
|
||||
"users": ["bstein"],
|
||||
"users": ["bstein", "zot-ui-proxy"],
|
||||
"actions": ["read", "create", "update", "delete"]
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,6 +35,9 @@ spec:
|
||||
image: ghcr.io/project-zot/zot-linux-arm64:v2.1.8
|
||||
imagePullPolicy: IfNotPresent
|
||||
args: ["serve", "/etc/zot/config.json"]
|
||||
env:
|
||||
- name: UI_PROXY_HTPASSWD
|
||||
value: "zot-ui-proxy:$2y$05$ctfbLo5KBoNA6pluLGGWde6TK8eOPnIH9u8x/IivAhcE/k0qCCR3y"
|
||||
ports:
|
||||
- { name: http, containerPort: 5000 }
|
||||
volumeMounts:
|
||||
@ -45,7 +48,6 @@ spec:
|
||||
- name: htpasswd
|
||||
mountPath: /etc/zot/htpasswd
|
||||
subPath: htpasswd
|
||||
readOnly: true
|
||||
- name: zot-data
|
||||
mountPath: /var/lib/registry
|
||||
readinessProbe:
|
||||
@ -60,13 +62,41 @@ spec:
|
||||
periodSeconds: 10
|
||||
resources:
|
||||
requests: { cpu: "50m", memory: "64Mi" }
|
||||
initContainers:
|
||||
- name: merge-htpasswd
|
||||
image: busybox:1.36
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
if [ -f /src/htpasswd ]; then
|
||||
cp /src/htpasswd /merged/htpasswd
|
||||
else
|
||||
touch /merged/htpasswd
|
||||
fi
|
||||
if [ -n "${UI_PROXY_HTPASSWD}" ]; then
|
||||
echo "${UI_PROXY_HTPASSWD}" >> /merged/htpasswd
|
||||
fi
|
||||
env:
|
||||
- name: UI_PROXY_HTPASSWD
|
||||
value: "zot-ui-proxy:$2y$05$ctfbLo5KBoNA6pluLGGWde6TK8eOPnIH9u8x/IivAhcE/k0qCCR3y"
|
||||
volumeMounts:
|
||||
- name: htpasswd-source
|
||||
mountPath: /src
|
||||
readOnly: true
|
||||
- name: htpasswd
|
||||
mountPath: /merged
|
||||
volumes:
|
||||
- name: cfg
|
||||
configMap:
|
||||
name: zot-config
|
||||
- name: htpasswd
|
||||
- name: htpasswd-source
|
||||
secret:
|
||||
secretName: zot-htpasswd
|
||||
optional: true
|
||||
- name: htpasswd
|
||||
emptyDir: {}
|
||||
- name: zot-data
|
||||
persistentVolumeClaim:
|
||||
claimName: zot-data
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: zot
|
||||
name: zot-cli
|
||||
namespace: zot
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt
|
||||
@ -12,10 +12,10 @@ metadata:
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
tls:
|
||||
- hosts: [ "registry.bstein.dev" ]
|
||||
secretName: registry-bstein-dev-tls
|
||||
- hosts: [ "cli.registry.bstein.dev" ]
|
||||
secretName: cli-registry-bstein-dev-tls
|
||||
rules:
|
||||
- host: registry.bstein.dev
|
||||
- host: cli.registry.bstein.dev
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
@ -25,3 +25,30 @@ spec:
|
||||
name: zot
|
||||
port:
|
||||
number: 5000
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: zot-ui
|
||||
namespace: zot
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
traefik.ingress.kubernetes.io/router.tls: "true"
|
||||
traefik.ingress.kubernetes.io/router.middlewares: zot-zot-ui-auth-header@kubernetescrd, zot-zot-resp-headers@kubernetescrd
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
tls:
|
||||
- hosts: [ "web.registry.bstein.dev" ]
|
||||
secretName: web-registry-bstein-dev-tls
|
||||
rules:
|
||||
- host: web.registry.bstein.dev
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: zot-oauth2-proxy
|
||||
port:
|
||||
number: 80
|
||||
|
||||
@ -7,5 +7,8 @@ resources:
|
||||
- deployment.yaml
|
||||
- configmap.yaml
|
||||
- service.yaml
|
||||
- oauth2-proxy-deployment.yaml
|
||||
- oauth2-proxy-service.yaml
|
||||
- ingress.yaml
|
||||
- middleware.yaml
|
||||
- middleware-ui.yaml
|
||||
|
||||
10
services/zot/middleware-ui.yaml
Normal file
10
services/zot/middleware-ui.yaml
Normal file
@ -0,0 +1,10 @@
|
||||
# services/zot/middleware-ui.yaml
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: zot-ui-auth-header
|
||||
namespace: zot
|
||||
spec:
|
||||
headers:
|
||||
customRequestHeaders:
|
||||
Authorization: "Basic em90LXVpLXByb3h5OlRlbXBTc29VaVBhc3MhMjAyNQ=="
|
||||
83
services/zot/oauth2-proxy-deployment.yaml
Normal file
83
services/zot/oauth2-proxy-deployment.yaml
Normal file
@ -0,0 +1,83 @@
|
||||
# services/zot/oauth2-proxy-deployment.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: zot-oauth2-proxy
|
||||
namespace: zot
|
||||
labels: { app: zot-oauth2-proxy }
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels: { app: zot-oauth2-proxy }
|
||||
template:
|
||||
metadata:
|
||||
labels: { app: zot-oauth2-proxy }
|
||||
spec:
|
||||
nodeSelector:
|
||||
node-role.kubernetes.io/worker: "true"
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- weight: 50
|
||||
preference:
|
||||
matchExpressions:
|
||||
- key: hardware
|
||||
operator: In
|
||||
values: ["rpi4","rpi5"]
|
||||
containers:
|
||||
- name: oauth2-proxy
|
||||
image: quay.io/oauth2-proxy/oauth2-proxy:v7.6.0
|
||||
imagePullPolicy: IfNotPresent
|
||||
args:
|
||||
- --provider=oidc
|
||||
- --redirect-url=https://web.registry.bstein.dev/oauth2/callback
|
||||
- --oidc-issuer-url=https://sso.bstein.dev/realms/atlas
|
||||
- --scope=openid profile email
|
||||
- --email-domain=*
|
||||
- --cookie-domain=web.registry.bstein.dev
|
||||
- --cookie-name=_zot_ui_oauth
|
||||
- --set-xauthrequest=true
|
||||
- --set-authorization-header=false
|
||||
- --pass-authorization-header=false
|
||||
- --pass-access-token=false
|
||||
- --cookie-secure=true
|
||||
- --cookie-samesite=lax
|
||||
- --cookie-refresh=20m
|
||||
- --cookie-expire=168h
|
||||
- --upstream=http://zot:5000
|
||||
- --http-address=0.0.0.0:4180
|
||||
- --skip-provider-button=true
|
||||
- --skip-jwt-bearer-tokens=true
|
||||
env:
|
||||
- name: OAUTH2_PROXY_CLIENT_ID
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: zot-oidc
|
||||
key: client_id
|
||||
- name: OAUTH2_PROXY_CLIENT_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: zot-oidc
|
||||
key: client_secret
|
||||
- name: OAUTH2_PROXY_COOKIE_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: zot-oidc
|
||||
key: client_secret
|
||||
ports:
|
||||
- containerPort: 4180
|
||||
name: http
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /ping
|
||||
port: 4180
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /ping
|
||||
port: 4180
|
||||
initialDelaySeconds: 20
|
||||
periodSeconds: 20
|
||||
resources:
|
||||
requests: { cpu: "25m", memory: "64Mi" }
|
||||
14
services/zot/oauth2-proxy-service.yaml
Normal file
14
services/zot/oauth2-proxy-service.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
# services/zot/oauth2-proxy-service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: zot-oauth2-proxy
|
||||
namespace: zot
|
||||
labels: { app: zot-oauth2-proxy }
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector: { app: zot-oauth2-proxy }
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: 4180
|
||||
Loading…
x
Reference in New Issue
Block a user