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

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

SSLCertificateFile: file certificado.crt does not exist or empty

En caso de recibir este error tras realizar una instalación de un certificado SSL en un servidor web Apache debemos revisar (en RHEL, CentOS, Scientific Linux, Fedora…) el estado de SElinux. Si está activado, probablemente sea el origen del problema:

# sestatus
SELinux status:                 enabled
SELinuxfs mount:                /selinux
Current mode:                   enforcing
Mode from config file:          enforcing
Policy version:                 24
Policy from config file:        targeted

En el log de auditoría veremos los siguientes errores:

# tail -200 /var/log/audit/audit.log  | grep crt
type=AVC msg=audit(1330769968.611:22): avc:  denied  { getattr } for  pid=1657 comm="httpd" path="/etc/ssl/cert.crt" dev=dm-0 ino=22027 scontext=unconfined_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:admin_home_t:s0 tclass=file
type=AVC msg=audit(1330770107.699:23): avc:  denied  { getattr } for  pid=1676 comm="httpd" path="/etc/ssl/cert.crt" dev=dm-0 ino=22034 scontext=unconfined_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:admin_home_t:s0 tclass=file
type=AVC msg=audit(1330770206.289:24): avc:  denied  { getattr } for  pid=1697 comm="httpd" path="/etc/ssl/cert.crt" dev=dm-0 ino=22034 scontext=unconfined_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:admin_home_t:s0 tclass=file

Se presupone además que hemos revisado que el path al certificado es correcto y no está vacío. Para solucionar el problema debemos aplicar (etiquetar) los atributos de SElinux correctos a los ficheros del certificado (certificado, private key, CA). Una forma sencilla es “copiar” estos atributos de un fichero de configuración Apache, que ya los tendrá asignados:

# chcon --reference=/etc/httpd/conf.d/ssl.conf cert.*

O aplicándolo especificando todos los atributos:

# chcon -u system_u -r object_r -t httpd_config_t cert.*

Si listamos ya tenemos etiquetados los ficheros y podemos reiniciar Apache sin recibir el error:

# ls -Z
-rw-r--r--. apache apache system_u:object_r:httpd_config_t:s0 cert.crt
-rw-r--r--. apache apache system_u:object_r:httpd_config_t:s0 cert.csr
-rw-r--r--. apache apache system_u:object_r:httpd_config_t:s0 cert.key

ReverseProxy en Apache con SSL

Hoy vamos a ver los pasos para activar soporte SSL en un proxy reverse de Apache. Algunos de los pasos ya los he comentado en otros artículos así que los citaré directamente.

Lo primero es Configurar Apache como Reverse Proxy (Proxy Inverso), pinchad en el enlace antes de seguir leyendo. Una vez realizado tenemos que tener un certificado SSL, podemos contratarlo en una entidad certificadora o generarlo nosotros mismos. Para ello revisar este enlace: generar un certificado SSL propio con openssl.

Ahora que tenemos Apache listo para ser usado como reverse proxy sólo nos queda realizar los pasos finales. Lo primero es habilitar soporte para mod_ssl instalando el módulo de Apache. Podemos hacer vía yum o apt, si Apache ha sido compilado a mano habrá que recompilar.

# yum install mod_ssl

Tenemos que modificar la configuración de mod_proxy para permitir el acceso vía SSL, para ello buscamos la configuración del módulo en el fichero de configuración de Apache httpd.conf. Podríamos configurar estas directivas únicamente en el Virtualhost que queramos si no se desea hacer para todo el servidor. Básicamente activamos SSLProxyEngine (lo hacemos en el virtualhost) y restringimos el acceso al proxy a la red 10.0.0.0/24 a nivel general.

<IfModule mod_proxy.c>
...
...
<Proxy *>
Order deny,allow
Deny from all
Allow from 10.0.0.0/24
</Proxy>
....
....
</IfModule>

Y el Virtualhost, veréis que activamos los Engine de SSL y de SSLProxy, especificamos las rutas al certificado SSL y su PrivateKey y que finalmente especificamos las directivas de proxy, en este caso al acceder a https://proxy.com/google nos llevará a https://google.com. He omitido configuraciones de log y demás en el vhost para hacerlo más sencillo:

<VirtualHost 0.0.0.0:443>
    ServerName proxy.com
    SSLEngine on
    SSLProxyEngine on
    SSLCertificateFile /etc/ssl/cert.crt
    SSLCertificateKeyFile /etc/ssl/cert.key
    ProxyPass /google/ https://google.es
    ProxyPassReverse /google/ https://google.es
</VirtualHost>

Instalación y configuración de suPHP

suPHPHe hablado varias veces sobre configuraciones y posibles errores de suPHP pero nunca sobre el modo de instalarlo. Para quienes no lo sepan, suPHP es un módulo de Apache y a su vez una utilidad independiente que permite que los scripts PHP se ejecuten con el usuario propietario del script. Así mismo, permite establecer restricciones de seguridad como evitar permisos 777 en directorios, establecer un UID y GID mínimos para los usuarios, forzar a que los scripts de encuentren en el document_root del host, etc.

La instalación es sencilla. Vamos a suponer que tenemos tanto Apache como PHP compilados o instalados. La forma más sencilla de activar suPHP es instalando el rpm disponible desde el sitio web oficial, aunque por supuesto podemos compilarlo con los requerimientos que tengamos bajando las sources.

En el caso de rpm, para RHEL, CentOS, Scientific Linux, etc es tan sencillo como bajar el rpm de nuestra arquitectura e instalarlo:

# wget http://pkgs.repoforge.org/mod_suphp/mod_suphp-0.7.1-1.el6.rf.x86_64.rpm
# rpm -ivh mod_suphp-0.7.1-1.el6.rf.x86_64.rpm

Una vez instalado, se debería haber creado el fichero de configuración en /etc/httpd/conf.d/suphp.conf. Antes de nada, debemos mover, eliminar o comentar todo el contenido del fichero /etc/httpd/conf.d/php.conf para no cargar el módulo DSO de php, que como sabéis hace que todos los scripts php se ejecuten con el usuario de Apache (www-root, apache, nobody…) con el problema de seguridad que conlleva.

Lo más importante del fichero de configuración es tener cargado el módulo y especificados los handler que dirán como gestionar los scripts php, así como tener el motor de suPHP activado:

LoadModule suphp_module modules/mod_suphp.so

# This option tells mod_suphp if a PHP-script requested on this server (or
# VirtualHost) should be run with the PHP-interpreter or returned to the
# browser "as it is".
suPHP_Engine on

# To use suPHP to parse PHP-Files
AddHandler x-httpd-php .php
AddHandler x-httpd-php .php .php4 .php3 .phtml

El otro fichero de configuración crítico es /etc/suphp.conf, cuyas variables son suficientemente descriptivas, los valores por defecto son válidos para una configuración correcta. Tened en cuenta únicamente el bug que ya comenté de los handlers. Revisad la documentación oficial para más info.

[global]
logfile=/var/log/httpd/suphp_log
loglevel=info
webserver_user=apache
docroot=/
env_path=/bin:/usr/bin
umask=0077
min_uid=500
min_gid=500

; Security options
allow_file_group_writeable=false
allow_file_others_writeable=false
allow_directory_group_writeable=false
allow_directory_others_writeable=false

;Check wheter script is within DOCUMENT_ROOT
check_vhost_docroot=true

;Send minor error messages to browser
errors_to_browser=false

[handlers]
;Handler for php-scripts
x-httpd-php="php:/usr/bin/php-cgi"

;Handler for CGI-scripts
x-suphp-cgi="execute:!self"

Finalmente, en cada virtualhost tendremos que especificar el usuario y grupo que ejecutará los scripts php, usamos la directiva suPHP_UserGroup en cada Virtualhost:

suPHP_UserGroup usuario grupo

Validamos la configuración y reiniciamos Apache. Tened en cuenta las siguientes consideraciones básicas (revisad los logs de errores de Apache y suPHP porque tendréis seguro algunos), por defecto tenemos lo siguiente:

  • Los scripts php no pueden tener permiso de escritura para el grupo ni para el resto, es decir, lo máximo sería 0644
  • Los directorios no pueden tener permisos para otros ni el grupo, es decir, 777 no es válido y 775 tampoco, recomendable 755.
  • Las directivas php_flag de .htaccess pasan a un fichero php.ini en la misma ruta con el formato estándar de php.
  • El propietario y grupo de los scripts debe ser el especificado en suPHP_UserGroup.

star: empaquetar preservando ACL y atributos extendidos (SElinux)

El comando star permite comprimir y descomprimir y/o empaquetar múltiples ficheros con la posibilidad de mantener tanto los atributos extendidos de SElinux como las ACL que puedan tener configurados los ficheros/directorios.

# yum info star.i686
...
Name        : star
Arch        : i686
Summary     : An archiving tool with ACL support
URL         : http://cdrecord.berlios.de/old/private/star.html
Description : Star saves many files together into a single tape or disk archive,
            : and can restore individual files from the archive. Star supports ACL.
...

No es un comando que venga por defecto, así que lo instalamos con yum:

# yum info star.i686

La utilización no es igual que el comando tar, es más, es bastante compleja. Sólo hay que mirar la página man, que es muy extensa para darse cuenta de las posibilidades que ofrece. Nosotros vamos a ver unos ejemplos básicos, luego cada uno que haga las pruebas e investigaciones oportunas.

Mantener los atributos extendidos de SElinux y ACL

Para incluir en el fichero empaquetado/comprimido cualquier atributo extendido de ficheros así como las ACL utilizamos los parámetros -xattr (podemos recordarlo por extended attributes) y -H=exustar. Para preservar las ACL es más sencillo (-acl). Vamos a empaquetar los siguientes ficheros, que tienen atributos extendidos de SElinux:

# ls -Z
-rw-------. root root system_u:object_r:etc_t:s0       ftpusers
-rw-------. root root system_u:object_r:etc_t:s0       user_list
-rw-------. root root system_u:object_r:etc_t:s0       vsftpd.conf
-rwxr--r--. root root system_u:object_r:etc_t:s0       vsftpd_conf_migrate.sh

Probamos también a meter una ACL para ver que la mantiene. Podéis revisar esta entrada sobre ACLs: ACL (Access Control List) en sistemas de ficheros GNU/Linux.

# setfacl -m u:prueba:r-- ftpusers
# getfacl ftpusers
# file: ftpusers
# owner: root
# group: root
user::rw-
user:prueba:r--
group::---
mask::r--
other::---

El comando sería el siguiente. Podéis ver que especificamos la creación de un nuevo fichero (-c) y su nombre (-f=):

# star -xattr -H=exustar -acl -c -f=ejemplo.star *
star: 1 blocks + 0 bytes (total of 10240 bytes = 10.00k).

Desempaquetar el fichero es más sencillo, usamos el parámetro -x (desempaquetar) y especificamos el fichero de la misma forma que antes (-f=nombre). Si queremos mantener las ACL hay que especificarlo de la misma forma que al empaquetar (-acl):

# star -x -acl -f=ejemplo.star
star: 1 blocks + 0 bytes (total of 10240 bytes = 10.00k).

Y revisamos los atributos de SElinux:

# ls -Z
-rw-r-----. root root system_u:object_r:etc_t:s0       ftpusers
-rw-r--r--. root root unconfined_u:object_r:etc_t:s0   test.star
-rw-------. root root system_u:object_r:etc_t:s0       user_list
-rw-------. root root system_u:object_r:etc_t:s0       vsftpd.conf
-rwxr--r--. root root system_u:object_r:etc_t:s0       vsftpd_conf_migrate.sh

Y las ACL:

# getfacl ftpusers
# file: ftpusers
# owner: root
# group: root
user::rw-
user:prueba:r--
group::---
mask::r--
other::---

De esta forma veréis que únicamente hemos empaquetado, no hemos comprimido. Con el parámetro -compress-program= podemos especificar el compresor a utilizar (gzip, bzip2,etc)

# star -xattr -H=exustar -compress-program=gzip -c -f=test.star *
star: 2 blocks + 0 bytes (total of 20480 bytes = 20.00k).

Vemos que efectivamente el peso es mínimo:

-rw-r--r--. 1 root root 2997 feb 27 20:41 test.star

Para descomprimir es igual, el propio comando detectará la compresión y actuará en consecuencia:

# star -x -acl -f=test.star
star: WARNING: Archive is 'gzip' compressed, trying to use the -z option.

Clonar una máquina virtual KVM con virt-clone

Clonar una máquina virtual es realmente sencillo. En el caso de KVM es tan simple como apagar la máquina virtual para tener consistencia de datos y utilizar el comando virt-clone.

Podemos hacerlo de dos formas, siguiendo un asistente ejecutando virt-clone con el parámetro –prompt o pasar a mano todos los parámetros necesarios.

En el primer caso simplemente respondemos las preguntas que van saliendo, que básicamente son:

  1. Nombre de la máquina virtual a clonar
  2. Nombre para la máquina virtual resultante
  3. Ruta al disco o discos clonados (discos destino).
# virt-clone --prompt
What is the name of the original virtual machine?
virtual01
What is the name for the cloned virtual machine?
clon_virtual01
What would you like to use as the cloned disk (file path) for '/var/lib/libvirt/images/virtual01.img'?
/var/lib/libvirt/images/clon_virtual01.img
Allocating 'clon_virtual01.img'      5% [=-             ]  29 MB/s | 437 MB     04:25
...
...
Clone clon_virtual01 created successfully

Este mismo ejemplo podríamos ejecutarlo directamente de esta forma (si ejecutáis un –help veréis todos los paramétros:

# virt-clone -o virtual01 -n clon_virtual01 -f /var/lib/libvirt/images/clon_virtual01.img
Cloning virtual01.img 4% [==- ] 134 MB/s | 390 MB 00:58 ETA

Otra opción que hace aún más sencillo el trabajo es dejar al propio sistema que elija automáticamente el nombre de la máquina virtual destino y las rutas a los nuevos discos. Para ello usamos el parámetro auto-clone:

# virt-clone -o virtual01 --auto-clone

Un consejo: la primera vez que arranquéis un clon, hacedlo en single-user, runlevel 1 o cualquiera que os permita modificar la configuración de red para evitar conflictos, MAC e IPs duplicadas, etc.

Subnetting y cálculo de netmask desde línea de comandos

Si se os da tan mal como a mí el cálculo de subredes o simplemente queréis una herramienta rápida para hacerlo aquí tenéis ipcalc o sipcalc. A través de este comando podremos calcular subredes directamente desde la línea de comandos. La principal diferencia entre ambos es que sipcalc acepta IPV6 y permite subnetting.

Para instalarlos utilizamos los gestores de paquetes correspondientes, apt, yum…

$ sudo apt-get install ipcalc
$ sudo yum install ipcalc

Nota: en RHEL y CentOS hay que activar el repositorio rpmforge.

La utilización es bien sencilla, podemos pasar como parámetro la subred en formato CIDR y nos devuelve toda la información de la misma, máscara, red, nº de hosts, etc:

$ ipcalc 10.0.0.0/24
Address:   10.0.0.0             00001010.00000000.00000000. 00000000
Netmask:   255.255.255.0 = 24   11111111.11111111.11111111. 00000000
Wildcard:  0.0.0.255            00000000.00000000.00000000. 11111111
=>
Network:   10.0.0.0/24          00001010.00000000.00000000. 00000000
HostMin:   10.0.0.1             00001010.00000000.00000000. 00000001
HostMax:   10.0.0.254           00001010.00000000.00000000. 11111110
Broadcast: 10.0.0.255           00001010.00000000.00000000. 11111111
Hosts/Net: 254                   Class A, Private Internet

IPV6 con sipcalc:

$ sipcalc 2001:DB8::/24
-[ipv6 : 2001:DB8::/24] - 0

[IPV6 INFO]
Expanded Address	- 2001:0db8:0000:0000:0000:0000:0000:0000
Compressed address	- 2001:db8::
Subnet prefix (masked)	- 2001:d00:0:0:0:0:0:0/24
Address ID (masked)	- 0:b8:0:0:0:0:0:0/24
Prefix address		- ffff:ff00:0:0:0:0:0:0
Prefix length		- 24
Address type		- Aggregatable Global Unicast Addresses
Network range		- 2001:0d00:0000:0000:0000:0000:0000:0000 -
			  2001:0dff:ffff:ffff:ffff:ffff:ffff:ffff

Otra característica interesante es la posibilidad de sipcalc de especificar una red y hacer subneting con ella, si por ejemplo queremos dividir un /21 en redes /22 o /24:

$ sipcalc -s24 10.0.0.0/21
-[ipv4 : 10.0.0.0/21] - 0

[Split network]
Network			- 10.0.0.0        - 10.0.0.255
Network			- 10.0.1.0        - 10.0.1.255
Network			- 10.0.2.0        - 10.0.2.255
Network			- 10.0.3.0        - 10.0.3.255
Network			- 10.0.4.0        - 10.0.4.255
Network			- 10.0.5.0        - 10.0.5.255
Network			- 10.0.6.0        - 10.0.6.255
Network			- 10.0.7.0        - 10.0.7.255

-
$ sipcalc -s22 10.0.0.0/21
-[ipv4 : 10.0.0.0/21] - 0

[Split network]
Network			- 10.0.0.0        - 10.0.3.255
Network			- 10.0.4.0        - 10.0.7.255

Si revisáis la ayuda del comando encontraréis otras posibilidades de interés.

Cambiar el AP_DOC_ROOT en suEXEC instalado por RPM

suEXEC por seguridad trae los siguientes parámetros compilados y sin posibilidad de ser modificados:

# suexec -V
 -D AP_DOC_ROOT="/var/www"
 -D AP_GID_MIN=100
 -D AP_HTTPD_USER="apache"
 -D AP_LOG_EXEC="/var/log/httpd/suexec.log"
 -D AP_SAFE_PATH="/usr/local/bin:/usr/bin:/bin"
 -D AP_UID_MIN=500
 -D AP_USERDIR_SUFFIX="public_html"

Si lo hemos instalado compilando a través de las sources, no hay problema, se recompila y ya está. Pero si lo hemos instalado por RPM requiere un trabajo distinto. Es necesario bajar las sources del RPM y editarlas con los parámetros que queramos, luego se compila el nuevo RPM y ya podemos instalarlo.

En este caso se trata un Apache con suEXEC en RHEL 6 e instalado por RPM así que nos bajamos las sources de Apache correspondientes:

# curl -O ftp://ftp.redhat.com/pub/redhat/linux/enterprise/6Server/en/os/SRPMS/httpd-2.2.15-15.el6_2.1.src.rpm

Desempaquetamos el RPM, Si el usuario mockbuild no existe veréis un montón de avisos y las sources se guardarán en la home de root en lugar de en src:

# rpm -Uvh httpd-2.2.15-15.el6_2.1.src.rpm

Ahora tenemos que editar el fichero httpd.spec:

# vi /root/rpmbuild/SPECS/httpd.spec

En nuestro caso queremos cambiar el AP_DOC_ROOT o contentdir, que es la ruta raíz en la que vamos a alojar el contenido de los websites:

%define contentdir /var/www

Modificamos a la ruta correcta:

%define contentdir /www

Para que al hacer el upgrade no nos diga que la versión ya está instalada, también podemos cambiar el número de versión:

Release: 15%{?dist}.3

Es necesario para volver a compilar el rpm el paquete rpm-build, también necesitaréis las sources de ciertas dependencias, lo veréis según vayáis haciendo el trabajo:

# yum install rpm-build
# yum install libselinux-devel openssl-devel

Recompilamos el RPM:

# rpmbuild -bb /root/rpmbuild/SPECS/httpd.spec

Una vez terminado ya tenemos los RPM listos para instalar en /root/rpmbuild/RPMS/x86_64:

-rw-r--r--. 1 root root 3229796 Feb 21 06:26 httpd-2.2.15-15.el6.1.x86_64.rpm
-rw-r--r--. 1 root root  156508 Feb 21 06:26 httpd-devel-2.2.15-15.el6.1.x86_64.rpm
-rw-r--r--. 1 root root  128945 Feb 21 06:26 httpd-tools-2.2.15-15.el6.1.x86_64.rpm
-rw-r--r--. 1 root root  403246 Feb 21 06:26 mod_ssl-2.2.15-15.el6.1.x86_64.rpm
# rpm -Uvh *rpm

Reiniciamos apache y:

# suexec -V
 -D AP_DOC_ROOT="/www"
 -D AP_GID_MIN=100
 -D AP_HTTPD_USER="apache"
 -D AP_LOG_EXEC="/var/log/httpd/suexec.log"
 -D AP_SAFE_PATH="/usr/local/bin:/usr/bin:/bin"
 -D AP_UID_MIN=500
 -D AP_USERDIR_SUFFIX="public_html"

Por supuesto podéis cambiar el resto de valores del mismo modo si fuera necesario.

Sincronizar hora en Windows Server 2008 contra un servidor NTP

Vamos a ver como configurar un Windows Server 2008 R2 para sincronizar la hora con un servidor NTP externo en lugar de a través del sistema propio de Windows. Esto es útil cuando el servidor no tiene salida a internet pero sí dispone de servidores NTP en la red privada.

En este caso vamos a configurar un controlador de dominio (Active Directory) para que sincronice la hora contra un servidor NTP Linux. Accedemos al CMD y ejecutamos lo siguiente:

C:\Users\Administrator>w32tm /config /manualpeerlist:192.168.1.100,0x8 /syncfromflags:MANUAL

The command completed successfully.

El significado de la flag 0×8 es “mandar las solicitudes NTP como cliente”. Hemos configurado la sincronización contra el servidor NTP 192.168.1.100. Si quisieramos añadir varios:

C:\Users\Administrator>w32tm /config /manualpeerlist:"192.168.1.100 192.168.1.101 192.168.1.102",0x8 /syncfromflags:MANUAL

Reiniciamos el servicio w32tm:

C:\Users\Administrator>net stop w32time

The Windows Time service is stopping.
The Windows Time service was stopped successfully.

C:\Users\Administrator>net start w32time
The Windows Time service is starting.
The Windows Time service was started successfully.

Ahora ya podemos forzar la sincronización con el siguiente comando. Si la desincronización es muy amplia (+ de 5 horas) es recomendable cambiar la hora a mano para acercarnos a la real y luego sincronizar:

C:\Users\Administrator>w32tm /resync
Sending resync command to local computer
The command completed successfully.

Por último hay que asegurarse de tener el puerto 123 abierto en el firewall:

ntp firewall;