La red de la empresa es un quilombo. Tenemos la red de la oficina por un lado, con los desktops en una red privada y los servers en una red pública (parte de la red pública del edificio donde está la oficina). Tenemos dos CoLos, uno en Rotterdam (Verizon; no me pidan comentarios) y otro en Amsterdam (xs4all; parece que le da housing a la mitad de .nl). En el primero tenemos tres rangos públicos y al menos dos privados, y en el otro tenemos sólo un rango público y unos dos o tres privados, incluyendo un cross-over entre los dos firewalls para hacer fail-over. En resumen, 5 redes públicas y vayasabersecuántas privadas desperdigadas.
Para complicar un poco más las cosas, montamos (bueno, yo no tuve nada que ver, pero ya trabajaba acá) una VPN entre los dos CoLos, teniendo ambas puntas en los firewalls (sin contar de que una punta tenemos dos; uno duerme hasta que el otro se cae).
El último ingrediente de esta sopa: graficamos el tráfico con MRTG (si, ya sé de Munin, ya lo sugerí, pero son como 80 servers en total) sacando los datos por SNMP (que, como dicen en todos lados, de simple tiene sólo el nombre). Pero para el caso es simple: un cliente corre en el server graficador, en este caso en Rotterdam, y en el nodo a graficar un servidorcito SNMP; el tráfico es por UDP.
'Bueno, ¿y dónde está la complicación?', se preguntarán. Les cuento.
Esta gente (notar cómo cuando se mandan una 'cagada' me despego
:-P) tiene unos firewalls implementados a mano en
bash. No es la típica ristra de reglas
iptables, sino algo un poco más elaborado, con
funcioncitas, pero aún así son 1400 líneas de bash y
otras 630 de configuración (en bash también, obvio; no
hay manera más fácil de hacer configuración de scripts
bash que en bash mismo). Ya les comenté
de shorewall también, pero todo a su tiempo.
Entonces me tocó la tarea de poner a andar el SMNP contra los firewalls de Amsterdam. Parece una boludez, sobre todo porque el MRTG tiene scripts con los que crear la configuración automáticamente leyendo lo que hay disponible por el mismo protocolo (lo diseñadores tuvieron la decencia de ponerle 'descubribilidad'). Por simple que parezca, no andaba nipatrásnipadelante. Le dimos quichicientas vueltas y náa.
Lo más raro fue cuando empezamos a usar tcpdump en
las interfaces externas de los firewalls. Filtrando por el puerto
161 (snmp), en el firewall de Amsterdam veíamos el
tráfico entrante pero no el saliente. Eso nos llevó a pensar que el
firewall estaba jodiendo, pero entonces vimos que el tráfico que
generaba el gráfico para el otro firewall, el durmiente, hacía lo
mismo: veíamos el request pero no la respuesta. Pero el gráfico
andaba. Y para complicar las cosas, con IPSec andando no deberíamos
ver el tráfico, pues tcpdump no sabe desencriptar esos
paquetes, sino apenas reconocerlos.
Entonces sospechamos.
Probando el tcpdump en el firewall de la red de
Rotterdam vimos un comportamiento simétrico: no veíamos el request
pero si la respuesta. Y sospechamos mas fuerte.
Mirando con mas fuerza me dí con que los scripts de firewall
tenían un par de bugs: la función que seteaba las excepciones para
el firewall mismo no era llamada nunca y además llamaba a
iptables con parámetros erróneos.
La conclusión a la que llegamos, basada únicamente en lo que pudimos ver, es que el túnel anda, y que IPSec hace cosas raras con los paquetes; creemos que cuando los termina de procesar un paquete lo inyecta en la interfaz por la que entró, y eso es lo que hace que veamos el tráfico para un lado. Algún día que esté muy aburrido me voy a poner a ver si esta teoría es cierta.
sysadmin security
Since a couple of hal versions
(0.5.12~git20090406.46dc48-1) in Debian Sid, it
stopped using system groups as the method to grant privileges to
users, and started using something called PolicyKit
instead. Just to remember what are we talking about here, a user in
the powerdevil group used to suspend or hibernate his
machine sucessfully. But now Mr. PL is a not-understood monster and
the package does not give a smooth transition. That's what I'll
try to do here.
To make sure this is the problem, let's try to suspend the
machine simply by calling hal through
d-bus. I use qdbus here, found in the
libqt4-dbus package, because it lets me introspect the
dbus interface, but you could use dbus-send
instead:
qdbus --system org.freedesktop.Hal /org/freedesktop/Hal/devices/computer org.freedesktop.Hal.Device.SystemPowerManagement.Suspend 0
Error: org.freedesktop.Hal.Device.PermissionDeniedByPolicy
org.freedesktop.hal.power-management.suspend no <-- (action, result)
If we edit the file /etc/PolicyKit/PolicyKit.conf
we'll see the empty default config that comes with the package.
According to its
documentation, we just need to add a couple of nested
<match> tags with a <return>
inside. The outher match will filter the
hal action, the inner one the group, and
the return will grant access.
... we wish! There's no way to match to a group, only to a user. Ok, the first shot will use the user, then we'll see if we really can do it.
How do we discover which action we must use? We can use
polkit-action to see what are the available actions.
In the first case,
org.freedesktop.hal.power-management.* will do.
<match action="org.freedesktop.hal.power-management.*">
<!--match group="powerdev"-->
<match user="mdione">
<return result="yes"/>
</match>
</match>
Now the previous command suspends the machine allright! Next, removable devices:
<match action="org.freedesktop.hal.storage.mount-removable">
<!--match group="plugdev"-->
<match user="mdione">
<return result="yes"/>
</match>
</match>
<match action="org.freedesktop.hal.storage.eject">
<!--match group="plugdev"-->
<match user="mdione">
<return result="yes"/>
</match>
</match>
I added the eject action just in case. All this
works fine for my user, but I really liked the group interface. The
complaining about it was that it was too coarse, and now this way
one can grant specific actions in amore fine grained way, but I
think is too fine grained. Unluckly we cannot (ab)use the
define_admin_auth tag to give a similar functionality,
it doesn't work that way (I don't know what way it works
either).
As a final note, see that I used a per-action approach, where the outer match points to an action. we could use a per-user approach, where the outer match points to a user:
<match user="mdione">
<match action="...">
<match action="...">
<match action="...">
</match>
debian sysadmin
El sábado pasado recibo un mail de un chango del laburo con subject «ayuuuudaaa urgenteeeeee»[sic]. Me dice que estaba boludeando el viernes a la noche cuando la máquina se quedó primero sin sonido y luego se tildaba. Cuando la quizo reiniciar no arrancó más. «dice algo de disco no booteable...».
El lunes caigo como siempre y me voy a verla. Efectivamente, decía algo de disco no booteable. Booteo con un GRML que tengo en un pendrive y descubro que el disco no tiene particiones.
Pánico.
El tipo está acá haciendo un posdoc en algo astronómico (literalmente) y todo su laburo está ahí dentro. No hay backups, como corresponde, así que me preparo a hacer un rescate de particiones.
En el mismo pendrive tengo un sistema con parted. Booteo con eso y trato de usar el rescue del parted. Naranja. Le pregunto cómo eran las particiones, etc. Me responde que él sólo instaló un Kubuntu por defecto. Por defecto Kubuntu crea una partición swap y una ext3 para / y eso es todo, lo cual hacía mas fácil lo que estaba por venir.
Reninicio en el GRML y con hexdump -C /dev/sda |
more me pongo a ver el contenido del diso a pelo. No es la
primera vez que hago malabares con particiones y MBRs, pero antes
lo hacía con una herramienta que creo que a esta ahora está
discontinuada (El programa se llamaba adecuadamente DiskEdit, de
una empresa que lleva el mismo nobre que un vino y que el apellido
del dueño, cuyo nombre es el mismo que el padre en Family Guy, y
que vienen haciendo Utilities para Windows desde que las hacían
para DOS) y que tenía visores especiales para estos sectores y
también editar FATs y un montón cosas útiles... en el universo
M$.
Primero confirmo que, efectivamente, el primer sector es un MBR
(al menos tiene el signature 0x55aa en los últimos dos
bytes), y toda la tabla
de particiones está vacía, pero que en el segundo sector parece
haber una copia. Agarro papel y lápiz, transcribo lo que parece
haber, pero al final resulta que no sólo tengo la mitad de los
datos, sino que es una partición muy chica.
Entonces me propongo buscar la partición ext3 a mano. Para ello tuve que averiguar cómo es que hace un ext3 para saber que la partición realmente es una ext3 y no cualquier verdura. Sabía que que sería con un magic, pero no tenía ni idea. Instalé las fuentes del 2.6.29 en mi laptop y me puse a mirar el código de ext3. Después de dar bastante vueltas, incluyendo seguir el código que se ejecuta cuando montás un filesystem ext3, donde podemos ver que usa un magic[3] y también la estructura del superblock del ext3, donde vemos que el offset del magic es 0x38.
Entonces el problema de encontrar un ext3 en un disco se reduce
a buscar un 0x53ef (fucking little endian) en la posición 0x38 de
un sector en el disco. Por suerte more tiene para
buscar, así que me siento a buscar toooodas las ocurrencias de
53 ef esperando que la dirección a la derecha termine
con 30 y que sean el 9no y el 10mo byte de la línea
(maldito 0 based).
Unos cuantos next después, tengo mi primer candidato. Pinta muy
bien, porque además lo estaba comparando con el mismo dump pero del
pendrive (que tengo formateado en ext2, y por suerte ext2 y ext3
comparten todas estas estructuras), y además pude ver algo que
tenía toda la pinta de ser un
uuid.
Saco que la dirección del magic es 0x80731038. A
eso le resto los 0x38 y me da que el superblock
empieza en 0x80731000, un lindo número redondito.
Pasado a decimal me dá el byte 2.155.024.384, unos
2GiB desde del comienzo del disco. ¡Pinta muy bien! El swap podría
estar primero, y ser de unos 2GiB.
Arranco un fdisk /dev/sda y al mostrar la tabla
(aún vacía) de particiones me dice que hay 16.065
sectores por cilindro*512 bytes por sector=
8.225.280 bytes por cilindro. Casi todas las distros
(en realidad creo que todas) particionan los discos por
cilindros[1], por lo que si el sector éste está justo al principio
de un cilindro...
Divido 2.155.024.384/8.225.280=...
(suspenso)[2]
262.000124494...
¡Damn! Casi lo tenía... Hmm, ¿y cuánto es lo que sobra?
(262.000124494-262)*8.225.280=... ¡1024!
¿Será que...?
Arranco un strace debugfs -R show_super_stats
/dev/sdb1 (la partición en mi pendrive) y veo que
¡efectivamente hace un seek de 1024 bytes dentro de la
partición para leer el superblock!
This is it. Con el 262 en la cabeza arranco fdisk
/dev/sda y creo dos particiones: un swap en los cilindros
1-261 y una linux del cilindro 262 en adelante. Guardo, salgo,
cruzo los dedos y corro un debugfs -R show_super_stats
/dev/sda1. ¡Fail! ¿Qué pasa? Reinicio y pruebo de nuevo, no
vaya a ser que el kernel no haya leído bien la nueva tabla de
particiones. Tampoco. ¿WTF?
Ah, duh, es sda2. Ok, debugfs -R
show_super_stats /dev/sda2... ¡Anda, el muy HDP anda! No lo
puedo creer. Me la juego: fsck -n /dev/sda2.
«Filesystem is clean» Damn, vamos de nuevo: fsck -n -f
/dev/sda2...
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
/dev/sda2 sarasa sarasa...
¡Intacto! Pero el MBR no tiene un grub, así que hago el habitual proceso de reinstalar Grub, reinicio...
Bootea perfecto, y termina en un hermoso login. Satisfecho, me doy unas palmaditas en la espalda, cargo mis cosas y comienzo el fin de semana.
sysadmin rescue
[1] ... desperdiciando unos 8MiB entre el MBR y la primera partición
[2] Los perspicaces se darán cuenta al toque que eso no puede ni a ganchos dar entero.
[3] Son muy graciosos los magics de Reiser. Parece haber iniciado una moda que ahora usan los AdOlEsCeNtEs.
Esto capaz es una boludez, pero buéh...
En uno de mis laburos (ahora tengo 3) tenemos un server que da
DHCP y DNS a una red interna, un Debian Etch con
dnsmasq. El punto es que dnsmasq
concentra muy bien estos dos servicios, haciendo muy simple el
poder resolver nombres de máquinas que tomaron su IP dinámicamente
por DHCP. Esto se logra haciendo que el cliente mande un nombre de
máquina, y según el ip que le asigna agrega una entrada de DNS.
Para el entorno donde estoy trabajando esto viene de pelos.
También toma DNS del /etc/hosts. El problema es que
si en la entrada del 127.0.0.1, además del típico
localhost y/o localhost.localdomain
(puaj) hay otro nombre de máquina, y está primero, antes que
ninguna otra máquina, esa entrada queda en el DNS. Así, si desde
dentro de la red preguntás por el nombre del servidor, el DNS
responde que 127.0.0.1. Parrafraseando a un famoso
personaje de una serie televisiva: “D-oh!”.
sysadmin dnsmasq
¿Se acuerdan de
la primera parte? Bueno, venía aplicando esto varias veces
últimamente (nuevamente tracs y nuevamente plugins). Sé que
mi jefe me va a retar, pero la verdad es que lo prefiero así.
Generalizé esa idea y lo hice un script, y para marcar su
diferencia con el easy_install, lo llamé
useful_install:
#! /usr/bin/python
__requires__ = 'setuptools==0.6c3'
import sys
path= sys.argv.pop (1)
sys.path.append (path)
sys.argv= [sys.argv[0], '--install-dir', path, '--site-dirs', path ]+sys.argv[1:]
from pkg_resources import load_entry_point
sys.exit(
load_entry_point('setuptools==0.6c3', 'console_scripts', 'easy_install')()
)
Como verán es idéntico al easy_install, salvo que
el primer parámetro es el directorio donde vamos a instalar las
cosas y que hace la magia necesaria de agregarlo al
sys.path y armar los argumentos para e_i.
Además, le podemos pasar más parámetros a voluntad y pasan derecho
al load_entry_point del e_i. Que lo
disfruten.
Desde hace como dos semanas que vengo esporádicamente peleando
con un problema: Tenía que poner a andar el plugin de Mercurial
para Trac. Según las instrucciones de
la página, sólo es cuestión de generar un .egg
(¿se acuerdan?),
ponerlo en el directorio plugins del environment y ya.
Pero corriendo tracd y entrando con un browser me
daba:
TracError: Unsupported version control system "hg"
Mi primer sospechoso era el .egg; creía que no lo
estaba encontrando. Prendiendo el logging en el
trac.ini descubrí que en realidad si lo levantaba.
Siguiendo un par de links encontré una sugerencia de correr todos
los imports que corre el backend.py del plugin.
Finalmente descubrí que la línea:
from mercurial.revlog import LookupError
fallaba. Se vé
que el Mercurial que viene en Etch (0.9.1-1+etch1) no es del todo
compatible, a pesar que la página del plugin dice que lo es,
inclusive hasta con 0.8. Para "repararlo" simplemente saué las (1)
referencias a LookupError.
En la oficina tenemos un server que entrega nfs;
también tenemos mucha gente que compila cosas. Estos dos parámetros
hacen que tener sincronizadas las horas de las máquinas sea una
necesidad. Pra esto se pueden usar los paquetes
ntpdate (para on-time-sync) y ntp (para
keep-in-sync). Hoy estuve revisando bien como interactúan ambos,
sobre todo al momento del booteo. antes una descripción de qué hace
cada uno.
ntp se encarga de mantener la hora de la máquina
mas-o-menos sincronizada con la de una fuente externa. Si llega a
haber cierta diferencia, se encarga de ir acomodando la hora de a
poco, para que el sistema no sufra por cambios bruscos. Un problema
que tiene es que si la diferencia con la referencia externa es muy
grande, ntp no es capaz de salvar las diferencias y
entonces ''no hace nada''. ntpdate se encarga
simplemente de setear incondicionalmente la hora local según la
hora que consiga de esta referencia externa. El escenario ideal
sólo haría uso de ntp, pero en general se podría usar
también ntpdate para máquinas con el reloj
para-el-carajo, como parece ser el caso de al menos una de nuestra
máquinas.
El tema es que, por defecto, ntpdate utiliza un
archivo de configuración de ntp, pero a su vez corre
despúes de éste, en cuya condición falla pues el puerto UDP que usa
ya está en uso por ntp. Desinstalando ntp
nos deja sin el archivo de configuración, y en realidad tiene
sentido quedarnos con él.
La solución que encontré es simplemente hacer un symlink al
script de ntpdate en /etc/network/if-up.d
a un nombre anterior al de ntp (por ejemplo,
mntpdate). Voy a ver si en debian/ubuntu me dan pelota
con lo de ponerles mejor orden que simplemente el nombre.
Hoy llegué a la oficina con la idea de que algunos
tracs aún no andaban. Resultó que la auth contra LDAP
no estaba lista. Encontré que esta configuración si andaba:
AuthLDAPURL ldap://ldap.except.com.ar/ou=People,dc=except,dc=com,dc=ar
Require valid-user
Mientras que esta otra no:
AuthLDAPURL ldap://ldap.except.com.ar/ou=People,dc=except,dc=com,dc=ar
AuthLDAPGroupAttribute memberUid
AuthLDAPGroupAttributeIsDN off
Require group cn=except,ou=Group,dc=except,dc=com,dc=ar
Resulta que ahora tenés que avisarle que el grupo lo tiene que buscar en el LDAP:
Require ldap-group cn=except,ou=Group,dc=except,dc=com,dc=ar
Hoy me tocó hacer una negrada marca cañón. Resulta que tenemos un Xarope andando. En la arquitectura de Xarope tenemos un Apache que nos hace de frontend a todos los sitios Zope que corren en los DomU's.
El tema es que estaba poniendo a andar en uno de los DomU's un
repo Mercurial, el cual
corre detrás de un lighty.
Ponerlo a andar no fue muy duro, salvo que no sabía mucho ni de uno
ni del otro. Cuestión que llegué al punto en que podía hacer un
hg clone para bajarme una copia del repo, mas no hacer
un push. Acá es donde se ponía peluda la cosa.
El problema es el siguiente: para que las passwds no anden
volando en cueros tenía que poder acceder al repo por
https. Ahora, repiensen el esquema: tengo un Apache al
que le llegan los pedidos https, el que a través de un
Rewrite forwardea el pedido al Lighttpd corriendo en
otra máquina (virtual, pero no viene al caso). Por razones casi
obvias forwardear de un 443 a un 443 no anda. ¡Pero resulta que si
el forward es al 80 si!
Es decir, el Apache recibe https, y cuando
forwardea el pedido, lo hace en http plano. Esto
responde a mi pregunta "¿Comocatzo vamos a hacer cuando los
usuarios de Zope pidan acceso por https?". La única
contra que le veo a este setup es que el sitio final (sea un Zope o
un repo Mercurial) no sabe si el acceso viene con SSL o no.
hoy finalmente hicimos el doble salto mortal en nuestras 11 máquinas. he aquí un reporte.
una de las máquinas volvió a tener el hipo de hwdb-client* que no podían instalarse por lo que comenté en el post anterior. el tema es que el truco de hacer dpkg -i hwdb-client* no funcionó poruqe hwdb-client a secas iba a ser removido luego de ser instalados los otros. para salir del paso, hubo que recurrir a la fuerza bruta:
dpkg --force-depends -P hwdb-client
en otra no anduvo porque no se pudo instalar python-central. sólo hubo que darle otra vielta por dselect antes de hacer la manganeta con dpkg. cabe aclarar que si bien todas son *buntus, la mayoría son ubuntus, hay una que otra kubuntu, y en general cada una tiene un montón de paquetes instalados para satisfacer las necesidades de aquellos que alguna vez la usaron.
ya a la cuarta máquina a la que le hacía el upgrade me sabía de memoria qué hacer cuándo y dónde. se volvió tan aburrido que en un momento dado estaba upgradeando 5 máquinas a la vez. el vinito del mediodía le puso un poco de pimienta al asunto.
salvo algunos detalles con una placa de video via k8m890 todo salió a la perfección. creo que esta pirueta no habría salido si no fuera por debian y ubuntu. estoy seguro que en alguna otra distro no debian-based aún estaría renegando.
sysadmin ubuntu