feat(easypanel): agregar instrucciones y validaciones para secretos en EasyPanel, incluyendo generación de contraseñas seguras y actualización de archivos de configuración
Some checks are pending
Container / meta (analyzer) (push) Waiting to run
Container / meta (api) (push) Waiting to run
Container / meta (legacy) (push) Waiting to run
Container / meta (nginx) (push) Waiting to run
Container / meta (playout) (push) Waiting to run
Container / meta (worker) (push) Waiting to run
Container / build (push) Blocked by required conditions
Project / pre-commit (push) Waiting to run
Project / test-tools (push) Waiting to run
Release-Please / release-please (push) Waiting to run
Some checks are pending
Container / meta (analyzer) (push) Waiting to run
Container / meta (api) (push) Waiting to run
Container / meta (legacy) (push) Waiting to run
Container / meta (nginx) (push) Waiting to run
Container / meta (playout) (push) Waiting to run
Container / meta (worker) (push) Waiting to run
Container / build (push) Blocked by required conditions
Project / pre-commit (push) Waiting to run
Project / test-tools (push) Waiting to run
Release-Please / release-please (push) Waiting to run
This commit is contained in:
parent
ac2806e2f6
commit
83724ddc26
@ -12,8 +12,32 @@ Requisitos/Notas:
|
||||
- `update.sh` usa utilidades estándar de shell y `perl` (normalmente disponible en sistemas UNIX). Si tu entorno no tiene `perl`, el script puede adaptarse a `python`.
|
||||
- Revisa y completa las variables de entorno requeridas por `docker-compose.easypanel.yml` (p. ej. secretos: `POSTGRES_PASSWORD`, `RABBITMQ_DEFAULT_PASS`, etc.) desde la UI de EasyPanel.
|
||||
|
||||
|
||||
Siguientes pasos recomendados:
|
||||
- (Opcional) Añadir un `update.js` si prefieres implementar clonación/actualizaciones con las utilidades de EasyPanel (no obligatorio).
|
||||
- (Opcional) Añadir un `update.js` si prefieres implementar clonación/actualizaciones con las utilidades de EasyPanel (no obligatorio). Este repo incluye `easypanel/update.js` que realiza la misma función que `update.sh` usando Node.js.
|
||||
- (Opcional) Añadir un `update.js` si prefieres implementar clonación/actualizaciones con las utilidades de EasyPanel (no obligatorio). Este repo incluye `easypanel/update.js` que realiza la misma función que `update.sh` usando Node.js.
|
||||
- (Opcional) Añadir un archivo `README.md` con la lista de variables que EasyPanel debe exponer en la interfaz de instalación.
|
||||
|
||||
## Instrucciones específicas para EasyPanel
|
||||
|
||||
1. En la UI de EasyPanel, crea un nuevo servicio apuntando a este repositorio (o sube un ZIP).
|
||||
2. En la sección de variables/Environment, pega el contenido de `easypanel/service.env` (o sube el archivo).
|
||||
- Marca como `secret` las variables: `POSTGRES_PASSWORD`, `RABBITMQ_DEFAULT_PASS`, `ICECAST_SOURCE_PASSWORD`, `ICECAST_ADMIN_PASSWORD`, `ICECAST_RELAY_PASSWORD`.
|
||||
3. EasyPanel ejecutará `easypanel/update.sh` por convención; el script intenta ejecutar `easypanel/update.js` si Node.js está disponible, y usa un fallback en shell si no lo está.
|
||||
4. Después de ejecutar el script, EasyPanel usará `easypanel/code/docker-compose.yml` para desplegar el stack. Revisa ese archivo antes de confirmar el despliegue.
|
||||
5. Si necesitas exponer puertos manualmente en EasyPanel, revisa `docker-compose.easypanel.yml` y ajusta `NGINX_PORT`, `ICECAST_PORT`, etc., o deja que EasyPanel gestione el ruteo.
|
||||
|
||||
Consejos:
|
||||
- Asegúrate de que `LIBRETIME_GENERAL_PUBLIC_URL` apunte a tu dominio público antes de desplegar.
|
||||
- Para entornos de producción, establece `LIBRETIME_DEBUG=false` y usa contraseñas fuertes en los secrets.
|
||||
|
||||
Generar contraseñas seguras
|
||||
---------------------------
|
||||
Puedes usar el script `easypanel/generate_secrets.sh` para generar contraseñas seguras y sus hashes SHA-256. El script imprime un bloque `.env` listo para pegar en la UI de EasyPanel. Ejecútalo localmente y no subas las contraseñas al repositorio.
|
||||
|
||||
Ejemplo:
|
||||
```bash
|
||||
cd easypanel
|
||||
chmod +x generate_secrets.sh
|
||||
./generate_secrets.sh
|
||||
```
|
||||
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
version: "3.9"
|
||||
|
||||
# LibreTime Docker Compose (EasyPanel-ready)
|
||||
# Esta versión está pensada para usarse desde un repositorio en EasyPanel.
|
||||
@ -16,7 +15,7 @@ services:
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: ${POSTGRES_USER:-libretime}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-libretime}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-Jz/XxRUodVl2g0HE59DszTBJVY8Sdmv7}
|
||||
POSTGRES_DB: ${POSTGRES_DB:-libretime}
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
@ -33,7 +32,7 @@ services:
|
||||
environment:
|
||||
RABBITMQ_DEFAULT_VHOST: ${RABBITMQ_DEFAULT_VHOST:-/libretime}
|
||||
RABBITMQ_DEFAULT_USER: ${RABBITMQ_DEFAULT_USER:-libretime}
|
||||
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS}
|
||||
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS:-Bn321PQSRXanvmZlppuulVCB0ShN5Dz2}
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "rabbitmq-diagnostics check_port_connectivity"]
|
||||
interval: 30s
|
||||
@ -97,9 +96,9 @@ services:
|
||||
image: ghcr.io/libretime/icecast:2.4.4
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
ICECAST_SOURCE_PASSWORD: ${ICECAST_SOURCE_PASSWORD:-hackme}
|
||||
ICECAST_ADMIN_PASSWORD: ${ICECAST_ADMIN_PASSWORD:-hackme}
|
||||
ICECAST_RELAY_PASSWORD: ${ICECAST_RELAY_PASSWORD:-hackme}
|
||||
ICECAST_SOURCE_PASSWORD: ${ICECAST_SOURCE_PASSWORD:-dna1g1GcaaHakSN6C9X7rcPRpIIc/jV2}
|
||||
ICECAST_ADMIN_PASSWORD: ${ICECAST_ADMIN_PASSWORD:-BLoYLPlUXfmkxrsvGF7LP0TtVtuKNuzJ}
|
||||
ICECAST_RELAY_PASSWORD: ${ICECAST_RELAY_PASSWORD:-jYzhEjwdiJlTk30QOYHum6UE61FHo+sd}
|
||||
ICECAST_ADMIN_USER: ${ICECAST_ADMIN_USER:-admin}
|
||||
ICECAST_HOSTNAME: ${ICECAST_HOSTNAME:-localhost}
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
},
|
||||
"update_script": "easypanel/update.js",
|
||||
"compose_path": "easypanel/code/docker-compose.yml",
|
||||
"env_file": "easypanel/service.env",
|
||||
"services": {
|
||||
"notes": "El archivo compose generado tendrá los servicios principales: postgres, rabbitmq, api, legacy, nginx, icecast, playout, liquidsoap, worker, analyzer y config-generator."
|
||||
},
|
||||
@ -120,4 +121,6 @@
|
||||
],
|
||||
"notes": "Después de clonar, EasyPanel debe ejecutar el `update_script` y usar el compose generado en `compose_path`. Asegúrate de proveer los secretos marcados como `secret: true` en la UI de EasyPanel.",
|
||||
"last_updated": "2025-10-01"
|
||||
,
|
||||
"install_instructions": "1) Copia el contenido de easypanel/service.env en la sección Environment/Secrets del servicio en EasyPanel.\n2) Marca como secreto las variables que contienen contraseñas (POSTGRES_PASSWORD, RABBITMQ_DEFAULT_PASS, ICECAST_*_PASSWORD).\n3) EasyPanel ejecutará el script indicado en 'update_script' (easypanel/update.js o update.sh) para generar el compose final en easypanel/code/docker-compose.yml.\n4) Revisa el compose generado y despliega el servicio."
|
||||
}
|
||||
|
||||
63
easypanel/generate_secrets.sh
Normal file
63
easypanel/generate_secrets.sh
Normal file
@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# genera contraseñas seguras y muestra sus hashes SHA-256
|
||||
# Uso: ./generate_secrets.sh
|
||||
# Salida: muestra las contraseñas (plaintext) y sus hashes; imprime un bloque .env listo para pegar en EasyPanel
|
||||
|
||||
rand_pass() {
|
||||
# 24 bytes -> 32 chars base64 approx
|
||||
openssl rand -base64 24
|
||||
}
|
||||
|
||||
sha256() {
|
||||
if command -v openssl >/dev/null 2>&1; then
|
||||
printf "%s" "$1" | openssl dgst -sha256 -r | awk '{print $1}'
|
||||
else
|
||||
# fallback to python
|
||||
python3 - <<PY
|
||||
import hashlib, sys
|
||||
print(hashlib.sha256(sys.stdin.read().encode()).hexdigest())
|
||||
PY
|
||||
fi
|
||||
}
|
||||
|
||||
echo "Generando contraseñas seguras...\n"
|
||||
|
||||
POSTGRES_PASSWORD=$(rand_pass)
|
||||
RABBITMQ_DEFAULT_PASS=$(rand_pass)
|
||||
ICECAST_SOURCE_PASSWORD=$(rand_pass)
|
||||
ICECAST_ADMIN_PASSWORD=$(rand_pass)
|
||||
ICECAST_RELAY_PASSWORD=$(rand_pass)
|
||||
|
||||
# Mostrar en pantalla con hashes
|
||||
cat <<EOF
|
||||
POSTGRES_PASSWORD: $POSTGRES_PASSWORD
|
||||
POSTGRES_PASSWORD.sha256: $(sha256 "$POSTGRES_PASSWORD")
|
||||
|
||||
RABBITMQ_DEFAULT_PASS: $RABBITMQ_DEFAULT_PASS
|
||||
RABBITMQ_DEFAULT_PASS.sha256: $(sha256 "$RABBITMQ_DEFAULT_PASS")
|
||||
|
||||
ICECAST_SOURCE_PASSWORD: $ICECAST_SOURCE_PASSWORD
|
||||
ICECAST_SOURCE_PASSWORD.sha256: $(sha256 "$ICECAST_SOURCE_PASSWORD")
|
||||
|
||||
ICECAST_ADMIN_PASSWORD: $ICECAST_ADMIN_PASSWORD
|
||||
ICECAST_ADMIN_PASSWORD.sha256: $(sha256 "$ICECAST_ADMIN_PASSWORD")
|
||||
|
||||
ICECAST_RELAY_PASSWORD: $ICECAST_RELAY_PASSWORD
|
||||
ICECAST_RELAY_PASSWORD.sha256: $(sha256 "$ICECAST_RELAY_PASSWORD")
|
||||
|
||||
# Bloque .env para pegar en EasyPanel (REEMPLAZA/NO COMMIT)
|
||||
cat <<ENDF
|
||||
|
||||
# ------------------ EasyPanel .env snippet (DO NOT COMMIT) ------------------
|
||||
POSTGRES_PASSWORD=$POSTGRES_PASSWORD
|
||||
RABBITMQ_DEFAULT_PASS=$RABBITMQ_DEFAULT_PASS
|
||||
ICECAST_SOURCE_PASSWORD=$ICECAST_SOURCE_PASSWORD
|
||||
ICECAST_ADMIN_PASSWORD=$ICECAST_ADMIN_PASSWORD
|
||||
ICECAST_RELAY_PASSWORD=$ICECAST_RELAY_PASSWORD
|
||||
# ---------------------------------------------------------------------------
|
||||
ENDF
|
||||
EOF
|
||||
|
||||
printf "\nAVISO: No dejes estas contraseñas en el repositorio. Copia y pégalas en la UI de EasyPanel y marca las variables como SECRET.\n"
|
||||
59
easypanel/service.env
Normal file
59
easypanel/service.env
Normal file
@ -0,0 +1,59 @@
|
||||
# Archivo de variables para EasyPanel (alineado con docker-compose.easypanel.yml)
|
||||
# Copia/pega este bloque en la sección Environment/Secrets del servicio en EasyPanel.
|
||||
# Marca como SECRET las variables indicadas (POSTGRES_PASSWORD, RABBITMQ_DEFAULT_PASS, ICECAST_*_PASSWORD, etc.).
|
||||
|
||||
# ------------------ General ------------------
|
||||
# Versión de las imágenes de LibreTime (usa la etiqueta de la imagen)
|
||||
LIBRETIME_VERSION=4.5
|
||||
# URL pública de la instalación (REEMPLAZA por tu dominio)
|
||||
LIBRETIME_GENERAL_PUBLIC_URL=https://tu-dominio.example
|
||||
# Habilitar debug (false en producción)
|
||||
LIBRETIME_DEBUG=false
|
||||
# Claves opcionales para la API/secretos internos (opcional)
|
||||
LIBRETIME_API_KEY=
|
||||
LIBRETIME_SECRET_KEY=
|
||||
|
||||
# ------------------ Postgres ------------------
|
||||
POSTGRES_USER=libretime
|
||||
# SECRET (REQUIRED): contraseña de Postgres
|
||||
POSTGRES_PASSWORD=Jz/XxRUodVl2g0HE59DszTBJVY8Sdmv7
|
||||
POSTGRES_DB=libretime
|
||||
# Opcional: host/puerto si los cambias
|
||||
POSTGRES_HOST=postgres
|
||||
POSTGRES_PORT=5432
|
||||
|
||||
# ------------------ RabbitMQ ------------------
|
||||
RABBITMQ_DEFAULT_VHOST=/libretime
|
||||
RABBITMQ_DEFAULT_USER=libretime
|
||||
# SECRET (REQUIRED): contraseña de RabbitMQ
|
||||
RABBITMQ_DEFAULT_PASS=Bn321PQSRXanvmZlppuulVCB0ShN5Dz2
|
||||
RABBITMQ_HOST=rabbitmq
|
||||
RABBITMQ_PORT=5672
|
||||
|
||||
# ------------------ Nginx / puertos ------------------
|
||||
# EasyPanel suele gestionar ruteo; ajusta si es necesario
|
||||
NGINX_PORT=8080
|
||||
NGINX_WORKER_PROCESSES=auto
|
||||
|
||||
# ------------------ Icecast ------------------
|
||||
ICECAST_PORT=8000
|
||||
# SECRET: contraseñas de Icecast (marcar como secret)
|
||||
ICECAST_SOURCE_PASSWORD=dna1g1GcaaHakSN6C9X7rcPRpIIc/jV2
|
||||
ICECAST_ADMIN_PASSWORD=BLoYLPlUXfmkxrsvGF7LP0TtVtuKNuzJ
|
||||
ICECAST_RELAY_PASSWORD=jYzhEjwdiJlTk30QOYHum6UE61FHo+sd
|
||||
ICECAST_ADMIN_USER=admin
|
||||
ICECAST_HOSTNAME=stream.tu-dominio.example
|
||||
|
||||
# ------------------ Liquidsoap ------------------
|
||||
LIQUIDSOAP_HARBOR_PORT=8001
|
||||
LIQUIDSOAP_TELNET_PORT=8002
|
||||
|
||||
# ------------------ Opcionales / Compatibilidad ------------------
|
||||
# Estas variables las usa el generador de config; es seguro incluirlas aquí
|
||||
RABBITMQ_HOST=${RABBITMQ_HOST:-rabbitmq}
|
||||
RABBITMQ_PORT=${RABBITMQ_PORT:-5672}
|
||||
POSTGRES_HOST=${POSTGRES_HOST:-postgres}
|
||||
POSTGRES_PORT=${POSTGRES_PORT:-5432}
|
||||
|
||||
# FIN: reemplaza los valores marcados con 'CAMBIA...' por secretos reales y
|
||||
# marca las variables sensibles como SECRET en la UI de EasyPanel antes del deploy.
|
||||
@ -12,9 +12,53 @@ const SRC = path.resolve(__dirname, '../docker-compose.easypanel.yml');
|
||||
const DEST_DIR = path.resolve(__dirname, './code');
|
||||
const DEST = path.join(DEST_DIR, 'docker-compose.yml');
|
||||
|
||||
// Variables secret requeridas para evitar despliegues con valores vacíos
|
||||
const REQUIRED_SECRETS = ['POSTGRES_PASSWORD', 'RABBITMQ_DEFAULT_PASS'];
|
||||
|
||||
function parseEnvFile(filePath) {
|
||||
const out = {};
|
||||
if (!fs.existsSync(filePath)) return out;
|
||||
const raw = fs.readFileSync(filePath, 'utf8');
|
||||
raw.split(/\n/).forEach(line => {
|
||||
line = line.trim();
|
||||
if (!line || line.startsWith('#')) return;
|
||||
const m = line.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
|
||||
if (m) {
|
||||
let val = m[2];
|
||||
// strip optional quotes
|
||||
if ((val.startsWith("\'") && val.endsWith("\'")) || (val.startsWith('"') && val.endsWith('"'))) {
|
||||
val = val.slice(1, -1);
|
||||
}
|
||||
out[m[1]] = val;
|
||||
}
|
||||
});
|
||||
return out;
|
||||
}
|
||||
|
||||
function validateRequiredSecrets() {
|
||||
const envFilePath = path.join(__dirname, 'service.env');
|
||||
const fileEnv = parseEnvFile(envFilePath);
|
||||
const missing = [];
|
||||
REQUIRED_SECRETS.forEach(k => {
|
||||
const v = (process.env[k] || '').trim() || (fileEnv[k] || '').trim();
|
||||
// treat obvious placeholders as missing
|
||||
if (!v || /^\s*(CAMBIA|pon_aqui|tu_|CHANGE|REPLACE)/i.test(v)) {
|
||||
missing.push(k);
|
||||
}
|
||||
});
|
||||
if (missing.length) {
|
||||
console.error('ERROR: faltan valores seguros para las siguientes variables:', missing.join(', '));
|
||||
console.error('Asegúrate de definirlas en la sección Environment/Secrets de EasyPanel o en', envFilePath);
|
||||
console.error('Variables requeridas:', REQUIRED_SECRETS.join(', '));
|
||||
process.exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
function removeContainerNamesAndPorts(content) {
|
||||
// Eliminar container_name: <valor> líneas
|
||||
content = content.replace(/^[ \t]*container_name:[^\n]*\n/gm, '');
|
||||
// Eliminar version: ... para evitar advertencias en EasyPanel
|
||||
content = content.replace(/^[ \t]*version:[^\n]*\n/gm, '');
|
||||
// Eliminar bloques de ports:
|
||||
// ports:\n - "..."\n - ...
|
||||
content = content.replace(/^[ \t]*ports:[^\n]*\n(?:^[ \t]+-.*\n)*/gim, '');
|
||||
@ -22,6 +66,8 @@ function removeContainerNamesAndPorts(content) {
|
||||
}
|
||||
|
||||
async function main() {
|
||||
// Validar secretos antes de generar el compose final
|
||||
validateRequiredSecrets();
|
||||
if (!fs.existsSync(SRC)) {
|
||||
console.error('ERROR: no se encuentra', SRC);
|
||||
process.exit(2);
|
||||
|
||||
@ -10,6 +10,39 @@ SRC="$BASE_DIR/../docker-compose.easypanel.yml"
|
||||
DEST_DIR="$BASE_DIR/code"
|
||||
DEST="$DEST_DIR/docker-compose.yml"
|
||||
|
||||
REQUIRED_SECRETS=(POSTGRES_PASSWORD RABBITMQ_DEFAULT_PASS)
|
||||
|
||||
function read_env_file() {
|
||||
local f="$BASE_DIR/service.env"
|
||||
if [ ! -f "$f" ]; then
|
||||
return
|
||||
fi
|
||||
# shellcheck disable=SC1090
|
||||
# do not source to avoid side effects; parse manually
|
||||
grep -E '^[A-Za-z_][A-Za-z0-9_]*=' "$f" | sed "s/^[^=]*=//" >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
function check_required_secrets() {
|
||||
local missing=()
|
||||
for key in "${REQUIRED_SECRETS[@]}"; do
|
||||
# prefer environment then service.env
|
||||
val="${!key:-}"
|
||||
if [ -z "$val" ] && [ -f "$BASE_DIR/service.env" ]; then
|
||||
val=$(grep -E "^${key}=" "$BASE_DIR/service.env" | head -n1 | sed -E "s/^${key}=//")
|
||||
fi
|
||||
if [ -z "$val" ] || echo "$val" | grep -Eiq '^(CAMBIA|pon_aqui|tu_|CHANGE|REPLACE)'; then
|
||||
missing+=("$key")
|
||||
fi
|
||||
done
|
||||
if [ ${#missing[@]} -ne 0 ]; then
|
||||
echo "ERROR: faltan valores seguros para las siguientes variables: ${missing[*]}"
|
||||
echo "Definelas en la sección Environment/Secrets de EasyPanel o en $BASE_DIR/service.env"
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
check_required_secrets
|
||||
|
||||
if command -v node >/dev/null 2>&1 && [ -f "$BASE_DIR/update.js" ]; then
|
||||
echo "Node.js detectado, ejecutando easypanel/update.js"
|
||||
# Ejecutar con node (permitirá usar la versión JS en lugar del sed/perl)
|
||||
@ -35,11 +68,14 @@ cp "$SRC" "$DEST"
|
||||
if command -v perl >/dev/null 2>&1; then
|
||||
perl -0777 -pe "s/^[ \t]*container_name:[^\n]*\n//mg" -i "$DEST"
|
||||
perl -0777 -pe "s/^[ \t]*ports:[^\n]*\n(?:^[ \t]+-.*\n)*//mg" -i "$DEST"
|
||||
perl -0777 -pe "s/^[ \t]*version:[^\n]*\n//mg" -i "$DEST"
|
||||
else
|
||||
# Fallback con awk/sed si perl no está disponible
|
||||
# Elimina líneas 'container_name:' y bloques 'ports:' simples
|
||||
sed -E '/^[ \t]*container_name:/d' -i "" "$DEST" 2>/dev/null || sed -E '/^[ \t]*container_name:/d' -i '$DEST'
|
||||
awk 'BEGIN{skip=0} /^[ \t]*ports:[ \t]*$/ {skip=1; next} /^[ \t]*[^ \t]/ { if(skip){skip=0} } { if(!skip) print $0 }' "$DEST" > "$DEST.tmp" && mv "$DEST.tmp" "$DEST"
|
||||
# Elimina 'version:' si queda
|
||||
sed -E '/^[ \t]*version:/d' -i "" "$DEST" 2>/dev/null || sed -E '/^[ \t]*version:/d' -i '$DEST'
|
||||
fi
|
||||
|
||||
echo "Preparado $DEST para EasyPanel. Revisa variables de entorno en el README y súbelas en la UI de EasyPanel."
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user