Nisu - UJI

Sobre la configuración de este servidor

24 November 2012

Contenido

  1. Introducción
  2. El firewall
  3. Los usuarios
  4. Los usuarios remotos
  5. El DNS
  6. El correo
  7. Apache y PHP
  8. Las copias de seguridad
    1. Las bases de datos
  9. 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 comando passwd para el cambio de contraseña Linux está deshabilitado para evitar incoherencias con las contraseñas de las bases de datos.
Si un usuario pierde la condición de alumno de la UJI deja de poder cambiar su contraseña. Para evitarlo, mientras se es alumno, una vez autenticado en http://al.nisu.org, hay un enlace que permite habilitar la autenticación en ésta página basada en certificado.

Los usuarios remotos

Los usuarios que tengan un directorio de nombre home_remoto pueden montarlo por NFS desde otro ordenador. Para ello es necesario crear un túnel que dé autenticidad al cliente. El tunel debe crearse mediante OpenVPN, autenticando. Una vez realizado el túnel, la IP asignada está autorizada a montar el home_remoto del usuario. Desde las aulas, de forma automatizada, puede realizarse todo ello junto con la creación del usuario, usando un script 3. Esto lo motiva el hecho de que cuando empleamos los ordenadores de los laboratorios de Informática de la ESTCE (no las aulas informáticas), accedemos con un usuario genérico, de modo que no podemos tener, por ejemplo, un Firefox configurado al gusto y lo más obvio, no podemos guardar los archivos de trabajo de forma fiable sin usar FTP o similar.

El script se ejecuta en el arranque de los equipos de algunos laboratorios y se activa condicionalmente según los siguientes criterios:

  • Si se encuentra el archivo homenisu.pem4 en el directorio raíz de un disco USB (que el usuario habrá introducido durante el arranque), se espera que contenga el certificado de la GVA (sólo ese) y la llave en formato PEM, y se utilizarán como credenciales para OpenVPN.
  • Si no, si se encuentra el archivo homenisu.p12 en dicho disco, se espera lo mismo, el certificado de la GVA.
  • Si no, si se encuentra el archivo homenisu.iden en dicho disco, se espera que contenga el nombre de usuario, produciendo la activación.
  • Si no, el script se activa manteniendo pulsada la tecla Retroceso (Backspace).
Una vez activado, el script establece un túnel OpenVPN contra el servidor, empleando las credenciales que proceda según el modo de activación. Dentro del túnel, el script obtiene los datos del usuario autenticado y crea el usuario en el ordenador local, con la misma contraseña, uid y gid que tiene en el servidor. Después, monta por NFS el directorio personal del usuario local sobre el directorio home_aulas que el usuario tiene en el servidor. El uso de NFS permite máxima compatibilidad entre sistemas, resultando transparente para el usuario. Por último el script prepara el gestor de entrada al sistema gráfico (gdm) de modo que el usuario recién creado entra sin autenticar.
Como facilidad adicional, el directorio public_html dentro de home_aulas se monta (con --bind) con el public_html del HOME, de modo que desde las aulas informáticas se puede editar la web sin acceder al servidor por SSH. Para evitar confusiones, desde el servidor, el directorio home_aulas/public_html no es accesible cuando no está montado.
Los usuarios pueden crear un script .homenisu en home_aulas con instrucciones adicionales que se ejecutarán en el momento del arranque del sistema. Para un ejemplo consultad conmigo.

El DNS

El propio servidor contiene el DNS del dominio nisu.org5. 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 script6 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 7.

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 genérico de nisu y el de los estudiantes, al.nisu.org.

Existe un VirtualHost que recoge las peticiones por defecto, y su misión es banear a todos los que acceden directamente a la IP o generan peticiones incorrectas. Se consigue redirigiendo todos los errores a la URL relativa "/", de modo que el script index.php8 acaba recibiendo todas las peticiones incorrectas. Este script escribe la IP del cliente en un fichero pipe que es leída por un proceso del root e insertada en la cadena BAN del firewall:

while true; do read ip </var/www/def/banea ; [ "$ip" ] || continue; echo +$ip >/proc/net/xt_recent/BAN; done &

Son varios los VirtualHost que atienden las peticiones de nisu.org, siendo el más interesante el destinado a los estudiantes. Cada estudiante tiene su propia URL que es implementada a través de una reescritura, de modo que al crear un usuario o hay que alterar la configuración del servidor web, basta con que el usuario cree su directorio de web y el site pasa a funcionar directamente. Expliquemos brevemente las configuración de este VirtualHost9. Atiende todas las peticiones a los subdominios de al.nisu.org gracias a la potente directiva ServerAlias. La raíz del VirtualHost sirve para poco más que el tratamiento de las excepciones. Dentro de las reglas de reescritura, si un PATH comienza por ~usuario , que es lo típico en apache, se redirge al scritp miserv.php que simplemente informa que la URL correcta es http://usuario.al.nisu.org/. A continuación, si se cumplen las condiciones de que el Host solicitado es de la forma usuario.al.nisu.org y existe el directorio public_html del usuario se reescribe la dirección del fichero (no la URL) de modo que accede a los achivos del usuario, se acaba la reescritura y se define la variable de entorno ES_AL Si no se cumplen las condiciones anteriores se redirige a un script indormativo nopage.php.
La directiva ErrorDocument no permite redirigir a una URL global sin que aparezca en la barra de direcciones del navegador del visitante. Por ello, para disponer del mismo tratamiento de Errores para todos los usuarios, los errores se redirigen a unas URLs falsas locales, que mediante una regla de reescritura (situada al comienzo de la lista de reglas) se reescribe internamente a un fichero local.

Para dar independencia máxima a los sites de los estudiantes, es importante que desde PHP tengan la percepción de que realmente es su servidor. Para ello es necesario ejecutar PHP a través de CGI con suPHP, no puede usarse el módulo típico de apache, porque entonces los scripts de los usuarios se ejecutan con el usuario del servidor web. Gracias a suPHP, cada PHP se ejecuta con el usuario propietario del fichero, de modo que un script puede escribir en un directorio del usuario, sin obligarle a poner permisos de escritura a todo el mundo. Además los scripts no necesitan ser leídos por el servidor, sino sólo por el usuario, manteniendo los permisos a rw-------, de modo que un usuario no puede leer los scripts de los demás usuarios (y por tanto no puede acceder a las contraseñas de los motores de base de datos almacenadas en ellos). El mecanismo suPHP resta eficiencia al servidor, pero es la única forma de garantizar la independencia y la seguridad sin virtualización. PHP dispone de un modo denominado safe_mode que podría ayudar a alcanzar esa seguridad sin suPHP, pero después de probarlo unos años, llegué a la conclusión de que crea más inconvenientes que ventajas.
El mecanismo suPHP está definido con Location /, de modo que impide que se pueda impedir el uso de suPHP desde los ficheros .htaccess que el usuario está autorizado a definir.

Los logs que no son de los usuarios (que son pocos), se envían al log del sistema, pero los logs de los usuarios, identificados por la variable de entorno ES_AL, se envían a cada usuario a través de un ineficiente script10. 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.
El log de los errores es similar, desafortunadamente es mucho más difícil filtrar los errores pues no siempre contienen la identificación del usuario propietario de la página, algunos errores se pierden, pero el mecansmo funciona bastante bien para ayudar a los usuarios. Se realiza con un script11 similar.

Para evitar el crecimiento desmesurado del archivo accesos.log, que no puede ser editado por el usuario, todas las mañanas otro script12 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.

El resto de las configuraciones son similares. La configuración del genérico de nisu.org13 es una versión simplificada del de los alumnos. Tiene como particularidad que se exige SSL para ciertas ubicaciones. Efectivamente, el servidor admite peticiones SSL sobre sec.nisu.org 14 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 15, 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 indica16 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 script17 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 nrl=1 >>/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 [nrl]=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
[ "$nrl" ] || /usr/sbin/service apache2 reload
3
/var/www/nisu/mollar/homenisu
{
     (  trap 'umount -f /mnt/usb 2>/dev/null ; umount -f /mnt 2>/dev/null' exit
	mount -t tmpfs tmpfs /mnt || exit
	chmod 700 /mnt
	for us in $(sed /etc/passwd -n -e 's/^\([^:]*\):.*NISUAUTOBORRADO.*/\1/p'); do
	  if ! mount | grep ":/home/$us/" ; then
	    userdel $us # sin -f
	  fi
	done
	if [ "$(head -c 100 2>/dev/null /dev/sdb)" ]; then
	  mkdir /mnt/usb
	  if mount /dev/sdb /mnt/usb 2>/dev/null || mount /dev/sdb1 /mnt/usb 2>/dev/null ; then
	    for key in {pem,p12,iden} ; do
	      [ -f /mnt/usb/homenisu.$key ] && x=1 && break;
	    done
	  fi
	fi
	#if [ ! "$x" ]; then
	#  stty -echo
	#  read -n 1 -t 1 x
	#  stty echo 2>/dev/null
	#  [ "$x" != $'\177' ] && return 1
	#fi
	if [ -x /usr/bin/plymouth ] && /usr/bin/plymouth --ping ; then
	  /usr/bin/plymouth --hide-splash
	fi
	/usr/sbin/setenforce 0
	stty echo
	pid=/var/run/openvpn.$RANDOM$RANDOM.pid
	ca=$(ls "$(openssl version -d | sed -e 's/^OPENSSLDIR: *"\(.*\)"/\1/')/certs/"*.crt | head -1)
	echo "Montaje de NiSu. Deja el Username en blanco para ignorar"
	while true; do
	  al=''
	  if [ "$x" = 1 ]; then
	    if [ ! -f /mnt/usb/homenisu.$key ]; then
	      x=''
	      continue
	    fi
	    if [ "$key" = iden ]; then
	      al=$(</mnt/usb/homenisu.iden)
	      x=''
	    else
	      [ "$key" = p12 ] && akey='--pkcs12 /mnt/usb/homenisu.p12' \
			       || akey='--cert /mnt/usb/homenisu.pem --key /mnt/usb/homenisu.pem'
	      openvpn --remote sec.nisu.org 1195 tcp --dev-type tun --dev tun --ca "$ca" $akey \
		--route-up '/bin/bash -c "echo $route_network_1 >/mnt/remoto"' --verify-x509-name sec.nisu.org name \
		--script-security 2 system \
		--client --tls-client --comp-lzo --daemon --writepid $pid
	    fi
	  else
	    x=''
	  fi
	  if [ ! "$x" ]; then
	    [ "$al" ] || read -p "Username: " al
	    al=$(echo -n "$al" | tr -c -d 'a-z0-9')
	    [ "$al" ] || return 1
	    stty -echo
	    read -p "Password para $al: " pw
	    stty echo
	    echo
	    ( echo $al; echo $pw ) >/mnt/tmpauth
	    openvpn --remote sec.nisu.org 1194 tcp --dev-type tun --dev tun --ca "$ca" \
		--script-security 2 system --verify-x509-name sec.nisu.org name \
		--route-up '/bin/bash -c "echo $route_network_1 >/mnt/remoto; rm -f /mnt/tmpauth"' \
		--client --tls-client --comp-lzo --auth-user-pass /mnt/tmpauth \
		--daemon --writepid $pid
	  fi
	  c=1; sleep 1
	  while [ ! -f /mnt/remoto -a -f $pid ]; do
	    echo -n $'\rEspera ... '$c
	    sleep 1
	    c=$[c+1]
	    [ $c -gt 50 ] && break
	  done
	  echo
	  if [ ! -f /mnt/remoto -o ! -f $pid ]; then
	    kill $(cat $pid 2>/dev/null) 2>/dev/null
	    echo $'\n\n\nLo siento, prueba otra vez o deja el Username en blanco'
	    continue
	  fi
	  wget -q -O - https://openvpn.nisu.org/id.php >/mnt/id
	  for f in al pw uid gid ; do
	    read -r x; eval "$f='$x';"
	  done </mnt/id
	  if [ ! "$uid" ]; then
	    kill $(cat $pid 2>/dev/null) 2>/dev/null
	    echo $'\n\n\nLo siento, prueba otra vez o deja el Username en blanco'
	    continue
	  fi
	  if mount | grep -q /$al/home_ ; then
	    kill $(cat $pid 2>/dev/null) 2>/dev/null
	    return 1
	  fi
	  rem=$(</mnt/remoto)
	  break;
	done
	groupadd -g $gid group$gid 2>/dev/null
	useradd -s /bin/bash -m -u $uid -g $gid -p "$pw" $al
	chfn -p NISUAUTOBORRADO $al
	d=$(eval cd ~$al; pwd)
	if ! mount -o nfsvers=3,nolock $rem:/home/$al/home_aulas $d; then
	  echo $'\n\n\nError al montar'
	  kill $(cat $pid) 2>/dev/null
	  userdel $al
	  read -p "Pulsa Intro para continuar"
	  return 2
	fi
	su -c "find /etc/skel/ -maxdepth 1 -type f | xargs -I %%% cp -n %%% $d;
	      mkdir /tmp/$al; chmod 700 /tmp/$al;
	      [ -x $d/.homenisu ] && cp -p $d/.homenisu /tmp/extrahomenisu" $al
	cfg=/etc/gdm/gdm.conf
	[ -f $cfg ] || cfg=/etc/gdm/custom.conf
	[ -f $cfg ] || cfg=''
	if [ "$cfg" ] ; then
	  au=$(sed $cfg -ne 's/^AutomaticLogin=\(.*\)/\1/p')
	  if [ "$au" ]; then
	    sed $cfg -i -e "s/^\(AutomaticLogin=\).*/\\1$al/"
	    ( while ! ps axuw | grep -q "^$al " && grep -q "^$al:" /etc/passwd ; do sleep 10; done
	      sed $cfg -i -e "s/^\(AutomaticLogin=\).*/\\1$au/"
	    )&
	  else
	    sed $cfg -i -e "/\[daemon\]/ a AutomaticLoginEnable=true\nAutomaticLogin=$al"
	    ( while ! ps axuw | grep -q "^$al " && grep -q "^$al:" /etc/passwd ; do sleep 10; done
	      sed $cfg -i -e '/^AutomaticLogin/d'
	    )&
	  fi
        fi
	eval "$(wget -q -O - https://openvpn.nisu.org/homenisu.start)"
	[ "$disa" ] || [ -x /tmp/extrahomenisu ] && . /tmp/extrahomenisu
	rm -f /tmp/extrahomenisu
	killall gdm
      )
      return 0
}

do_stop()
{
	uno=''
	killall -STOP gdm
	for al in $(sed /etc/passwd -n -e 's/^\([^:]*\):.*NISUAUTOBORRADO.*/\1/p'); do
	  ( while true ; do
	      ps axuw | awk '/^'"$al"' / { print $2 }' | xargs kill 2>/dev/null
	      sleep 1
	      ps axuw | awk '/^'"$al"' / { print $2 }' | xargs -r kill -9
	      sleep 5
	      umount -f /home/$al
	      mount | grep ":/home/$us/" || break
	    done
	    userdel -f $al
	    rm -rf /tmp/$al
	  ) &
	  uno=1
	done
	wait
	[ "$uno" ] && eval "$(wget -q -O - https://openvpn.nisu.org/homenisu.stop)"
	killall gdm
	killall -CONT gdm
	kill $(cat /var/run/openvpn.*.pid) 2>/dev/null
	rm -f /var/run/openvpn.*.pid
	[ "$uno" ] && reboot
}

case "$1" in
  start)
	do_start
	;;
  stop)
	exec 2>/tmp/log >&2
	setsid bash -c "$0 fstop" &
	;;
  fstop)
	do_stop
	;;
  status)
       mount | grep -q "home_aulas" && exit 0 || exit 3
       ;;
  *)
	echo "Usage: $0 {start|stop|status}" >&2
	exit 3
	;;
esac

4
Para que el servidor te autentique por certificado, debes darlo de alta según se explica aquí.
5
Es irrelevante que esté instalado en el propio servidor
5
6
/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

7
/usr/local/bin/reenvia
#!/bin/bash

export LANG=es_ES@euro

m=$(cat)

if echo "$m" | grep -q  "^X-Loop: al.nisu.org"; then
  echo "$m" | procmail
  exit
fi

sub=$(echo "$m" | formail -x Subject)

des=${1:-$LOGNAME@alumail.uji.es}

bou="nisu.$$.$(date +%Y%m%d%H%M%S%Z).$RANDOM"
/usr/sbin/sendmail "$des" <<EOF
Subject: Auto-reenviado: $sub
X-Loop: al.nisu.org
Content-type: multipart/mixed; boundary=$bou

--$bou
Content-type: text/plain; charset=ISO-8859-1
Content-transfer-encoding: 8bit

Mensaje reenviado desde al.nisu.org
Para evitarlo, borra o renombra el archivo ~/.forward que tienes allí
--$bou
Content-type: message/rfc822
Content-transfer-encoding: 7bit

$m
--$bou--
EOF
8
/var/www/def/index.php
<?php
//  if (!preg_match('/uji.es/',$_SERVER['HTTP_HOST']) and substr(gethostbyaddr($_SERVER['REMOTE_ADDR']),-6)  != 'uji.es') {
//    fwrite(fopen('banea','w'),$_SERVER['REMOTE_ADDR']."\n");
//    echo "Banned ",$_SERVER['REMOTE_ADDR']."<br>";
//  }
  echo "Petici&oacute;n incorrecta, revisa tu URL o recarga si es un sitio de nueva creaci&oacute;n";
9
1   
10
11
12
13
/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>

14
/etc/apache2/sites-available/nisu.org-ssl
NameVirtualHost *:443
NameVirtualHost *:444

<VirtualHost *:444>

# montado adrede mal para examen

  DocumentRoot "/var/www/nisu/sec"
  ServerName sec.nisu.org:444
  RewriteEngine On
  ErrorLog /var/log/apache2/error.log
  CustomLog /var/log/apache2/access.log combined
  SSLEngine on
  SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
  #SSLCertificateChainFile /etc/apache2/ssl/sec.nisu.org.crt
  SSLCertificateFile /etc/apache2/ssl/cp-sec.nisu.org.crt
  SSLCertificateKeyFile /etc/apache2/ssl/sec.nisu.org.key
  SSLCACertificateFile /etc/apache2/ssl/ca-bundle.crt
  SSLVerifyClient require
  SSLVerifyDepth  3
  SSLInsecureRenegotiation off
  <Files ~ "\.(cgi|shtml|phtml|php)$">
      SSLOptions +StdEnvVars +OptRenegotiate +ExportCertData
  </Files>
  <Directory />
    AllowOverride All
  </Directory>
  Options FollowSymLinks Multiviews  SetEnvIf User-Agent ".*" \
  	 nokeepalive ssl-unclean-shutdown \
  	 downgrade-1.0 force-response-1.0
</VirtualHost>

#<VirtualHost *:443>
#  DocumentRoot "/var/www/nisu/esurvey"
#  ServerName esurvey.nisu.org:443
#  RewriteEngine On
#  Alias /icons "/usr/share/apache2/icons"
#  ErrorLog /var/log/apache2/error.log
#  CustomLog /var/log/apache2/access.log combined
#  SSLEngine on
#  SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
#  SSLCertificateChainFile /etc/apache2/ssl/esurvey.nisu.org.crt
#  SSLCertificateFile /etc/apache2/ssl/esurvey.nisu.org.crt
#  SSLCertificateKeyFile /etc/apache2/ssl/esurvey.nisu.org.key
#  SSLCACertificateFile /etc/apache2/ssl/ca-bundle.crt
#  SSLVerifyClient none
#  <Files ~ "\.(cgi|shtml|phtml|php)$">
#    SSLOptions +StdEnvVars +OptRenegotiate +ExportCertData
#  </Files>
#  <Directory />
#    AllowOverride All
#  </Directory>
#  Options FollowSymLinks Multiviews
##  #RewriteEngine On
#  #RewriteRule ^/al/([^/]+)/(.*)$ /home/$1/public_html/$2 [L,E=ES_AL:yes]
#  SetEnvIf User-Agent ".*" \
#  	 nokeepalive ssl-unclean-shutdown \
#  	 downgrade-1.0 force-response-1.0
#  <Location ~ "/common">
#    ExpiresActive On
#    ExpiresDefault "modification plus 1 week"
#  </Location>
#  <Location ~ "/common/auth.*">
#    SSLVerifyClient optional
#    SSLVerifyDepth  3
#  </Location>
#</VirtualHost>

<VirtualHost *:443>
  DocumentRoot "/var/www/nisu/www"
  ServerName www.nisu.org
  ServerAlias nisu.org
  RewriteEngine On
  Alias /icons "/usr/share/apache2/icons"
  ErrorLog /var/log/apache2/error.log
  CustomLog /var/log/apache2/access.log combined
  SSLEngine on
  SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
  SSLCertificateFile /etc/letsencrypt/live/nisu.org/fullchain.pem
  SSLCertificateKeyFile /etc/letsencrypt/live/nisu.org/privkey.pem
  SSLCACertificateFile /etc/apache2/ssl/ca-bundle.crt
  SSLVerifyClient none
  <Files ~ "\.(cgi|shtml|phtml|php)$">
    SSLOptions +StdEnvVars +OptRenegotiate +ExportCertData
  </Files>
  <Directory /var/www/>
    AllowOverride All
  </Directory>
  Options FollowSymLinks Multiviews
  RewriteEngine On
#hay que revisar esto , corregir apuntes SI
  RewriteRule ^/al/[^/]+/.*\.php(/|$) / [L,E=ES_AL:yes]
  RewriteRule ^/al/([^/]+)/(.*)$ /home/$1/public_html/$2 [L,E=ES_AL:yes]
  <Location ~ "/common">
    ExpiresActive On
    ExpiresDefault "modification plus 1 week"
  </Location>
  <Location ~ "/common/auth.*">
    SSLVerifyClient optional
    SSLVerifyDepth  3
  </Location>
</VirtualHost>

<VirtualHost *:443>
  DocumentRoot "/var/www/nisu/sec"
  ServerName sec.nisu.org
  RewriteEngine On
  Alias /icons "/usr/share/apache2/icons"
  ErrorLog /var/log/apache2/error.log
  CustomLog /var/log/apache2/access.log combined
  SSLEngine on
  SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
  SSLCertificateFile /etc/letsencrypt/live/nisu.org/fullchain.pem
  SSLCertificateKeyFile /etc/letsencrypt/live/nisu.org/privkey.pem
  SSLCACertificateFile /etc/apache2/ssl/ca-bundle.crt
  SSLVerifyClient none
  <Files ~ "\.(cgi|shtml|phtml|php)$">
    SSLOptions +StdEnvVars +OptRenegotiate +ExportCertData
  </Files>
  <Directory /var/www/>
    AllowOverride All
  </Directory>
  Options FollowSymLinks Multiviews
  RewriteEngine On
#hay que revisar esto , corregir apuntes SI
  RewriteRule ^/al/[^/]+/.*\.php(/|$) / [L,E=ES_AL:yes]
  RewriteRule ^/al/([^/]+)/(.*)$ /home/$1/public_html/$2 [L,E=ES_AL:yes]
  <Location ~ "/common">
    ExpiresActive On
    ExpiresDefault "modification plus 1 week"
  </Location>
  <Location ~ "/common/auth.*">
    SSLVerifyClient optional
    SSLVerifyDepth  3
  </Location>
</VirtualHost>

<VirtualHost *:443>
  DocumentRoot "/var/www/nisu/openvpn"
  ServerName openvpn.nisu.org:443
  RewriteEngine On
  Alias /icons "/usr/share/apache2/icons"
  ErrorLog /var/log/apache2/error.log
  CustomLog /var/log/apache2/access.log combined
  SSLEngine on
  SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
  SSLCertificateFile /etc/letsencrypt/live/openvpn.nisu.org/fullchain.pem
  SSLCertificateKeyFile /etc/letsencrypt/live/openvpn.nisu.org/privkey.pem
  Include /etc/letsencrypt/options-ssl-apache.conf
  SSLCACertificateFile /etc/apache2/ssl/ca-bundle.crt
  SSLVerifyClient none
  <Files ~ "\.(cgi|shtml|phtml|php)$">
    SSLOptions +StdEnvVars +OptRenegotiate +ExportCertData
  </Files>
  <Directory /var/www/>
    AllowOverride All
    OPtion -Indexes
  </Directory>
  Options FollowSymLinks Multiviews
  SetEnvIf User-Agent ".*" \
  	 nokeepalive ssl-unclean-shutdown \
  	 downgrade-1.0 force-response-1.0
  <Location ~ "/common">
    ExpiresActive On
    ExpiresDefault "modification plus 1 week"
  </Location>
  <Location ~ "/common/auth.*">
    SSLVerifyClient optional
    SSLVerifyDepth  3
  </Location>
</VirtualHost>

15
/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>

16
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.
17
/usr/local/sbin/cpsec.sh
#!/bin/bash

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

log=/var/log/cpsec
ahora=$(date +%Y-%m-%d-%H-%M)
exec 2>>$log/$ahora
chmod 600 $log/$ahora
exec >&2
set -x

# $1 parcial es por defecto
[ "$1" = incremental ] && incremental=1
[ "$1" = completo ] && full=1
[ "$1" = transfer ] && full=1 && trans=1

exec 9>>/var/log/cpsec/lock
flock -n 9 || { [ ! "$incremental" ] && { fuser -v /var/log/cpsec/lock; ps axuwf; } | mail -s 'Error cpsec' mm@nisu.org ; exit; }

dfus=mollar

function cp1() {
  runload rsync -vSaxbHAX --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 \
	$* | grep -v '^cannot delete non-empty directory'
}

date

if [ "$incremental" ]; then
  cd /home
  IFS=$'\n'
  # listfic se toma del último completo o parcial y NO se modifica con los incrementales
  for f in $(cat $log/listfic); do
    [ -f "$f" ] && echo "$f"
  done | cp1 --files-from=- /home desp.nisu.org::nisu/home/
  date
  exit
fi

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

date

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"
}

(IFS=$'\n'
 umask 0077
 tmp=/tmp/.$$
 trap "rm $tmp" exit
 for db in $(echo show databases | mysql -s 2>/dev/null); do
   if [ ! "$full" -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 [ ! "$full" -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

date

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

date

if [ "$full" ]; then
  # no vale /home/* sin 'sed' porque el --delete no borraría usuarios borrados
  cp1 "/home --delete" desp.nisu.org::nisu/ | sed -e 's/^home\///'
else
  cp1 $ush desp.nisu.org::nisu/home/
fi | tee >(grep -v '=>' | grep -v '/$' | grep -v '^deleting ' >$log/listfic)

date

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

date

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

date

flock -u 9

if [ "$trans" ] ; 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>>/var/log/cpsec/lockL
  flock -n 9 || { { fuser -v /var/log/cpsec/lockL; 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 -vSaHAX --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