Nisu - UJI

Sobre la configuración de este servidor

14 December 2010

Contenido

  1. Introducción
  2. El firewall
  3. Los usuarios
  4. El DNS
  5. El correo
  6. Apache y PHP
  7. Las copias de seguridad
    1. Las bases de datos
  8. Notas.
Este documento describe las particularidades de la configuración de este servidor. A mí me es últil, espero que también a otros.

Introducción

Este servidor no es otra cosa que un PC con dos discos duros donde tengo mis cosas y donde mis alumnos y los de otros colegas pueden desarrollar su actividad académica, especialmente proyectos web basados en PHP. Su configuración no es fruto del azar sino el resultado de 15 años de mantener servidores Linux multiusuario, pero obviamente es discutible en todos sus aspectos.

En primer lugar notar que los discos no están en RAID. Simplemente prefiero mantener copias de seguridad selectivas y coherentes que duplicar a bajo nivel. Si un disco se rompe (espero que no), me cuesta poco reconstruirlo. Poner más discos y hacer RAID es una opción, pero no con el hard que tengo ahora, pues me resultaría muy difícil mantener los discos a 30º como los tengo ahora.
Un disco contiene /home y el otro el resto del sistema. El /tmp no es un disco aparte, lo que sería deseable para ciertas prácticas que hacemos, pero es lo que hay.

El firewall

Se trata de un conjunto de reglas de iptables1 que explicaré un poco. En primer lugar se establecen unas reglas para intentar evitar el syn flood, todas las conexiones TCP con SYN se envían a la cadena SYN_FLOOD que limita a 5 por segundo. Cuando un paquete que entra en la cadena BAN, es eliminada su IP de PBAN, marcada la IP y rechazado. Cuando un paquete que entra en la cadena PBAN es marcado y admitido, slavo que haya entrado otro desde la misma IP en el último minuto, que entonces es rechazado y actualizada la hora.

Es preciso aceptar los paquetes de retorno de las conexiones TCP, identificados por el estado ESTABLISHED o RELATED. A partir de aquí hay que autorizar algunas cosas, apicar las reglas de entrada en PBN y rechazar lo demás.

En primer lugar, cualquier IP que viene al SMB, que no se usa en la máquina, es baneada, todos los Windozes que van escaneando son baneados. Para ciertas protecciones al Apache todo lo que va al puerto 8888 es baneado inmediatamente. Así, desde Apache cuando se detecta cualquier intento de ataque o similar, el servidor redirige al puerto 8888, si el cliente acepta la redirección, es baneado de inmediato.

Después, todas las IP que están ya en BAN, si hacen cualquier conexión siguen en BAN hasta que estén 2 días sin conectar. El criterio para entrar en BAN (desde PBAN) son 20 conexiones por minuto, que ningún cliente no-agresivo debería hacer en ningún caso. La entrada en PBAN se produce con 10 coenxiones por minuto y simplemente bloquea durante un minuto.

El criterio para acceder a PBAN es acceder a puertos que requieren autenticación, concretamente ssh, ftp, pop3, pop3s, imap, imaps, 998, 5900, 5500.
Por último, el cortafuegos acepta conexiones a ciertos puertos usuales y el resto las rechaza.

Los usuarios

La UJI registra cientos de incidencias anuales del tipo "no recuerdo mi contraseña". En este servidor, de uso menos frecuente, la probabilidad de olvido se dispara. Para evitarlo, la estrategia es simple: los usuarios tienen los mismos nombres que en la UJI y pueden (re)establecer su contraseña desde la página http://al.nisu.org. Esta página, después de validar que usuario existe, añade a un archivo el usuario y la contraseña elegida. Un proceso del root desde el Cron lee archivo y cambia la contraseña del sistema y de las bases de datos 2.

El DNS

El propio servidor contiene el DNS del dominio nisu.org3. Lo relevante es que implementa dos comodines:
	al		IN	A	150.128.97.91
	*.al		IN	A	150.128.97.91
	*		IN	A	150.128.97.91
Es decir, cualquier nombre de la forma xxxx.nisu.org resuelve a una determinada IP y lo mismo para xxxx.al.nisu.org. Realmente al ser actualmente la misma IP, el comodín xxxx.al.nisu.org no es necesario, pero lo sería si el servidor de alumnos no coincidiera con el general de nisu, como sucedía en el pasado.

Además el servidor delega autoridad sobre zonas del tipo xxxx.dyn.nisu.org. Se obtiene simplemente accediendo a la URL http://gsi.nisu.org/prDNS.php?dele=xxxxxx, que realiza la delegación llamando a un script4 con el nombre solicitado y la IP desde la que se invocó.

El correo

El servidor de correo Postfix está configurado para escuchar el puerto 26 además del 25. Con esto puede recibir correo de fuera de la UJI con ayuda de un relay exterior. Como sistema de filtrado en el reparto se emplea Maildrop (de Courier). El correo se almacena en formato maildir, establecido en /etc/maildroprc con
DEFAULT="$HOME/Maildir"

Es de esperar que los alumnos no vayan a leer el correo en este servidor, por lo que cuando se crea el usuario, se le instala un .forward conteniendo:

"| /usr/local/bin/reenvia"
El script reenvia es sencillo. Compone el mensaje original como un adjunto y con un aviso lo envía a la cuenta oficial de la UJI, evitando bucles 5.

Apache y PHP

El servidor concentra en una sola IP, varios VirtualHost que a su vez se expanden en infinitos nombres que se gestionan mediante reglas de reescritura. Actualmente están en servicio el VirtualHost del grupo GiNtAL, que es el único que atiende peticiones SSL, el genérico de nisu, el de los estudiantes (http://alxxxxxx.al.nisu.org) y el que atiende las peticiones por defecto6.

Lo más interesante es entender cómo un servidor virtual atiende un número indefinido de servidores, veamos como se implementa en el caso del servidor de alumnos7. En las primeras líneas se define el nombre del servidor (Servername) como al.nisu.org, aunque podría usarse otro cualquiera. Lo importante es el ServerAlias, que es el comodín *.al.nisu.org que coincide por ejemplo con al007007.al.nisu.org o con www.pepito.al.nisu.org. Dado que estamos definiendo el propio al.nisu.org, establecemos un DocumentRoot para él. Las reglas de reescritura permiten cambiar las URLs para poder implementar el manejo de los múltiples servidores virtuales. La primera regla que debemos observar es RewriteMap que define una función lowercase que convierte mayúsculas en minúsculas. Aplicamos entonces una condición (RewriteCond) sobre la variable de entorno HTTP_HOST de modo que si coincide con la expresión ^.*?\.?([^.]+)+\.al\.nisu, definirá %1 como el nombre que va justo delante de al.nisu.org y aplica la siguiente condición que se cumple si existe un usuario con ese nombre y tiene creado el directorio public_html. De ser así se aplica la regla de reescritura que reescribe la ubicación del objeto solicitado al directorio donde está la web del usuario, es decir, algo como http://pepe.al.nisu.org/img/es.gif se dirige internamente (sin modificar la URL) al objeto /home/pepe/public_html/img/es.gif. Se define entonces la variable de entorno ES_AL a true.
Si algo ha fallado pasa a la siguiente regla, que con la condición de que se haya pedido una página de alumno, redirige a una página informativa de que el usuario no existe o no tiene directorio web.

Con el objeto de poder tratar los errores 403, 404 y 500 sin que cambie la URL, se redirigen a una ubicación local, pero la directiva no permite rutas absolutas, lo que se resuelve con las reglas de reescritura que hay al inicio. Estos archivos de error, contienen información para el visitante de la web, como es habitual, pero también para el desarrollador, pues suelen producirse por errores en los permisos.

Si no está definida la variable ES_AL los logs se envían a un archivo común del sistema, pero si está definida, cuando se trata de una URL de usuario, los logs de los usuarios se envían a cada usuario a través de un ineficiente script8. El script genera un archivo accesos.log propiedad del administrador (para no consumir cuota de disco), que está en un directorio accesos propiedad del usuario y de sólo lectura para él. Para evitar el crecimiento desmesurado del archivo accesos.log, que no puede ser editado por el usuario, todas las mañanas otro script9 deja el archivo en 500 líneas, pero asegurándose de que no se borren los logs del día ni del día anterior. El número de líneas puede ajustarse entre 0 y 5000 escribiendo el número deseado en el archivo accesos.auto en el mismo directorio.

A nivel de todo el servidor (Location /) se define que los archivos PHP deben ser tratados por suPHP y no por el módulo interno de apache, que no está habilitado a nivel de servidor. El uso de Location impide que se pueda impedir el uso de suPHP desde los ficheros .htaccess que el usuario está autorizado a definir.

El uso de suPHP es menos eficiente que el módulo interno, impidiendo además el uso de cachés, pero ésta no es una máquina muy ocupada y la seguridad e independencia que proporciona suPHP en un entorno multiusuario no puede alcanzarse de ningún otro modo (el safe_mode de PHP es inútil y no crea más que incompatibilidades).

La configuración de suPHP exige que los permisos de los archivos PHP sean 700. de modo que un archivo en producción que pueda contener contraseñas de base de datos o seceretos de autenticación, no podrá ser leído por otro usuario. El resto de las configuraciones son similares. La configuración del genérico de nisu.org10 es una versión simplificada del de los alumnos. Lo mismo el de gintal11 que tiene como particularidad que se exige SSL para ciertas ubicaciones. Efectivamente, el servidor admite peticiones SSL sobre sec.gintal.com 12, por el puerto estándar (443) sin solicitud de certificado de cliente (sólo requerida en ciertas ubicaciones con renegociación) y por el puerto no estándar 444, donde se requiere siempre certificado de cliente para acceder.

La ubicación /common/auth* implementa el protocolo de autenticación cruzada que permite autenticación con certificado de cliente en servidores noSSL.
Igualmente el servidor por defecto, que atiende al nombre de la máquina en la UJI, es el que permite la autenticación usando el login único de la UJI a los usuarios que deseen implementarlo.

Comentar por último que ciertos servidores virtuales específicos, como todos los expires*.nisu.org, empleados para control de versiones en software producido por nosotros, tiene una definición propia 13, en este caso usa el módulo del servidor para atender el PHP por razones de eficiencia y realiza separación de logs (hay decenas de accesos por segundo para estos servidores).

Las copias de seguridad

Hay dos discos, las copias de uno se realizan sobre el otro. Aunque se les indica14 que utilicen SVN o que desarrollen en casa, los estudiantes desarrollan directamente sobre la máquina, con lo que los disgustos tipo "se me ha borrado el script principal de mi proyecto" no son infrecuentes. Para ello decidí montar el sistema de copias de la siguiente forma:
  • Hay una copia idéntica de directorios seleccionados.
  • En la copia se almacenan copias antiguas de los archivos. El tiempo hasta que se borran depende del tamaño del archivo.
  • La copia se hace varias veces al día.
Esto se consigue fácilmente con el comando:
  function cpsec () {
bck=${!#}
rsync -Sqaxb --delete --suffix="~~$(date +%s)" -f "P *~~*" $*
Los archivos borrados o modificados en la fuente son renombrados en la copia añadiendo la fecha actual en segundos precedida de ~~. El filtro impide que el propio rsync borre las copias antiguas. Para ir limpiando esas copias antiguas, a continuación se ejecuta:
    find "$bck" -regex ".*~~[0-9]+" criterios | xargs -d '\n' -r rm
}
En los criterios debe usarse ctime y no mtime. Para los archivos modificados sería indiferente, pero no para los borrados. El atributo ctime permite establecer el momento de la copia de seguridad (cuando el archivo es renombrado), mientras que si se usara mtime, un archivo antiguo borrado en el original, sería renombrado en la copia e inmediatamente borrado por el find.

Con esta función, todo el /home se copia en el raíz, pero evitando archivos de ciertos tipos (avi, mp3, etc.), de modo que la copia no tiene apenas impacto en la ocupación del otro disco.

Este esquema de copia tiene dos problemas, que no han tenido impacto hasta su resolución (pues los estudiantes, o no saben o son buena gente):

  • Realmente se está duplicando el HOME del usuario, pues el usuario puede escribir en la copia. En particular sería necesario poner cuotas de disco en el raíz para impedir que lo usen de almacén. Pero el usuario podría decidir borrar la copia y emplear el espacio para guardar otras cosas.
  • En el otro extremo, el usuario puede impedir indefinidamente el borrado de las copias antiguas, simplemente haciendo touch de los archivos con ~~.
Después de darle vueltas a complejas soluciones basadas en permisos, totalmente inoperativas (el usuario y sólo él debe poder leer sus copias de seguridad), ví claramente que se tataba de implementar un acceso read-only a bajo nivel sobre las copias. En principio sólo se me ocurría usar NFS (sobre el propio equipo), pero los núcleos actuales disponen de una opción de montaje que permite hacerlo de la siguiente manera:
  • Creo un directorio /wcopias con permisos 700 del root, de modo que sólo él puede siquier entrar. Dentro de él está copias, que es accesible por los usuarios, con los debidos permisos.

  • Creo un directorio /copias y ejecuto:
    mount --bind /wcopias/copias /copias
    mount -o remount -o ro /copias
    Los usuarios tienen acceso a /copias, pero el sistema de archivos es read-only. El administrador realiza las copias sobre /wcopias/copias.
Por último comentar la necesidad de usar la opción -S (sparse) en cualquier copia cuya fuente sea el usuario, pues por ejemplo, en prácticas de GSI se generan archivos gigantes dispersos, que de no copiarse igual, desbordarían la copia.
El resultado es un sistema de copias incrementales sólido y fácil de consultar.

Las bases de datos

Copiar las bases de datos es necesario, y también lo es tener una copia incremental. Lo más sencillo sería volcar las bases de datos de golpe, mediane mysqldump --all-databases o pg_dumpall. El inconveniente es que los usuarios no podrían leer la copia, pues debe guardarse en privado. La solución adoptada es crear un volcado individual de la base de datos en el directorio del usuario. Si esto se hace cada vez que se realiza una copia de seguridad, el archivo de volcado se actualiza cada vez, y por tanto hay una copia incremental (un archivo *~~*) varias veces al día. Para evitarlo, se realiza el volcado y si no es diferente de la copia actual, no se guarda. Las copias se realizan así:
	para cada base de datos mysql
	  establecer destino con creadm
	  mysqldump de la base de datos sobre un archivo temporal
	  si el archivo es diferente del último volcado, acualizarlo
Y lo mismo para cada base de datos PostgreSQL. Hecho así, directamente, tiene algunos inconvenientes. Si el archivo de volcado es propiedad del usuario, le consume cuota de disco. Si es del root, hay que protegerlo contra lectura del resto de usuarios. Además es iportante que el usuario torpe no pueda borrar el volcado, porque podría suponer también borrar los volcados incrementales.

Para resolver todo ello, la mencionada función creadm determina el usuario destino de la copia. Actualmente el método es sencillo: si existe un usuario con el mismo nombre de la base de datos, elige ese, si no, elige un usuario por defecto. Crea, si no existe, un directorio copia_bd propiedad del usuario con permisos 700 y dentro de él un directorio del root que contiene el volcado, también del root. Si el volcado no estuviera en un directorio del root, podría ser borrado, así no se puede borrar y por ello tampoco el directorio intermedio.
Una posible mejora al sistema sería determinar qué usuario debe ser el receptor del volcado en base a los usuarios del sistema de base de datos y no al nombre de la base de datos.

El sistema incremental no se aplica a todo. Por ejemplo, el directorio Maildir de los usuarios es copiado de forma diferente, simplemente acumulativa, sin borrado. Lo mismo para los logs del sistema.
Al final de todas las copias, los respaldos son copiados a su vez a otro ordenador, con mucho espacio, pero que a veces está apagado.

El volcado de PostgreSQL es bastante lento y carga la máquina, al igual que los rsync, por lo que se realiza todo mediante runload, que al no afectar a jerarquías de procesos, debe hacerse por proceso, no para todo el script.

El resultado es un script15 de copias invocado periódicamente desde el cron:
5 9,11,13,15,18,20,23,1 * * *	/usr/local/bin/cpsec.sh

Notas.

1
/usr/local/sbin/firewall
iptables -N SYN_FLOOD
iptables -A SYN_FLOOD -m limit --limit 5/s --limit-burst 20 -j RETURN
iptables -A SYN_FLOOD -j DROP
iptables -A INPUT -p tcp --syn -j SYN_FLOOD

# Conexiones establecidas OK
iptables -A INPUT --match state --state ESTABLISHED,RELATED -j ACCEPT

# protejo samba
#iptables -A INPUT -p tcp -m multiport --dport 139,445 -j BAN
#iptables -A INPUT -p udp -m multiport --dport 135:139 -j BAN

iptables -A INPUT -p tcp -m multiport \
	--dport openvpn,1195,ssh,ftp,pop3,pop3s,imap,imaps,998,5900:5910,5500,8662,smtp,26 -j ACCEPT
iptables -A INPUT -p tcp -m multiport \
	--dport http,8881:8891,https,444,domain,44444:44446,5555,8880,22222,22223,1214,6891,4662 -j ACCEPT
iptables -A INPUT -p udp -m multiport --dport domain,8672 -j ACCEPT
iptables -A INPUT -p udp --sport domain -j ACCEPT

iptables -A INPUT -j REJECT --reject-with icmp-host-prohibited
iptables -P INPUT ACCEPT
2
/usr/local/sbin/creau.sh
#!/bin/bash

if [ ! "$1" ]; then
  echo "$0 usu=user [pwd=pwd] [nap='Apellidos nombre'] [prf=profesor] [dni=DNI] [fyo=fichero json con datos personales] [web=solo_web]"
  exit 1
fi

# invocado desde cron hace bucle para cambiar contrasenas
# prim param fichero con los cambios
if [ "${1:0:1}" = "/" ]; then
  (flock -x 0
   while read lin; do
     cmb=1
     eval $0 $lin >>/var/log/cambiaus.log 2>&1
   done
   [ "$cmb" ] && echo -n '' >"$1" && /usr/sbin/service apache2 reload
  ) <"$1"
  exit 0 # cron necesita esto
fi

declare -A prmok=( [usu]=1 [pwd]=1 [nap]=1 [prf]=1 [dni]=1 [fyo]=1 [web]=1 [bor]=1 [tar]=1 [rip]=1 )

shr=/usr/local/share/creau.sh.d

for prm in "$@"; do
  q=${prm%%=*}
  if [ ! "${prmok[$q]}" ]; then
    echo "$q desconocido"
    exit
  fi
  eval $q=\${prm#*=}
done

if [ "$tar" ]; then
  [ -f /var/www/nisu/al/tmp/$usu-* ] && exit
  url=$RANDOM$RANDOM$RANDOM$RANDOM
  url=$usu-${url:0:10}.tgz
  cd $tar || exit
  umask 0022
  tar zcf /var/www/nisu/al/tmp/$url .
  /usr/sbin/sendmail -f noreply@nisu.org $usu@uji.es <<-EOF
	MIME-Version: 1.0
	Content-type: text/plain; charset=utf-8
	Content-transfer-encoding: quoted-printable
	Subject: Copia solicitada

	La copia solicitada se encuentra en http://al.nisu.org/tmp/$url , durante 10 minutos
	EOF
  echo rm /var/www/nisu/al/tmp/$url | at now +10 minutes 2>/dev/null
  exit 0
fi

if ! eval $usu=1 ; then
  echo el nombre de usuario debe ser una variable de shell valida
  exit 1
fi

date
PATH=$PATH:/usr/sbin

if [ "$rip" ]; then
  fail2ban-client set sshd unbanip "$rip"
fi

echo Creo user $usu
if useradd -s /bin/bash -g users -m -d "/home/$usu" $usu; then
  creado=1
  eval ho=~$usu
  chmod 701 $ho
  mkdir $ho/.yo
  chmod 755 $ho/.yo
  touch $ho/.yo/datos.json
  chown $usu:nogroup $ho/.yo/datos.json
  chmod 660 $ho/.yo/datos.json
  mv /saco/BORRADO/$usu $ho/cuenta_antigua 2>/dev/null
  chown -R $usu:users $ho/cuenta_antigua 2>/dev/null
  if ! grep -q "Use alumno $usu$" /etc/apache2/sites-enabled/alus.conf; then
    echo "Use alumno $usu" >>/etc/apache2/sites-enabled/alus.conf
    if [ ! "$web" ]; then
      touch $shr/certs/$usu
    fi
  fi
else
  eval ho=~$usu
  # pongo el shell por si deshace un bloqueo
  usermod -s /bin/bash $usu
  # reparo el .htaccess
  ht=$ho/public_html/.htaccess
  if [ -f $ht ] ; then
    ta=$(wc -c </usr/local/etc/block-htaccess.txt); ta=$[ta]
    if tail -c $ta $ht | cmp - /usr/local/etc/block-htaccess.txt; then
      truncate -s -$ta $ht
    fi
  fi
fi

if [ "$pwd" ]; then
  salt=$(head -c 1000 /dev/urandom | base64)
  salt=${salt//+/};
  salt=${salt:0:8}
  pwd=$(mkpasswd -H sha-512 "$pwd" $salt)
fi

# chfn -f "Apellidos, Nombre" -r "-de que profesor-/-profe-" -w "DNI" -h "bloqueaYMata" -o "año"

#usuario principal y auxiliares de shell (creados a mano)
for eu in '' '-1' '-2'; do
  chfn -h '' $usu$eu # por si bloqueaYmata
  if [ "$pwd" ]; then
    # cambio pass system a $pwd
    echo Cambio pwd
    usermod -p "$pwd" $usu$eu || break
  fi
  # nombre y apellidos
  if [ "$nap" ]; then
    echo Cambio NAP
    chfn -f "$nap" $usu$eu
  fi
  # profesor
  if [ "$prf" ]; then
    echo Cambio prof $prf
    chfn -r "-$prf-" $usu$eu
    #eval setfacl --set=u::rwx,g::---,o::--x ~$usu$eu
    #eval setfacl -R -m u:"$prf":r-x,m::r-x,d:"$prf":r-x ~$usu$eu
  fi
  if [ "$prf" -o "${usu:0:2}" = al ]; then
    # heuristico de curso academico
    y=$(date +%Y); [ $(date +%m) -lt 9 ] && y=$[y-1]
    chfn -o "$y" $usu$eu
    # cuota
    setquota -u $usu$eu 900000 1000000 10000 12000 -a
  fi
done

if [ "$dni" ]; then
  echo Cambio DNI $dni
  chfn -w "$dni" $usu
fi

# datos personales en HOME
if [ "$fyo" ]; then
  mkdir $ho/.yo 2>/dev/null && chmod 755 $ho/.yo
  cat "$fyo" > $ho/.yo/datos.json
  chown $usu:nogroup $ho/.yo/datos.json
  chmod 660 $ho/.yo/datos.json
  rm "$fyo"
fi

alg="$ho/accesos"
if [ ! -f $alg/error.log -o ! -f $alg/accesos.log ]; then
  mkdir $alg 2>/dev/null
  chown $usu $alg 2>/dev/null
  chmod 700 $alg
  touch $alg/error.log $alg/accesos.log
  chmod 444 $alg/error.log $alg/accesos.log
fi

# solo web
mkdir $ho/public_html 2>/dev/null
chmod 711 $ho/public_html
if [ "$web" ]; then
  echo Creo $ho/public_html solo web
  chown $usu:www-data $ho/public_html
  usermod -g www-data $usu
  chown root:root $ho
  chmod 755 $ho
else
  [ "$creado" ] && echo '<h1>P&aacute;gina por defecto</h1>' >$ho/public_html/index.html
  chown -R $usu:users $ho/public_html
fi

# peticiones borrado
case "$bor" in
  'fic')
    echo Borro los archivos del usuario
    find $ho -type f -print0 | xargs -0 rm -f
     ;;
  'my')	
     echo Borro Base de datos y usuario mysql
     echo "DROP DATABASE IF EXISTS \`$usu\`;" | mysql
     echo "DROP USER '$usu'@'localhost';" | mysql
     ;;
  'pg')
    echo Borro Base de datos y usuario pgsql
    echo "DROP DATABASE \"$usu\";" | psql
    echo "DROP USER \"$usu\";" | psql
    ;;
  'wdpress')
    echo Reinicio wordpress
    chmod 000 $ho/public_html
    # en 2 pasos para que no cambie los permisos del dir hasta el final
    rsync -aq $shr/muestra-wordpress/* $ho/public_html/
    rsync --delete -aq $shr/muestra-wordpress/ $ho/public_html/
    cp -p $shr/multiwordpress.php $ho/public_html/
    touch $ho/public_html/.htaccess; chmod 644 $ho/public_html/.htaccess
    sed -i $ho/public_html/wp-config-sample.php -e "/stop editing/i define('WP_ALLOW_MULTISITE', true);"
    chown -R $usu:www-data $ho/public_html/
    # se va a requerir leer el public for ulti
    chmod 755 $ho/public_html
    echo "DROP DATABASE IF EXISTS \`$usu\`;" | mysql
    echo "DROP USER '$usu'@'localhost';" | mysql
    ;;
esac

rsync -Sqaxb --delete --suffix="~~$(date +%s)" -f "P *~~*" /etc/passwd /saco/wcopias/copias/nisu/etc/

[ "$pwd" ] || exit

pwd="${pwd//\/}"
pwd="${pwd: -20}"

# al cambiar pwd el web= no está pero hay que cambiar config
if [ -f $ho/public_html/wp-config.php ]; then
  sed -i $ho/public_html/wp-config.php -e "s/define('DB_PASSWORD', '.*');/define('DB_PASSWORD', '$pwd');/"
fi

/usr/sbin/sendmail -f noreply@nisu.org $usu@uji.es <<-EOF
	MIME-Version: 1.0
	Content-type: text/plain; charset=utf-8
	Content-transfer-encoding: quoted-printable
	Subject: =?utf-8?Q?Cambio de contrase=C3=B1a en Nisu?=

	Se ha producido un cambio de contrase=C3=B1a en Nisu.
	La nueva contraseña para la base de datos $usu es:
	  $pwd
	$(if [ ! "$web" ]; then
		cat <<-EOF2
			Puede cambiarse desde una terminal usando:
			echo "SET PASSWORD FOR '$usu'@'localhost' =3D PASSWORD('nueva password');" | mysql -p'$pwd'
		EOF2
	fi)
	EOF

echo Creo database mysql $usu
mysql <<-EOF
	CREATE DATABASE $usu;
	GRANT USAGE ON * . * TO '$usu'@'localhost'
		IDENTIFIED BY '$pwd' WITH MAX_QUERIES_PER_HOUR 0
		MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0 ;
	GRANT ALL PRIVILEGES ON $usu . * TO '$usu'@'localhost' WITH GRANT OPTION ;
	GRANT TRIGGER,EVENT ON $usu . * TO '$usu'@'localhost' ;
	flush privileges;
	EOF
echo show databases | mysql -s 2>/dev/null | grep -q $usu ||
  { echo | mail -s "Fallo al crear mysql $usu" mm@nisu.org ; }
echo Cambio pass mysql
mysql <<-EOF
	SET PASSWORD FOR '$usu'@'localhost' = PASSWORD('$pwd');
	flush privileges;
	EOF
echo Creo database pgsql $usu
# todo esto no funciona ahora 2017 ....
psql <<-EOF
	create user $usu with password '$pwd' nocreatedb nocreateuser;
	create database $usu owner $usu;
	EOF
createlang plpgsql "$usu"
echo Cambio pass pgsql
psql <<-EOF
	ALTER USER $usu PASSWORD '$pwd';
	EOF
3
Es irrelevante que esté instalado en el propio servidor
4
/var/www/nisu/gsi/updata.sh
#!/bin/bash
PATH=/bin:/usr/bin
set -x
log=nsupdate.$1.error-log
rm -f $log.*
log=$log.$$.$(date +%s)
exec 2>$log >&2
nsupdate -v -k /home/mollar/Kdyn.mobelt.com.+157+29881.private <<-EOF
	server dns.mobelt.com.
	zone nisu.org.
	update delete $1.dyn.nisu.org.
	update delete dns.$1.dyn.nisu.org.
	update add dns.$1.dyn.nisu.org. 30 A $2
	update add $1.dyn.nisu.org. 30 NS dns.$1.dyn.nisu.org.
	send
EOF
at now +120 minutes <<-EOF
	[ -f $log ] || exit
	rm -f $log
	nsupdate -v -k /home/mollar/Kdyn.mobelt.com.+157+29881.private <<-EOF2
		server dns.mobelt.com.
		zone nisu.org.
		update delete $1.dyn.nisu.org.
		update delete dns.$1.dyn.nisu.org.
		send
		EOF2
	EOF

5
/usr/local/bin/reenvia
6
7
/etc/apache2/sites-available/alus
1   <VirtualHost *:80 *:8881>
2   	ServerAdmin webmaster@localhost
3   	ServerName al.nisu.org
4   	DocumentRoot /var/www/nisu/al
5   	<Directory /home>
6   		Options FollowSymLinks Multiviews
7   		AllowOverride All
8   	</Directory>
9   	RewriteEngine On
10   	LogLevel info rewrite:trace8
11   	RewriteRule ^/-----(e[0-9]*.html)$ %{DOCUMENT_ROOT}/$1 [L]
12   	ErrorDocument 500 /-----e500.html
13   	ErrorDocument 404 /-----e404.html
14   	ErrorDocument 403 /-----e403.html
15   	Options FollowSymLinks Multiviews	CustomLog /var/log/apache2/access.log combined
16   	Errorlog  /var/log/apache2/error.log
17   	ServerSignature Off
18   </VirtualHost>
19   <VirtualHost *:443>
20   	ServerName al.nisu.org
21   	UseCanonicalName on
22   	SSLStrictSNIVHostCheck on
23   
24   	LimitRequestBody 1048576
25   
26   	Include /etc/letsencrypt/options-ssl-apache.conf
27   	SSLCertificateFile /etc/letsencrypt/live/al.nisu.org/fullchain.pem
28   	SSLCertificateKeyFile /etc/letsencrypt/live/al.nisu.org/privkey.pem
29   	DocumentRoot /var/www/nisu/al
30   	<Directory /home>
31   		Options FollowSymLinks Multiviews
32   		AllowOverride All
33   	</Directory>
34   	RewriteEngine On
35   	Options FollowSymLinks Multiviews	CustomLog /var/log/apache2/access.log combined
36   	ErrorLog /var/log/apache2/error.log
37   	ServerSignature Off
38   </VirtualHost>
39   
40   
8
9
10
/etc/apache2/sites-available/nisu.org
<VirtualHost *:80 *:8881>
  ServerName nisu.org
  ServerAlias *.nisu.org
  RewriteEngine On
  RewriteMap lowercase int:tolower
  RewriteRule ^/-----(e[0-9]*.html)$ %{DOCUMENT_ROOT}/error/$1 [L]
  RewriteRule /icons(.*) /usr/share/apache2/icons$1 [L]
  RewriteCond %{HTTP_HOST} ^nisu.org [NC]
  RewriteRule ^(.+)$ %{DOCUMENT_ROOT}/www$1 [L]
  RewriteCond %{HTTP_HOST} ([^.]+)\.nisu [NC]
  RewriteRule ^(.+)$ %{DOCUMENT_ROOT}/${lowercase:%1}$1 [L]
  ErrorDocument 500 /-----e500.html
  ErrorDocument 404 /-----e404.html
  ErrorDocument 403 /-----e403.html
  #RewriteRule ^(.+)$ %{DOCUMENT_ROOT}/error/index.html
  DocumentRoot /var/www/nisu
  HostnameLookups Off
  UseCanonicalName Off
  ServerSignature Off
  ErrorLog /var/log/apache2/error.log
  CustomLog /var/log/apache2/access.log combined
  Options FollowSymLinks Multiviews 
  <Location ~ /tmp/>
    AddHandler application/octet-stream .php
  </Location>
  <Directory /var/www/>
    AllowOverride All
  </Directory>
  <Location ~ "/common">
    ExpiresActive On
    ExpiresDefault "modification plus 1 week"
  </Location>
</VirtualHost>

11
12
13
/etc/apache2/sites-available/expires.nisu
<VirtualHost *:80 *:8881>
	ServerAdmin webmaster@localhost
	ServerName expire.nisu.org
	ServerAlias expire-*.nisu.org
	DocumentRoot /var/www/nisu/
	RewriteEngine On
	RewriteMap lowercase int:tolower
	RewriteCond %{HTTP_HOST} ^([^.]+)+\.nisu [NC]
	RewriteRule ^(.+)$ %{DOCUMENT_ROOT}/${lowercase:%1}$1 [L]
	<Directory />
		Options FollowSymLinks Multiviews
		AllowOverride All
	</Directory>
	AddHandler application/x-httpd-php .php
	CustomLog /var/log/apache2/expire-access.log combined
	ServerSignature Off
</VirtualHost>

14
Es curioso observar como los estudiantes desechan lo aprendido en otras asignaturas. Por ejemplo, se les suministra un SVN oficial de la UJI, pero ni uno solo lo usa. En las asignaturas de base de datos aprenden PostgreSQL, pero cuando van a desarrollar en PHP optan por MySQL simplemente porque lo encuentran por Internet o en algún ejemplo que se les suministra.
15
/usr/local/sbin/cpsec.sh
#!/bin/bash

PATH=$PATH:/usr/local/bin/

ahora=$(date +%s)
exec 2>>/tmp/cpsec.$ahora
chmod 600 /tmp/cpsec.$ahora
exec >&2
set -x


exec 9>>/tmp/cpsec
flock -n 9 || { { fuser -v /tmp/cpsec; ps axuwf; } | mail -s 'Error cpsec' mm@nisu.org ; exit; }

dfus=mollar

dia=$(date +%u)
hora=$(date +%H)

function creadm() {
  # $1 = tipo , $2 = db
  if [ -d "/home/$2" ]; then
    dd="/home/$2/copia_bd/"
    us="$2"
  else
    dd="/home/$dfus/copia_bd/"
    us="$dfus"
  fi
  dm="$dd/$1/$2.dump.gz" # es preciso que lleve el $2 porque un us puede tener varias bd
  echo $dm
  if [ -f "$dm" ]; then
    chmod 700 "$dd" # por si acaso el us lo cambia
    return
  fi
  mkdir "$dd" 2>/dev/null
  chown "$us":users "$dd" 2>/dev/null
  chmod 700 "$dd"
  dd="$dd/$1"
  mkdir "$dd" 2>/dev/null # del root
  chmod 755 "$dd"
  touch "$dm"		  # del root
  chmod 644 "$dm"
}

date

ush=$(find /home/ -maxdepth 1 -type d ! -uid 0)

date

(IFS=$'\n'
 umask 0077
 tmp=/tmp/.$$
 trap "rm $tmp" exit
 for db in $(echo show databases | mysql -s 2>/dev/null); do
   if [ "$hora" != 01 -a "$ush" = "${ush/$db/}" ]; then
     continue
   fi 2>/dev/null
   dm=$(creadm my "$db")
   mysqldump 2>/dev/null -Q --opt --default-character-set=latin1 \
	"$db" | head -n -1 >$tmp
   nu=$(cat $tmp | md5sum)
   vi=$(zcat "$dm" 2>/dev/null | md5sum)
   if [ "$nu" != "$vi" ] ; then
     cat $tmp | gzip -9 >"$dm"
   fi
 done
 for db in $(su $pgus \
	-c "echo 'SELECT datname FROM pg_database;' | psql -q -t" |
	sed -e 's/^ *//'""); do
   if [ "$hora" != 01 -a "$ush" = "${ush/$db/}" ]; then
     continue
   fi 2>/dev/null
   dm=$(creadm pg "$db")
   pg_dump -O -s "$db" 2>/dev/null >$tmp
   if [ -s $tmp ]; then
     nu=$(cat $tmp | md5sum)
     vi=$(zcat "$dm" 2>/dev/null | md5sum)
     if [ "$nu" != "$vi" ] ; then
       cat $tmp | gzip -9 >"$dm"
     fi
   fi
 done
)&

runload -p $!

date

# los usuarios se equivocan con los permisos y da problemas al copiar en remoto
runload find $ush -xdev -type d ! -perm -100 -print0 | xargs -r -0 chmod u+x

function cp1() {
  runload rsync -vSaxb --suffix="~~$(date +%s)" -f "P *~~*" \
	--exclude Maildir \
	--exclude .cache \
	--exclude copias \
	--exclude \*avi \
	--exclude \*mp4 \
	--exclude \*mkv \
	--exclude \*flv \
	--exclude \*mp3 \
	--exclude \*mkv \
	--exclude tmp \
	--exclude INBOX\* \
	--exclude '/aquota.*' \
	--exclude 'accesos.log' \
	--exclude 'error.log' \
	--exclude \*avi.d \
	--exclude LOOP \
	--exclude .jd \
	--exclude \*.part\* \
	--exclude LO-QUE-ERA-DISCO-2 \
	$*
}

date

cp1 /boot /root /etc /var/www /usr/local desp.nisu.org::nisu

date

if [ "$hora" = 01 ]; then
  cp1 "/home --delete" desp.nisu.org::nisu
else
  cp1 $ush desp.nisu.org::nisu/home/
fi

date

runload rsync -vaSR /home/*/Maildir --exclude tmp desp.nisu.org::nisu

date

runload rsync -va /var/log desp.nisu.org::nisu

date

flock -u 9

if [ "$dia-$hora" = "1-01" ] ; then
# todo esto mejor sería hacerlo en desp, pero el NAS no me deja exportar por NFS a dos máquinas diferentes
  exec 9>>/tmp/cpsecL
  flock -n 9 || { { fuser -v /tmp/cpsecL; ps axuwf; } | mail -s 'Error cpsecL' mm@nisu.org ; exit; }
  if [ ! -f '/var/cosas/Importados/nas-cifrado/test' ] ; then
    echo cpsec no encuentra test en NAS | mail -s cpsec mm@nisu.org
    exit
  fi
  runload rsync -vSa --partial /saco/wcopias/copias/ --delete /var/cosas/Importados/nas-cifrado/copias/
  sa=$?
  date
  touch /var/cosas/Importados/nas-cifrado/test
  if [ $sa -ne 23 -a $sa -ne 0 ]; then
    echo Rsync nas fallo $sa
  fi
  flock -u 9
fi
Elige estilo - Legal