From d79bb52c4790238f7a5999e675dcf54523f840f5 Mon Sep 17 00:00:00 2001 From: devops Date: Sat, 18 Jul 2026 02:35:05 +0000 Subject: [PATCH] Update .gitea/workflows/build.yaml --- .gitea/workflows/build.yaml | 301 ++++++++++++++++++++++-------------- 1 file changed, 188 insertions(+), 113 deletions(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index 0442f2d..52df823 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -116,6 +116,7 @@ jobs: IMAGE_REPOSITORY: gitea.cruzcloud.net/devops/ecommerce-frontend NAMESPACE: ecommerce DEPLOY_TIMEOUT_SECONDS: '420' + KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }} K8S_SERVER: ${{ secrets.K8S_SERVER }} K8S_CA_B64: ${{ secrets.K8S_CA_B64 }} K8S_TOKEN: ${{ secrets.K8S_TOKEN }} @@ -134,133 +135,136 @@ jobs: echo "Timeout: ${DEPLOY_TIMEOUT_SECONDS} segundos" echo "==================================================" - for variable in K8S_SERVER K8S_CA_B64 K8S_TOKEN; do - if [ -z "${!variable:-}" ]; then - echo "ERROR: no está configurado el secret ${variable}." + normalize_base64() { + local value="$1" + value="$(printf '%s' "${value}" | tr -d '\r\n\t ')" + + if ! printf '%s' "${value}" | + grep -Eq '^[A-Za-z0-9+/]*={0,2}$'; then + return 1 + fi + + case $(( ${#value} % 4 )) in + 0) ;; + 2) value="${value}==" ;; + 3) value="${value}=" ;; + 1) return 1 ;; + esac + + printf '%s' "${value}" + } + + USE_ATOMIC_KUBECONFIG='false' + KUBE_CONFIG_DATA="$(printf '%s' "${KUBE_CONFIG_DATA:-}" | tr -d '\r\n\t ')" + + if [ -n "${KUBE_CONFIG_DATA}" ]; then + echo 'Usando secret atómico KUBE_CONFIG_DATA.' + + if ! KUBE_CONFIG_DATA="$(normalize_base64 "${KUBE_CONFIG_DATA}")"; then + echo 'ERROR: KUBE_CONFIG_DATA no contiene Base64 válido.' exit 1 fi - done - K8S_SERVER="$( - printf '%s' "${K8S_SERVER}" | - tr -d '\r\n\t ' - )" - - K8S_TOKEN="$( - printf '%s' "${K8S_TOKEN}" | - tr -d '\r\n' - )" - - K8S_CA_B64="$( - printf '%s' "${K8S_CA_B64}" | - tr -d '\r\n\t ' - )" - - K8S_TLS_SERVER_NAME="$( - printf '%s' "${K8S_TLS_SERVER_NAME:-}" | - tr -d '\r\n\t ' - )" - - if ! printf '%s' "${K8S_SERVER}" | - grep -Eq '^https://[^[:space:]]+:[0-9]+$'; then - echo "ERROR: K8S_SERVER no tiene un formato válido." - echo "Ejemplo: https://192.168.68.61:46421" - exit 1 - fi - - if printf '%s' "${K8S_SERVER}" | - grep -Eq '^https://(127\.0\.0\.1|localhost|0\.0\.0\.0)(:|/)'; then - echo "ERROR: K8S_SERVER usa una dirección no accesible desde el contenedor." - exit 1 - fi - - K8S_SERVER_HOST="$( - printf '%s' "${K8S_SERVER}" | - sed -E 's#^https://\[?([^]]+)\]?:[0-9]+$#\1#' - )" - - if [ -z "${K8S_TLS_SERVER_NAME}" ]; then - K8S_TLS_SERVER_NAME="${K8S_SERVER_HOST}" - fi - - if [ "${K8S_TLS_SERVER_NAME}" != "${K8S_SERVER_HOST}" ]; then - echo "ERROR: K8S_TLS_SERVER_NAME no coincide con el host de K8S_SERVER." - echo "Host de K8S_SERVER: ${K8S_SERVER_HOST}" - echo "Valor esperado del secret K8S_TLS_SERVER_NAME: ${K8S_SERVER_HOST}" - exit 1 - fi - - if ! printf '%s' "${K8S_CA_B64}" | - grep -Eq '^[A-Za-z0-9+/]*={0,2}$'; then - echo "ERROR: K8S_CA_B64 contiene caracteres que no pertenecen a Base64." - exit 1 - fi - - CA_REMAINDER=$(( ${#K8S_CA_B64} % 4 )) - - case "${CA_REMAINDER}" in - 0) - ;; - 2) - K8S_CA_B64="${K8S_CA_B64}==" - ;; - 3) - K8S_CA_B64="${K8S_CA_B64}=" - ;; - 1) - echo "ERROR: K8S_CA_B64 está truncado o dañado." + if ! printf '%s' "${KUBE_CONFIG_DATA}" | + base64 --decode > "${KUBECONFIG_PATH}" 2>/dev/null; then + echo 'ERROR: no se pudo decodificar KUBE_CONFIG_DATA.' exit 1 - ;; - esac + fi - if ! printf '%s' "${K8S_CA_B64}" | - base64 --decode > "${CA_PATH}" 2>/dev/null; then - echo "ERROR: no se pudo decodificar K8S_CA_B64." - exit 1 - fi + if ! grep -q '^kind:[[:space:]]*Config' "${KUBECONFIG_PATH}"; then + echo 'ERROR: KUBE_CONFIG_DATA no contiene un kubeconfig válido.' + exit 1 + fi - if ! grep -q 'BEGIN CERTIFICATE' "${CA_PATH}"; then - echo "ERROR: K8S_CA_B64 no contiene un certificado PEM." - exit 1 - fi + USE_ATOMIC_KUBECONFIG='true' + else + echo 'KUBE_CONFIG_DATA no está configurado; usando secrets K8S_* de compatibilidad.' - chmod 600 "${CA_PATH}" + for variable in K8S_SERVER K8S_CA_B64 K8S_TOKEN; do + if [ -z "${!variable:-}" ]; then + echo "ERROR: no está configurado el secret ${variable}." + exit 1 + fi + done - { - echo 'apiVersion: v1' - echo 'kind: Config' - echo 'clusters:' - echo '- name: lab-cluster' - echo ' cluster:' - echo " server: ${K8S_SERVER}" - echo " certificate-authority: ${CA_PATH}" + K8S_SERVER="$(printf '%s' "${K8S_SERVER}" | tr -d '\r\n\t ')" + K8S_TOKEN="$(printf '%s' "${K8S_TOKEN}" | tr -d '\r\n')" + K8S_CA_B64="$(printf '%s' "${K8S_CA_B64}" | tr -d '\r\n\t ')" + K8S_TLS_SERVER_NAME="$(printf '%s' "${K8S_TLS_SERVER_NAME:-}" | tr -d '\r\n\t ')" - if [ -n "${K8S_TLS_SERVER_NAME:-}" ]; then + if ! printf '%s' "${K8S_SERVER}" | + grep -Eq '^https://[^[:space:]]+:[0-9]+$'; then + echo 'ERROR: K8S_SERVER no tiene un formato válido.' + echo 'Ejemplo: https://192.168.68.61:46421' + exit 1 + fi + + if printf '%s' "${K8S_SERVER}" | + grep -Eq '^https://(127\.0\.0\.1|localhost|0\.0\.0\.0)(:|/)'; then + echo 'ERROR: K8S_SERVER usa una dirección no accesible desde el contenedor.' + exit 1 + fi + + K8S_SERVER_HOST="$( + printf '%s' "${K8S_SERVER}" | + sed -E 's#^https://\[?([^]]+)\]?:[0-9]+$#\1#' + )" + + if [ -z "${K8S_TLS_SERVER_NAME}" ]; then + K8S_TLS_SERVER_NAME="${K8S_SERVER_HOST}" + fi + + if [ "${K8S_TLS_SERVER_NAME}" != "${K8S_SERVER_HOST}" ]; then + echo 'ERROR: K8S_TLS_SERVER_NAME no coincide con el host de K8S_SERVER.' + echo "Host de K8S_SERVER: ${K8S_SERVER_HOST}" + echo "Valor esperado del secret K8S_TLS_SERVER_NAME: ${K8S_SERVER_HOST}" + exit 1 + fi + + if ! K8S_CA_B64="$(normalize_base64 "${K8S_CA_B64}")"; then + echo 'ERROR: K8S_CA_B64 no contiene Base64 válido.' + exit 1 + fi + + if ! printf '%s' "${K8S_CA_B64}" | + base64 --decode > "${CA_PATH}" 2>/dev/null; then + echo 'ERROR: no se pudo decodificar K8S_CA_B64.' + exit 1 + fi + + if ! grep -q 'BEGIN CERTIFICATE' "${CA_PATH}"; then + echo 'ERROR: K8S_CA_B64 no contiene un certificado PEM.' + exit 1 + fi + + chmod 600 "${CA_PATH}" + + { + echo 'apiVersion: v1' + echo 'kind: Config' + echo 'clusters:' + echo '- name: lab-cluster' + echo ' cluster:' + echo " server: ${K8S_SERVER}" + echo " certificate-authority: ${CA_PATH}" echo " tls-server-name: ${K8S_TLS_SERVER_NAME}" - fi - - echo 'users:' - echo '- name: gitea-postdeploy-validator' - echo ' user:' - echo " token: ${K8S_TOKEN}" - echo 'contexts:' - echo '- name: gitea-postdeploy-validator' - echo ' context:' - echo ' cluster: lab-cluster' - echo ' user: gitea-postdeploy-validator' - echo " namespace: ${NAMESPACE}" - echo 'current-context: gitea-postdeploy-validator' - } > "${KUBECONFIG_PATH}" + echo 'users:' + echo '- name: gitea-postdeploy-validator' + echo ' user:' + echo " token: ${K8S_TOKEN}" + echo 'contexts:' + echo '- name: gitea-postdeploy-validator' + echo ' context:' + echo ' cluster: lab-cluster' + echo ' user: gitea-postdeploy-validator' + echo " namespace: ${NAMESPACE}" + echo 'current-context: gitea-postdeploy-validator' + } > "${KUBECONFIG_PATH}" + fi chmod 600 "${KUBECONFIG_PATH}" export KUBECONFIG="${KUBECONFIG_PATH}" - echo "API Server configurado: ${K8S_SERVER}" - - if [ -n "${K8S_TLS_SERVER_NAME:-}" ]; then - echo "TLS server name configurado: ${K8S_TLS_SERVER_NAME}" - fi - if ! command -v kubectl >/dev/null 2>&1; then case "$(uname -m)" in x86_64) KUBECTL_ARCH='amd64' ;; @@ -288,6 +292,77 @@ jobs: kubectl version --client + EFFECTIVE_SERVER="$( + kubectl config view --raw --minify \ + -o jsonpath='{.clusters[0].cluster.server}' + )" + EFFECTIVE_TLS_NAME="$( + kubectl config view --raw --minify \ + -o jsonpath='{.clusters[0].cluster.tls-server-name}' + )" + EFFECTIVE_CA_B64="$( + kubectl config view --raw --minify \ + -o jsonpath='{.clusters[0].cluster.certificate-authority-data}' + )" + EFFECTIVE_TOKEN="$( + kubectl config view --raw --minify \ + -o jsonpath='{.users[0].user.token}' + )" + + if [ -z "${EFFECTIVE_TOKEN}" ]; then + echo 'ERROR: el kubeconfig no contiene un token de autenticación.' + exit 1 + fi + + echo "API Server efectivo: ${EFFECTIVE_SERVER}" + echo "TLS server name efectivo: ${EFFECTIVE_TLS_NAME:-}" + + if [ -n "${EFFECTIVE_CA_B64}" ]; then + printf '%s' "${EFFECTIVE_CA_B64}" | base64 --decode > "${CA_PATH}" + fi + + if [ ! -s "${CA_PATH}" ] || ! grep -q 'BEGIN CERTIFICATE' "${CA_PATH}"; then + echo 'ERROR: el kubeconfig no contiene una CA PEM válida.' + exit 1 + fi + + if command -v openssl >/dev/null 2>&1; then + echo 'Huella SHA256 de la CA usada por el workflow:' + openssl x509 -in "${CA_PATH}" -noout -fingerprint -sha256 + fi + + TOKEN_METADATA="$( + printf '%s' "${EFFECTIVE_TOKEN}" | + python3 -c ' +import base64 +import datetime +import json +import sys + +token = sys.stdin.read().strip() +parts = token.split(".") +if len(parts) != 3: + print("formato=no-jwt") + raise SystemExit(0) + +payload = parts[1] + "=" * (-len(parts[1]) % 4) +data = json.loads(base64.urlsafe_b64decode(payload.encode())) + +subject = data.get("sub", "desconocido") +expiration = data.get("exp") + +if expiration is None: + print(f"subject={subject}; expiracion=sin-exp") +else: + when = datetime.datetime.fromtimestamp( + expiration, datetime.timezone.utc + ).isoformat() + print(f"subject={subject}; expiracion={when}") +' + )" + + echo "Identidad del token: ${TOKEN_METADATA}" + if ! kubectl get deployments \ --namespace "${NAMESPACE}" \ --request-timeout='15s' >/dev/null; then