¿SELinux? Nah, dejalo en Disabled

En el mundo real muchos sysadmins y developers en sistemas operativos de la familia Red Hat deshabilitan SELinux. "¡Es muy difícil!" dicen. En Ubuntu ni en Debian existe esa cuestión ya que en su lugar se usa AppArmor. Este post no te dará servido en bandeja como solucionar un problema, sino algo mucho más valioso, que es entender el fundamento del comportamiento de SELinux y por qué funciona la solución que vayas a aplicar.

Usamos un caso práctico: tenemos una instalación basada en Laravel, php-fpm y con nginx como web server.

Tenemos una estructura de directorio así:

> tree -L 1 /home/sergio/laravel-voyager/
/home/sergio/laravel-voyager/
├── app
├── artisan
├── bootstrap
├── composer.json
├── composer.lock
├── config
├── database
├── lang
├── package.json
├── phpunit.xml
├── public
├── README.md
├── resources
├── routes
├── storage
├── tests
├── vendor
└── vite.config.js

11 directories, 7 files

Veamos la información de SELinux para algunos archivos y directorios:

[sergio@masterk laravel-voyager]$ sudo ls -Zd public/storage /etc/nginx /home/sergio/.bashrc /root/.bashrc /usr/share/nginx/ bootstrap/cache/
[sudo] password for sergio: 
unconfined_u:object_r:user_home_t:s0 bootstrap/cache/      unconfined_u:object_r:user_home_t:s0 public/storage
 system_u:object_r:httpd_config_t:s0 /etc/nginx               system_u:object_r:admin_home_t:s0 /root/.bashrc
    system_u:object_r:user_home_t:s0 /home/sergio/.bashrc            system_u:object_r:usr_t:s0 /usr/share/nginx/

Si bien la salida es demasiado críptica, la podemos descifrar:

Archivo/Directorio Usuario (SELinux) Rol (SELinux) Asignación basada en tipos (SELinux) Rango (SELinux) Descripción y Observaciones
bootstrap/cache/ unconfined_u object_r user_home_t s0 Considerado como parte del directorio personal de un usuario. Debe ser httpd_sys_rw_content_t para Laravel.
public/storage unconfined_u object_r user_home_t s0 Similar a bootstrap/cache. Debe ser httpd_sys_content_t para que Laravel sirva archivos estáticos.
/etc/nginx system_u object_r httpd_config_t s0 Archivo de configuración de Nginx. Correctamente etiquetado como httpd_config_t. No requiere cambios.
/home/sergio/.bashrc system_u object_r user_home_t s0 Archivo personal de configuración de un usuario. Etiquetado como user_home_t, lo cual es correcto.
/root/.bashrc system_u object_r admin_home_t s0 Archivo de configuración del usuario root. Etiquetado como admin_home_t, lo cual es correcto.
/usr/share/nginx/ system_u object_r usr_t s0 Etiquetado como usr_t, que es genérico. Debe ser httpd_sys_content_t si Nginx sirve contenido estático aquí.

Descripción de los campos

Concepto Descripción Ejemplo
Usuario (SELinux) Define el usuario de SELinux asociado al recurso. system_u: Archivo gestionado por el sistema.
unconfined_u: Archivo de un usuario sin restricciones.
Rol (SELinux) Define el rol asignado al recurso. Usualmente es object_r para archivos regulares y directorios.
Asignación basada en tipos Determina qué procesos o aplicaciones pueden interactuar con un recurso. httpd_sys_rw_content_t: Permite lectura/escritura por parte del servidor web.
httpd_sys_content_t: Permite solo lectura por parte del servidor web.
user_home_t: Para directorios o archivos en /home.
Rango (SELinux) Se usa para configuraciones de seguridad MLS o TE (usualmente s0 en configuraciones estándar). Normalmente no requiere ajustes en configuraciones básicas.

Ahora bien, hay varios tipos de políticas en cuanto a funcionalidades que se pueden aplicar en SELinux, la que se usa en general es targeted. El tipo de política targeted significa que determinados servicios se ejecutan de manera confinada, es decir limitada para realizar solamente determinadas acciones más allá de los permisos ordinarios.

Y es importante enfatizar que cuando usamos targeted el campo que más nos tiene que interesar de un contexto es el "Type Enforcement". En general no prestaremos demasiada atención al resto de los campos.

Lo más importante para entender es que las reglas de SELinux tienen sujetos y objetos. El contexto o etiqueta del objeto podría no permitir que el sujeto realice una determinada acción al sujeto, y es allí cuando se produce un error. Dicho error tiene por objetivo proteger al sistema. En este ejemplo los sujetos son nginx y php-fpm y los objetos son los directorios bootstrap/cache y public/storage (y sus respectivos contenidos).

Por ejemplo, nginx sería el sujeto y con el siguiente comando vemos el contexto del proceso:

> ps -C nginx,php-fpm -Z
LABEL                               PID TTY          TIME CMD
system_u:system_r:httpd_t:s0        898 ?        00:00:00 php-fpm
system_u:system_r:httpd_t:s0       1118 ?        00:00:00 php-fpm
system_u:system_r:httpd_t:s0       1119 ?        00:00:00 php-fpm
system_u:system_r:httpd_t:s0       1120 ?        00:00:00 php-fpm
system_u:system_r:httpd_t:s0       1121 ?        00:00:00 php-fpm
system_u:system_r:httpd_t:s0       1122 ?        00:00:00 php-fpm
system_u:system_r:httpd_t:s0       1451 ?        00:00:00 nginx
system_u:system_r:httpd_t:s0       1452 ?        00:00:00 nginx
system_u:system_r:httpd_t:s0       1453 ?        00:00:00 nginx

Pero habrá problemas...

La cuestión es que tanto nginx como php-fpm se ejecutan con la asignación de tipo (TE) httpd_t y la TE que tienen directorior como bootstrap/cache y public/storage es user_home_t.

Con el siguiente comando podemos verifucar qué cosas puede hacer el dominio httpd_t en el contexto del objeto que tiene el tipo de asignación user_home_t:

> sesearch -s httpd_t -A -t user_home_t
allow daemon user_home_t:file { append getattr };
allow domain file_type:blk_file map; [ domain_can_mmap_files ]:True
allow domain file_type:chr_file map; [ domain_can_mmap_files ]:True
allow domain file_type:file map; [ domain_can_mmap_files ]:True
allow domain file_type:lnk_file map; [ domain_can_mmap_files ]:True
allow httpd_t file_type:dir { getattr open search };
allow httpd_t file_type:filesystem getattr;
allow httpd_t user_home_t:file map; [ httpd_read_user_content ]:True
allow httpd_t user_home_type:dir { getattr ioctl lock open read search }; [ httpd_read_user_content ]:True
allow httpd_t user_home_type:dir { getattr open search }; [ httpd_enable_homedirs ]:True
allow httpd_t user_home_type:dir { getattr open search }; [ httpd_read_user_content ]:True
allow httpd_t user_home_type:dir { getattr open search }; [ httpd_read_user_content ]:True
allow httpd_t user_home_type:file { getattr ioctl lock open read }; [ httpd_read_user_content ]:True
allow httpd_t user_home_type:lnk_file { getattr read }; [ httpd_enable_homedirs ]:True

Cada regla tiene:

  • El tipo de regla ( allow )
  • El sujeto de la regla ( httpd_t )
  • El contexto y tipo del objeto ( user_home_t:file )
  • Los permisos (append y getattr)

¡No hay ninguna regla que permita la modificación total del contenido del archivo ni la creación de archivos en la carpeta (por ejemplo, el permiso write)!. Esto significa que, de manera predeterminada, servicios como apache o nginx no tienen permiso para escribir en los directorios home de los usuarios. Y por ese motivo hay cosas que fallarán, sin importar los permisos ni el usuario...

Por lo tanto los directorios public/storage y bootstrap/cache y su contenido necesitan otro contexto para que Laravel funcione correctamente cuando está ubicado en un subdirectorio de /home. Mirando la documentación podemos ver que httpd_sys_content_t y httpd_sys_rw_content son respectivamente los apropiados para esos directorios y su contenido:

httpd_sys_content_t

- Set files with the httpd_sys_content_t type, if you want to treat the files as httpd sys content.

Paths:
     /srv/([^/]*/)?www(/.*)?,  /var/www(/.*)?,  /etc/htdig(/.*)?,   /srv/gallery2(/.*)?,   /var/lib/trac(/.*)?,   /var/lib/htdig(/.*)?,
     /var/www/icons(/.*)?,     /usr/share/glpi(/.*)?,     /usr/share/htdig(/.*)?,     /usr/share/drupal.*,     /usr/share/z-push(/.*)?,
     /var/www/svn/conf(/.*)?,         /usr/share/icecast(/.*)?,          /var/lib/cacti/rra(/.*)?,          /usr/share/ntop/html(/.*)?,
     /usr/share/nginx/html(/.*)?,      /usr/share/doc/ghc/html(/.*)?,      /usr/share/openca/htdocs(/.*)?,      /usr/share/selinux-pol
     icy[^/]*/html(/.*)?

y

httpd_sys_rw_content_t

- Set files with the httpd_sys_rw_content_t type, if you want to treat the files as httpd sys read/write content.

Paths:
     /etc/rt(/.*)?,  /etc/glpi(/.*)?,  /etc/horde(/.*)?,  /etc/drupal.*,  /etc/z-push(/.*)?,  /var/lib/svn(/.*)?,   /var/www/svn(/.*)?,
     /etc/owncloud(/.*)?,    /var/www/html(/.*)?/uploads(/.*)?,    /var/www/html(/.*)?/wp-content(/.*)?,   /var/www/html(/.*)?/wp_back
     ups(/.*)?,  /var/www/html(/.*)?/sites/default/files(/.*)?,  /var/www/html(/.*)?/sites/default/settings.php,  /etc/mock/koji(/.*)?,
     /etc/nextcloud(/.*)?,       /var/lib/drupal.*,      /etc/zabbix/web(/.*)?,      /var/lib/moodle(/.*)?,      /var/log/z-push(/.*)?,
     /var/spool/gosa(/.*)?,   /etc/WebCalendar(/.*)?,   /usr/share/joomla(/.*)?,   /var/lib/dokuwiki(/.*)?,    /var/lib/owncloud(/.*)?,
     /var/spool/viewvc(/.*)?, /var/lib/nextcloud(/.*)?, /var/lib/pootle/po(/.*)?, /var/lib/phpMyAdmin(/.*)?, /var/www/moodledata(/.*)?,
     /srv/gallery2/smarty(/.*)?,      /var/www/moodle/data(/.*)?,      /var/lib/graphite-web(/.*)?,      /var/log/shibboleth-www(/.*)?,
     /var/www/gallery/albums(/.*)?,  /var/www/html/owncloud/data(/.*)?, /var/www/html/nextcloud/data(/.*)?, /usr/share/wordpress-mu/wp-
     content(/.*)?, /usr/share/wordpress/wp-content/upgrade(/.*)?, /usr/share/wordpress/wp-content/uploads(/.*)?, /var/www/html/config
     uration.php

De modo que esos son los contextos que tendrías que aplicar a esos directorios y sus contenidos. El resto, si sos administrador o desarrollador de Linux con algo de experiencia es anecdótico.

Fuentes y más recursos

Cómo Hacer Túneles SSH

Una de las funcionalidades extraordinarias que posee openssh es la de encapsular el tráfico inseguro a puertos TCP. A continuación vemos algunos ejemplos útiles de cuándo y cómo utilizarlos.

Túneles

unsplash-logo Aaron Burden

Algunas suposiciones:

  • openssh-server.example.com: Es el host que posee el servicio ssh y el servicio sin cifrar
  • otrohost.example.com: Es un host que posee un servidor web sin TLS pero que carece de ssh.
  • 192.0.2.1/24: Es la dirección IP del cliente ssh.
  • filtrado.example.com: Es un host que tiene el puerto del servicio al que queremos acceder pero se encuentra filtrado para el exterior.
  • casa.example.com: Es un host remoto respecto a un servidor ssh accesible desde la red de filtrado.example.com. Este host está en un lugar en el cual podemos trabajar cómodamente 😊 por fuera de la infraestructura de filtrado.example.com.

Caso 1: Redireccionado desde un puerto local a un puerto remoto para segurizar un servicio sin cifrar

Queremos segurizar el acceso a un servidor web sin TLS.

ssh -N -f -L 10001:localhost:80 openssh-server.example.com

  • 80: Es el puerto que tiene el servicio sin cifrar
  • localhost: Es la dirección en el host servicio-remoto.example.com que escucha en el puerto 80.
  • 10001: Es el puerto local al cual tendremos que conectarnos para obtener el servicio web.
  • -N No ejecuta comandos en el lado remoto.
  • -f El proceso va al segundo plazo antes de la ejecución de comandos.

El url que habrá que poner en el navegador es http://localhost:10001

Caso 2: Redireccionado desde un puerto local a un puerto remoto permitiendo el acceso al túnel usuarios de la misma red local para Segurizar un servicio sin cifrar

Queremos permitir que otros hosts de la red puedan acceder nuestro servicio encapsulado.

ssh -g -N -f -L 10001:localhost:80 openssh-server.example.com

  • -g Permite acceder a otros hosts de la red 192.0.2.0/24 acceder a el url http://192.0.2.1:10001/

Caso 3: Redireccionado desde un puerto local a un puerto remoto usando otro servidor ssh para acceder a un servicio sin TLS ni ssh

ssh -N -f -L 10001:otrohost.example.com:80 openssh-server.example.com

  • Aquí la url será http://localhost:10001

Caso 4: Crear un túnel en dirección inversa para acceder a un puerto SSH filtrado (utilización de un túnel remoto)

En este caso, queremos acceder al puerto SSH de filtrado.example.com, que no es accesible desde internet. Utilizamos casa.example.com con acceso SSH temporal para redirigir el puerto SSH de filtrado.example.com.

El comando debe ejecutarse en filtrado.example.com:

ssh  -N -f -R 9000:localhost:22 casa.example.com
  • 22: Es el puerto SSH de filtrado.example.com.
  • 9000: Es el puerto redirigido en casa.example.com.

Para conectarse al puerto SSH de filtrado.example.com desde casa.example.com:

ssh -p 9000 localhost

Caso 5: Redireccionamiento remoto para permitir acceso compartido a un puerto filtrado

En este caso, queremos redirigir el puerto SSH de un servidor filtrado (filtrado.example.com) hacia un servidor intermedio (casa.example.com) y permitir que otros hosts que están en la misma red local puedan acceder al túnel.

Comando para configurar el túnel:

ssh  -N -f -R 0.0.0.0:9000:localhost:22 casa.example.com
  • 0.0.0.0:9000: Permite que el puerto redirigido esté accesible en todas las interfaces de red de casa.example.com.
  • localhost:22: Redirige el tráfico al puerto SSH de filtrado.example.com.

Para conectarse al túnel desde cualquier host con acceso a casa.example.com:

ssh -p 9000 casa.example.com
Requisito en casa.example.com

Habilitar GatewayPorts en /etc/ssh/sshd_config:

GatewayPorts clientspecified

Con esta configuración si el cliente no especifica explícitamente 0.0.0.0 al crear el túnel, los puertos siguen siendo locales por defecto.

Reinicia el servicio SSH:

sudo systemctl restart sshd
Notas de Seguridad

Si el servidor casa.example.com tiene una IP pública, usar firewalls para limitar el acceso al puerto 9000 y restringirlo solo a las máquinas necesarias. Para mayor seguridad, se puede especificar una interfaz específica en lugar de 0.0.0.0.

Troubleshooting

En todas las casos usar la opción -v para obtener realizar debugging. Recordar que dicha opcioń se puede usar múltiples veces para aumentar el detalle.

Como instalar Zabbix 7.x usando contenedores podman

Namespaces en Linux

Zabbix es una potente herramienta de monitoreo. Recientemente salió la versión 7.0 la cual incorporó funcionalidades importantes, como son los items de browser que usan webdrivers. A continuación veremos como implementar Zabbix 7.x usando podman.

La guía siguiente supone que:

  • Ya generaste o adquiriste los certificados ssl.
  • Tenés los conocimientos fundamentales sobre Linux (usamos RHEL8).
  • Poseés conocimientos básicos de troubleshooting en contenedores y entendiendo que este "HowTo" es un punto al que deberás adaptar a tu entorno y necesidades. No es consultoría 😀.

1. Crear el Pod con Puertos Correctos

Primero, crear el pod con el puerto 443 en el host mapeado al puerto 8443 en el contenedor de NGINX. También asignar el puerto 10051 para Zabbix Server.

podman pod create --name zabbix -p 443:8443 -p 10051:10051

2. Desplegar el Contenedor MySQL

Iniciar el contenedor mysql-server dentro del pod, configurando las variables de entorno necesarias para la base de datos de Zabbix:

mkdir mysql && podman run -d --name mysql-server --pod=zabbix \
    -v ./mysql:/var/lib/mysql:Z \
    -e MYSQL_DATABASE="zabbix" \
    -e MYSQL_USER="zabbix" \
    -e MYSQL_PASSWORD="zabbix_pwd" \
    -e MYSQL_ROOT_PASSWORD="root_pwd" \
    docker.io/library/mysql:8.0

3. Configurar log_bin_trust_function_creators en MySQL

Para evitar problemas con permisos de creación de funciones, habilita esta configuración en el servidor MySQL:

Darle tiempo a que MySQL esté listo:

until podman exec mysql-server mysqladmin ping -u root -p'root_pwd' --silent; do
    echo "Esperando a que MySQL esté listo..."
    sleep 5
done

Ejecutar la configuración:

podman exec -i mysql-server mysql -u root -p'root_pwd' -e "SET GLOBAL log_bin_trust_function_creators = 1;"

4. Desplegar el Contenedor Zabbix Server

Iniciar el contenedor zabbix-server-mysql en el pod, conectándolo a MySQL y asegurándote de que se conecte al Java Gateway:

podman run -d --name zabbix-server-mysql --pod=zabbix \
    -e DB_SERVER_HOST="127.0.0.1" \
    -e MYSQL_USER="zabbix" \
    -e MYSQL_PASSWORD="zabbix_pwd" \
    -e MYSQL_DATABASE="zabbix" \
    -e ZBX_JAVAGATEWAY="127.0.0.1" \
    docker.io/zabbix/zabbix-server-mysql:alpine-latest

5. Verificar la Creación de Tablas en la Base de Datos

Confirmar que zabbix-server-mysql crea correctamente las tablas en la base de datos:

podman exec -i mysql-server mysql -u root -p'root_pwd' -e "USE zabbix; SHOW TABLES;"

6. Desplegar el Contenedor Zabbix Java Gateway

Iniciar el contenedor zabbix-java-gateway, asegurándote de que esté en el pod zabbix:

podman run -d --name zabbix-java-gateway --pod=zabbix docker.io/zabbix/zabbix-java-gateway:alpine-latest

7. Desplegar el Contenedor NGINX con Certificados SSL y Configuración

Ejecutar el contenedor de Zabbix Web NGINX MySQL (zabbix-web-mysql-ssl), montando los certificados y el archivo de configuración zabbix-ssl.conf correctamente:

El archivo de configuración zabbix-web-mysql-ssl puede ser algo así:

server {
    listen 8443 ssl;
    server_name _;

    ssl_certificate /etc/ssl/nginx/fullchain.pem;
    ssl_certificate_key /etc/ssl/nginx/privkey.pem;

    root /usr/share/zabbix;

    index index.php index.html index.htm;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_pass unix:/tmp/php-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME /usr/share/zabbix$fastcgi_script_name;
    }

    error_log /tmp/zabbix_ssl_error.log;
    access_log /tmp/zabbix_ssl_access.log;
}
podman run -d --name zabbix-web-mysql-ssl --pod=zabbix \
    -v ./certs/fullchain.pem:/etc/ssl/nginx/fullchain.pem:ro \
    -v ./certs/privkey.pem:/etc/ssl/nginx/privkey.pem:ro \
    -v ./zabbix-ssl.conf:/etc/nginx/http.d/zabbix-ssl.conf:ro \
    -e ZBX_SERVER_HOST="127.0.0.1" \
    -e DB_SERVER_HOST="127.0.0.1" \
    -e MYSQL_DATABASE="zabbix" \
    -e MYSQL_USER="zabbix" \
    -e MYSQL_PASSWORD="zabbix_pwd" \
    docker.io/zabbix/zabbix-web-nginx-mysql:alpine-latest

8. Verificación Final

Hacer una solicitud a la interfaz de Zabbix para confirmar que todo esté funcionando y que la configuración SSL esté activa:

curl -v  https://sebelk.lab

Deberías ver una respuesta HTTP 200 desde NGINX, indicando que Zabbix está activo y funcionando con SSL.

9. Instalación del agente

podman run -d --name zabbix-agent --pod=zabbix \
    -e ZBX_SERVER_HOST="127.0.0.1" \
    -e ZBX_SERVER_PORT="10051" \
    -e ZBX_HOSTNAME="ZabbixServerAgent" \
    docker.io/zabbix/zabbix-agent2:alpine-latest

10. Deshabilitar variable en MySQL

La variable que habilitamos antes no es imprescindible, de modo que se puede desactivar:

podman exec -i mysql-server mysql -u\
     root -p'root_pwd'\
    -e "SET GLOBAL log_bin_trust_function_creators = 0;"

Todo listo

¡Listo! Trabajo terminado 😉

Zabbix Implementado usando podman

¿Qué te pareció la guía? Podés usar la sección de Comentarios.

Qué son los cgroups y para qué sirven

Namespaces en Linux

En el post anterior vimos los namespaces, que sirven para aislar diferentes aspectos del sistema, como los procesos, la red y los puntos de montaje, creando entornos independientes para cada grupo de procesos. En este post, exploraremos los cgroups, que permiten controlar cuánto de los recursos del sistema, como CPU, memoria y disco, puede usar cada grupo de procesos. Ambas tecnologías se complementan: los namespaces proporcionan el aislamiento necesario, mientras que los cgroups gestionan y limitan los recursos que esos entornos aislados pueden consumir.

¿Qué son los cgroups?

Los cgroups (control groups) son una funcionalidad del kernel Linux que permite agrupar procesos y controlar de manera granular los recursos del sistema que consumen, como CPU, memoria, I/O, y otros. Se incorporaron en 2008 en el kernel de Linux 2.6.24, los cgroups permiten a los administradores de sistemas gestionar el uso de recursos por grupos de procesos de forma eficiente, garantizando una distribución justa y controlada.

Los cgroups crean una jerarquía de control donde es posible asignar límites a recursos específicos para un conjunto de procesos, de manera que el sistema operativo pueda monitorear, limitar, priorizar o aislar estos procesos según sea necesario. Esta capacidad es clave para garantizar que los sistemas multitarea, servidores, y contenedores funcionen de manera eficiente.

¿Para qué sirven los cgroups?

Los cgroups sirven para:

  • Limitar el uso de recursos

  • Priorizar procesos

  • Aislar procesos

  • Monitorear el consumo de recursos

  • Controlar procesos en contenedores (Docker, podman,etc.)

Un pantallazo de los cgroups

Dentro del propio directorio /proc podemos encontrar información sobre los cgroups:

 cat /proc/cgroups 
#subsys_name    hierarchy       num_cgroups     enabled
cpuset  0       241     1
cpu     0       241     1
cpuacct 0       241     1
blkio   0       241     1
memory  0       241     1
devices 0       241     1
freezer 0       241     1
net_cls 0       241     1
perf_event      0       241     1
net_prio        0       241     1
hugetlb 0       241     1
pids    0       241     1
rdma    0       241     1
misc    0       241     1

Este archivo muestra 4 columnas

Campo Significado
subsys_name Componente del kernel que controlar un recurso específico un aspecto del sistema.
hierarchy La columna hierarchy indica el ID de jerarquía asignado a cada subsistema. En este caso, todas las jerarquías tienen el valor 0, lo que indica que todos los subsistemas listados están utilizando una jerarquía unificada, común en sistemas que usan cgroups v2. En cgroups v2, todos los controladores utilizan una única jerarquía para gestionar los recursos de manera más eficiente, a diferencia de cgroups v1, donde cada subsistema podía tener su propia jerarquía.
num_cgroups Muestra el número de cgroups activos o instancias creadas bajo cada controlador. Estos cgroups pueden pertenecer a procesos que están ejecutándose actualmente en el sistema, como servicios, contenedores, o procesos individuales.
enabled Esta columna muestra si el subsistema está habilitado o no. El valor 1 indica que el controlador está habilitado y activo, mientras que un valor de 0 indicaría que está deshabilitado.

Evolución de los cgroups:

  1. cgroups v1: En la versión original, cada tipo de recurso (CPU, memoria, etc.) tenía su propia jerarquía de control, lo que permitía una gestión separada pero a veces compleja. Aunque era útil, la administración independiente de subsistemas podía resultar difícil de manejar en entornos complejos.

  2. cgroups v2 (2016): Para mejorar la gestión de recursos, se introdujo cgroups v2 en el kernel de Linux 4.x, que unificó los diferentes controladores bajo una jerarquía única. Esta versión simplificó el control de recursos y mejoró la eficiencia, permitiendo que todos los subsistemas trabajaran de manera más coordinada y con configuraciones más claras.

Cómo crear un cgroup y limitar el uso de CPU en cgroups v2

En este ejemplo, limitaremos el uso de CPU de un proceso utilizando cgroups v2 en un entorno moderno.

Paso 1: Crear un directorio para el cgroup

En cgroups v2, se gestiona todo bajo una jerarquía unificada. Primero, crea un nuevo directorio en /sys/fs/cgroup:

sudo mkdir /sys/fs/cgroup/mi_cgroup
Paso 2: Establecer un límite de CPU

En cgroups v2, puedes establecer límites en el uso de CPU mediante el archivo cpu.max. Este archivo recibe dos valores: el primero es el límite de uso de CPU en microsegundos por cada período de 100 milisegundos, y el segundo es la duración del período en microsegundos.

Para limitar el uso de CPU al 50%, puedes hacer lo siguiente:

echo "50000 100000" | sudo tee /sys/fs/cgroup/mi_cgroup/cpu.max

Este comando establece que los procesos en este cgroup pueden usar un máximo de 50.000 microsegundos de CPU en un período de 100.000 microsegundos (100 ms), lo que equivale al 50% de un núcleo de CPU.

Paso 3: Añadir un proceso al cgroup

Para añadir un proceso existente al cgroup, escribe su PID (ID del proceso) en el archivo cgroup.procs de ese cgroup. Supongamos que el PID del proceso es 78435:

echo 78435| sudo tee /sys/fs/cgroup/mi_cgroup/cgroup.procs

Esto moverá el proceso con PID 78435 al cgroup mi_cgroup, aplicando las restricciones de CPU configuradas.

Ejemplo de uso de cgroups para limitar el uso de CPU

En cgroups v1, los recursos se gestionan usando archivos como cpu.shares para establecer prioridades relativas de CPU, y tasks para asignar procesos a un cgroup. En cambio, en cgroups v2, el control de CPU se realiza a través de cpu.max, donde se puede establecer un límite absoluto en el uso de CPU (como un porcentaje). Además, en v1 cada subsistema tiene su propio directorio (como cpu/, memory/), mientras que en v2 todo se gestiona dentro de una única jerarquía unificada bajo /sys/fs/cgroup/.

Conclusión

Hoy en día, los cgroups son una herramienta crítica en la gestión moderna de servidores. RHEL8 usa de manera predeterminada la versión 1, pero es posible usar la versión 2, dependiendo del software que utilicemos. En definitiva, los cgroups son fundamentales para la operación de tecnologías como contenedores (Docker, Podman), orquestadores como Kubernetes, y cualquier sistema que necesite control granular de recursos. La capacidad de aislar, priorizar y monitorear el uso de recursos en servidores es clave para garantizar la estabilidad y el rendimiento en entornos de alta demanda.

Fuentes y más información

Qué son los namespaces y que podemos hacer con ellos

En el post anterior, exploramos qué es Flatpak, una excelente alternativa o complemento a los métodos tradicionales de instalación de programas en Linux. Flatpak utiliza una herramienta llamada bubblewrap, que a su vez emplea la tecnología de namespaces.

Namespaces en Linux

Veamos con lupa lo que hay debajo de una aplicación flatpak.

Listado de procesos de aplicaciones flatpak

 flatpak ps --columns=pid,child-pid,application
PID  PID-hijo Aplicación
4588 4610     com.github.marktext.marktext
4629 4639     com.github.marktext.marktext
4841 4853     com.spotify.Client

MarkText y Spotify (aplicaciones instaladas con flatpak)

Este listado nos muestra que hay dos aplicaciones corriendo: MarkText y Spotify. Vamos a ver qué pasa con uno de los procesos relacionados con MarkText.

El comando detrás de flatpak

 ps p  4610 o args
COMMAND
/usr/bin/bwrap --args 42 -- spotify

bwrap es una herramienta para crear sandboxes por medio de namespaces. Un namespace es un recurso del sistema que se puede aislar, pero... en lugar de abundar en tecnicismos, para comprender este concepto en la práctica, veremos algunas propiedades del proceso 4610 (MarkText):

 ls -l /proc/4610/ns
total 0
lrwxrwxrwx 1 sergio sergio 0 jul 31 18:46 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 sergio sergio 0 jul 31 18:46 ipc -> 'ipc:[4026531839]'
lrwxrwxrwx 1 sergio sergio 0 jul 31 18:40 mnt -> 'mnt:[4026532856]'
lrwxrwxrwx 1 sergio sergio 0 jul 31 18:46 net -> 'net:[4026531840]'
lrwxrwxrwx 1 sergio sergio 0 jul 31 18:40 pid -> 'pid:[4026532857]'
lrwxrwxrwx 1 sergio sergio 0 jul 31 18:46 pid_for_children -> 'pid:[4026532857]'
lrwxrwxrwx 1 sergio sergio 0 jul 31 18:46 time -> 'time:[4026531834]'
lrwxrwxrwx 1 sergio sergio 0 jul 31 18:46 time_for_children -> 'time:[4026531834]'
lrwxrwxrwx 1 sergio sergio 0 jul 31 18:40 user -> 'user:[4026532858]'
lrwxrwxrwx 1 sergio sergio 0 jul 31 18:46 uts -> 'uts:[4026531838]'

Ese listado nos muestra que están los namespaces cgroup, ipc, mnt, net, pid, time, user y uts. Cada uno de ellos tiene un número que lo identifica.

Podemos compararlo con otros procesos por ejemplo con el pid 1 y con el pid del shell que estamos usando:

Comparación de procesos y namespaces

Podemos ver que tanto bash como systemd comparten todos los namespaces. Sin embargo, bwrap tiene namespaces distintos para mnt, pid y user. Es decir tiene esos recursos aislados.

El cuadro siguiente nos sirve para saber qué namespace usar de acuerdo a lo que queramos aislar:

Para aislar Usar namespace...
Límites de recursos cgroups
Colas de mensaje, semáforos, memoria compartida ipc
Lista de montajes mount
Interfaces de red, ruteo, sockets, etc. net
Números de pid pid
UID's, GID's, capabilities, etc user
Hostnames uts
Relojes/Tiempo time

El soporte en el kernel lo podemos verificar de la siguiente manera:

grep -E 'CONFIG_[A-Z]+_NS=y' /boot/config-$(uname -r)
CONFIG_UTS_NS=y
CONFIG_TIME_NS=y
CONFIG_IPC_NS=y
CONFIG_USER_NS=y
CONFIG_PID_NS=y
CONFIG_NET_NS=y

Crear nuevos namespaces para procesos y usuarios

La herramienta unshare nos permite experimentar y solucionar problemas en aplicaciones y servicios que usan namespaces. En el siguiente ejemplo abrimos un shell con namespaces aislados para usuarios y números de procesos:

 unshare --mount-proc --pid --fork --user bash
❯ ps aux
   USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
   nobody         1  2.8  0.0 237808 12996 pts/1    S    13:43   0:00 bash
   nobody       104  0.0  0.0 230836  4096 pts/1    R+   13:43   0:00 ps aux
  • Aquí vemos que bash usa el PID 1 en lugar de systemd, y que el comando ps aux toma el pid 2.
  • Además, el usuario pasa a ser nobody.
  • Para salir del namespace, simplemente hay que ejecutar exit. Es importante asegurarse de que todos los procesos dentro del namespace hayan terminado, de lo contrario, puede ser que exit sea insuficiente para salir completamente del namespace.

¿Por qué usamos --mount-proc?

Bueno... porque si no lo hacemos sucede esto:

 unshare --mount --pid --fork --user /bin/bash
basename: falta un operando
Pruebe 'basename --help' para más información.

Este mensaje aparece porque el comando basename usa readlink para ver hacia dónde apunta /proc/$$/exe. Como el PID de la shell en el nuevo namespace es 1, intenta acceder incorrectamente al directorio /proc en los namespaces del host. Linux impide este acceso para mantener el aislamiento y la seguridad, lo que resulta en ese error.

Particularidades de namespaces

 unshare --mount-proc --pid --user /bin/bash
unshare: mount /proc failed: Operación no permitida
❯ unshare --pid --user /bin/bash
bash: fork: No se pudo asignar memoria

En el primer intento, ni siquiera pudo crear los namespaces; en el segundo, aunque lo logró, muestra un mensaje críptico:

bash: fork: No se pudo asignar memoria

Estos errores ocurren porque unshare, por defecto, ejecuta el comando indicado en el mismo proceso (similar a exec), lo que resulta en:

  • Error de Montaje: El sistema no permite montar /proc porque el proceso no tiene el contexto completo de namespace de usuario y PID necesario.
  • Error de Memoria: Debido a que bash no se convierte en PID 1 ni puede acceder a otro proceso con PID 1, Linux no permite la asignación de memoria porque no hay un proceso para manejar la recolección de zombies y otras tareas esenciales.

Por lo tanto, usar --fork es necesario para asegurar que el proceso tenga el contexto de namespace completo y para crear un nuevo proceso que actúe como PID 1 en el nuevo namespace.

Entrar en namespaces de una app de flatpak

 sudo nsenter --mount --pid -S $UID -t 4610
-bash-5.2$ ps auxwww
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
sergio         1  0.0  0.0   3764  1288 ?        S    18:40   0:00 /usr/bin/bwrap --args 40 -- marktext
sergio         2  0.9  0.5 21637908 182648 ?     SLl  18:40   0:15 /app/marktext/marktext
sergio         5  0.0  0.0   5640  1664 ?        S    18:40   0:00 cat
sergio         6  0.0  0.0   5640  1792 ?        S    18:40   0:00 cat
sergio        10  0.0  0.1 16987244 46720 ?      S    18:40   0:00 /app/marktext/marktext --type=zygote --no-zygote-sandbox
sergio        12  0.0  0.0      0     0 ?        Z    18:40   0:00 [zypak-sandbox] <defunct>
sergio        15  0.0  0.0   3768  1152 ?        S    18:40   0:00 /usr/bin/bwrap --args 42 -- /app/bin/zypak-helper child - /app/marktext/marktext --type=zygote
sergio        16  0.0  0.1 16989892 49152 ?      S    18:40   0:00 /app/marktext/marktext --type=zygote
sergio        48  1.4  0.2 17065152 72664 ?      Sl   18:40   0:23 /app/marktext/marktext --type=gpu-process --field-trial-handle=9170851604328465342,17458150017059513700,131072 --disable-features=SpareRendererForSitePerProcess --enable-crash-reporter=7744fdfa-fe51-4b1f-adf6-daefea565c51,no_channel --global-crash-keys=7744fdfa-fe51-4b1f-adf6-daefea565c51,no_channel,_companyName=marktext,_productName=marktext,_version=0.17.1 --user-data-dir=/home/sergio/.var/app/com.github.marktext.marktext/config/marktext --gpu-preferences=UAAAAAAAAAAgAAAIAAAAAAAAAAAAAAAAAABgAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAAAAAAGAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAACAAAAAAAAAA= --use-gl=swiftshader-webgl --shared-files
sergio        53  0.0  0.0 16998948 14448 ?      S    18:40   0:00 /app/marktext/marktext --type=broker
sergio        63  0.0  0.1 17044740 57216 ?      Sl   18:40   0:00 /app/marktext/marktext --type=utility --utility-sub-type=network.mojom.NetworkService --field-trial-handle=9170851604328465342,17458150017059513700,131072 --disable-features=SpareRendererForSitePerProcess --lang=es-419 --service-sandbox-type=none --enable-crash-reporter=7744fdfa-fe51-4b1f-adf6-daefea565c51,no_channel --global-crash-keys=7744fdfa-fe51-4b1f-adf6-daefea565c51,no_channel,_companyName=marktext,_productName=marktext,_version=0.17.1 --user-data-dir=/home/sergio/.var/app/com.github.marktext.marktext/config/marktext --shared-files=v8_context_snapshot_data:100
sergio        74  5.2  0.6 25595728 212480 ?     Sl   18:40   1:27 /app/marktext/marktext --type=renderer --enable-crash-reporter=7744fdfa-fe51-4b1f-adf6-daefea565c51,no_channel --global-crash-keys=7744fdfa-fe51-4b1f-adf6-daefea565c51,no_channel,_companyName=marktext,_productName=marktext,_version=0.17.1 --user-data-dir=/home/sergio/.var/app/com.github.marktext.marktext/config/marktext --app-path=/app/marktext/resources/app.asar --no-sandbox --no-zygote --field-trial-handle=9170851604328465342,17458150017059513700,131072 --disable-features=SpareRendererForSitePerProcess --disable-gpu-compositing --lang=es-419 --num-raster-threads=4 --enable-main-frame-before-activation --renderer-client-id=4 --no-v8-untrusted-code-mitigations --shared-files=v8_context_snapshot_data:100
sergio       144  0.0  0.0   7884  4224 ?        S    19:08   0:00 -bash
sergio       145  0.0  0.0  11032  4608 ?        R+   19:08   0:00 ps auxwww

La herramienta nsenter permite ejecutar comandos en namespaces de un proceso determinado, en este caso, el de bwrap. Como no especificamos ningún comando, corre el shell bash.

Allí podemos ver el espacio de nombres aislados de números de procesos, como se ve arriba, o como mostramos a continuación, los montajes aislados:

-bash-5.2$ df -h  
S.ficheros     Tamaño Usados  Disp Uso% Montado en  
tmpfs             16G    92K   16G   1% /etc/timezone  
/dev/sda6        511G   434G   76G  86% /cs  
tmpfs             16G      0   16G   0% /usr/lib/x86_64-linux-gnu/GL  
/dev/sda6        511G   434G   76G  86% /home  
tmpfs            3,2G    11M  3,2G   1% /run/host/monitor  
tmpfs             16G    15M   16G   1% /dev  
devtmpfs         4,0M      0  4,0M   0% /dev/tty  
tmpfs             16G      0   16G   0% /home/sergio/.local/share/flatpak  
tmpfs             16G      0   16G   0% /home/sergio/.var/app  
/dev/sda3        382G   100G  283G  27% /mnt/win10  
tmpfs            6,3G   2,3M  6,3G   1% /run/media  
tmpfs             16G   4,0M   16G   1% /tmp  
tmpfs             16G      0   16G   0% /tmp/.X11-unix  

También podemos pensar que estos recursos que se aíslan se virtualizan. Cuando hablamos de virtualización, probablemente pensemos en un disco o en una interfaz de red virtual. Pero aquí vemos, por ejemplo, que un espacio de nombres de PID's también se puede virtualizar.

Y cuando más de un recurso se puede virtualizar, de ahí a crear un contenedor hay una corta distancia. De hecho, los namespaces sirve tanto para un usuario final con flatpak como en la creación de contenedores lxc (ah, sí lxc existe desde 2008, aunque ya no esté más de moda 😁) docker o podman para un sysadmin o un desarrollador.

A continuación, veremos algunos ejemplos de cómo utilizar los namespaces en contenedores Podman. Es importante conocer el PID del contenedor en el host para estos ejemplos.

Ejemplo 1: Ver la configuración de red del contenedor que usa network mode pasta

 podman unshare nsenter --target 29420 --net   ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host proto kernel_lo 
       valid_lft forever preferred_lft forever
2: wlp108s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 65520 qdisc fq_codel state UNKNOWN group default qlen 1000
    link/ether ea:38:5b:0b:a8:a8 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.144/24 brd 192.168.0.255 scope global noprefixroute wlp108s0
       valid_lft forever preferred_lft forever
    inet6 fe80::e838:5bff:fe0b:a8a8/64 scope link proto kernel_ll 
       valid_lft forever preferred_lft forever

Ejemplo 2: Ver la configuración de red del contenedor que usa network mode slirp4netns

  podman unshare nsenter --target 46222 --net  

  root in ~/to_delete 
   ip a
  1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
      link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
      inet 127.0.0.1/8 scope host lo
         valid_lft forever preferred_lft forever
      inet6 ::1/128 scope host proto kernel_lo 
         valid_lft forever preferred_lft forever
  2: tap0: <BROADCAST,UP,LOWER_UP> mtu 65520 qdisc fq_codel state UNKNOWN group default qlen 1000
      link/ether 92:b6:67:25:13:33 brd ff:ff:ff:ff:ff:ff
      inet 10.0.2.100/24 brd 10.0.2.255 scope global tap0
         valid_lft forever preferred_lft forever
      inet6 fd00::90b6:67ff:fe25:1333/64 scope global dynamic mngtmpaddr proto kernel_ra 
         valid_lft 86356sec preferred_lft 14356sec
      inet6 fe80::90b6:67ff:fe25:1333/64 scope link proto kernel_ll 
         valid_lft forever preferred_lft forever

Ejemplo 3: Probar la resolución de nombres usando el archivo /etc/resolv.conf del contenedor

 container_id=$(podman inspect --format '{{.Id}}' namespace-demo) cp /run/user/1000/containers/overlay-containers/$container_id/userdata/resolv.conf /tmp/container_resolv.conf     podman unshare nsenter --target 46222  --net dig google.com @$(grep -m 1 'nameserver' /tmp/container_resolv.conf | awk '{print $2}')

; <<>> DiG 9.18.26 <<>> google.com @10.0.2.3
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 25213
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;google.com.                    IN      A

;; ANSWER SECTION:
google.com.             263     IN      A       142.251.133.78

;; Query time: 30 msec
;; SERVER: 10.0.2.3#53(10.0.2.3) (UDP)
;; WHEN: Thu Jul 25 19:50:18 -03 2024
;; MSG SIZE  rcvd: 55

Ejemplo 4: Ver los procesos de un contenedor rootless

 sudo nsenter --pid=/proc/$container_pid/ns/pid unshare --mount-proc
[sudo] contraseña para sergio:  ps
  PID TTY          TIME CMD
 1508 pts/6    00:00:00 bash
 1612 pts/6    00:00:00 ps
❯ ps aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
sergio         1  0.0  0.0  14932  7168 ?        Ss   jul25   0:00 sudo /usr/sbin/sshd -D
sergio         2  0.0  0.0  14084  7936 ?        S    jul25   0:00 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
root        1508  0.5  0.0 231260  6528 pts/6    S    19:00   0:00 -bash
root        1641  0.0  0.0 230836  3968 pts/6    R+   19:00   0:00 ps aux

Ejemplo 5: Crear namespaces no privilegiados con un poco de ayuda

Por último, veamos como usar la herramienta rootlesskit para crear fácilmente namespaces rootless:

 rootlesskit bash
❯ id
uid=0(root) gid=0(root) grupos=0(root),65534(nobody) exit rootlesskit --copy-up=/etc bash   touch /etc/sample
❯ ls -l /etc/sample
-rw-r--r-- 1 root root 0 jul 26 12:12 /etc/sample
❯ exit ls -l /etc/sample
ls: no se puede acceder a '/etc/sample': No existe el fichero o el directorio

Conclusión

En este post, hemos visto que los namespaces sirven para aislar recursos con diferentes propósitos y que herramientas como unshare y nsenter son muy útiles para entender cómo funciona una aplicación instalada con Flatpak, pero también un contenedor podman o similar, lo cual facilita la resolución de problemas. Algunas actividades que propongo para continuar ejercitando este tema son:

  1. Instalar aplicaciones de Flatpak y observar qué sucede cuando más de un proceso está involucrado.
  2. Examinar qué ocurre con las tablas de ruteo, las listas de iptables o nftables, etc., en un nuevo namespace de red.
  3. Crear un namespace de usuario y explorar cómo este afecta la ejecución de comandos y procesos.

¡Hasta la próxima!

Fuentes y Recursos

  1. man7.org - namespaces
  2. man1.org - unshare)
  3. man1.org - nsenter)
  4. Docker - Namespaces Overview
  5. Linux-native "fake root" for implementing rootless containers

¿Qué es flatpak?

Es conveniente y merecido que Flatpak tenga un post especialmente dedicado a él, más allá de su interacción con polkit que dejamos planteado en el post anterior.

Flatpak


Brevísimo repaso histórico

En agosto de 2007 el desarrollador sueco Alexander Larsson lanza su primer intento de empaquetar aplicaciones con todas sus dependencias. A fines de 2014 comienza a trabajar en el proyecto xdg-app, el cual cambia de nombre a Flatpak en 2016.

¿Qué es Flatpak?

Flatpak es un sistema para compilar, distribuir y ejecutar aplicaciones. Las aplicaciones se pueden compilar y distribuir independientemente del sistema en que se usan, y al ejecutarse están aisladas del mismo hasta cierto punto.

Cada aplicación está en un sandbox, de manera predeterminada solamente puede acceder al contenido del mismo. Cada programa en flatpak tiene acceso a

  • ~/.var/app/$FLATPAK_ID, y $XDG_RUNTIME_DIR/app/$FLATPAK_ID.
  • De manera limitada a llamadas del sistema.
  • De manera limitada a la instancia de D-BUS.

Aun así, para ser realistas, las aplicaciones en general necesitan más permisos, en este caso se insta a los empaquetadores a que adviertan qué accesos necesitará la aplicación para que el usuario esté conciente de ello, tal como cuando se instala una app desde el Play Store de Android.

Flatseal mostrando los permisos que usa cada una de las aplicaciones instaladas

Flatpak ofrece un repositorio llamado Flathub. Nada impide que cualquier persona o proyecto (como es el caso de Fedora) tenga el suyo propio.

Flatpak usa OSTree (Fedora usa OCI) para distribuir y desplegar datos. OSTree es similar a Git pero está diseñado para hacer control de versiones de binarios y archivos grandes de datos. El uso de OSTree permite además que un mismo archivo sea usado por más de una misma aplicación, ahorrando de esta manera espacio en disco. Cada aplicación que se instala es almacenada en un repositorio local de control de versiones, y luego se mapea en el sistema de archivos local.

Los cambios entre las diferentes versiones de una aplicación se aplican en el filesystem mediante enlaces duros.

Por ejemplo, con el comando siguiente podemos ver log del último commit de una aplicación flatpak (Logseq): .

 ostree log --repo=/var/lib/flatpak/repo  deploy/app/com.logseq.Logseq/x86_64/stable
commit a54ee70b0ec9047c2083f77b9f9175e8e8edcac998e3be8d395009255a6a03f6
Parent:  8c36feebe7ea23654917f542eb5419b71426359a42165f53496c9d82049a6806
ContentChecksum:  4d06f4e875d0335f2ff1c7a30ffa5b200c9a3eb3aeb0b608588eadb045994b22
Date:  2024-06-09 07:22:27 +0000

    Remove screenshot locale (8a160109)

    Name: com.logseq.Logseq
    Arch: x86_64
    Branch: stable
    Built with: Flatpak 1.14.8

<< History beyond this commit not fetched >>

En realidad el comando mostrado es de bajo nivel solamente a los efectos de mostrar ostree como software subyacente, para obtener información a más alto nivel, es conveniente usar directamente el comando flatpak para obtener información de la aplicación:

> flatpak info com.logseq.Logseq 

Logseq - Connect your notes and knowledge

                 ID: com.logseq.Logseq
         Referencia: app/com.logseq.Logseq/x86_64/stable
       Arquitectura: x86_64
               Rama: stable
            Versión: 0.10.9
           Licencia: AGPL-3.0-or-later
             Origen: flathub
          Colección: org.flathub.Stable
        Instalación: system
          Instalada: 437,7 MB
Tiempo de ejecución: org.freedesktop.Platform/x86_64/23.08
                Sdk: org.freedesktop.Sdk/x86_64/23.08

             Commit: a54ee70b0ec9047c2083f77b9f9175e8e8edcac998e3be8d395009255a6a03f6
              Padre: 8c36feebe7ea23654917f542eb5419b71426359a42165f53496c9d82049a6806
             Asunto: Remove screenshot locale (8a160109)
              Fecha: 2024-06-09 07:22:27 +0000

Comparación entre dnf, apt y flatpak

Flatpak está pensado para todo tipo de aplicaciones de escritorio y se esfuerza por ser tan neutral como sea posible en cuanto a los métodos que usa para compilar aplicaciones.

Además, flatpak proporciona actualizaciones atómicas, rollback y consistencia post-instalación.

El siguiente cuadro nos ayuda a ver las diferencias con los dos gestores populares de paquetes: dnf y apt.

Característica DNF APT Flatpak
Distribución Principal Fedora, RHEL, CentOS Debian, Ubuntu Varias distribuciones de Linux
Formato de Paquete RPM DEB OSTree, Flatpak Bundle
Gestión de Dependencias Manejo automático de dependencias Manejo automático de dependencias Manejo dentro del sandbox
Aislamiento No No Sí, aplicaciones ejecutadas en sandbox
Instalación de Aplicaciones Desde repositorios específicos del sistema Desde repositorios específicos del sistema Desde repositorios centralizados (Flathub) y repositorios personalizados
Actualizaciones dnf update apt update && apt upgrade flatpak update
Permisos de Instalación Requiere permisos de root Requiere permisos de root Puede instalarse sin permisos de root
Compatibilidad entre Distros Limitada a sistemas basados en RPM Limitada a sistemas basados en DEB Compatible con múltiples distribuciones
Seguridad Depende de la configuración del sistema Depende de la configuración del sistema Sandboxing y permisos controlados por portales
Facilidad de Uso Interfaz de línea de comandos Interfaz de línea de comandos Interfaz de línea de comandos y GUI
Espacio de Disco Compartido entre aplicaciones Compartido entre aplicaciones Deduplicación y compartición de runtimes
Formato de Archivo Individual No aplica No aplica Flatpak Bundle (.flatpak)
Desventajas Depende de la política de la distro Dependencia de la política de la distro Las aplicaciones pueden tener un mayor tamaño inicial
Compatibilidad con Aplicaciones Antiguas Sí, con soporte de versiones específicas Sí, con soporte de versiones específicas Generalmente más adecuado para nuevas aplicaciones
Desarrollo y Mantenimiento Desarrollado por el proyecto Fedora Desarrollado por el proyecto Debian Desarrollado por el proyecto Flatpak, con apoyo de Red Hat y GNOME
Adopción Usado por muchas distribuciones y entornos de escritorio Usado principalmente en Debian, Ubuntu y derivadas Usado en diversas distribuciones, especialmente para aplicaciones portátiles
Ejemplo de Comando de Instalación dnf install gedit apt install gedit flatpak install flathub org.gnome.Gedit

Inconvenientes para usuarios y desarrolladores de aplicaciones Linux

Si bien Linux mejoró significativamente en cuando a usabilidad y experiencia de usuario en los últimos años, hay obstáculos tanto para usuarios como para desarrolladores:

  • Una aplicación para un entorno de escritorio en Linux se debe empaquetar para que esté disponible (como Debian, Fedora, Ubuntu, Arch Linux, etc.), y asegurarse de que cumpla con los requisitos específicos de cada sistema de paquetes (deb, rpm, etc.). Esto requiere un esfuerzo considerable para mantener múltiples versiones y asegurarse de que cada paquete funcione correctamente en su respectiva distribución. Todo esto demanda o más tiempo o bien más gente idónea dedicada en la tarea de crear y mantener paquetes.

  • Si un usuario quiere usar aplicación que no está empaquetada en la distribución, tendrá que realizar algún paso extra, que van desde operaciones relativamente sencillas como bajar un ejecutable (darle permisos, ubicarlo en un directorio del PATH, etc.), hasta procedimientos bastante más complejos y fuera del interés y conocimiento de un usuario final como puede ser compilar el programa.

  • Una distribución de soporte a largo plazo como Debian Stable no posee versiones actualizadas de la mayoría de las aplicaciones. Por lo tanto un usuario tiene que elegir estabilidad a cambio de software un tanto anticuado.

Flatpak tiene como propósito justamente eliminar esas barreras. Gracias a Flatpak un usuario podrá instalar una aplicación como Logseq aun cuando no esté incluida en los repositorios de su distribución, o instalar una versión reciente de Kdenlive en Debian Stable.

En la actualidad existen distribuciones con la mirada puesta en la innovación que se benefician de Flatpak, por ejemplo:

Características de flatpak

Analicemos la app ONLYOFFICE en flatpak:

flatpak info org.onlyoffice.desktopeditors

ONLYOFFICE Desktop Editors - Office productivity suite

                 ID: org.onlyoffice.desktopeditors
         Referencia: app/org.onlyoffice.desktopeditors/x86_64/stable
       Arquitectura: x86_64
               Rama: stable
            Versión: 8.0.1
           Licencia: AGPL-3.0-only
             Origen: flathub
          Colección: org.flathub.Stable
        Instalación: system
          Instalada: 863,8 MB
Tiempo de ejecución: org.freedesktop.Platform/x86_64/23.08
                Sdk: org.freedesktop.Sdk/x86_64/23.08

             Commit: c9f73db43c9639df65e597108eb4c685025fe484fde05f73f650d05f9ea28512
              Padre: a38db7bbfaa18eda6d26e9d113ce850c077bf533d9ebf5aa7e34988a58b91b28
             Asunto: Update version to 8.0.1 (#117) (a88ad400)
              Fecha: 2024-03-04 15:34:56 +0000

La aplicación ONLYOFFICE usa como runtime org.freedesktop.Platform/x86_64/23.08. El runtime org.freedesktop.Platform/x86_64/23.08 es un entorno estandarizado y versionado que provee FreeDesktop.org para ejecutar aplicaciones de escritorio en Linux. Incluye una colección de librerías y servicios comunes, que aseguran la compatibilidad y la estabilidad entre las diferentes distribuciones de Linux. Este runtime está compilado específicamente para arquitecturas de 64-bit y fue actualizado o liberado en agosto de 2023.

Los runtimes no dependen ni de una distribución ni de una versión en particular de una distribución. Y una aplicación instalada en flatpak será exactamente igual no importa si se instala en Debian o en Fedora. Una aplicación como GIMP, empaquetada como Flatpak, debería proporcionar la misma funcionalidad principal y experiencia de usuario tanto en Debian como en Fedora, dado que todas las dependencias y configuraciones críticas se gestionan dentro del contenedor Flatpak. Desde luego, hay diferencias potenciales: Si una aplicación Flatpak utiliza características específicas del sistema, como integraciones con servicios de notificación de escritorio, la forma en que estas integraciones funcionan puede variar ligeramente entre GNOME en Fedora y KDE en Debian.

Existen diferentes runtimes para Flatpak, tales como freedesktop, GNOME, KDE y Elementary

Objeciones en cuanto a la seguridad

Han existido críticas severas a la seguridad de flatpak, en especial por el sitio anónimo Flatkill. El siguiente cuaddro es un resumen de los argumentos en contra y favor de Flatpak en cuanto a este tema:

Objeción de Seguridad Descripción Respuesta / Acción Tomada
Sandboxing Ineficaz Muchas aplicaciones en Flathub tienen permisos amplios como filesystem=host o filesystem=home, lo que permite acceso completo al sistema de archivos del usuario. Flatpak ha mejorado la transparencia sobre los permisos de las aplicaciones y trabaja en fortalecer el modelo de sandboxing. Se han introducido portales para permitir el acceso controlado a los recursos del sistema. Flatpak Documentation
Actualizaciones de Seguridad Retrasadas Algunas aplicaciones y runtimes en Flatpak no reciben actualizaciones de seguridad oportunas, lo que deja a los usuarios expuestos a vulnerabilidades conocidas. La herramienta flatpak-external-data-checker automatiza la verificación de fuentes externas y la generación de solicitudes de fusión cuando se encuentran actualizaciones. Al implementar f-e-d-c, los desarrolladores pueden asegurar que las aplicaciones en Flathub se mantengan actualizadas más fácilmente y de manera más eficiente, lo que reduce el tiempo de espera para las actualizaciones de los usuarios finales
Integración Limitada del Escritorio Problemas con la integración de aplicaciones Flatpak en escritorios Linux, como la falta de soporte para configuraciones de fuentes y temas de escritorio. Se han mejorado los mecanismos de integración de escritorio, incluyendo mejor soporte para fuentes y temas en KDE y GNOME. Flatpak sigue trabajando en mejorar la compatibilidad con configuraciones del sistema host.)
Explotación Local de Root Riesgo de explotación de root local debido a aplicaciones Flatpak instaladas con permisos suid. Flatpak utiliza nuevas APIs de libostree para rechazar cualquier archivo con permisos suid o de escritura mundial al instalar aplicaciones.
Gestión de Permisos Confusa para el Usuario Los usuarios pueden ser engañados por íconos y descripciones que indican un falso sentido de seguridad sobre las aplicaciones sandboxed. Flatpak ha mejorado la interfaz de usuario para mostrar de manera más clara los permisos que solicita cada aplicación, ayudando a los usuarios a tomar decisiones informadas sobre las aplicaciones que instalan.


Sitio de Flathub mostrando las advertencias pertinentes a una determinada aplicación

¿Cómo hace flatpak para no pedirle a un usuario de un grupo administrativo que se autentique nuevamente?

En el post anterior vimos como funciona polkit. Flatpak se vale de polkit para facilitar la instalación de paquetes por parte de usuarios con permisos administrativos. Basta con mirar el archivo /usr/share/polkit-1/rules.d/org.freedesktop.Flatpak.rules.

polkit.addRule(function(action, subject) {
    if ((action.id == "org.freedesktop.Flatpak.app-install" ||
         action.id == "org.freedesktop.Flatpak.runtime-install"||
         action.id == "org.freedesktop.Flatpak.app-uninstall" ||
         action.id == "org.freedesktop.Flatpak.runtime-uninstall" ||
         action.id == "org.freedesktop.Flatpak.modify-repo") &&
        subject.active == true && subject.local == true &&
        subject.isInGroup("wheel")) {
            return polkit.Result.YES;
    }

    return polkit.Result.NOT_HANDLED;
});

polkit.addRule(function(action, subject) {
    if (action.id == "org.freedesktop.Flatpak.override-parental-controls") {
            return polkit.Result.AUTH_ADMIN;
    }

    return polkit.Result.NOT_HANDLED;
});

Este archivo tiene dos reglas:

  • Permite a los usuarios del grupo "wheel", que están loogoueados localmente y tienen una sesión activa instalar, desinstalar, y modificar aplicaciones y repositorios sin autenticación adicional.
  • No se pueden pasar por arriba de los controles parentales sin autenticarse con una cuenta de usuario administrativa.

Para ambos casos, si las condiciones no se cumplen, el resultado dependerá de otras reglas o de la regla predeterminada.

Para terminar...

Flatpak elimina barreras y obstáculos tanto para desarrolladores como para usuarios finales. Para los primeros, significa les ofrece la posibilidad de poner aplicaciones disposición de cualquier distribución de Linux. Para los segundos representa una mayor facilidad para obtener los programas que están necesitando.

Discover mostrando aplicaciones disonibles en Flathub

Fuentes y Más Recursos

  1. Flatpak Documentation - Using Flatpak
  2. Flathub
  3. Flatkill - Flatpak, a security nightmare
  4. Respuestas a objecioones planteadas en Flatkill
  5. Flatpak en Fedora

Entendiendo Polkit

Introducción

El esquema de permisos en Linux es simple:

  • Un superusuario con todos los poderes para leer, modificar y ejecutar todo.

  • El resto de los usuarios con permisos limitados.

  • Es decir, la clasificación de usuarios en tres categorías, el famoso UGO (User, Group, Others) y el trío de permisos: rwx (read, write y execution).

Pero esa simpleza tiene sus limitaciones, como vimos ya anteriormente, con el paso del tiempo se han ideado maneras de hacer esos permisos más granulares y que superusuario root pueda delegar atribuciones.

Una herramienta muy importante en este sentido es polkit (antiguamente conocido como PolicyKit). En este post vamos a explorar para develar qué es polkit, como funciona, y como se diferencia de sudo. Además, en el próximo post, veremos la relación que tiene con flatpak.

¿Qué es polkit?

Polkit

Leer más…

¿Qué significa en Linux que todo es archivo?

En un viejo artículo, hay una presentación en la que mencioné una frase bastante habitual en el mundo Unix/Linux: «Todo es archivo». Vamos a explicar qué significa realmente esa frase.

Todo es archivo

¿Todo es Archivo?

En realidad, ese dicho no es del todo correcto. Sin embargo, proviene de lo que hoy tal vez podríamos llamar la filosofía Unix. Probablemente, nadie dijo esa frase de manera literal en los primeros años de esa familia de sistemas operativos. Aun así, ideas de ese estilo estaban en la mente desde los comienzos y en los trabajos de por ejemplo, Bill Joy (pionero de TCP/IP). Linus Torvalds, no estuvo ausente en esta discusión. Y mucho más cerca en el tiempo, Lennart Poettering, retoma esa idea una vez más para defender el trabajo realizado en systemd. Por si no se dieron cuenta dice Poettering la interfaz de cgroups están expuesta en un pseudo-sistema de archivos...

Sin embargo, es más apropiado decir que todo es un descriptor de archivo o que todo puede tener un descriptor de archivo. En Linux un descriptor de archivo es un identificador único de proceso para un archivo u otro recurso de entrada o salida. Por ejemplo, no existe necesariamente un archivo para una interfaz de red. Sin embargo, existen sockets relacionados con ellas.

En el siguiente ejemplo podemos ver los descriptores de archivos para la dirección ip 127.0.0.1 de la interfaz lo.

sudo lsof -n -i@127.0.0.1
COMMAND    PID   USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
chronyd   1337 chrony    5u  IPv4   8171      0t0  UDP 127.0.0.1:323 
cupsd     1538   root    7u  IPv4  14727      0t0  TCP 127.0.0.1:ipp (LISTEN)
qemu-syst 1774   qemu   32u  IPv4  19480      0t0  TCP 127.0.0.1:rfb (LISTEN)
syncthing 2620 sergio   19u  IPv4  22825      0t0  TCP 127.0.0.1:8384 (LISTEN)

En este caso, vemos los descriptores de archivos de lectura y escritura 5u, 7u, 32u y 19u.

Para ver la relación entre descriptores y sockets podemos hacer un ping a una determinada dirección ip:

ping -c 10000 9.9.9.9 &> /dev/null

Si buscamos los descriptores de archivos con lsfd, mostrará el siguiente resultado:

lsfd -p 914323 -Q '(FD >= 0)' 
COMMAND    PID   USER ASSOC  XMODE   TYPE SOURCE MNTID    INODE NAME
ping    914323 sergio     0 rw-D--    CHR  pts:6    36        9 /dev/pts/6 
ping    914323 sergio     1 -w----    CHR  mem:3    34        4 /dev/null
ping    914323 sergio     2 -w----    CHR  mem:3    34        4 /dev/null
ping    914323 sergio     3 rw---m   PING sockfs     9 30437500 state=close id=35 laddr=0.0.0.0
ping    914323 sergio     4 rw---- PINGv6 sockfs     9 30437501 socket:[30437501]

Analicemos cada descriptor de archivo:

  • 0: Es la "standard input" que apunta a una pseudoterminal (dispositivo de caracteres), al tratarlo como un archivo tiene permisos: de lectura y escritura. Usa un sistema de archivos y como tal también maneja inodos.
  • 1: Es la "standard output" que apunta a /dev/null. Solamente tiene permisos de escritura, el sistema de archivos proviene de la memoria principal y tiene permisos solamente de escritura.
  • 2: Es el "standard error" que apunta también a /dev/null.
  • 3: Apunta a un socket de tipo IPv4, y tiene permisos de lectura y además está siendo utilizado por un evento de multiplexación.
  • 4: Finalmente tenemos el descriptor de 5 que apunta a un tipo de socket que usa IPv6.

Dentro del directorio /proc podemos ver que los descriptores 1 (salida estándar) y 2 (error estándar) se redirigen al archivo /dev/null, mientras tanto, 3 y 4 apuntan a sockets:

ls -l /proc/914323/fd
total 0
lrwx------ 1 sergio sergio 64 may 19 17:33 0 -> '/dev/pts/6 (deleted)'
l-wx------ 1 sergio sergio 64 may 19 17:33 1 -> /dev/null
l-wx------ 1 sergio sergio 64 may 19 17:33 2 -> /dev/null
lrwx------ 1 sergio sergio 64 may 19 17:33 3 -> 'socket:[30437500]'
lrwx------ 1 sergio sergio 64 may 19 17:33 4 -> 'socket:[30437501]'

Podemos hacer otra prueba, en este caso usaremos solamente ping sobre ipv6 y en lugar de redireccionar a /dev/null, lo haremos redirigiendo stdin y stderr a archivos regulares distintos:

ping6 -c 10000 ::1 > out.txt 2> errors.txt &

Aquí solamente usará un socket (ya que no usa ipv4):

lsfd -p 926928 -Q '(FD >= 0)'
COMMAND    PID   USER ASSOC  XMODE   TYPE SOURCE MNTID    INODE NAME
ping6   926928 sergio     0 rw----    CHR  pts:7    36       10 /dev/pts/7
ping6   926928 sergio     1 -w----    REG   0:34    52 38207299 /tmp/out.txt
ping6   926928 sergio     2 -w----    REG   0:34    52 38207300 /tmp/errors.txt
ping6   926928 sergio     3 rw---- PINGv6 sockfs     9 31193485 state=close id=44 laddr=::

¿Qué sucede si se borra /tmp/errors.txt?

lsfd -p 926928 -Q '(FD >= 0)'
COMMAND    PID   USER ASSOC  XMODE   TYPE SOURCE MNTID    INODE NAME
ping6   926928 sergio     0 rw----    CHR  pts:7    36       10 /dev/pts/7
ping6   926928 sergio     1 -w----    REG   0:34    52 38207299 /tmp/out.txt
ping6   926928 sergio     2 -w-D--    REG   0:34    52 38207300 /tmp/errors.txt
ping6   926928 sergio     3 rw---- PINGv6 sockfs     9 31193485 state=close id=44 laddr=::

Bueno, veremos que el archivo aparece con el flag D indicando que borró del respectivo sistema de archivos.

Por lo tanto, si bien literalmente no todo es archivo, podemos afirmar que en general podríamos mediante los descriptores de archivos establecer un puente entre un proceso y los recursos a los que está accediendo. En definitiva, un descriptor de archivos, permite al proceso comunicarse con el recurso subyacente de manera eficiente y uniforme, independientemente de si el recurso es un archivo en el sistema de archivos, un dispositivo de hardware o un socket de red.

Enlaces útiles

Gestión de Contraseñas Usando Contenedores Podman

Podemos administrar nuestros datos personales también con estándares profesionales. En este contexto, vamos a explorar cómo llevar la gestión de contraseñas usando utilizando herramientas viejas y confiables como pass, gpg, git combinada con la potente solución de contenedores Podman. Será una experiencia desafiante.

Claves

Almacenamiento de Contraseñas Elevado

Para gestionar nuestras passwords usaremos pass. La herramienta pass no estra cosa que un potente script de bash de algo más de 720 líneas de wrapper principalemente para gpg y git.

Ahora, lo realmente interesante: configuraremos un repositorio dentro de un contenedor Podman. Este repositorio, administrado con git, puede ser compartido de manera segura y efectiva en todos tus dispositivos.

Acceso Simplificado desde Cualquier Lugar

La accesibilidad es clave en tu entorno profesional. Ya sea trabajando en sistemas Windows o Linux, puedes aprovechar QtPass, una interfaz gráfica para pass. Para usuarios de iPhone, passforios es una excelente opción, mientras que los dispositivos Android pueden beneficiarse de Password-Store, disponible en Google Play Store y F-Droid para mantener tus contraseñas siempre sincronizadas.

Esquema de podman con pass

Características Clave para Profesionales

  • Control Total: Somos nosotros quienes gestionan nuestros repositorios. Los datos sensibles están en tus dispositivos, evitando intermediarios y asegurando la confidencialidad.

  • Portabilidad Sin Esfuerzo: Se puede cambiar de dispositivos o realiza copias de seguridad con facilidad. Tu repositorio te seguirá sin problemas.

  • Seguridad de Alto Nivel: tus credenciales seguras con estándares de confiabilidad, integridad y disponibilidad.

  • Compartir con Confianza: Podrás compartirlo fácilmente en círculos de confianza.

Configuración Inicial y Uso Avanzado

En esta primera fase, te guiaremos a través de la configuración de tu repositorio en un contenedor Podman y las opciones para interactuar con él desde distintos dispositivos.

Nota 1:

El enfoque primordial es establecer un almacén personal de contraseñas. Además de utilizarlo en tus dispositivos, podrás extender su uso a usuarios de confianza. Aunque podría requerirse alguna asistencia técnica inicial para la configuración, las acciones esenciales son "actualizar desde el repositorio (pull)" y "enviar cambios al repositorio (push)".

Esta idea es adaptable a ambientes profesionales con para grupos reducidos de usuarios en una red corporativa, donde cada miembro tiene su copia del repositorio principal. El repositorio podría estar incluso en algún contenedor que esté disponible 24x7. Esto facilita la consulta, creación, modificación y eliminación colaborativa de contraseñas, consolidando luego los cambios en un repositorio compartido.

Nota 2:

Para aprovechar plenamente esta guía, asumimos que poseés conocimientos de:

  • Gestión de claves públicas ssh
  • Gestión de claves gpg
  • Uso básico de git
  • Uso básico de Podman
  • Conocimientos generales de Linux: bash, systemd, firewalld (iptables/nftables), etc.

¿Qué usaremos para lograr todo esto?

  • OS: Fedora 38
  • podman
  • pass

Se podría usar otra distribución, sin embargo es importante que cuente con una versión relativamente reciente de podman.

¿Por qué usamos podman? Porque tiene una gran similitud con docker, y además, posee la capacidad de correr contenedores de manera mucho más segura y sencilla. En este caso particular, crearemos un contenedor que alojará el repositorio compartido de passwords.

No será necesario crear servicios web y/o de bases de datos.

Instalación de paquetes

dnf install -y git pass

Inicialización de repositorio de passwords

En este ejemplo le pasamos los identificadores de las clave públicas gpg en el siguiente usamos 3 direcciones de mail correspondientes a la clave del host, la del celular y la de otra persona respectivamente.

pass init jkm@example.com fxi@example.com pyn@example.net

pass git init

pass generate puertablanca

Preparación del container

Creamos el siguiente Dockerfile

FROM fedora:38
RUN dnf update -y && dnf install -y git openssh-server
RUN useradd -ms /bin/bash git
RUN mkdir /home/git/.ssh
RUN ssh-keygen -A
COPY  ssh-pks /home/git/.ssh/authorized_keys
RUN echo "git ALL=(ALL:ALL) NOPASSWD: /usr/sbin/sshd" >> /etc/sudoers && git clone --bare /mnt/.password-store /home/git/.password-store && chown -R git:git /home/git  && chmod 700 /home/git/.ssh && chmod 600 /home/git/.ssh/authorized_keys
EXPOSE 22
CMD ["sudo","/usr/sbin/sshd", "-D"]

Esto nos permitirá crear un repositorio con las siguientes características:

  • Basado en Fedora 38
  • Un usuario llamado git que tendrá el único privilegio de correr el servicio ssh
  • Obtendrá una copia del repositorio git antes creado con pass

Crear la imagen de podman

podman build -v /home/sergio/.password-store:/mnt/.password-store -t passteiner .

Crear el container

podman run -d --name container-pass_git --user git -p 60003:22 passteiner

En este punto, ya estamos en condiciones de crear, editar, modificar nuestras passwords y subirlas al repo del contenedor de manera que esté disponible para otros dispostivos y/o usuarios.

Cabe aclarar que para usar Password Store en Android hace falta instalar OpenKeychain. Esa aplicación nos permitirá crear un el par de clave privada + clave pública GnuPG, como así también importar la clave pública del resto de los usuarios con quien compartiremos el llavero. Tener en cuenta que es muy importante la passphrase que usemos para cifrar nuestra clave privada: debe ser fácil de memorizar y a la vez robusta. Esa misma passphrase se nos pedirá cuando necesitamos acceder a las contraseñas:

Y luego con Password Store tendremos que:

  • Ingresar los parámetros de repositorio que está en el contenedor que hemos creado, tanto la url (por ejemplo ssh://git@10.0.0.10:60003/git/,password-store) y la branch (master).
  • Generar la clave pública ssh para autenticarse al repositorio. Esta clave tendrá que copiarse al archivo /home/git/.ssh/authorized_keys del contenedor.

podman exec ontainer-pass_git bash -c 'echo "clave_publica_ssh" /home/git/.ssh/authorized_keys'

(Si queremos que este archivo sea persistente, podríamos modiicar el Dockerfile para que use un volumen).

  • Clonar el repositorio del contenedor.

Una vez que hemos clonado el repositorio, obtendremos el listado de passwords y el menú para operar con él:

Menú de Password Store.

Actualizando nuestro repositorio

Hay varias configuraciones posibles, pero la que recomiendo es la siguiente:

pass git config pull.rebase false

Para bajar las actualizaciones del contenedor:

pass git pull

Para subir las propias modificaciones al contenedor:

pass git push

Comentario finales

Al finalizar habrás conseguido que:

  • El contenedor se ejecuta como un usuario sin privilegios dentro del sistema.

  • Todo - excepto el proceso sshd - se ejecutará como un usuario sin privilegios aun dentro del container. Salvo que explícitamente uses algo como docker run --user root..... . Pero ¿por qué lo harías?

  • Por fuera del contenedor en realidad, mapea a nuestro propio usuario.

Enlaces útiles

3 pasos para configurar unidad de systemd para VM de Virtualbox

Bonita imagen ilustrativa 😄

Foto de Ashutosh Dave en Unsplash

1. Encontrar el UUID de la VM, por ejemplo si tenemos una máquina llamada RHEL7, ejecutamos:

$ VBoxManage showvminfo RHEL7 | grep '^UUID' | awk '{ print $NF }'
f02a9f08-2ff2-4a92-b3cd-a8dfb17513c6

2. Crear el archivo template de servicio ~/.config/systemd/user/virtualbox_vm@.service:

[Unit]
Description=VirtualBox VM %i

[Service]
Type=simple
KillMode=mixed
ExecStart=/usr/lib/virtualbox/VBoxHeadless --comment RHEL7 --startvm f02a9f08-2ff2-4a92-b3cd-a8dfb17513c6 --vrde config

[Install]
WantedBy=default.target

3. Recargar systemd, Habilitar y arrancar el servicio

$ systemctl --user daemon reload && systemctl --user enable --now virtualbox_vm@RHEL7.service

¡Listo!