# Linux

Tips para linux como usuario final

# Extraer páginas de un PDF en Linux desde el shell

## Antecedentes
Mucha sveces necesitamos extraer de un docuemnto PDF una o varias páginas, y para mi la forma más comoda es la de usar un comando de terminal. Tampoco soy amigo de instalar software pirata como hacen muchos usuarios de Linux, que instalarían el Acrobat Pro en un windows emulado. Ni amigo de instalar apps de escritorio que muchas veces dependen de mucho software.

## Pdftk
[Pdftk](https://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/) es una herramienta para trabajar con documentos PDF en la linea de comandos.

### Instalación
#### Apt (Debian, Ubuntu)
```
sudo apt install pdftk
```

#### Snap (Preferida por mi)
```
sudo snap install pdftk
```

## Extraer páginas de un PDF con PDFtk

```
pdftk [nombre_original.pdf] cat [paginas_numeros] output [nombre_del_fichero_extraido.pdf]
```
- nombre_original.pdf es el fichero del que queremos extraer la o las páginas
- paginas_numeros, es la página o págins que queremos extraer, "2", "2-4" (de la 2 a la 4 inclusive)
- nombre_del_fichero_extraido.pdf nombre del fichero resultante

## Extrarer páginas especificas

```
pdftk [nombre_original.pdf] cat 2 4 6 output [nombre_del_fichero_extraido.pdf]
```



## Otros enlaces
- [Tutorial uniendo archivos pdf con PDFTK en Linux](https://fortinux.gitbooks.io/humble_tips/content/capitulo_2/tutorial_uniendo_archivos_pdf_con_pdftk_en_linux.html)

# Instalar la última version de HPLIP Driver para impresoras HP en Ubuntu 20.04

## Introducción
No soy fan de las impresoras HP por motivos políticos y su inmersión en el territorio ocupado de Palestina. Pero en su día compré una HP Officejet Pro 7220. Como casi siempre, HP y su sistema de driver es penoso con linux. 

En mi caso me era imposible instalar el HPLIP para Ubuntu 20.04 por error de dependencias con python, `python-pyqt5`


## Arreglar la dependencia de python-pyqt5
El tema está en que para compatibilizar las versiones de python 2 y 3 hay un juego de enlace simbólicos y en Ubuntu esa vinculación hará que el instalador HPLIP intentará por error instalar bibliotecas dependientes de Python 2 en lugar de las de Python 3.

### Instalar python-is-python3
```
sudo apt install python-is-python3
Leyendo lista de paquetes... Hecho
Creando árbol de dependencias        
Leyendo la información de estado... Hecho
Los paquetes indicados a continuación se instalaron de forma automática y ya no son necesarios.
 hplip-data libqrcodegencpp1 librlottie0-1 libxxhash0 python3-notify2
Utilice «sudo apt autoremove» para eliminarlos.
Los siguientes paquetes se ELIMINARÁN:
 python-is-python2
Se instalarán los siguientes paquetes NUEVOS:
 python-is-python3
0 actualizados, 1 nuevos se instalarán, 1 para eliminar y 6 no actualizados.
Se necesita descargar 2.364 B de archivos.
Se utilizarán 0 B de espacio de disco adicional después de esta operación.
¿Desea continuar? [Y/n] y
Des:1 http://es.archive.ubuntu.com/ubuntu focal/main amd64 python-is-python3 all 3.8.2-4 [2.364 B]
Descargados 2.364 B en 0s (9.362 B/s)          
(Leyendo la base de datos ... 468110 ficheros o directorios instalados actualmente.)
Desinstalando python-is-python2 (2.7.17-4) ...
Seleccionando el paquete python-is-python3 previamente no seleccionado.
(Leyendo la base de datos ... 468108 ficheros o directorios instalados actualmente.)
Preparando para desempaquetar .../python-is-python3_3.8.2-4_all.deb ...
Desempaquetando python-is-python3 (3.8.2-4) ...
Configurando python-is-python3 (3.8.2-4) ...
```
## Intentar de nuevo la instalación

Podemos instalar de nuevo nuestro paquete instalador en modo automático descargado de la [página de drivers de HP](https://developers.hp.com/hp-linux-imaging-and-printing/gethplip)
```
$ ./hplip-3.21.12.run
…
```

## Agradecimientos

A Ji m, en su atirulo [How to Install the Latest HPLIP Driver in Ubuntu 20.04](https://ubuntuhandbook.org/index.php/2021/07/install-latest-hplip-ubuntu-20-04-fix/)


## Aviso
Esta documentación y su contenido, no implica que funcione en tu caso o determinados casos.  También implica que tienes conocimientos sobre lo que trata, y que en cualquier caso tienes copias de seguridad. El contenido el contenido se entrega, tal y como está, sin que ello implique ningún obligación ni responsabilidad por parte de [Castris](https://castris.com)

Si necesitas soporte profesional puedes contratar con Castris [soporte profesional](https://intranet.castris.com/cart.php?gid=18).

# OpeVPN WireGuard: Servidor y cliente en unos minutos (Ubuntu 20.04)

# VPN WireGuard, como VPN
WireGuard es un protocolo de comunicación open source que implementa un sistema de creación de redes virtuales privadas (vpn) con gran facilidad de uso y un perfil bajo de susceptibilidad a ataques. 

En este doc cómo instalar una vpn en un pequeño VPS con Ubuntu 20.04, y como instalar tu cliente en linux y en android

# Instalación del servidor
Existe un instalador muy simpático llamado [wireguard-install](https://github.com/Nyr/wireguard-install) que funciona a la perfección 

> Existe una versión del mismo para [OpenVPN](https://github.com/Nyr/openvpn-install)

```bash
# wget https://git.io/vpn -O openvpn-install.sh && bash openvpn-install.sh
Welcome to this WireGuard road warrior installer!

What port should WireGuard listen to?
Port [51820]: 

Enter a name for the first client:
Name [client]: castris

Select a DNS server for the client:
   1) Current system resolvers
   2) Google
   3) 1.1.1.1
   4) OpenDNS
   5) Quad9
   6) AdGuard
DNS server [1]: 1
….
suggested packages:
  openresolv | resolvconf
The following NEW packages will be installed:
  wireguard wireguard-tools
0 upgraded, 2 newly installed, 0 to remove and 0 not upgraded.
Need to get 0 B/86.6 kB of archives.
After this operation, 344 kB of additional disk space will be used.
Selecting previously unselected package wireguard-tools.
(Reading database ... 76796 files and directories currently installed.)
Preparing to unpack .../wireguard-tools_1.0.20200513-1~20.04.2_amd64.deb ...
Unpacking wireguard-tools (1.0.20200513-1~20.04.2) ...
Selecting previously unselected package wireguard.
Preparing to unpack .../wireguard_1.0.20200513-1~20.04.2_all.deb ...
Unpacking wireguard (1.0.20200513-1~20.04.2) ...
Setting up wireguard-tools (1.0.20200513-1~20.04.2) ...
wg-quick.target is a disabled or a static unit, not starting it.
Setting up wireguard (1.0.20200513-1~20.04.2) ...
Processing triggers for man-db (2.9.1-1) ...
Created symlink /etc/systemd/system/multi-user.target.wants/wg-iptables.service → /etc/systemd/system/wg-iptables.service.
Created symlink /etc/systemd/system/multi-user.target.wants/wg-quick@wg0.service → /lib/systemd/system/wg-quick@.service.
```

Al final verás un código QR que puedes guardar (a buen seguro) que te permite con la aplicación de Android, configurar con simple escaneo de la imagen tu conexión VPN en Android.

![Código QR Wiredguard](https://multimedia.castris.com/imagenes/wiki/sysadmin/qr-wireguard.jpg)


También verás el fichero de configuración del cliente que has creado.

> Si quieres crear más clientes, desinstalar el servidor, etc, solo tienes que volver a ejecutar `bash openvpn-install.sh`

## Cliente Ubuntu 20.04

### Instalar
Sencillo. Los comandos de abajo instalar wireguard y wireguard-tools

```bash
$ sudo apt update
$ sudo apt install wireguard
```
### Configurar

Copiar el contenido del fichero que la pantalla de instalación nombre_de_cliente.conf en el fichero de tu equipo local.

```bash
sudoedit /etc/wireguard/wg0.conf
```

### Arranque de la VPN

```bash
sudo wg-quick up wg0
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 10.7.0.2/24 dev wg0
[#] ip link set mtu 1420 up dev wg0
[#] resolvconf -a tun.wg0 -m 0 -x
[#] wg set wg0 fwmark 51820
[#] ip -6 route add ::/0 dev wg0 table 51820
[#] ip -6 rule add not fwmark 51820 table 51820
[#] ip -6 rule add table main suppress_prefixlength 0
[#] ip6tables-restore -n
[#] ip -4 route add 0.0.0.0/0 dev wg0 table 51820
[#] ip -4 rule add not fwmark 51820 table 51820
[#] ip -4 rule add table main suppress_prefixlength 0
[#] sysctl -q net.ipv4.conf.all.src_valid_mark=1
[#] iptables-restore -n
```

### Apagado de la conexión
```bash

sudo wg-quick down wg0
[#] ip -4 rule delete table 51820
[#] ip -4 rule delete table main suppress_prefixlength 0
[#] ip -6 rule delete table 51820
[#] ip -6 rule delete table main suppress_prefixlength 0
[#] ip link delete dev wg0
[#] resolvconf -d tun.wg0 -f
[#] iptables-restore -n
[#] ip6tables-restore -n
``` 

### Arranque automático

Si deseas que cuando se cargue el sistema, se conecte por defecto a la VPN puedes habilitar el arranque automático de la VPN con systemctl

```bash
$ sudo systemctl enable wg-quick@wg0
```

O deshabilitar el arranque automático.
```bash
$ sudo systemctl disable wg-quick@wg0
```

### Bonus
Si tu servidor Wireguard quieres proteger, y CSF Firewall quieres usar, un truco tendrás que cocinar.

#### Editar /usr/local/csf/bin/csfpre.sh
**IP** En la última línea es la red que tiene tu wireguard configurada en la interface en el fichero de configuración `/etc/wireguard/wg0.conf` 
**eth0** Es la interface de red de tu servidor. Puede ser esa, ens18, u otra. 

```bash
sudo tee -a /usr/local/csf/bin/csfpre.sh > /dev/null <<EOT 
iptables -A INPUT -i wg0 -j ACCEPT
iptables -A OUTPUT -o wg0 -j ACCEPT
iptables -A FORWARD -i wg0 -o eth0 -j ACCEPT
iptables -A FORWARD -i eth0 -o wg0 -j ACCEPT
iptables -t nat -A POSTROUTING -s  IP/24 -o eth0 -j MASQUERADE
EOT
```

#### Puertos abiertos
Wireguard necesita el puerto que indica el fichero de configuración del servidor, por lo general 51820

Bien debes ponerlo en TCP_IN, UDP_IN

En cuanto al OUT, deberías abrir todos. (o los que quieras, es tu lio)

```bash
TCP_OUT = "1:65535"
UDP_OUT = "1:65535"
```

Ya está. Reinicia tu firewall.

## Enlaces 
- [WireGuard : Configure Client (Ubuntu)](https://www.server-world.info/en/note?os=Ubuntu_20.04&p=wireguard&f=2)
- [WireGuard](https://www.wireguard.com/)
- [Linux - Autostart WireGuard in systemd](https://www.ivpn.net/knowledgebase/linux/linux-autostart-wireguard-in-systemd/)



## Aviso
Esta documentación y su contenido, no implica que funcione en tu caso o determinados casos.  También implica que tienes conocimientos sobre lo que trata, y que en cualquier caso tienes copias de seguridad. El contenido el contenido se entrega, tal y como está, sin que ello implique ningún obligación ni responsabilidad por parte de [Castris](https://castris.com)

Si necesitas soporte profesional puedes contratar con Castris [soporte profesional](https://intranet.castris.com/cart.php?gid=18).





Durante mucho tiempo use Squid para ciertas cosas, pero bueno, la verdad es que una VPN cumple muchas más expectativas de uso para la administración de sistemas.
En particular, que puedo tener un punto de acceso a los sistemas como administrador (root) sin tener que usar un host dinámico, para cuando salgo fuera de la oficina y tener que andar con frikadas en el móvil.

# Centos 8 : Failed to download metadata for repo 'appstream'

## Introducción
Algunasveces con los cambios que existen en el ecosistema linux, como pueden ser los movimientos derivados de las actividades de Redhat, y en consecuencia sus derivados más directo Fedora y Centos, te saltan problemas esta vez y gracias a [Proyectoa](https://proyectoa.com/solucion-al-error-failed-to-download-metadata-for-repo-appstream-en-centos-8/) no perdi mucho el tiempo. ¡¡Gracias!!

## Error

El error es simple, quieres actualiza o instalar algo y zas.

> CentOS Linux 8 – AppStream Error: Failed to download metadata for repo ‘appstream’: Cannot prepare internal mirrorlist: No URLs in mirrorlist

```bash
yum -y install elink                                               
CentOS Linux 8 - AppStream                                                                                                                                   163  B/s |  38  B     00:00    
Error: Failed to download metadata for repo 'appstream': Cannot prepare internal mirrorlist: No URLs in mirrorlist
```

## Solución
### Comentar los mirrorlist
```bash
sed -i -e "s|mirrorlist=|#mirrorlist=|g" /etc/yum.repos.d/CentOS-*
```

### Modificar el baseurl
Modificaremos el baseurl de todos los ficheros del yum.repos.d

```bash 
sed -i -e "s|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g" /etc/yum.repos.d/CentOS-*
``` 

### Limpieza de caches y actualización del repositorio
```bash 
[root@monitor01 ~] dnf clean all
91 files removed
[root@monitor01 ~]# dnf swap centos-linux-repos centos-stream-repos
CentOS Linux 8 - AppStream                                                                                                                                   2.2 MB/s | 8.4 MB     00:03    
CentOS Linux 8 - BaseOS                                                                                                                                      2.5 MB/s | 4.6 MB     00:01    
CentOS Linux 8 - Extras                                                                                                                                       70 kB/s |  10 kB     00:00    
MariaDB                                                                                                                                                      516 kB/s | 601 kB     00:01    
ELRepo.org Community Enterprise Linux Repository - el8                                                                                                       321 kB/s | 171 kB     00:00    
Extra Packages for Enterprise Linux 8 - x86_64                                                                                                               3.1 MB/s |  11 MB     00:03    
Extra Packages for Enterprise Linux Modular 8 - x86_64                                                                                                       1.1 MB/s | 1.0 MB     00:00    
Remi's Modular repository for Enterprise Linux 8 - x86_64                                                                                                    1.3 MB/s | 973 kB     00:00    
Safe Remi's RPM repository for Enterprise Linux 8 - x86_64                                                                                                   2.4 MB/s | 2.1 MB     00:00    
Webmin Distribution Neutral                                                                                                                                  3.2 MB/s |  18 MB     00:05    
Zabbix Official Repository - x86_64                                                                                                                          212 kB/s | 240 kB     00:01    
Zabbix Official Repository non-supported - x86_64                                                                                                            1.9 kB/s | 1.2 kB     00:00    
Dependencies resolved.
=============================================================================================================================================================================================
 Package                                               Architecture                             Version                                      Repository                                 Size
=============================================================================================================================================================================================
Installing:
 centos-stream-repos                                   noarch                                   8-3.el8                                      extras                                     19 k
Removing:
 centos-linux-repos                                    noarch                                   8-3.el8                                      @baseos                                    26 k

Transaction Summary
=============================================================================================================================================================================================
Install  1 Package
Remove   1 Package

Total download size: 19 k
Is this ok [y/N]: y
Downloading Packages:
centos-stream-repos-8-3.el8.noarch.rpm                                                                                                                       227 kB/s |  19 kB     00:00    
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Total                                                                                                                                                        216 kB/s |  19 kB     00:00     
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transaction
  Preparing        :                                                                                                                                                                     1/1 
  Installing       : centos-stream-repos-8-3.el8.noarch                                                                                                                                  1/2 
  Erasing          : centos-linux-repos-8-3.el8.noarch                                                                                                                                   2/2 
warning: /etc/yum.repos.d/CentOS-Linux-PowerTools.repo saved as /etc/yum.repos.d/CentOS-Linux-PowerTools.repo.rpmsave
warning: /etc/yum.repos.d/CentOS-Linux-Plus.repo saved as /etc/yum.repos.d/CentOS-Linux-Plus.repo.rpmsave
warning: /etc/yum.repos.d/CentOS-Linux-HighAvailability.repo saved as /etc/yum.repos.d/CentOS-Linux-HighAvailability.repo.rpmsave
warning: /etc/yum.repos.d/CentOS-Linux-FastTrack.repo saved as /etc/yum.repos.d/CentOS-Linux-FastTrack.repo.rpmsave
warning: /etc/yum.repos.d/CentOS-Linux-Extras.repo saved as /etc/yum.repos.d/CentOS-Linux-Extras.repo.rpmsave
warning: /etc/yum.repos.d/CentOS-Linux-Devel.repo saved as /etc/yum.repos.d/CentOS-Linux-Devel.repo.rpmsave
warning: /etc/yum.repos.d/CentOS-Linux-ContinuousRelease.repo saved as /etc/yum.repos.d/CentOS-Linux-ContinuousRelease.repo.rpmsave
warning: /etc/yum.repos.d/CentOS-Linux-BaseOS.repo saved as /etc/yum.repos.d/CentOS-Linux-BaseOS.repo.rpmsave
warning: /etc/yum.repos.d/CentOS-Linux-AppStream.repo saved as /etc/yum.repos.d/CentOS-Linux-AppStream.repo.rpmsave

  Verifying        : centos-stream-repos-8-3.el8.noarch                                                                                                                                  1/2
  Verifying        : centos-linux-repos-8-3.el8.noarch                                                                                                                                   2/2

Installed:
  centos-stream-repos-8-3.el8.noarch
Removed:
  centos-linux-repos-8-3.el8.noarch

Complete! 
```

Ya lo tienes.

##### Aviso
Esta documentación y su contenido, no implica que funcione en tu caso o determinados casos. 
También implica que tienes conocimientos sobre lo que trata, y que en cualquier caso tienes copias de seguridad.
El contenido el contenido se entrega, tal y como esta, sin que ello implique ningún obligación ni responsabilidad por parte de [Castris](https://castris.com)

Si necesitas soporte profesional puedes contratar con Castris [soporte profesional](https://intranet.castris.com/cart.php?gid=18).

# Apuntes de bash, sed, awk para administradores

A veces hay que analizar, extrare datos de los logs (benditos logs) y viene bien una chuleta a mano, para los que no estamos todo el día con el sistema, o la memoria muy floja por la edad.

# Básicos de sed

## Eliminar comentarios y lineas en blanco con sed (bash o zsh)

Correción realziada el 22/08/2024 
~~sed '/^\s*#/d' file_original.txt~~
```zsh
sed '/^\s*#/d;/^\s*$/d' file_original.txt

```
## Eliminar de un fichero todo menos una cadena (csf.deny)
)
Una comando muy útil cuando queremos, por ejemplo, vaciar el csf.deny salvo las lineas que contiene un cadena "do not delete" o similar

```
sed -i '/do not delete/!d' /etc/csf/csf.deny
## O la de abajo segun configuraciones
sed -i -e '/do not delete/!d' /etc/csf/csf.deny
```

##### Aviso
Esta documentación y su contenido, no implica que funcione en tu caso o determinados casos. 
También implica que tienes conocimientos sobre lo que trata, y que en cualquier caso tienes copias de seguridad.
El contenido el contenido se entrega, tal y como está, sin que ello implique ningún obligación ni responsabilidad por parte de [Castris](https://castris.com)

Si necesitas soporte profesional puedes contratar con Castris [soporte profesional](https://intranet.castris.com/cart.php?gid=18).

# Eliminar la extensión de ficheros para procesos en lote bash

## Introducción
Alguna vez uno tiene que hacer cositas, en el que queremos restaurar un lote de ficheros de backups cuyo formato es `nombre_de_usuario.tar.gz` pero el script para hacer el restore requiere el nombre del usuario y no el nombre de fichero


## Eliminar la extnesón de ficheros tar.gz
```
ls | sed -n '/\.tar\.gz$/s///p'
```
Asi nos quedaria algo asi.

```
for user in $(ls *.tar.gz | sed -n '/\.tar\.gz$/s///p'); do echo $user; command $user; done 

```

##### Aviso
Esta documentación y su contenido, no implica que funcione en tu caso o determinados casos. 
También implica que tienes conocimientos sobre lo que trata, y que en cualquier caso tienes copias de seguridad.
El contenido el contenido se entrega, tal y como esta, sin que ello implique ningún obligación ni responsabilidad por parte de [Castris](https://castris.com)

Si necesitas soporte profesional puedes contratar con Castris [soporte profesional](https://intranet.castris.com/cart.php?gid=18).

# Guía de Comando `find` - Especialidad para Sysadmin

## Introudcción

Como sysadmin experimentado, sabes que `find` es una navaja suiza. Aquí tienes los patrones más útiles, organizados por casos de uso prácticos:

## Limpieza por Antigüedad

**Archivos más antiguos de X días:**

```bash
# Ver qué se borraría (siempre prueba primero)
find /ruta -type f -mtime +30 -ls

# Borrar archivos de más de 30 días
find /ruta -type f -mtime +30 -delete

# Más seguro: con confirmación
find /ruta -type f -mtime +30 -ok rm {} \;
```

**Por horas (útil para logs):**

```bash
# Archivos de más de 24 horas
find /var/log -type f -mmin +1440 -delete

# De más de 2 horas
find /tmp -type f -mmin +120 -delete
```

## Limpieza por Tamaño

**Archivos grandes:**

```bash
# Archivos de más de 100MB
find /ruta -type f -size +100M -ls

# Mayores de 1GB
find /ruta -type f -size +1G -delete

# Combinar: grandes Y antiguos
find /ruta -type f -size +50M -mtime +7 -delete
```

**Archivos vacíos:**

```bash
# Archivos de 0 bytes
find /ruta -type f -empty -delete

# Directorios vacíos
find /ruta -type d -empty -delete
```

## Patrones de Limpieza Combinados

**El combo perfecto para /tmp:**
```bash
find /tmp -type f \( -mtime +7 -o -size +500M \) -delete
```

**Logs rotativos:**
```bash
# Solo .log antiguos, preservar los .gz
find /var/log -name "*.log" -mtime +30 -delete
```

**Core dumps y temporales:**
```bash
# Múltiples patrones
find /ruta \( -name "core.*" -o -name "*.tmp" -o -name "*.temp" \) -mtime +1 -delete
```

## Modificadores de Tiempo Útiles

- `-mtime +N` : modificado hace MÁS de N días
- `-mtime -N` : modificado hace MENOS de N días  
- `-mtime N` : modificado hace EXACTAMENTE N días
- `-atime` : último acceso
- `-ctime` : cambio de metadatos

## Consejos de Veterano

**Siempre prueba primero:**

```bash
# Reemplaza -delete por -ls para ver qué haría
find /ruta -criteria -ls
```

**Para grandes volúmenes, usa xargs:**

```bash
find /ruta -type f -mtime +30 -print0 | xargs -0 rm
```

**Excluir directorios específicos:**

```bash
find /var -path "/var/lib" -prune -o -type f -mtime +30 -delete
```

La clave está en la combinación de criterios. El `find` permite ser muy preciso, pero siempre con la prudencia que dan los años de experiencia.

##### Aviso
Esta documentación y su contenido, no implica que funcione en tu caso o determinados casos. 
También implica que tienes conocimientos sobre lo que trata, y que en cualquier caso tienes copias de seguridad.
El contenido el contenido se entrega, tal y como está, sin que ello implique ningún obligación ni responsabilidad por parte de [Castris](https://castris.com)

Si necesitas soporte profesional puedes contratar con Castris [soporte profesional](https://intranet.castris.com/store/soporte-profesional).

# NetworkManager

## Introducción
**NetworkManager** es una herramienta de configuración y control de la red, que suele formar parte de distribuciones como RedHat.

Un inconveniente que tiene para los que manejamos servidores es que no es una herramienta muy amigable para hacer cambios, y suele crear un enlace simbólico a un fichero que contiene la información.

Muchas de las soluciones propuestas que puedes encontrar pasan por la eliminación del enlace duro y el bloque de escritura del fichero, pero a mi entender eso es una ñapa.

## Opciones
Una es desinstalar el NetworkManager (para mi la mejor) pero si no quieres o se trata de un sistema en el que tienes que hacer cambios sin meterte en líos, por lo que sea, hay otra opción. 

Esta opción está comprobada en sistemas Redhat y derivados. Pero probablemente pueda servirte con matices la información para hacerlo en Ubuntu, Debian, Alma Linux etc.

### Desactivar la configuración DNS en el NetworkManager
Como root con el siguiente contenido

#### Crear fichero /etc/NetworkManager/conf.d/90-dns-none.conf

```
[main]
dns=none
```
### Recarga del NetworkManager
```
# systemctl reload NetworkManager
```

#### Eliminación del texto Generated by NetworkManager

Para evitar confusiones, deberíamos eliminar el texto que originalmente había en el fichero `/etc/resolv.conf` **Generated by NetworkManager**



## Aviso
Esta documentación y su contenido, no implica que funcione en tu caso o determinados casos.  También implica que tienes conocimientos sobre lo que trata, y que en cualquier caso tienes copias de seguridad. El contenido el contenido se entrega, tal y como está, sin que ello implique ningún obligación ni responsabilidad por parte de [Castris](https://castris.com)

Si necesitas soporte profesional puedes contratar con Castris [soporte profesional](https://intranet.castris.com/cart.php?gid=18).

# Eliminación de correos de un emisor en la cola de Postfix

## Introducción
A veces tras una alerta de spam, si se ha producido un bloqueo de nuetsra IP de parte del proveedor, como por ejemplo OVH que tiene un sistema antispam de salida que boquea el puerot 25, es necesario limpiar la cola pendiente de correos en postfix, antes de desbloquear nuestra IP, o podría ser baneada nuevamente. 


## Postfix

En mi caso para clientes díscolos con problemas con Microsoft, tengo un cluster de microservicios con postfix que hace de servidores de smart relay de correo, que usan Postfix.


**Editado el 09/10/2025:** Corrección relativa al comando de eliminacion

```bash
> email=sender@example.com
// Devuelve los emails en la cola con el ID de Postfix
> mailq | grep $email
D0336A03FD     4946 Tue Jun 13 12:32:51  info@xxxxxx.com
D321C9F989    71037 Tue Jun 13 11:58:16  info@xxxxxx.com
D001CA03E5     4938 Tue Jun 13 12:32:51  info@xxxxxx.com
// este comando borraria un email por id
> postsuper -d <queue_id> 
// Este sería la combinación de todo en uno, que eliminaria todos los crreos en la cola para esa cuenta de correo electrónico
> mailq | grep "$email" | awk '/^[A-F0-9]+/ {print $1}' | sed 's/\*$//' | postsuper -d -
postsuper: C7A93A0F84: removed
postsuper: C776AA0F83: removed
postsuper: C5C4DA0DF9: removed
postsuper: C62F1A0E02: removed
postsuper: CFFF6A03E3: removed
...
postsuper: Deleted: 227 messages
```

**Editado el 09/10/2025:** Añadido
### Si el caso es limpiar el destinatario por bounces

```
postqueue -p | awk -v email="$email" '
    /^[A-F0-9]+/ {queue_id = $1}
    $0 ~ email && queue_id != "" {
        gsub(/[*!]$/, "", queue_id)
        print queue_id
    }
' | postsuper -d -
```

## Bonus
El de abajo, es ya en caso de que queramos borrar todo, aunque yo nunca suelo usarlo porque no pueden pagar justos por pecadores.

```
postsuper -d ALL
```

##### Aviso
Esta documentación y su contenido, no implica que funcione en tu caso o determinados casos. 
También implica que tienes conocimientos sobre lo que trata, y que en cualquier caso tienes copias de seguridad.
El contenido el contenido se entrega, tal y como está, sin que ello implique ningún obligación ni responsabilidad por parte de [Castris](https://castris.com)

Si necesitas soporte profesional puedes contratar con Castris [soporte profesional](https://intranet.castris.com/cart.php?gid=18).

# RaspberryPi

# Ubuntu 22.04 para RaspberryPi 4 64bits no configura la red cableada

## RaspberryPi 4B con Ubuntu 22.04
La verdad es que no me lo esperaba. No suelo configurar las raspberry con pantalla, y esta vez me fallo, y anda a ciegas ya que no tenía un cable **hdmi A-D** así que me sonaba raro.

## Instalación del S.O.
Habia usado el [RaspberryPi Imager](https://www.raspberrypi.com/software/) para macos y había elegido como S.O., una **Ubuntu 22.04** de 64 bits, mas que nada porque la voy a usar con MariaDB y Elasticsearch y prefiero que sea con 64 bits.

Cuando instalé el S.O. en las tarjetas configuró con los datos de acceso, mi llave, el nombre de hosts y esas cosas que me hacen la vida más fácil cuando se trata de un serverless sin pantalla. 

La cuestión es que no me funcionaba la red de cable, pero si me saltaba la wifi. 

## Análisis 

> NOTA: Muchos sitios usan sudo porque están en modo user. Yo tras muchos años me siento más cómodo como root, porque sé lo que hago, pero no aconsejo el uso de root porque estoy muy habituado a arreglar desaguisados de “expertos” en sistema en producción. (Mis rasp no están en producción)

Tras entrar via wifi, observe que la tarjeta de red estaba reconocida pero desactivada.

```
abkrim@raspdev:~$ sudo su -
root@raspdev:~# ip addr
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
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether d8:3a:dd:34:4d:6f brd ff:ff:ff:ff:ff:ff
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether d8:3a:dd:34:4d:70 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.82/24 metric 600 brd 192.168.1.255 scope global dynamic wlan0
       valid_lft 43061sec preferred_lft 43061sec
    inet6 fe80::da3a:ddff:fe34:4d70/64 scope link
       valid_lft forever preferred_lft forever

root@raspdev:~# networkctl
IDX LINK  TYPE     OPERATIONAL SETUP
  1 lo    loopback carrier     unmanaged
  2 eth0  ether    off         unmanaged
  3 wlan0 wlan     routable    configured

3 links listed.

```

### Probar a levantarlo
Simple la prueba que funcionó y levanto la red cableada

```
root@raspdev:~# dhclient -v
Internet Systems Consortium DHCP Client 4.4.1
Copyright 2004-2018 Internet Systems Consortium.
All rights reserved.
For info, please visit https://www.isc.org/software/dhcp/

Listening on LPF/wlan0/d8:3a:dd:34:4d:70
Sending on   LPF/wlan0/d8:3a:dd:34:4d:70
Listening on LPF/eth0/d8:3a:dd:34:4d:6f
Sending on   LPF/eth0/d8:3a:dd:34:4d:6f
Sending on   Socket/fallback
DHCPDISCOVER on wlan0 to 255.255.255.255 port 67 interval 3 (xid=0xbe3f7d64)
DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 3 (xid=0x98bad722)
DHCPOFFER of 192.168.1.82 from 192.168.1.1
DHCPREQUEST for 192.168.1.82 on wlan0 to 255.255.255.255 port 67 (xid=0x647d3fbe)
DHCPACK of 192.168.1.82 from 192.168.1.1 (xid=0xbe3f7d64)
RTNETLINK answers: File exists
bound to 192.168.1.82 -- renewal in 18230 seconds.
root@raspdev:~# ip addr
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
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether d8:3a:dd:34:4d:6f brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.81/24 brd 192.168.1.255 scope global dynamic eth0
       valid_lft 43195sec preferred_lft 43195sec
    inet6 fe80::da3a:ddff:fe34:4d6f/64 scope link
       valid_lft forever preferred_lft forever
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether d8:3a:dd:34:4d:70 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.82/24 metric 600 brd 192.168.1.255 scope global dynamic wlan0
       valid_lft 42684sec preferred_lft 42684sec
    inet6 fe80::da3a:ddff:fe34:4d70/64 scope link
       valid_lft forever preferred_lft forever
```

Luego lei un tip, [Fix Eth0 rpi ubuntut](https://www.freshblurbs.com/blog/2022/08/07/fix-eth0-rpi-ubuntu.html) pero su salida me parecio un poco fea.


## Solución 

Editar netplan teniendo en cuenta la advertencia de sobre escritura. En mi caso no quiero tenerla desactivada o eliminada de la configuración pero podría eliminar la wifi.

```
root@raspdev:~# cat /etc/netplan/50-cloud-init.yaml
# This file is generated from information provided by the datasource.  Changes
# to it will not persist across an instance reboot.  To disable cloud-init's
# network configuration capabilities, write a file
# /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following:
# network: {config: disabled}
network:
    version: 2
    ethernets:
      eth0:
        dhcp4: yes
    wifis:
        renderer: networkd
        wlan0:
            access-points:
                DIWAN:
                    password: e717960c10ec0b8216d0HJKHJKHJKHJHJKHKH58b60d6f91493837a3a49
            dhcp4: true
            optional: true
```

Es necesito crear el otro fichero con `network: {config: disabled}`
```
root@raspdev:~# cat /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg
network: {config: disabled}
```

Ejecutamos ` netplan apply` y voila ya lo tenemos.

Podemos reiniciar nuestra máquina y veremos que tenemos las dos redes.

## Desactivación de la wifi sin eliminar de netplan
Bueno, en lugar de script extraños, usaremos el **Systemctl** para esto

### CRear el fichero de servicio
` /etc/systemd/system/deactivate-wifi.service`con el contenido
```
[Unit]
After=network.target

[Service]
ExecStart=/usr/local/bin/off-wifi.sh

[Install]
WantedBy=default.target
```

### Crear el fichero off-wifi.sh
Fichero `/usr/local/bin/off-wifi.sh`
```
#!/bin/bash
ip link set dev wlan0 down
```

### Permisos y recarga del Systemctl

```
root@raspdev:~# chmod 664 /etc/systemd/system/disk-space-check.service
root@raspdev:~# chmod 664 /etc/systemd/system/deactivate-wifi.service.sh
root@raspdev:~# systemctl daemon-reload

```

Ya está listo para reiniciar y tras el reinicio eficaz con la red cableada, se apagará la wifi.



##### Aviso
Esta documentación y su contenido, no implica que funcione en tu caso o determinados casos. 
También implica que tienes conocimientos sobre lo que trata, y que en cualquier caso tienes copias de seguridad.
El contenido el contenido se entrega, tal y como está, sin que ello implique ningún obligación ni responsabilidad por parte de [Castris](https://castris.com)

Si necesitas soporte profesional puedes contratar con Castris [soporte profesional](https://intranet.castris.com/cart.php?gid=18).

# Tip cortos para linux

## Instala paquetes desde una lista de paquetes linea a linea
```
sudo apt update && xargs -a fichero.txt -I {} sudo apt install -y {}
```

## Llaves con algoritmo EdDSA (Ed25519) 🔐 

En esta era donde la inteligencia artificial avanza rápidamente y la computación cuántica comienza a dejar de ser ciencia ficción, es prudente reforzar nuestras prácticas de seguridad, especialmente en entornos de administración remota.

Una mejora sencilla pero significativa es abandonar el tradicional algoritmo RSA en favor de Ed25519, una variante del algoritmo EdDSA (Edwards-curve Digital Signature Algorithm), que ofrece:
- Mayor seguridad con claves más pequeñas (256 bits vs 2048/4096 en RSA)
- Mejor rendimiento y menor consumo de recursos
- Recomendado por OpenSSH y ampliamente compatible con servidores modernos

### Generar una clave Ed25519 sin interacción (ssh-keygen)

Para generar una clave SSH sin contraseña y sin preguntas interactivas, ejecuta:

```
ssh-keygen -t ed25519 -a 100 -C "$(whoami)@$(hostname)" -f ~/.ssh/id_ed25519 -N ""
```

🔍 Explicación de cada parámetro:

```
Opción	Significado
-t ed25519	Tipo de clave: Ed25519 (moderna y segura)
-a 100	Número de rondas de derivación con bcrypt (protege si hay passphrase)
-C "$(whoami)@$(hostname)"	Comentario con nombre de usuario y host para identificar la clave
-f ~/.ssh/id_ed25519	Ruta donde se guarda la clave privada (y .pub para la pública)
-N ""	Passphrase vacía (clave sin cifrar, útil para automatismos)
```

### ¿Cuándo usar claves sin passphrase?

Este tipo de clave puede ser útil en:
- Automatizaciones y scripts donde no es posible ingresar manualmente la passphrase
- Acceso controlado a través de restricciones en authorized_keys (from=, command=, etc.)
- Sistemas de CI/CD o servidores internos sin exposición directa

> ⚠️ Advertencia: No usar claves sin passphrase en equipos compartidos o inseguros. Si alguien obtiene la clave privada, tendrá acceso inmediato sin requerir nada más.

### 🧪 Verificar la clave generada

Puedes ver el fingerprint de la clave:

```
ssh-keygen -lf ~/.ssh/id_ed25519
```

### 📌 Referencias
- [OpenSSH Key Management](https://man.openbsd.org/ssh-keygen)
- [SSH Key: Ed25519 vs RSA](https://security.stackexchange.com/questions/90077/ssh-key-ed25519-vs-rsa)

##### Aviso
Esta documentación y su contenido, no implica que funcione en tu caso o determinados casos. 
También implica que tienes conocimientos sobre lo que trata, y que en cualquier caso tienes copias de seguridad.
El contenido el contenido se entrega, tal y como está, sin que ello implique ningún obligación ni responsabilidad por parte de [Castris](https://castris.com)

Si necesitas soporte profesional puedes contratar con Castris [soporte profesional](https://intranet.castris.com/store/soporte-profesional).

# Mysql Master-Slave como volcar los datos

## Escenario
Una instalación **master-slave** de Mysql/MariaDB no es el escenario perfecto, pero muchas veces tenemos que ajustarnos a economias de medios por necesidad del cliente o por costes. 

Tambin es cierto que hay alguno escenerios que no requiere de un **master-master** como puede ser el ejecutar las copias de seguridad de grandes bases de datos, en un entorno esclavo con el fin de no bloquear o saturar el servidor maestro, o ejecutar consultas pesadas, en el **esclavo**

### Instalación

Bien no entro en detalles ya que no es el alcance de este tip salvo:
- Ambos deben tener la misma versión de MySQL/MariaDb (no es obligatario en muchos caso, pero es altamente aconsejable)
- Uso de red privada en su defecto conexion [mysql encriptada](https://dev.mysql.com/doc/refman/8.0/en/encrypted-connections.html)

#### Servidor maestro

```
mysqldump -u root --all-databases --master-data --single-transaction > masterdump.sql
```

#### Servidor esclavo
Si el servidor esclavo esta en modo `SLAVE` debemos pararlo con `SLAVE STOP;` dentro de la consola `mysql>` 

```
rsync -avzzz --progress -e "ssh -pPUERTO" host.master.tld:/root/masterdump.sql .
mysql < masterdump.sql
``` 

> Esto es una aproximación y una operativo que yo uso. Requiere, que tengas las llaves SSH entre ambos servidores, que sepas usar rsync. Puedes buscar aqui en mis tips, o en [Manuales Castris](https://manuales.castris.com) más información. También tienes una colección de tips en [Castris :: blog :: MySQL](https://castris.com/?s=mysql)

Después debes volver a activar el `SLAVE` dentro de la consola mysql

```
mysql> START SLAVE;
mysql> SHOW SLAVE STATUS\G
*************************** 1. row ***************************
                Slave_IO_State: Waiting for master to send event
                   Master_Host: master.hosts.tld
                   Master_User: repli_user
                   Master_Port: 3306
                 Connect_Retry: 10
               Master_Log_File: mariadb-bin.000001
           Read_Master_Log_Pos: 9341629
                Relay_Log_File: relay-bin.000003
                 Relay_Log_Pos: 319222
         Relay_Master_Log_File: mariadb-bin.000001
              Slave_IO_Running: Yes
             Slave_SQL_Running: Yes
               Replicate_Do_DB:
           Replicate_Ignore_DB:
            Replicate_Do_Table:
        Replicate_Ignore_Table:
       Replicate_Wild_Do_Table:
   Replicate_Wild_Ignore_Table:
                    Last_Errno: 0
                    Last_Error:
                  Skip_Counter: 0
           Exec_Master_Log_Pos: 9341629
               Relay_Log_Space: 3247284
               Until_Condition: None
                Until_Log_File:
                 Until_Log_Pos: 0
            Master_SSL_Allowed: Yes
            Master_SSL_CA_File:
            Master_SSL_CA_Path:
               Master_SSL_Cert:
             Master_SSL_Cipher:
                Master_SSL_Key:
         Seconds_Behind_Master: 0
 Master_SSL_Verify_Server_Cert: No
                 Last_IO_Errno: 0
                 Last_IO_Error:
                Last_SQL_Errno: 0
                Last_SQL_Error:
   Replicate_Ignore_Server_Ids:
              Master_Server_Id: 1
                Master_SSL_Crl:
            Master_SSL_Crlpath:
                    Using_Gtid: No
                   Gtid_IO_Pos:
       Replicate_Do_Domain_Ids:
   Replicate_Ignore_Domain_Ids:
                 Parallel_Mode: conservative
                     SQL_Delay: 0
           SQL_Remaining_Delay: NULL
       Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it
              Slave_DDL_Groups: 0
Slave_Non_Transactional_Groups: 2528
    Slave_Transactional_Groups: 2035
1 row in set (0.000 sec)
```


##### Aviso
Esta documentación y su contenido, no implica que funcione en tu caso o determinados casos. 
También implica que tienes conocimientos sobre lo que trata, y que en cualquier caso tienes copias de seguridad.
El contenido el contenido se entrega, tal y como está, sin que ello implique ningún obligación ni responsabilidad por parte de [Castris](https://castris.com)

Si necesitas soporte profesional puedes contratar con Castris [soporte profesional](https://intranet.castris.com/store/soporte-profesional).

# Por qué no uso Triggers ni Procedures en SQL

## Triggers/Procedures versus Observers

Llevo más de 30 años trabajando con SQL en sistemas UNIX, dando soporte a miles de clientes y actuando como bombero IT. Sí, bombero, porque apago fuegos técnicos que surgen como resultado de malas prácticas, o, lo que es peor, de seguir utilizando reliquias del pasado sin actualizarse.

Una de esas reliquias son los **TRIGGERS** y las **PROCEDURES**.

Qué bonitas eran en su día, qué problemáticas eran y qué problemáticas siguen siendo.

La cuestión es que un motor de bases de datos es lo que es, y en mi opinión, introducir lógicas de programación dentro del motor de bases de datos, a día de hoy, está obsoleto y es peligroso (siempre lo fue).

- Un cambio de versión del motor **SGBD**.
- Un cambio de **SGBD**.
- Un problema que obliga a restaurar desde un backup y este es incompleto por error del comando usado en la copia de seguridad.

Y seguramente habrá más.

Lo cierto es que todo eso incumple los principios SOLID, al desplazar en un nivel superior, ya no en la aplicación, sino en el motor de datos, unas lógicas que son **responsabilidad** del programa. Al menos así lo veo en el año 2024.

Por ejemplo, podemos utilizar **Observers** a nivel de aplicación.

- Fáciles de programar.
- Dentro de la app.
- Cumplen el objetivo de ser **sólidos** en nuestra aplicación, y dotan de la responsabilidad al **modelo** sobre el que se ejecutarían esos triggers.
- Fácilmente testeables (o los tests, esa cosa que se sigue odiando en muchos lugares, quizás por la falta de... visión o ganas de actualizarse).

### Anécdota

En el caso que me ocupa, un Magento 1.9, obsoleto, corriendo en un MariaDB 10.4, PHP 5.4, etc., y que está en producción con decenas de ventas al día.

No se puede trabajar en producción, y mucho menos si la aplicación se llama **Magento**, paradigma de los problemas en el comercio electrónico.

Así que cuando me enfrenté a ello, y pese a ser root, estar acostumbrado a lidiar con MySQL (y sus forks), me entró la pena negra al ver que había triggers heredados de un intento de migración con la herramienta oficial de Magento para tratar de pasar a Magento 2.

No era la primera vez. Alguna vez lo había visto en un restore de urgencia de otro cliente.

Algo que te rompe cuando estás con bases de datos de 5 a 10Gb, bajo presión, en producción,...

```sql
ERROR 1449 (HY000) at line 924: The user specified as a definer ('migration'@'99.99.99.99') does not exist
```

Bueno, como ya sabía de qué iba al ver la palabra **migration** y el contexto **Magento**, sabía que había más de un trigger.



### Procesos de reparación

#### Obtener todos los triggers
```sql
mysql> SHOW TRIGGERS;
Trigger Event   Table   Statement       Timing  Created sql_mode        Definer character_set_client    collation_connection    Database Collation
trg_catalog_category_entity_after_insert        INSERT  catalog_category_entity BEGIN\nINSERT INTO m2_cl_catalog_category_entity (`entity_id`, `operation`) VALUES (NEW.entity_id, 'INSERT')ON DUPLICATE KEY UPDATE operation = 'INSERT';\nEND  AFTER   NULL            migration@176.31.31.251 utf8    utf8_general_ci latin1_swedish_ci
...
...
```

Script para eliminar triggers
Mejor un script, que llamaremos `drop_triggers.sh`.


```sql 
# Reemplaza los valores de `usuario`, `password`, y `nombre_de_tu_base` con tus propios valores.
MYSQL_USER="usuario"
MYSQL_PASS="password"
DATABASE_NAME="nombre_de_tu_base"

# Obtiene la lista de triggers y los elimina uno por uno
mysql -u"$MYSQL_USER" -p"$MYSQL_PASS" -D"$DATABASE_NAME" -e "SELECT TRIGGER_NAME FROM information_schema.TRIGGERS WHERE TRIGGER_SCHEMA = '$DATABASE_NAME';" | grep -v TRIGGER_NAME | while read TRIGGER_NAME; do
    mysql -u"$MYSQL_USER" -p"$MYSQL_PASS" -D"$DATABASE_NAME" -e "DROP TRIGGER IF EXISTS \`$TRIGGER_NAME\`;";
done
```

> Por favor, en serio. Este es algo a tener en cuenta. Backups y backups, y de los que sabes que funcionarán. Yo no soy responsable de tus actos (ni nadie).


#### Pruebas en docker

Bueno, si antes de hacer líos, necesitas trabajar en un entorno de desarrollo, con docker, te dejo unos pasos, ya que el otro puede ser algo más completo.

##### Obtener la lista
Puedes como alternativa obtener la lista de trigger de la base de datos, con [TablePlus](https://tableplus.com/) en formato CSV, y crear un fichero con la columna que define el nombre del trigger, por ejemplo `triggers.txt`.

##### Docker
Obtenemos el nombre del container de mysql

```bash
$ docker ps
CONTAINER ID   IMAGE               COMMAND                  CREATED        STATUS        PORTS                    NAMES
b49e62fa5b29   web4-apache-httpd   "httpd-foreground"       19 hours ago   Up 19 hours   0.0.0.0:8080->80/tcp     web4-apache-httpd-1
bfd56c6f6c65   web4-php-fpm        "docker-php-entrypoi…"   19 hours ago   Up 19 hours   9000/tcp                 web4-php-fpm-1
d804a906babd   mariadb:10.1        "docker-entrypoint.s…"   19 hours ago   Up 19 hours   0.0.0.0:3337->3306/tcp   web4-mysql-db-1
```
Subimos el fichero
```bash
docker cp triggers.sh web4-mysql-db-1:/root/
```

Y ejecutarlo en el container.

```
$ docker exec -it  web4-mysql-db-1 bash
root@d804a906babd:/# cd /root
# chmod 755  delete_triggers.sh
# ./delete_triggers.sh
Eliminando trigger: trg_catalog_category_entity_after_insert
Eliminando trigger: trg_catalog_category_entity_after_update
Eliminando trigger: trg_catalog_category_entity_after_delete
Eliminando trigger: trg_catalog_category_entity_datetime_after_insert
Eliminando trigger: trg_catalog_category_entity_datetime_after_update
Eliminando trigger: trg_catalog_category_entity_datetime_after_delete
Eliminando trigger: trg_catalog_category_entity_decimal_after_insert
Eliminando trigger: trg_catalog_category_entity_decimal_after_update
Eliminando trigger: trg_catalog_category_entity_decimal_after_delete
Eliminando trigger: trg_catalog_category_entity_int_after_insert
Eliminando trigger: trg_catalog_category_entity_int_after_update
Eliminando trigger: trg_catalog_category_entity_int_after_delete
Eliminando trigger: trg_catalog_category_entity_text_after_insert
Eliminando trigger: trg_catalog_category_entity_text_after_update
Eliminando trigger: trg_catalog_category_entity_text_after_delete
Eliminando trigger: trg_catalog_category_entity_varchar_after_insert
Eliminando trigger: trg_catalog_category_entity_varchar_after_update
Eliminando trigger: trg_catalog_category_entity_varchar_after_delete
Eliminando trigger: trg_catalog_category_product_after_insert
Eliminando trigger: trg_catalog_category_product_after_update
Eliminando trigger: trg_catalog_category_product_after_delete
Eliminando trigger: trg_catalog_compare_item_after_insert
Eliminando trigger: trg_catalog_compare_item_after_update
Eliminando trigger: trg_catalog_compare_item_after_delete
Eliminando trigger: trg_catalog_product_bundle_option_after_insert
Eliminando trigger: trg_catalog_product_bundle_option_after_update
Eliminando trigger: trg_catalog_product_bundle_option_after_delete
Eliminando trigger: trg_catalog_product_bundle_option_value_after_insert
Eliminando trigger: trg_catalog_product_bundle_option_value_after_update
Eliminando trigger: trg_catalog_product_bundle_option_value_after_delete
Eliminando trigger: trg_catalog_product_bundle_selection_after_insert
Eliminando trigger: trg_catalog_product_bundle_selection_after_update
Eliminando trigger: trg_catalog_product_bundle_selection_after_delete
Eliminando trigger: trg_catalog_product_bundle_selection_price_after_insert
Eliminando trigger: trg_catalog_product_bundle_selection_price_after_update
Eliminando trigger: trg_catalog_product_bundle_selection_price_after_delete
Eliminando trigger: trg_catalog_product_entity_after_insert
Eliminando trigger: trg_catalog_product_entity_after_update
Eliminando trigger: trg_catalog_product_entity_after_delete
Eliminando trigger: trg_catalog_product_entity_datetime_after_insert
Eliminando trigger: trg_catalog_product_entity_datetime_after_update
Eliminando trigger: trg_catalog_product_entity_datetime_after_delete
Eliminando trigger: trg_catalog_product_entity_decimal_after_insert
Eliminando trigger: trg_catalog_product_entity_decimal_after_update
Eliminando trigger: trg_catalog_product_entity_decimal_after_delete
Eliminando trigger: trg_catalog_product_entity_group_price_after_insert
Eliminando trigger: trg_catalog_product_entity_group_price_after_update
Eliminando trigger: trg_catalog_product_entity_group_price_after_delete

```


Toda una aventura, que en modo stress, no es buena para nadie. 

## Finales

Muchas veces oigo en mi trabajo, "Es que se puede hacer así".

Con mil cosas, caracteres extendidos, desarrollos con millones de ficheros en la misma carpeta, etc. Y la misma cantinela. "Ahora el límite..." "Se puede..."

La realidad es muy distinta. Y eso se aprende en el infierno de los sistemas, cuando llaman a nuestra puerta.

Una cosa es que se pueda y otra muy distinta, es que se deba.


##### Aviso
Esta documentación y su contenido, no implica que funcione en tu caso o determinados casos. 
También implica que tienes conocimientos sobre lo que trata, y que en cualquier caso tienes copias de seguridad.
El contenido el contenido se entrega, tal y como está, sin que ello implique ningún obligación ni responsabilidad por parte de [Castris](https://castris.com)

Si necesitas soporte profesional puedes contratar con Castris [soporte profesional](https://intranet.castris.com/store/soporte-profesional).

# No puedo cambiar de puerto SSH porque falla. Ubuntu 22.10 y superiores (24.04 LTS)

## Introducción

Siempre cambio el puerto SSH para evitar miles de correos del firewall sobre idiotas intentando entrar en el sistema.

Hoy, en una instalación Ubuntu 24.04 LTS, me tropecé con un problema. No podía cambiar el puerto de SSH pues luego no podia acceder.

Pensé que era similar a lo que ocurre con las distros Redhat (y sus forks) pero no.

Se trata de que dese las 22.10 de Ubuntu SSH esta configurado via sockets

Te dejo el enlace. Ya escribí mi artículo [Cambiar el puerto SSH en Ubuntu 24.04 y Debian Bookworm](https://wiki.castris.com/books/tips-sobre-la-administracion-de-linux/page/cambiar-el-puerto-ssh-en-ubuntu-2404-y-debian-bookworm)

# nginx: [emerg] open() "/var/log/nginx/domains/domain.es.error.log" failed (24: Too many open files)

## Introducción

Nunca me había enfrentado a este problema con nginx, pero me surgio en una instalación de Directadmin. 

```
> nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: [emerg] open() "/var/log/nginx/domains/DOMAIN.es.error.log" failed (24: Too many open files)
nginx: configuration file /etc/nginx/nginx.conf test failed
```

La cuestión es que pese a ello, ngins esta arriba, renicia bien, y no muestra error es un serio handicap para el trabajo con nginx, pues en un entorno en que tenemos y editamos las configruaciones, ya sea de forma directa o indirecta, es necesaio verificar que todo esta correcto antes de proceder al reinicio.

## Solución temporal

Como he tratado de hacer varias cosas que no em han servidor, estoy tratando el tema con el comando de abajo, ya que el nginx funciona correctamente sin problemas

```bash
> bash -c 'ulimit -n 65535 && nginx -t'
22536
```

## Solución definitiva

Editar el fichero `/etc/security/limits.conf` añadiendo al final el código de abajo.

```bash
root soft nofile 65535
root hard nofile 65535
```

> Debemos salir de la sesión para que sea efectivo.

##### Aviso
Esta documentación y su contenido, no implica que funcione en tu caso o determinados casos. 
También implica que tienes conocimientos sobre lo que trata, y que en cualquier caso tienes copias de seguridad.
El contenido el contenido se entrega, tal y como está, sin que ello implique ningún obligación ni responsabilidad por parte de [Castris](https://castris.com)

Si necesitas soporte profesional puedes contratar con Castris [soporte profesional](https://intranet.castris.com/store/soporte-profesional).

# Convertir el historial de bash en .zsh_history

## Introducción

Los formatos de los archivos de historial de Bash y Zsh son diferentes. Zsh almacena información adicional como timestamps, lo que hace que simplemente concatenar los archivos no sea la mejor solución. Aquí te muestro cómo puedes convertir el historial de Bash al formato de Zsh:

### Pasos para Convertir e Importar el Historial

1. **Hacer una Copia de Seguridad**:
   - Asegúrate de hacer una copia de seguridad de ambos archivos de historial antes de realizar cambios.

     ```bash
     cp ~/.bash_history ~/.bash_history_backup
     cp ~/.zsh_history ~/.zsh_history_backup
     ```

2. **Convertir el Historial de Bash al Formato de Zsh**:
   - Puedes usar un script para convertir cada línea del historial de Bash al formato que Zsh espera. Aquí tienes un ejemplo simple:

     ```bash
     while read -r line; do
         echo ": $(date +%s):0;$line"
     done < ~/.bash_history >> ~/.zsh_history
     ```

   - Este script lee cada línea del `.bash_history`, añade un timestamp actual y lo formatea de acuerdo al formato de Zsh.

3. **Eliminar Duplicados (Opcional)**:
   - Si deseas eliminar comandos duplicados, puedes usar un comando como `awk`:

     ```bash
     awk '!seen[$0]++' ~/.zsh_history > ~/.zsh_history_temp
     mv ~/.zsh_history_temp ~/.zsh_history
     ```

4. **Recargar el Historial de Zsh**:
   - Para asegurarte de que Zsh cargue el nuevo historial, puedes cerrar y volver a abrir la terminal o ejecutar:

     ```zsh
     source ~/.zshrc
     ```

### Consideraciones

- **Formato de Timestamps**: El script anterior utiliza el timestamp actual para cada comando importado. Si necesitas los timestamps originales, requerirás un enfoque más complejo.
- **Configuración de Zsh**: Verifica las configuraciones de historial en tu archivo `.zshrc` para asegurarte de que el historial se guarde y cargue correctamente.

##### Aviso
Esta documentación y su contenido, no implica que funcione en tu caso o determinados casos. 
También implica que tienes conocimientos sobre lo que trata, y que en cualquier caso tienes copias de seguridad.
El contenido el contenido se entrega, tal y como está, sin que ello implique ningún obligación ni responsabilidad por parte de [Castris](https://castris.com)

Si necesitas soporte profesional puedes contratar con Castris [soporte profesional](https://intranet.castris.com/store/soporte-profesional).

# Nginx versus Apache, falacias de ayer y de hoy

## 🤔 ¿"Apache para contenido dinámico y Nginx para estático"? Una aclaración técnica

Existe una generalización bastante extendida, que consdiero una falacia que muchas veces se usa para no avanzar en el uso de nginx:

> **Apache para contenido dinámico, Nginx para contenido estático.**

A partir de ahí, muchas veces se concluye que:

- Si una aplicación tiene procesos dinámicos (como sincronización, API, backend), Apache es mejor.
- Si es solo para servir archivos estáticos, entonces Nginx.

Sin embargo, esta conclusión **no es técnicamente precisa**, y puede llevar a decisiones mal fundamentadas.

## 🧠 ¿Qué hace realmente una aplicación web moderna?

Una aplicación típica puede incluir:

- Archivos estáticos: HTML, CSS, JS, imágenes
- Código backend dinámico (PHP, Python, Node.js…)
- APIs REST o GraphQL
- Procesos de subida, descarga y manipulación de archivos
- Tareas en segundo plano (cron, workers, colas)

Todo esto hace que la clasificación "dinámico vs estático" no sea suficiente para determinar qué servidor es más adecuado.

## 🔍 Comparativa técnica entre Apache y Nginx

### Nginx

- ⚡ Muy eficiente para servir archivos estáticos.
- ✅ Manejo asíncrono y event-driven: excelente bajo carga.
- ✅ Usa PHP-FPM para ejecutar código dinámico, separando claramente funciones.
- ❌ No soporta `.htaccess` (toda la configuración va en los archivos del servidor). Eso puede ser un handicap para algunos, pero para otros es justo lo contrario. Es **seguridad**, **estabilidad** y **eficacia**

### Apache

- ✅ Servidor muy compatible y ampliamente soportado.
- ✅ Soporta `.htaccess`, útil en entornos compartidos.
- 🔧 Más pesado en escenarios de alta concurrencia (según el MPM usado).
- ✅ También puede usar PHP-FPM o `mod_php` según configuración.

## ✅ ¿Cuál es mejor?

Depende del caso:

- **Nginx** destaca por su rendimiento, menor consumo de recursos y eficiencia en entornos concurrentes.
- **Apache** es más flexible en cuanto a configuración por usuario, y puede ser más cómodo en ciertos entornos como hosting compartido y/o sysadmin, un poco vagones o acomodados.
- Ambos pueden manejar contenido estático y dinámico sin problemas cuando están bien configurados.

## 💡 Conclusión

Elegir entre Apache y Nginx **no debería basarse únicamente** en si una aplicación sirve "contenido estático" o "dinámico".

Lo correcto es considerar:

- Arquitectura de tu aplicación
- Escalabilidad esperada
- Modelo de despliegue
- Nivel de personalización y control que necesitas
- Consumo de recursos

Ambos servidores son válidos y potentes. La elección óptima no es cuestión de gustos, sino de contexto técnico. 🚀


##### Aviso
Esta documentación y su contenido, no implica que funcione en tu caso o determinados casos. 
También implica que tienes conocimientos sobre lo que trata, y que en cualquier caso tienes copias de seguridad.
El contenido el contenido se entrega, tal y como está, sin que ello implique ningún obligación ni responsabilidad por parte de [Castris](https://castris.com)

Si necesitas soporte profesional puedes contratar con Castris [soporte profesional](https://intranet.castris.com/store/soporte-profesional).

# Actualización de rsync a la versión 3.4.1 en Ubuntu 24.04.2 LTS

### 📌 Objetivo

Actualizar la herramienta rsync a su versión estable más reciente (v3.4.1) en sistemas basados en Ubuntu 24.04.2 LTS, dado que los repositorios oficiales aún no ofrecen esta versión en sus paquetes estándar.

### ⚠️ Observaciones

- Versiones disponibles pueden variar con el tiempo. Siempre consulta la fuente oficial de Debian y Ubuntu para obtener la última versión disponible del paquete .deb.
- Mantener rsync actualizado es especialmente importante en sistemas donde se confía en la herramienta para copias de seguridad, ya que versiones nuevas corrigen errores, mejoran el rendimiento y refuerzan la seguridad.
- Esta guía fue escrita y probada sobre Ubuntu 24.04.2 LTS, y puede requerir ajustes mínimos en otras versiones.


### 📦 Requisitos previos

- Sistema Ubuntu 24.04.2 LTS actualizado.
- Acceso root o permisos sudo.


## 🧩 Pasos para la actualización

### Descargar el paquete .deb desde el repositorio oficial de Debian

```
wget http://ftp.debian.org/debian/pool/main/r/rsync/rsync_3.4.1+ds1-3_amd64.deb
```

> 💡 Si la versión ha cambiado, reemplaza el nombre del archivo por la última disponible en el repositorio oficial.


### Instalar dependencias necesarias (si no están ya)

```
sudo apt update
sudo apt install libxxhash0 libzstd1
```

### Instalar el paquete .deb
```
sudo dpkg -i rsync_3.4.1+ds1-3_amd64.deb
```

### Verificar la versión instalada

```
rsync --version

#Deberías ver algo como:

❯ rsync --version
rsync  version 3.4.1  protocol version 32
Copyright (C) 1996-2025 by Andrew Tridgell, Wayne Davison, and others.
Web site: https://rsync.samba.org/
Capabilities:
    64-bit files, 64-bit inums, 64-bit timestamps, 64-bit long ints,
    socketpairs, symlinks, symtimes, hardlinks, hardlink-specials,
    hardlink-symlinks, IPv6, atimes, batchfiles, inplace, append, ACLs,
    xattrs, optional secluded-args, iconv, prealloc, stop-at, no crtimes
Optimizations:
    SIMD-roll, no asm-roll, openssl-crypto, no asm-MD5
Checksum list:
    xxh128 xxh3 xxh64 (xxhash) md5 md4 sha1 none
Compress list:
    zstd lz4 zlibx zlib none
Daemon auth list:
    sha512 sha256 sha1 md5 md4

rsync comes with ABSOLUTELY NO WARRANTY.  This is free software, and you
are welcome to redistribute it under certain conditions.  See the GNU
General Public Licence for details.
```


### (Opcional) Bloquear la versión para evitar que apt upgrade la sobrescriba

```
sudo apt-mark hold rsync
```

Para revertir esto más adelante:

```
sudo apt-mark unhold rsync
```


## ✅ Resultado esperado

El sistema ahora cuenta con una versión reciente y segura de rsync, ideal para entornos de producción que dependen de su fiabilidad en tareas de sincronización y respaldo de datos.##### Aviso
Esta documentación y su contenido, no implica que funcione en tu caso o determinados casos. 
También implica que tienes conocimientos sobre lo que trata, y que en cualquier caso tienes copias de seguridad.
El contenido el contenido se entrega, tal y como está, sin que ello implique ningún obligación ni responsabilidad por parte de [Castris](https://castris.com)

Si necesitas soporte profesional puedes contratar con Castris [soporte profesional](https://intranet.castris.com/store/soporte-profesional).

# Deshabilitar IPv6 de forma efectiva en Ubuntu (24.04, 22.04, 20.04...)

## 🛠️ Métodos para deshabilitar IPv6 en Ubuntu

> Para UButnu 24.04 y para todos en general recomiendo usar el meotodo GRUB

### Deshabilitar IPv6 mediante sysctl (temporal)

Este método desactiva IPv6 hasta el próximo reinicio:

```bash
sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1
sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1
sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=1
```

Para verificar que IPv6 está deshabilitado: ￼

```
ip a
```

No deberías ver direcciones inet6 en la salida. ￼

### Deshabilitar IPv6 de forma permanente editando /etc/sysctl.conf

Para que los cambios persistan después de reiniciar:

#### Editar el archivo de configuración: ￼

```bash
sudo nano /etc/sysctl.conf
```

#### Agrega al final del archivo:

```bash
net.ipv6.conf.all.disable_ipv6=1
net.ipv6.conf.default.disable_ipv6=1
net.ipv6.conf.lo.disable_ipv6=1
````

#### Aplica los cambios:
```bash
sudo sysctl -p
```


### Deshabilitar IPv6 mediante GRUB (a nivel de kernel)

Este método desactiva IPv6 desde el arranque del sistema:

#### Edita el archivo de configuración de GRUB:

```bash
sudo nano /etc/default/grub
```

#### Modifica las líneas correspondientes:

```bash
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash ipv6.disable=1"
GRUB_CMDLINE_LINUX="ipv6.disable=1"
```

#### Actualiza GRUB:

```bash
sudo update-grub
```

### Reinicia el sistema:

```bash
sudo reboot
````


### Deshabilitar IPv6 en Netplan (Ubuntu 18.04 y versiones posteriores)

Si tu sistema utiliza Netplan para la configuración de red:

#### Edita el archivo de configuración de Netplan (por ejemplo):

```
sudo nano /etc/netplan/01-netcfg.yaml
````

Agrega o modifica la sección correspondiente:

```yaml
network:
  version: 2
  ethernets:
    enp0s3:
      dhcp4: true
      link-local: []

```

#### Aplica los cambios:


```bash
sudo netplan apply
```


## 🔄 Revertir los cambios (re-habilitar IPv6)

Para volver a habilitar IPv6, revierte los cambios realizados en los archivos de configuración mencionados anteriormente, cambiando los valores de 1 a 0 o eliminando las líneas agregadas, y aplica los cambios correspondientes.


##### Aviso
Esta documentación y su contenido, no implica que funcione en tu caso o determinados casos. 
También implica que tienes conocimientos sobre lo que trata, y que en cualquier caso tienes copias de seguridad.
El contenido el contenido se entrega, tal y como está, sin que ello implique ningún obligación ni responsabilidad por parte de [Castris](https://castris.com)

Si necesitas soporte profesional puedes contratar con Castris [soporte profesional](https://intranet.castris.com/store/soporte-profesional).