# rm-rf.es | Administración de sistemas

Bitácora personal de un SysAdmin Gnu/Linux, Windows, BSD...

Debug de procesos en Solaris con Truss

Podríamos decir que truss es el equivalente en Solaris a strace de Linux, si bien existen también otras alternativas como dtrace que también cumplen perfectamente esta función.

Truss es una utilidad que permite ejecutar el comando especificado o un proceso concreto y muestra durante su ejecución las llamadas al sistema que ejecuta y las señales que recibe. Esto permite ver todo el proceso de ejecución y hacer debug de cualquier error o fault que ocurre.

Vamos a ver unos ejemplos. Lo más básico sería ejecutar truss seguido del comando del que queremos ver su debug, en el siguiente ejemplo simplemente ejecutamos el comando df:

# truss df
execve("/usr/gnu/bin/df", 0x08047E6C, 0x08047E74)  argc = 1
sysinfo(SI_MACHINE, "i86pc", 257)               = 6
mmap(0x00000000, 32, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON, -1, 0) = 0xD1BB0000
mmap(0x00000000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0) = 0xD1BA0000
mmap(0x00000000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0) = 0xD1B90000
mmap(0x00000000, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON, -1, 0) = 0xD1B80000
memcntl(0xD1BB8000, 32064, MC_ADVISE, MADV_WILLNEED, 0, 0) = 0
memcntl(0x08050000, 15312, MC_ADVISE, MADV_WILLNEED, 0, 0) = 0
resolvepath("/usr/lib/ld.so.1", "/lib/ld.so.1", 1023) = 12
resolvepath("/usr/gnu/bin/df", "/usr/gnu/bin/df", 1023) = 15
sysconfig(_CONFIG_PAGESIZE)                     = 4096
stat64("/usr/gnu/bin/df", 0x08047AB0)           = 0
open("/var/ld/ld.config", O_RDONLY)             Err#2 ENOENT
stat64("/lib/libc.so.1", 0x08047260)            = 0
...
...
...

No muestro todo el output debido a su longitud, podemos volcarlo a un fichero para analizarlo mejor en lugar de la salida stderr:

# truss -o salida.out df

En el siguiente ejemplo hacemos debug de un proceso que ya se encuentra en ejecución:

# truss -rall -wall -f -p <PID>

“-rall” implica ver todos los datos de lectura y “-wall” todos los de escritura, con “-f” vemos los procesos fordked y “-p” especifica el PID.

Podéis ver más ejemplos e información del comando en la página man correspondiente. A continuación podéis ver un par:

$ man truss

Trazar las llamadas de sistema open, close, read y write únicamente:

# truss -t open,close,read,write find . -print >salida.out

Trazar todas las llamads a funciones a nivel de usuario desde y hacia cualquier sitio:

# truss -u a.out -u ld:: -u :: <comando>

DNS: configurar bind como Forward Only

Si queremos que nuestro servidor DNS actúe a modo de proxy DNS y reenviar todas las peticiones que reciba a otros servidores DNS debemos configurarlo como “Forward Only“. Esto tiene todos los beneficios que puede darte un proxy y otros como por ejemplo, si tenemos Bind configurado con vistas, especificar el forwarding para la vista externa pero mantener las consultas a las zonas locales en el propio DNS.

Únicamente hay que prestar atención a dos directivas en la sección de opciones (options), marcadas en negrita:

options {
listen-on port 53 { 127.0.0.1; 10.0.0.100; };
listen-on-v6 port 53 { ::1; };
directory "/var/named";
 allow-query { redes_privadas; }; forward only;
forwarders { 8.8.8.8; };
};

La directiva “forward” permite los valores “only” y “first”. Si especificamos “only” todas las consultas serán reenviadas a los nameserver especificados después. Si ponemos “first” se realizará un primer intento contra los NS forward, si estos no responden la consulta se intentará resolver en local.

Después, en forwarders podemos especificar los servidores DNS contra los que vamos a enrutar las peticiones DNS. En este caso he configurado el de google, 8.8.8.8.

Tras reiniciar named, podemos verificar (con un tcpdump por ejemplo) que las consultas efectivamente se están enrutando a esos DNS:

# dig @localhost www.rm-rf.es
...
...

Y efectivamente vemos el tráfico en el tcpdump contra el NS de google:

# tcpdump -n -s 1500 -i eth0 udp port 53
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 1500 bytes
23:19:21.230065 IP 192.168.1.101.22581 > 8.8.8.8.domain: 31993+ [1au] A? www.rm-rf.es. (41)
23:19:21.371260 IP 8.8.8.8.domain > 192.168.1.101.22581: 31993 3/0/1 CNAME rm-rf.es., A 93.93.112.55, RRSIG (239)
23:19:38.683764 IP 192.168.1.128.57988 > 192.168.1.1.domain: 64751+ A? rm-rf.es. (26)
23:19:38.722404 IP 192.168.1.1.domain > 192.168.1.128.57988: 64751 1/0/0 A 93.93.112.55 (42)

Apache: permitir acceso a un directorio con autenticación para una IP / CIDR

ApacheEn su día, en el artículo de trucos sobre .htaccess vimos como configurar la autenticación (solicitando usuario y password) para un directorio dentro de un website (o para el website completo). En este caso lo que vamos a hacer es proteger un directorio pero excluir una IP o rango CIDR para que pueda acceder al mismo saltando esta protección.

Partimos de la base de que tenemos configurado en nuestro website, dentro del fichero .htaccess (o a nivel de virtualhost) la autenticación para todos los usuarios:

AuthType Basic
AuthName “Prompt”
AuthUserFile /var/webs/test.com/.htpasswd
Require valid-user

Ahora queremos que todos los visitantes se autentiquen excepto los que provengan de la IP 192.168.1.128:

AuthType Basic
AuthName “Prompt”
AuthUserFile /var/webs/test.com/.htpasswd
Require valid-user
Order allow,deny
allow from 192.168.1.128
satisfy any

La directiva satisfy any permite, tanto para la autenticación como el acceso, que en el momento que se cumpla uno de los requerimientos se permita la petición, haciendo así bypass de la autenticación. Si por el contrario, se quisiera que se cumplieran todos los requerimientos para acceder usaríamos satisfy all.

Podríamos tambien permitir rangos completos, denegar a IPs o CIDR, etc

allow from 192.168.1
allow from 192.168.1.0/24
...

Limitar recursos por usuario en MySQL

MySQLYo, hasta el momento, la única forma que conocía de limitar recursos por usuario en MySQL era estableciendo la variable max_user_connections o en su defecto creando instancias independientes del servicio para distintas bases de datos. Revisando la documentación de MySQL me he dado cuenta de que es posible aplicar más restricciones de forma independiente para cada usuario, entre las que se encuentran:

  • Número de consultas que un usuario pueda hacer cada hora.
  • Número de updates que un usuario puede hacer cada hora.
  • Número de veces que un usuario puede acceder al servidor a la hora.
  • Número de conexiones simultaneas permitidas para cada usuario (como max_user_connections pero a nivel individual en lugar de global).

Estos límites tendrán un contador para cada acceso del usuario,excepto cuando su consulta sea servida a través de la cache. Este tipo de contextos se establecen en la tabla mysql.user y los podemos aplicar mediante GRANT. En el siguiente ejemplo estableceríamos todos estos límites para el usuario prueba cuando sus conexiones se realizan desde localhost y contra la base de datos test:

mysql> CREATE USER 'prueba'@'localhost' IDENTIFIED BY 'PASSWORD';
mysql> GRANT ALL ON test.* TO 'prueba'@'localhost'
->     WITH MAX_QUERIES_PER_HOUR 100
->          MAX_UPDATES_PER_HOUR 30
->          MAX_CONNECTIONS_PER_HOUR 200
->          MAX_USER_CONNECTIONS 10;

Para realizar modificaciones en estos límites a posteriori, utilizaremos a nivel global GRANT USAGE:

mysql> GRANT USAGE ON *.* TO 'prueba'@'localhost'
    ->    WITH MAX_QUERIES_PER_HOUR 50;

Para eliminar cualquier límite establecido ponemos el valor a 0:

mysql> GRANT USAGE ON *.* TO 'prueba'@'localhost'
    ->    WITH MAX_QUERIES_PER_HOUR 0;

Para poner a 0 los contadores de limites a nivel general podemos ejecutar el comando:

mysql> FLUSH USER_RESOURCES

También recargando las tablas de privilegios:

mysql> FLUSH PRIVILEGES

NetAPP: crear desde CLI un usuario para acceder a la API

NetAppPara poder utilizar la API que ofrece NetAPP es necesario disponer de un usuario en la cabina con permisos específicos. Por supuesto no debemos utilizar el usuario root para ello así que tenemos que crear un usuario específico. Por ejemplo, si queremos utilizar el SDK de NetAPP y graficar en Cacti necesitaremos este usuario.

Los permisos (llamados roles en Data OnTap) necesarios para utilizar la API son todo lo referente a la api y el acceso http-admin: api-*,login-http-admin.

Lo primero que verificamos desde la shell de la cabina es que está activado a nivel general el acceso httpd.admin:

NetApp01> options httpd.admin.enable
httpd.admin.enable           on

En este caso está activado, si no lo estuviera lo activamos:

NetApp01> options httpd.admin.enable on

Una vez activado tenemos que crear un nuevo rol con nuestros permisos específicos, un grupo y finalmente el usuario. Para ello utilizamos el comando useradmin:

NetApp01> useradmin role add accesoapi -a api-*,login-http-admin
Sat Mar 17 18:16:52 GMT [useradmin.added.deleted:info]: The role 'accesoapi' has been added.
Role <accesoapi> added.

Como veis, hemos especificado simplemente el nombre y añadido los permisos del rol con -a. Ahora creamos el grupo de usuarios y le indicamos que tendrán asignado el rol creado anteriormente:

NetApp01> useradmin group add grupoapi -r accesoapi
Sat Mar 17 18:18:04 GMT [useradmin.added.deleted:info]: The group 'grupoapi' has been added.
Group <grupoapi> added.

Y finalmente creamos el usuario con el que haremos uso de la API, le asignamos el grupo creado:

NetApp01> useradmin user add usuarioapi -g grupoapi
New password: XXXX
Retype new password: XXXX
User <usuarioapi> added.
NetApp01> Sat Mar 17 18:19:15 GMT [useradmin.added.deleted:info]: The user 'usuarioapi' has been added.

Communigate Pro: enrutar un dominio local a un servidor externo

Hay situaciones, que aunque sean poco comunes pueden suceder. Por ejemplo, tener un dominio dado de alta en el sistema como local pero cuyo servidor de correo real está en un servidor externo. En un comportamiento normal, al detectarse el correo como local, directamente se enruta localmente sin hacer consulta al DNS.

En estos casos, podemos configurar el enrutamiento para ese dominio en concreto y que sea el servidor local el que haga relay contra ese MX externo. En Communigate Pro se hace como vemos a continuación.

Vamos a suponer que tenemos dado de alta el dominio pruebas.com en el servidor local Communigate Pro, pero realmente el servicio de correo lo gestiona el servidor de correo 10.0.0.100. Lo que tenemos que hacer es decir al sistema que cuando detecte la recepción de un correo para este dominio lo enrute y envíe al servidor 10.0.0.100.

Para ello accedemos desde la HTTPA a Settings > Router y añadimos la siguiente línea a la tabla de enrutamiento:

prueba.com = prueba.com@[10.0.0.100]

Como podéis ver, la estructura es:

dominio.com = dominio.com@[IP_MX]

Podemos realizar la prueba con el botón TEST que tenemos en la misma pantalla y verificar el enrutamiento:

Routed to SMTP host [10.0.0100](test@pruebas.com)

Para más información sobre el routing en CG Pro acceded a la ayuda oficial, sección routing.

Múltiples instancias MySQL en un servidor o Cluster

MySQLYa sea dentro de un Cluster o directamente en un servidor estándar, podemos necesitar tener más de una instancia diferenciada de un servicio, en este caso MySQL. Vamos a ver el proceso de instalación y configuración de dos instancias/servidores MySQL dentro de un único servidor.

Lo primero que hacemos es la instalación propiamente dicha de MySQL, ya sea por rpm, yum, apt o compilando. Nosotros bajamos el rpm de MySQL 5.5 y lo instalamos:

# wget http://mysql.mirrors.ovh.net/ftp.mysql.com/Downloads/MySQL-5.5/MySQL-5.5.20-1.linux2.6.x86_64.tar
# tar -xvf MySQL-5.5.20-1.linux2.6.x86_64.tar
# rpm -ivh MySQL-server-5.5.20-1.linux2.6.x86_64.rpm MySQL-client-5.5.20-1.linux2.6.x86_64.rpm

Una vez instalado vamos ver como estructurar el sistema:

  1. Todas las instancias compartirán los binarios y librerías, ubicados en /usr/bin/mysql /usr/lib64/mysql
  2. Cada instancia tendrá su filesystem o directorio en el que almacenar sus ficheros propios de configuración, bases de datos y logs. Si es un cluster se encontrará en una NAS y servido por iSCSI o FC.
    /mysql1
    /mysql1/etc
    /mysql1/data
    /mysql1/data/logs
    /mysql2
    /mysql2/etc
    /mysql2/data
    /mysql2/data/logs
    ...
    ...
  3. Cada instancia tendrá su propio script de arranque:
    /etc/init.d/mysql1
    /etc/init.d/mysql2
    ...
    ...

El punto 1 está hecho ya que tenemos MySQL instalado. Para el punto 2, creamos la estructura de directorios indicada y copiamos en /mysqlX/etc/ el fichero de configuración por defecto de MySQL my.cnf ubicado en /etc:

# cp -p /etc/my.cnf /mysql1/etc/

Para diferenciar cada instancia debemos hacer lo siguiente:

  1. Cada instancia escuchará por una IP distinta. Si comparten IP tendrán que escuchar por puertos distintos.
  2. Cada instancia usará un socket distinto.
  3. Cada instancia tendrá un PID file distinto.
  4. Cada instancia tendrá un datadir distinto.

Así pues, editamos dichos valores en cada my.cnf de cada instancia según requerimientos:

[mysqld]

# Ruta al datadir de cada instancia:
datadir=/mysql1/data

# Puerto de escucha, si las instancias comparten IP
# debe ser distinto en cada una
port=3306

# IP de escucha, si se quiere mantener el mismo puerto TCP
# para todas debe ser única en cada una:
bind-address=10.0.0.110

# Ruta al socket (único por instancia)
socket=/var/tmp/mysql1.sock

Una vez configurados los my.cnf de todas las instancias podemos inicializar e instalar las bases de datos por defecto:

# mysql_install_db --datadir=/mysql1/
# mysql_install_db --datadir=/mysql2/

Ya podríamos arrancar las dos instancias manualmente, únicamente debemos especificar el fichero de configuración:

# /usr/bin/mysqld_safe --defaults-file=/mysql1/etc/my.cnf &
# /usr/bin/mysqld_safe --defaults-file=/mysql2/etc/my.cnf &

Si queremos usar los scripts de arranque en init.d deberemos cambiar los siguientes parámetros:

# If you change base dir, you must also change datadir. These may get
# overwritten by settings in the MySQL configuration files.

# Únicamente si no se especifican en el my.cnf
basedir=
datadir=

Ruta al PID file de cada instancia:

mysqld_pid_file_path=/mysql1/data/mysql1.pid

En el case de arranque añadir el fichero de configuración:

case "$mode" in
  'start')
    # Start daemon

    # Safeguard (relative paths, core dumps..)
    cd $basedir

    echo $echo_n "Starting MySQL"
    if test -x $bindir/mysqld_safe
    then
      # Give extra arguments to mysqld with the my.cnf file. This script
      # may be overwritten at next upgrade.
      mysqld_safe --defaults-file=/mysql1/etc/my.cnf --pid-file="$mysqld_pid_file_path" >/dev/null 2>&1 &

Spamassassin: backup y restauración de la Bayes Database

Tanto para hacer una copia de seguridad como para método de migración de un servidor a otro de la Bayes Database de Spamassasin podemos hacer uso de la herramienta sa-learn. El sistema de filtros bayesianos hace uso de técnicas estadísticas y de probabilidad para detectar los email spam, dando lugar a un porcentaje muy bajo de falsos positivos.

Para hacer la copia de seguridad, es tan sencillo como ejecutar el comando con el parámetro –backup y volcando la salida a un fichero:

# sa-learn --backup > /var/tmp/bayes.back

Para proceder a la restauración, ya sea en el mismo servidor o en otro se usa el parámetro –restore seguido de la ruta al fichero del backup. Podemos forzar también la ruta en la que se almacenan las bases de datos de bayesian:

# sa-learn --restore /var/tmp/bayes.back --dbpath $HOME_BAYES

Os recomiendo leer la documentación de sa-learn para comprender el resto de funcionalidades, como el entrenamiento de los filtros bayesianos para que sean efectivos:

http://spamassassin.apache.org/full/3.0.x/dist/doc/sa-learn.html