¿Cómo ínstalo Redis usando contenedores (ea-podman) en cPanel?

Redis es un potente almacén de estructuras de datos en memoria, utilizado como base de datos, caché y agente de mensajes. Gracias a la herramienta ea-podman de cPanel, puedes instalar y gestionar Redis de forma aislada y segura dentro de contenedores. 📦

Esta guía te mostrará cómo instalar Redis, incluyendo la solución a un problema común en servidores con CloudLinux y CageFS.


1. Instalación Estándar de Podman y Redis

Estos son los pasos para un servidor cPanel estándar. Necesitarás acceso root para los primeros comandos.

Paso 1: Instalar Podman en el servidor (como root)

Primero, instala los repositorios y el paquete de ea-podman a través de la terminal de tu servidor.

dnf install -y ea-podman-repo
dnf install -y ea-podman

Paso 2: Habilitar “linger” para el usuario de cPanel:

Esto permite que los servicios del usuario se ejecuten incluso cuando no ha iniciado sesión. Reemplaza <username> con el nombre de usuario de cPanel.

loginctl enable-linger <username>

Paso 3: Instalar Redis (como usuario de cPanel)

Ahora, inicia sesión como el usuario de cPanel que necesita Redis y ejecuta el siguiente script:

/usr/local/cpanel/scripts/ea-podman install ea-redis62

En un sistema estándar, esto completará la instalación. Sin embargo, si tu servidor utiliza CloudLinux con CageFS, es muy probable que encuentres un error.


2. Solución para Servidores CloudLinux con CageFS

Si al ejecutar el comando de instalación como usuario de cPanel ves el siguiente error, significa que CageFS está impidiendo que el usuario acceda a los binarios necesarios.

El Error

[databyte@hercules ~]$ /usr/local/cpanel/scripts/ea-podman install ea-redis62
Adding a ea-redis62 instance for “databyte” …
… done!
Can't exec "podman": No such file or directory at /opt/cpanel/ea-podman/lib/ea_podman/util.pm line 90.
Failed to create container
[databyte@hercules ~]$

La Solución (como root)

Para solucionarlo, debes configurar CageFS para que permita al usuario ejecutar los comandos de podman y systemctl de forma segura. Sigue estos pasos como usuario root.

1. Crear un wrapper para systemctl:

Este script es necesario porque el entorno de CageFS no siempre propaga las variables de entorno requeridas.

Crea el archivo /usr/local/bin/systemctl con el siguiente contenido:

#!/usr/bin/env bash
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus
exec /usr/bin/systemctl "$@"

Y dale permisos de ejecución:

chmod a+x /usr/local/bin/systemctl

2. Configurar los comandos proxy de CageFS:

Crea el archivo /etc/cagefs/ea-podman.proxy.commands e incluye estas líneas para decirle a CageFS qué binarios puede ejecutar el usuario fuera de su jaula:

PODMAN=/usr/bin/podman
SYSTEMCTL=/usr/local/bin/systemctl

3. Añadir herramientas de cPanel a CageFS:

cagefsctl --addrpm ea-cpanel-tools

4. Crear la configuración de CageFS para Podman:

Crea el archivo /etc/cagefs/conf.d/ea-podman.cnf y añade las rutas a los binarios que permitiste en el paso 2.

[ea-podman]
paths=/usr/bin/podman, /usr/local/bin/systemctl

5. Actualizar CageFS:

Aplica todos los cambios ejecutando:

cagefsctl --force-update

3. (Opcional) Refuerzo de Seguridad para CloudLinux

Para un entorno más seguro, puedes usar un wrapper personalizado que valide los comandos que el usuario puede ejecutar con systemctl.

1. Modifica el archivo de comandos proxy:

Cambia el contenido de /etc/cagefs/ea-podman.proxy.commands por el siguiente:

PODMAN=/usr/bin/podman
SYSTEMCTL:cagefs.proxy.systemctl=/usr/local/bin/systemctl

2. Crea el script de wrapper seguro:

Crea el archivo /usr/share/cagefs/safeprograms/cagefs.proxy.systemctl con este contenido:

#!/bin/bash

# Main script logic
if [[ $EUID -eq 0 ]]; then
    echo 'Cannot be run as root'
    exit 1
fi

# Function to check if the service corresponds to a running container
is_container() {
    local container_name=${1#container-}
    container_name=${container_name%.service}
    podman ps -a --format "{{.Names}}" | grep -q "^${container_name}$"
}

# Function to check for the --user flag and trimming it
validate_params() {
    echo "$@"
    local user_flag_found=0
    local remaining_args=()
    for arg in "$@"; do
        if [[ "$arg" == "--user" ]]; then
            user_flag_found=1
        else
            remaining_args+=("$arg")
        fi
    done
    if [[ $user_flag_found -ne 1 ]]; then
        echo "Error: Script must be called with the --user flag."
        exit 1
    fi

    local action=${remaining_args[0]}

    # Check if the action is valid
    if [[ "$action" != "start" && "$action" != "stop" && "$action" != "restart" && "$action" != "enable" && "$action" != "disable" && "$action" == "daemon-reload" && "$action" == "reset-failed" ]]; then
        echo "Error: Action must be 'start', 'stop', 'restart', 'enable', 'disable', 'daemon-reload' and 'reset-failed'."
        exit 1
    fi

    if [[ "$action" != "daemon-reload" &&  "$action" != "reset-failed" ]]; then
        local service_name=${remaining_args[1]}
        # Check if the service corresponds to a running container
        if ! is_container "$service_name"; then
            echo "Error: No container corresponds to the provided service name."
            exit 1
        fi
    fi

}

validate_params "$@"

USR=`/usr/bin/whoami`
TOKEN=`/bin/cat /var/.cagefs/.cagefs.token`
# It's user's tmp directory and write to it is secure procedure
# because this script is running only under usual user
PIDFILE="/tmp/.cagefs.proxy.$$"
USER_INTERRUPT=13
CWD=`pwd`

ctrl_c_handler() {
    if [[ -f "$PIDFILE" ]]; then
        pid=`/bin/cat $PIDFILE`
        /bin/rm -f $PIDFILE > /dev/null 2>&1
        /bin/kill -s SIGINT "$pid" > /dev/null 2>&1
    fi
    exit $USER_INTERRUPT
}

if [[ -e /var/.cagefs/origin ]]; then
    ORIGIN=`/bin/cat /var/.cagefs/origin`
    REMOTE="/usr/bin/ssh -F /etc/ssh/cagefs-rexec_config $USR@$ORIGIN"
    $REMOTE CAGEFS_TOKEN="$TOKEN" /usr/sbin/proxyexec -c cagefs.sock "$USR" "$CWD" ALIAS $$ "$@"
    RETVAL=$?
else
    trap 'ctrl_c_handler' 2
    CAGEFS_TOKEN="$TOKEN" /usr/sbin/proxyexec -c cagefs.sock "$USR" "$CWD" ALIAS $$ "$@"
    RETVAL=$?
    /bin/rm -f $PIDFILE > /dev/null 2>&1
fi

exit $RETVAL

3. Actualiza CageFS nuevamente:

cagefsctl --force-update

4. Verificación y Conexión

Una vez aplicados los cambios, puedes volver a intentar la instalación como el usuario de cPanel.

Instalación Exitosa

Ahora, el comando debería funcionar y mostrar una salida similar a esta:

[databyte@hercules ~]$ /usr/local/cpanel/scripts/ea-podman install ea-redis62
Adding a ea-redis62 instance for “databyte” …
… done!
41a2fa35f9faf939eeabb17699a5068518d53767b6701145e6fd8154afb4fd30
...
Done, installed: ea-redis62.databyte.03

Verificar Contenedores Activos

Puedes listar los contenedores activos para tu usuario con:

/usr/local/cpanel/scripts/ea-podman containers

La salida te confirmará que el contenedor de Redis está en ejecución:

{
  "ea-redis62.databyte.03" : {
      "container_name" : "ea-redis62.databyte.03",
      "image" : "redis:6.2.19",
      "pkg" : "ea-redis62",
      "pkg_version" : "6.2.19-1",
      "user" : "databyte"
  }
}

Conectarse a Redis

Tu aplicación ahora puede conectarse a Redis usando el socket Unix que se ha creado. Lo encontrarás en el directorio home del usuario.

  • Ruta del Socket:
/home/<username>/ea-podman.d/<container_name>/redis.sock
  • Archivo de Configuración:
/home/<username>/ea-podman.d/<container_name>/redis.conf

Por ejemplo:

/home/databyte/ea-podman.d/ea-redis62.databyte.03/redis.sock

¡Y listo! Ya tienes una instancia de Redis funcional y segura en tu cuenta de cPanel.