Los diferentes servicios que nos ofrecen la posibilidad de resolver nombres de dominio a direcciones IP dentro de un sistema operativo GNU/Linux han ido evolucionando a lo largo del tiempo. En este artículo quiero introducir la situación actual acerca de este tema, y presentar los distintos servicios involucrados en la resolución de nombres.
Antes de comenzar a estudiar con detalle los distintos mecanismos de resolución, vamos a repasar algunos conceptos que nos serán necesarios:
/etc/resolv.conf
./etc/hosts
para guardar la correspondencia entre nombre y dirección.hosts
y como veremos en el fichero /etc/nsswitch.conf
se configura el orden de consulta que se realiza para resolver el nombre de un dominio a su dirección IP.Como hemos comentado, este archivo especifica los servidores DNS que el sistema utilizará para resolver nombres de dominio a direcciones IP. Los parámetros más importantes que podemos encontrar son:
nameserver
: Esta línea define los servidores DNS que el sistema utilizará para resolver nombres de dominio. Pueden haber múltiples líneas nameserver
, una para cada servidor DNS. El orden es importante ya que se intentará resolver el nombre utilizando el primer servidor, si este no funciona se intentará con el siguiente y así consecutivamente.search
: Esta línea especifica el dominio de búsqueda para las consultas de resolución de nombres de dominio. Si intentas resolver un nombre de dominio que no está completamente cualificado (es decir, sin un sufijo de dominio), el sistema intentará agregar los dominios de búsqueda especificados aquí para completar el nombre antes de enviar la consulta al servidor.domain
: Similar a search
, domain
especifica el dominio de búsqueda para las consultas de resolución de nombres de dominio, pero solo acepta un único dominio en lugar de una lista.options
: Esta línea se utiliza para especificar diversas opciones de configuración, como el tiempo de espera de resolución, la retransmisión de consultas, entre otros.Ejemplo de configuración de etc/resolv.conf
:
nameserver 8.8.8.8
nameserver 8.8.4.4
domain example.com
search example.com mycompany.com
options timeout:2 attempts:3
Estos parámetros se suelen recibir de forma dinámica por medio de un servidor DHCP, aunque también lo podemos indicar de forma estática en la configuración de red del sistema. Normalmente tenemos algún demonio instalado en el sistema, como resolvconf
que es el encargado de generar el fichero /etc/resolv.conf
a partir de la configuración de red que hayamos especificado.
En este caso no debemos cambiar directamente el fichero /etc/resolv.conf
porque el programa resolvconf
puede reescribirlo en algunas circunstancias, por ejemplo cuando se renueva la concesión del servidor DHCP.
Si queremos añadir contenido de forma estática al fichero /etc/resolv.conf
es necesario escribir el contenido en el fichero /etc/resolvconf/resolv.conf.d/head
si lo que queremos añadir se coloca antes de lo generado por resolvconf
, o en el fichero /etc/resolvconf/resolv.conf.d/tail
para añadirlo al final del fichero.
Como hemos indicado este fichero nos permite configurar el orden de los distintos mecanismos que podemos utilizar para consultar distintas informaciones del sistema. En nuestro caso nos interesa la configuración del orden de los mecanismos de resolución de nombres de dominio, por lo tanto nos tenemos que fijar en la base de datos hosts
. Por ejemplo en este fichero podemos encontrar una línea como está:
hosts: files dns
Como observamos en la primera columna tenemos el nombre de la base de datos, en nuestro ejemplo hosts
que se refiere a la consulta de nombres de dominios. A continuación encontramos una o varias especificaciones de servicios (en este caso de servicios de resolución de nombres), por ejemplo, “files”, “dns”,… El orden de los servicios en la línea determina el orden en que se consultarán dichos servicios, sucesivamente, hasta que se encuentre un resultado. Veamos los dos servicios que hemos puesto en el ejemplo:
files
: Este es el servicio de resolución estática, es decir nos permite resolver nombres de dominio consultando el fichero /etc/hosts
.dns
: Este es el servicio de resolución de nombres de dominio que realiza una consulta a los servidores DNS configurados en el fichero /etc/resolv.conf
.Por lo tanto con esta configuración, cualquier programa del sistema que necesite resolver un nombre de dominio a una dirección IP, primero usará la resolución estática y buscará el nombre en el fichero /etc/hosts
y si no lo encuentra realizará una consulta a los servidores DNS configurados en el fichero /etc/resolv.conf
.
Por ejemplo, si en el fichero /etc/hosts
tenemos la siguiente línea:
192.168.121.180 www.example.org
Y realizamos un ping a ese nombre, se consultará en primer lugar la resolución estática:
ping www.example.org
PING www.example.org (192.168.121.180) 56(84) bytes of data.
Sin embargo, si borramos esa línea del fichero /etc/hosts
, la resolución estática no funcionará (no hemos encontrado el nombre) y se realizará una consulta al servidor DNS que tengamos configurado en /etc/resolv.conf
:
ping www.example.org
PING www.example.org (93.184.216.34) 56(84) bytes of data.
Como podemos observar las direcciones IP resueltas son diferentes.
Tenemos a nuestra disposición utilidades que nos permiten hacer peticiones a servidores DNS para realizar resoluciones de nombres. Ejemplo de este tipo de herramienta son: dig
, nslookup
o host
. Estas herramientas no consultan el fichero /etc/nsswitch.conf
para determinar el orden de las consultas que tienen que realizar para la resolución de nombres. Estas herramientas sólo hacen consultas a un servidor DNS, no buscan nombres utilizando la resolución estática, no acceden al fichero /etc/hosts
.
NSS nos ofrece una herramienta para consultar las distintas informaciones, por ejemplo para consultar la resolución de nombres de dominio podemos usar el comando getent ahosts
. Está herramienta sí sigue el orden de mecanismos configurados en el fichero /etc/nsswitch.conf
, en nuestro ejemplo, primero buscara el nombre usando resolución estática, y si no lo encuentra hará la consulta DNS. Por ejemplo, si tenemos la línea de resolución en el fichero /etc/hosts
como anteriormente:
getent ahosts www.example.org
192.168.121.180 STREAM www.example.org
...
Y si quitamos la línea, se realizará una consulta al servidor DNS:
getent ahosts www.example.org
93.184.216.34 STREAM www.example.org
...
El mDNS (Multicast DNS) es un protocolo utilizado en redes locales para resolver nombres de dominio sin necesidad de servidores DNS centralizados. En lugar de depender de servidores DNS, el mDNS utiliza mensajes de difusión para descubrir y resolver nombres de dispositivos en la red local.
Con este sistema de resolución de nombres de dominio podemos referenciar cualquier equipo de nuestra red local, usando el dominio .local
.
En distribuciones GNU/Linux el servicio de mDNS lo ofrece normalmente un programa llamado avahi
, que es un demonio encargado de la resolución de los nombres de las máquinas locales.
Tenemos un nuevo mecanismo de resolución de nombres que podemos configurar en el orden de búsqueda establecido en el fichero /etc/nsswitch.conf
, en este caso podríamos tener la siguiente configuración:
hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4
Con esta configuración, el orden que se sigue para la resolución de nombres es la siguiente:
files
: Como ya hemos comentado, resolución estática.mdns4_minimal [NOTFOUND=return]
: Resolución por mDNS. mdns4_minimal
busca servicios mínimos, lo que significa que solo busca los servicios más básicos y esenciales. Por lo general, mdns4_minimal se utiliza en entornos donde se espera un bajo consumo de recursos o donde la red es simple y no tiene una gran cantidad de servicios anunciados. La opción [NOTFOUND=return]
indica que si el nombre no se puede resolver, que no se siga buscando con las opciones posteriores.dns
: Ya hemos indicado que se trata de una consulta a un servidor DNS.mdns4
: Resolución por mDNS, mdns4
buscará todos los servicios disponibles en la red, lo que podría significar un mayor consumo de recursos, especialmente en redes grandes o complejas con muchos servicios anunciados.En resumen, mdns4_minimal
es una opción más ligera que busca solo servicios mínimos, mientras que mdns4
busca todos los servicios anunciados a través de mDNS. La elección entre ellas dependerá de las necesidades y características específicas del entorno de red en cuestión.
Veamos un ejemplo, suponemos que en nuestra red hay un equipo cuyo hostname es stark
, podríamos hacer una consulta para averiguar su dirección IP:
getent ahosts stark.local
192.168.18.3 STREAM stark.local
Y comprobamos que tenemos conectividad:
ping stark.local
PING stark.local (192.168.18.3) 56(84) bytes of data.
64 bytes from stark (192.168.18.3): icmp_seq=1 ttl=64 time=0.276 ms
...
systemd-resolved es un servicio de systemd que proporciona resolución de nombres de dominio a aplicaciones locales. Este servicio ofrece resolución de nombres a través de tres vías:
nss-resolve
: Un DNS forward caché que permite a las aplicaciones que utilizan NSS resolver nombres.nss-myhostname
: Nos proporciona resolución de nombres de host locales sin tener que editar /etc/hosts
.nss-mymachines
: Nos proporciona resolución de nombres de host para los nombres de contenedores locales systemd-machined
.127.0.0.53
, que utilizarán las aplicaciones que hacen consultas a los servidores DNS configurados en /etc/resolv.conf
.systemd-resolved proporciona servicios de resolución para sistema de nombres de dominio (DNS) (incluyendo DNSSEC y DNS mediante TLS), multicast DNS (mDNS) y resolución de nombres de multidifusión de enlace local (LLMNR) (servicio similar a mDNS).
La resolución se puede configurar editando /etc/systemd/resolved.conf
o colocando archivos con extensión .conf
en /etc/systemd/resolved.conf.d/
. Para utilizar systemd-resolved debemos iniciar el servicio systemd-resolved.service
.
Como hemos visto, systemd-resolved nos ofrece un nuevo mecanismo de resolución de nombres de dominio, por lo tanto podremos configurar el orden de este nuevo mecanismo en el fichero /etc/nsswitch.conf
, en este caso podríamos tener la siguiente configuración:
hosts: mymachines resolve [!UNAVAIL=return] files myhostname dns
Como podemos ver en la documentación de nss-resolve
, se recomienda colocar resolve
al principio de la línea hosts:
de /etc/nsswitch.conf
. Debería estar antes de la entrada files
, ya que systemd-resolved soporta /etc/hosts
internamente, pero con caché. Por el contrario, debería estar después de mymachines
, para dar precedencia a los nombres de host dados a las máquinas virtuales y contenedores locales sobre los nombres recibidos a través de DNS. Finalmente, recomendamos colocar dns
en algún lugar después de resolve
, para recurrir a nss-dns
si systemd-resolved.service
no está disponible.
systemd-resolved tiene varios modos diferentes para manejar la resolución de nombres de dominio (para más información puede ver la documentación de systemd-resolved). En este artículo vamos a señalar los modos más interesantes:
/run/systemd/resolve/stub-resolv.conf
nos encontramos la configuración del servido DNS auxiliar forward caché 127.0.0.53
como el único servidor DNS y una lista de dominios de búsqueda. Este es el modo de operación recomendado. Normalmente el fichero /etc/resolv.conf
es un enlace simbólico a este fichero. Este servidor DNS es forward, es decir, reenvía las consultas DNS a los servidores que hayamos configurado en la configurado de red, por ejemplo los servidores DNS recibidos por un servidor DHCP. Esta es método que encontramos en la configuración de la distribución Linux Ubuntu 22.04./etc/resolv.conf
es un enlace simbólico al fichero /run/systemd/resolve/resolv.conf
donde systemd-resolved
configura los servidores DNS que hemos indicados en la configuración de la red. Este es el método escogido en la configuración de la distribución Linux Debian 12.resolv.conf
: Este modo conserva /etc/resolv.conf
y systemd-resolved es simplemente un cliente de aquel archivo. Este modo es menos disruptivo ya que /etc/resolv.conf
puede continuar siendo administrado por otros paquetes.systemd-resolved ofrece un mecanismo de resolución de nombres de dominio, que engloba todos los mecanismos anteriormente vistos. Por lo tanto podemos utilizar las herramientas de consultas de resolución que utilizábamos con los anteriores mecanismos:
dig
, host
o nslookup
.getent
.resolvectl
que es una utilidad que nos ofrece systemd-resolved para realizar consultas. Veamos algunas opciones de esta aplicación:
resolvectl status
resolvectl statistics
resolvectl flush-caches
resolvectl dns
resolvectl query
Veamos algunos ejemplos de consultas:
Resolución de un nombre que tenemos definido en /etc/hosts
:
resolvectl query www.example.org
www.example.org: 192.168.121.180
Resolución de un nombre local usando mDNS:
resolvectl query nodo1
nodo1: 10.0.0.249 -- link: ens3
ping nodo1
PING nodo (10.0.0.249) 56(84) bytes of data.
64 bytes from nodo (10.0.0.249): icmp_seq=1 ttl=64 time=3.04 ms
Resolución realizando una consulta DNS:
resolvectl query www.josedomingo.org
www.josedomingo.org: 37.187.119.60 -- link: ens3
(endor.josedomingo.org)
En primer lugar vamos a ver la configuración de systemd-resolved en Ubuntu 22.04. Como hemos comentado anteriormente Ubuntu 22.04 usa la configuración del “stub” DNS, es decir, utiliza la configuración de un servidor DNS auxiliar forward caché. Veamos el fichero /etc/resolv.conf
:
ls -l /etc/resolv.conf
lrwxrwxrwx 1 root root 39 Sep 14 02:09 /etc/resolv.conf -> ../run/systemd/resolve/stub-resolv.conf
cat /etc/resolv.conf
...
nameserver 127.0.0.53
options edns0 trust-ad
search dominio.org
Este sistema ha recibido por medio de un servidor DHCP la configuración del servidor DNS 172.22.0.1
, Cuando realizamos una consulta al DNS configurado 127.0.0.53
, este reenvía la consulta al servidor DNS configurado 172.22.0.1
, y la resolución la guarda en caché. Podemos ver el servidor DNS que estamos utilizando ejecutando la siguiente instrucción:
resolvectl dns
Global:
Link 2 (ens3): 172.22.0.1
Como vemos se ha configurado un DNS para la interfaz de red ens3
. Esto significa que cuando tu sistema envíe consultas de resolución de nombres de dominio a través de la interfaz de red ens3
, utilizará este servidor DNS en lugar del servidor DNS global, que será el configurado para resolver las consultas de nombres de dominio cuando no haya una configuración específica para una interfaz de red particular. Si queremos añadir la configuración del servidor DNS global a nuestro sistema lo tenemos que indicar en el fichero de configuración de systemd-resolved, en el fichero /etc/systemd/resolved.conf
, en concreto lo indicaremos en el parámetro DNS
:
DNS=1.1.1.1
Reiniciamos el servicio y volvemos a consultar los DNS del sistema:
systemctl restart systemd-resolved.service
resolvectl dns
Global: 1.1.1.1
Link 2 (ens3): 172.22.0.1
Pasemos ahora a la configuración de systemd-resolved en Debian 12. Este sistema también ha sido configurado por medio de un servidor DHCP con el servidor DNS 172.22.0.1
. En esta ocasión esta distribución configura su /etc/resolv.conf
con los servidor DNS del sistema, por lo tanto:
ls -l /etc/resolv.conf
lrwxrwxrwx 1 root root 32 Sep 10 04:26 /etc/resolv.conf -> /run/systemd/resolve/resolv.conf
cat /etc/resolv.conf
...
nameserver 172.22.0.1
search .
resolvectl dns
Global:
Link 2 (ens3): 172.22.0.1
En este caso si configuramos un DNS global en el fichero /etc/systemd/resolved.conf
, vemos como se ha configurado efectivamente el servidor DNS Global:
resolvectl dns
Global:1.1.1.1
Link 2 (ens3): 172.22.0.1
Pero también se ha modificado el fichero /etc/resolv.conf
de este modo:
nameserver 1.1.1.1
nameserver 172.22.0.1
En este caso el servidor DNS global es más preferente que el configurado específicamente para la interfaz ens3
.
Por último indicar que si queremos configurar nuestro equipo Debian con el servidor “stub” DNS como el de Ubuntu 22.04, solo tenemos que cambiar el enlace simbólico al que apunta /etc/resolv.conf
:
ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
En este artículo he pretendido dos cosas: recordar conceptos más tradicionales sobre la resolución de nombres de dominios e introducir los nuevos mecanismos de resolución, como systemd-resolved y las implacaciones que tiene su uso. Lo presentado en este artículo simplemte es una introducción a estos conceptos y han quedado fuera muchos otros aspectos de la configuración de los distintos mecanismos. Si el lector comprueba que he introducido algún error o los datos ofrecidos no son exactos, por favor escríbeme para cambiar la información del artículo. Espero, como siempre, que sea de utilidad.
]]>De tal forma, que estas dos compañías (presumiblemente el resto se irán sumando a estas nuevas políticas), exigirán que todos los correos cumplan con los estándares SPF, DKIM y DMARC. SPF autoriza al servidor que envía tus correos, DKIM garantiza la integridad del mensaje y DMARC indica al receptor qué hacer si alguno de los anteriores no coincide (por ejemplo, rechazarlo o enviarlo a carpeta de spam).
En este artículo, voy a hacer una introducción de cada uno de estos protocolos y cómo los podemos configurar para asegurar que los correos enviados por los servidores de correos que administramos “lleguen a buen puerto”.
En principio, cualquier máquina tiene la capacidad de enviar mensajes de correo a cualquier destino utilizando cualquier remitente. Esto se diseñó originalmente para permitir el envío de mensajes con diferentes remitentes a través de un mismo servidor de correos. Sin embargo, esta característica del correo electrónico ha sido ampliamente explotada para inundar los servidores de correo con mensajes no deseados y llevar a cabo suplantación de remitentes (email spoofing).
Dada la masiva utilización indebida de esta funcionalidad, se ha generalizado la implementación de medidas complementarias con el fin de reducir al máximo este problema. En la actualidad, la mayoría de los servidores de correo adoptan medidas que implican el rechazo o la clasificación como spam de los mensajes provenientes de servidores que no implementan algún mecanismo adicional de autenticación.
Sender Policy Framework (SPF) es un mecanismo de autenticación que mediante un registro DNS de tipo TXT describe las direcciones IPs y nombres DNS autorizados a enviar correo desde un determinado dominio. Actualmente muchos servidores de correo exigen como mínimo tener un registro SPF para tu correo o en caso contrario los mensajes provenientes de tu servidor se clasifican como spam o se descartan directamente.
Ejemplo de registro SPF:
DOMINIO. 600 IN TXT "v=spf1 a mx ptr ip4:X.X.X.X/32 ip6:XXXX:XXXX:XXXX::XXXX:XXXX/128 a:nombre_máquina -all"
En definitiva es una lista de direcciones IP que autorizamos el envío del correo de nuestro dominio. Los parámetros significan los siguiente:
Por ejemplo, si tenemos un dominio example.org
y nuestro servidor de correo posee una dirección IP pública 203.0.113.1
, el registro DNS podría definirse de la siguiente manera:
dig txt example.org
...
example.org. 0 IN TXT "v=spf1 ip4:203.0.113.1 ~all"
Es importante comentar el signo que aparece antes de all
, ya que podemos indicarle a los otros servidores lo que deben hacer si reciben correo desde otra dirección o máquina diferente a las que aparecen en el registro anterior:
-
: Descartar el mensaje.~
: Clasificarlo como spam.?
: Aceptar el mensaje (sería como no usar SPF).De esta forma el correo que enviemos desde nuestra máquina, pasará los filtros SPF en destino y la mayoría de nuestros correos llegarán a destino con poca probabilidad de que se clasifiquen como spam.
DomainKeys Identified Mail o DKIM es un método de autenticación pensado principalmente para reducir la suplantación de remitente. DKIM consiste en que se publica a través de DNS la clave pública del servidor de correos y se firman con la correspondiente clave privada todos los mensajes emitidos, así el receptor puede verificar cada correo emitido utilizando la clave pública.
Veamos los pasos principales para configurar DKIM en nuestro servidor de correos. Vamos a usar postfix
como MTA instalado sobre una distribución Linux Debian Bookworm. Suponemos, además, que nuestro dominio es axample.org
:
Lo primero será instalar los paquetes necesarios:
apt install opendkim opendkim-tools
A continuación vamos a crear el par de claves, para ello ejecutamos la siguiente instrucción:
sudo -u opendkim opendkim-genkey -D /etc/dkimkeys -d example.org -s clave_example_org
Es decir, con el usuario opendkim
se generan un par de claves que se guardan en el directorio /etc/dkimkeys
(-D
), del dominio example.org
y le ponemos un nombre al selector, en nuestro caso clave_example_org
. El selector es el nombre que se va a utilizar para identificar las claves.
En este caso en el directorio /etc/dkimkeys
se han creado dos ficheros:
clave_example_org.private
: Donde se guarda la clave privada con lo que se va a firmar los correos.clave_example_org.txt
: Donde se guarda la clave pública.A continuación vamos a configurar opendkim, para ello en el fichero /etc/opendkim.conf
descomentamos y ponemos los siguiente valores en los siguientes parámetros:
Domain example.org
KeyFile /etc/dkimkeys/clave_example_org.private
Selector clave_example_org
Socket inet:8892@localhost
Indicamos nuestro dominio, el fichero donde está la clave privada, el selector que hemos utilizado al crear las calves y por último configuramos el opendkim para que escuche peticiones en el puerto 8892/tcp de localhost (para que postfix se conecte a él).
Para integra postfix con opendkim, añadimos al fichero /etc/postfix/main.cf
las siguientes líneas:
smtpd_milters = inet:localhost:8892
non_smtpd_milters = $smtpd_milters
Por último reiniciamos los dos servicios:
systemctl restart opendkim
systemctl restart postfix
Por último tenemos que publicar nuestra clave pública en un registro TXT del DNS de nuestro dominio, para ello visualizamos el contenido de la clave pública:
cat clave_example_org.txt
clave_example_org._domainkey IN TXT ( "v=DKIM1; h=sha256; k=rsa; "
"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx2PSS+Z1bCqnUGay+rbr/0jjdBjlQ5SRdzX237NGv6YaeK7DqVFfWBY83Nk6QWCLjxg9Qg588AqjjnlLLDmVNNNPRpzytpFnBdIge6P5kUyJI8VPqw+c6uaNJ2yfG6awfWZvgvDGmqjO6ZFQX+vDV2yR7N0uejJd+WPvSMVN9fYGdBFWWnX+JZ8VVb49Cn9L4tbsMqhiDLY/4L"
"/3pLsJMzLOAVuzUac8p0CGPL/nJOKhaXDGdxyehxZW/FbT7ZYx/fYzSvG9OdEVHcTBxQkvE3hYWv/dPc617dJrO6YrB0AeJxOWmPJgeMbYehZYELUIMOGgIHt7z6/eR6du+27mYQIDAQAB" ) ; ----- DKIM key clave_example_org for dominio.algo
Tenemos que crear un registro TXT en nuestro DNS con el nombre clave_example_org._domainkey.example.org
con el contenido desde v=DKIM1 hasta la última comilla, quitando las comillas y poniendo todo en una misma línea.
Por ejemplo, para el registro de josedomingo.org
sería:
dig txt zoho._domainkey.josedomingo.org
...
zoho._domainkey.josedomingo.org. 0 IN TXT "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCsqnyWSY6LW0jaEAPLqVuNJPFGYDuDfskpMSRzInjv8jW6zoUPnmP+rrL2vOzQbNYV5nJyvkj63LiTMwdPgggPFeEdID5czfkscu8pfBFkQF4zMs5KWB10u6dw8RLagvfAC9Lg9lNOM0f0DwI5ZP1mPu6NsV1LA00jcg39z7j/PwIDAQAB"
Verificamos que la configuración del registro de DKIM es correcta utilizando alguna herramienta externa, que da los resultados de forma fácilmente interpretable:
Domain-based Message Authentication, Reporting, and Conformance o DMARC es el último mecanismo de autenticación que vamos a configurar, realmente lo hace DMARC es ampliar el funcionamiento de SPF y DKIM, mediante la publicación en DNS de la política del sitio, en el que decimos si usamos, SPF, DKIM o ambos,y que se debe hacer con nuestro correo si no cumple con los protocolos anteriores.
La configuración de DMARC para el correo saliente es sencilla, consiste en un registro DNS TXT en el que se especifica si se está usando SPF y/o DKIM y qué hacer con el correo que no cumpla con los mecanismos de autenticación que estén habilitados, como en este caso están ambos, creamos un registro como el siguiente:
_dmarc.DOMINIO. 3600 IN TXT "v=DMARC1; p=quarantine;adkim=r;aspf=r; rua=mailto:correo@DOMINIO"
Veamos algunos parámetros:
p=quarantine
indica que los servidores de correo electrónico deben “poner en cuarentena” los correos electrónicos que no superan DKIM y SPF, considerándolos potencialmente spam. Otras configuraciones posibles para esto son p=none
, que permite que los correos que suspenden sigan pasando, y p=reject
, que ordena a los servidores de correo electrónico que bloqueen los correos que suspenden.adkim=s
significa que las comprobaciones DKIM son “estrictas” (el dominio del from debe coincidir con tu dominio). Esto también se puede configurar como “relajado” (el dominio del from puede ser un subdominio de tu dominio) si se cambia por adkim=r
.El único parámetro obligatorio es el p.
Por ejemplo, el registro DMARC de josedomingo.org
es:
dig txt _dmarc.josedomingo.org
...
_dmarc.josedomingo.org. 0 IN TXT "v=DMARC1; p=quarantine; adkim=r; aspf=r;"
Si lo comprobamos con esdta herramienta:
En conclusión, la implementación conjunta de SPF, DKIM y DMARC se presenta como una estrategia fundamental en la lucha contra el correo no deseado y la suplantación de identidad en el ámbito del correo electrónico. Sender Policy Framework (SPF) establece una política de autorización para los servidores de correo saliente, DomainKeys Identified Mail (DKIM) ofrece un mecanismo de firma digital para verificar la autenticidad del contenido del mensaje, y Domain-based Message Authentication, Reporting, and Conformance (DMARC) proporciona directrices claras sobre cómo deben manejarse los mensajes que no cumplen con las políticas de autenticación establecidas. La combinación de estas tres tecnologías fortalece la seguridad de los correos electrónicos, reduce la posibilidad de suplantación y contribuye a la construcción de un entorno de comunicación más confiable y protegido contra amenazas cibernéticas.
]]>En esta entrada del bloque os dejo los contenidos de dos cursos que he imparte en mayo de este año en la plataforma de aprendizaje OpenWebinars sobre OpenShift v4.
En esta formación aprenderemos los recursos principales de Kubernetes usando OpenShift v4, y hacia la parte final, desplegaremos una aplicación completa para poner en práctica todo lo aprendido.
Curso: Aprende Kubernetes con OpenShift v4
En esta formación vamos a aprender las distintas estrategias de despliegues de aplicaciones que nos ofrece OpenShift v4, que nos permite enmarcar esta herramienta en un servicio PaaS.
]]>En el artículo anterior: Introducción a la criptografía repasamos los aspectos más importantes sobre criptografía. En este artículo vamos a hacer una aplicación práctica utilizando el software GnuPG.
GnuPG es una implementación completa y gratuita del estándar OpenPGP, también conocido como PGP. GnuPG nos permite usar criptografía simétrica y asimétrica para cifrar y firmar nuestros datos.
Para instalar gpg en nuestro sistema operativo basado en Debian, ejecutamos:
apt install gnupg
Recordamos que en la criptografía simétrica las claves encriptación/desencriptación usadas por el emisor y el receptor son las mismas.
Vamos a usar la opción -c
del comando gpg
para cifrar. Por ejemplo para cifrar un fichero, ejecutamos:
gpg -c fichero.txt
Nos pide la clave de encriptación (Nota: si estáis usando gnome al introducir la clave para realizar la encriptación se guarda en una cache, por lo que no os va a pedir la clave a la hora de desencriptar) y nos genera el fichero fichero.txt.gpg
.
Para desencriptar el fichero usamos la opción -d
, simplemente ejecutamos:
gpg -d fichero.txt.gpg
Por lo tanto, si queremos recuperar el fichero original podríamos ejecutar:
gpg -d fichero.txt.gpg > fichero2.txt
En la criptografía asimétrica o de clave pública, cada usuario tiene una clave pública conocida por todos y una clave privada, que es secreta.
Lo primero que tenemos que hacer es generar un par de claves (pública y privada) para un usuario de nuestro sistema. Tenemos varias formas de generar las claves, la más sencilla es usar la siguiente opción:
gpg --gen-key
Nos pedirá el nombre del usuario y su correo electrónico, además podremos introducir una frase de paso para proteger la clave privada, cad vez que la usemos se nos pedirá esta frase.
Muchas de las opciones para generar las claves se han tomado por defecto, si queremos que nos pida más detalle de la generación de las claves podemos usar la opción --full-generate-key
. Si usamos esta opción nos pedirá el tipo de clave que vamos a generar, el tamaño de la clave y la fecha de validez de la clave.
Podemos listar las claves públicas que tenemos, con el siguiente comando:
gpg --list-key
/home/usuario/.gnupg/pubring.kbx
--------------------------------
pub rsa3072 2023-10-16 [SC] [expires: 2025-10-15]
F8F2A90AF7A9BFCC530BFC8F603DCFEBDFC063AB
uid [ultimate] José Domingo <correo@example.org>
sub rsa3072 2023-10-16 [E] [expires: 2025-10-15]
Vemos que se ha generado una clave principal pública (pub
). También vemos la fecha de validez, así como su identificador (uid
) (el nombre de usuario, el correo o el identificador alfanumérico). Esta clave primaría nos permite realizar firmas digitales, para cifrar se ha generado una subclave (sub
) que está vinculada a la clave principal. Para más información sobre las subclaves puedes ver la wiki de Debian.
También podemos listar las claves privadas, ejecutando:
gpg --list-secret-key
/home/usuario/.gnupg/pubring.kbx
--------------------------------
sec rsa3072 2023-10-16 [SC] [expires: 2025-10-15]
F8F2A90AF7A9BFCC530BFC8F603DCFEBDFC063AB
uid [ultimate] José Domingo <correo@example.org>
ssb rsa3072 2023-10-16 [E] [expires: 2025-10-15]
Vemos que es la clave privada (sec
) y que de la misma forma que la anterior, se ha asociado una subclave (ssb
).
Si queremos que otros usuarios utilicen nuestra clave pública para cifrar mensajes, es necesaria enviarles nuestra clave pública. Para ello vamos a exportarla, usando la siguiente instrucción, donde tenemos que identificar nuestra clave pública (con el nombre, el correo o el identificador):
gpg --output josedom.gpg --export correo@example.org
El fichero josedom.gpg
es un binario con nuestra clave pública, si para facilitar el envío queremos generarlo en formato de texto plano, ejecutamos:
gpg --armor --output josedom.asc --export correo@example.org
Podemos enviar el fichero josedom.asc
a otros usuarios o subir nuestra clave pública a un servidor público de claves PGP, por ejemplo al de red.es.
Por último, si nosotros recibimos una clave pública de otro usuario en el fichero ahsoka.asc
y queremos importarlo, ejecutaremos:
gpg --import ahsoka.asc
Y podemos comprobar que la importación ha sido correcta:
gpg --list-key
...
--------------------------------
pub rsa3072 2023-10-16 [SC] [expires: 2025-10-15]
F8F2A90AF7A9BFCC530BFC8F603DCFEBDFC063AB
uid [ultimate] José Domingo <correo@example.org>
sub rsa3072 2023-10-16 [E] [expires: 2025-10-15]
pub rsa3072 2023-10-16 [SC] [expires: 2025-10-15]
2BF88208A94C08B3204F0131D0C833A59DC79BA6
uid [ultimate] Ahsoka Tano <ahsoka@example.org>
sub rsa3072 2023-10-16 [E] [expires: 2025-10-15]
Cada clave pública y privada tiene un papel específico en el cifrado y descifrado de documentos. Se puede pensar en una clave pública como en una caja fuerte de seguridad. Cuando un remitente cifra un documento usando una clave pública, ese documento se pone en la caja fuerte, la caja se cierra, y el bloqueo de la combinación de ésta se gira varias veces. La parte correspondiente a la clave privada, esto es, el destinatario, es la combinación que puede volver a abrir la caja y retirar el documento. Dicho de otro modo, sólo la persona que posee la clave privada puede recuperar un documento cifrado usando la clave pública asociada al cifrado.
Si queremos cifrar un documento para el usuario Ahsoka, lo haremos usando su clave pública. Solo el usuario Ashoka podrá descifrarlo con su clave privada. Por lo tanto, si queremos cifrar un documento cifrado para que solo lo descifre el usuario Ahoska, lo cifraremos (con la opción --encrypt
) con su clave pública:
gpg --output fichero.txt.gpg --encrypt --recipient ahsoka@example.org fichero.txt
Cuando el usuario recibe el fichero cifrado, utilizará su clave privada (por lo tanto se nos pedirá la frase de paso) para descifrarlo:
gpg --output fichero.txt --decrypt fichero.txt.gpg
gpg: encrypted with 3072-bit RSA key, ID FA920C331CD102E2, created 2023-10-16
"Ahsoka Tano <ahsoka@example.org>"
Una firma digital certifica un documento y le añade una marca de tiempo. Si posteriormente el documento fuera modificado en cualquier modo, el intento de verificar la firma fallaría. La utilidad de una firma digital es la misma que la de una firma escrita a mano, sólo que la digital tiene una resistencia a la falsificación. Para que un usuario firme un mensaje utilizará su clave privada, y para poder verificar dicha firma se utilizará la clave pública del usuario.
El parámetro --sign
se usa para generar una firma digital. El documento que se desea firmar es la entrada, y la salida es el documento firmado.
gpg --output fichero.sig --sign fichero.pdf
Si es necesario se pedirá la frase de paso de la clave privada. El documento se comprime antes de ser firmado, y la salida es en formato binario.
El usuario al que enviamos el documento firmado necesita tener nuestra clave pública para poder verificar la firma. Con un documento con firma digital el usuario puede llevar a cabo dos acciones: comprobar sólo la firma o comprobar la firma y recuperar el documento original al mismo tiempo. Para comprobar la firma se usa la opción --verify
.
gpg --verify fichero.sig
gpg: Firmado el dom 22 oct 2023 11:27:51 CEST
gpg: usando RSA clave 67379D6620EAD8BF2DA7111760DAB70F3B298B8C
gpg: Firma correcta de "José Domingo Muñoz Rodríguez <josedom24@josedomingo.org>" [absoluta]
Para verificar la firma y extraer el documento se usa la opción --decrypt
. El documento con la firma es la entrada, y el documento original recuperado es la salida.
gpg --output fichero_original.pdf --decrypt fichero.sig
En algunos casos es necesario no comprimir el fichero firmado, y generar firmas ASCII, para ello usaremos la opción --clearsign
:
gpg --clearsign fichero.txt
Se genera el fichero fichero.txt.asc
con el contenido del fichero y la firma digital en formato ASCII.
Si el receptor ya tiene el documento original, sólo mandamos la firma digital. Generamos la firma con la opción --detach-sign
de gpg.
gpg --output fichero.sig --detach-sig fichero.pdf
El fichero fichero.sig
es sólo la firma digital del fichero que hemos firmado.
Como hemos visto anteriormente es necesario poseer la clave pública de los usuarios con los que estoy intercambiando información. Esa clave pública nos servirá:
Cuando recibimos la clave pública de un usuario y la hayamos importado, es necesario validarla. Tenemos dos métodos para validar las claves:
En este caso, se verifica la huella digital (el hash de la clave pública), se comprueba que pertenece al usuario apropiado (garantizar que la persona con la que se está comunicando sea el auténtico propietario de la clave), y a continuación firmamos su clave pública con nuestra clave privada.
En nuestro ejemplo hemos importado la clave pública de Ahsoka Tano, tendríamos que calcular la huella digital de la clave, ejecutando el subcomando fpr
al editar la clave:
gpg --edit-key ahsoka@example.org
gpg (GnuPG) 2.2.40; Copyright (C) 2022 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
pub rsa3072/E0EE582D3C903041
created: 2023-10-22 expires: 2025-10-21 usage: SC
trust: unknown validity: unknown
sub rsa3072/11518C522A7B719A
created: 2023-10-22 expires: 2025-10-21 usage: E
[ unknown] (1). Ahsoka Tano <ahsoka@example.org>
gpg> fpr
pub rsa3072/E0EE582D3C903041 2023-10-22 Ahsoka Tano <ahsoka@example.org>
Primary key fingerprint: 11AD F9B8 DF66 0DFB D147 B4A5 E0EE 582D 3C90 3041
Ahora tendría que verificar la huella digital con el propietario de la clave, es decir, con Ahsoka Tano. Esto puede hacerse en persona o por teléfono, o por medio de otras maneras, siempre y cuando el usuario pueda garantizar que la persona con la que se está comunicando sea el auténtico propietario de la clave. Si la huella digital que se obtiene por medio del propietario es la misma que la que se obtiene de la clave, entonces se puede estar seguro de que se está en posesión de una copia correcta de la clave.
Después de esta comprobación, ya podríamos realizar la firma, con el subcomando --sign
(como haremos uso de nuestra clave privada, se nos pedirá la frase de paso):
gpg> sign
pub rsa3072/E0EE582D3C903041
created: 2023-10-22 expires: 2025-10-21 usage: SC
trust: unknown validity: unknown
Primary key fingerprint: 11AD F9B8 DF66 0DFB D147 B4A5 E0EE 582D 3C90 3041
Ahsoka Tano <ahsoka@example.org>
This key is due to expire on 2025-10-21.
Are you sure that you want to sign this key with your
key "José Domingo <correo@example.org>" (603DCFEBDFC063AB)
Really sign? (y/N) y
Para terminar la edición de la clave, guardamos los cambios con el subcomando save
.
Desafortunadamente este proceso es complicado cuando debemos validar un gran número de claves o cuando debemos comunicarnos con personas a las que no conocemos personalmente. GnuPG trata este problema con un mecanismo conocido como anillo de confianza. En el modelo del anillo de confianza la responsabilidad de la validación de las claves públicas recae en las personas en las que confiamos.
Pongamos un ejemplo:
Si yo confío en Ahsoka Tano, ya que he validado personalmente su clave, entonces puede deducir que las claves de Anakin Skywalker y de Obi-Wan Kenobi son válidas sin llegar a comprobarlas personalmente. Tendré que usar la clave pública de Ahsoka Tano para comprobar que las las claves de Anakin Skywalker y de Obi-Wan Kenobi son válidas.
Hay que introducir un nuevo concepto confianza en en el propietario (trust). Hay que diferenciar este concepto con el de validación (validity) que será la confianza en que una clave pertenece a la persona asociada con el identificador de clave.
En la práctica la confianza es algo subjetivo. Por ejemplo, la clave de Ahoska Tano es válida para mi, ya que la he firmado, pero puedo desconfiar de otras claves que hayan sido validadas por la firma de Ahsoka Tano. En este caso, puede que yo no acepte las claves de Anakin Skywalker y de Obi-Wan Kenobi como válidas sólo porque hayan sido firmadas por Ahsoka Tano. Si volvemos a editar la clave de Ahsoka:
gpg --edit-key ahsoka@example.org
gpg (GnuPG) 2.2.40; Copyright (C) 2022 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
pub rsa3072/E0EE582D3C903041
created: 2023-10-22 expires: 2025-10-21 usage: SC
trust: unknown validity: full
sub rsa3072/11518C522A7B719A
created: 2023-10-22 expires: 2025-10-21 usage: E
[ full ] (1). Ahsoka Tano <ahsoka@example.org>
gpg>
Vemos como la confianza en el propietario es desconocida (unknown) y sin embargo la confianza de que la clave es de Ahsoka Tano es total (full) ya que la he firmado anteriormente. El nivel de confianza en una clave es algo que sólo nosotros podemos asignar a la clave, y se considera información privada. El nivel de confianza no se exporta con la clave, de hecho no se almacena en los anillos de claves sino en una base de datos aparte. El editor de claves nos permite ajustar nuestra confianza en el propietario de una clave, para ello usamos el subcomando trust
:
gpg> trust
pub rsa3072/E0EE582D3C903041
created: 2023-10-22 expires: 2025-10-21 usage: SC
trust: unknown validity: full
sub rsa3072/11518C522A7B719A
created: 2023-10-22 expires: 2025-10-21 usage: E
[ full ] (1). Ahsoka Tano <ahsoka@example.org>
Please decide how far you trust this user to correctly verify other users' keys
(by looking at passports, checking fingerprints from different sources, etc.)
1 = I don't know or won't say
2 = I do NOT trust
3 = I trust marginally
4 = I trust fully
5 = I trust ultimately
m = back to the main menu
Your decision?
Como vemos podemos configurar la confianza del propietario de la clave en distintos niveles:
El anillo de confianza permite usar un algoritmo más elaborado para validar una clave. Hasta ahora, una clave sólo se consideraba válida si la firmábamos nosotros personalmente. Ahora es posible usar un algoritmo más flexible: una clave se considera válida si cumple dos condiciones:
La longitud del camino, en número de claves con confianza marginal requeridas, y el número de claves con confianza plena requeridas se pueden cambiar. Los números dados arriba son los valores por definición usados por GnuPG.
Veamos un ejemplo:
Si yo confio totalmente en Ahoska Tano:
gpg --list-keys
/home/vagrant/.gnupg/pubring.kbx
--------------------------------
...
uid [ultimate] José Domingo <correo@example.org>
...
uid [ full ] Ahsoka Tano <ahsoka@example.org>
...
uid [ full ] Obi-Wan Kenobi <obi@example.org>
...
uid [ undef ] Anakin Skywalker <anakin@example.org>
Sin embargo, si cambio el nivel de confianza de Ahsoka Tano a marginal:
gpg --list-keys
/home/vagrant/.gnupg/pubring.kbx
--------------------------------
...
uid [ultimate] José Domingo <correo@example.org>
...
uid [ full ] Ahsoka Tano <ahsoka@example.org>
...
uid [ marginal ] Obi-Wan Kenobi <obi@example.org>
...
uid [ unknown ] Anakin Skywalker <anakin@example.org>
En este artículo hemos hecho un resumen del uso de la herramienta GnuPG para trabajar con criptografía simétrica y asimétrica. Para seguir profundizando en el uso de esta herramienta os sugiero la lectura de la documentación oficial.
]]>La Criptografía (que viene de dos palabras “cripto” (secreto) y “grafía” (escritura) nos permite el cifrado o codificación de mensajes con el fin de hacerlos ininteligibles. Puedes profundizar en este concepto en la Wikipedia.
Veamos algunos conceptos sobre criptografía:
Podemos indicar dos tipos de criptografía dependiendo de las claves usadas:
Como hemos indicado anteriormente el emisor y el receptor usan la misma clave para cifrar y descifrar el mensaje. Por lo tanto, el emisor y el receptor, antes de poder comunicarse, deben ponerse de acuerdo en el valor de la clave. Una vez que ambas partes tienen acceso a esta clave, el remitente cifra un mensaje usando la clave, lo envía al destinatario, y este lo descifra con la misma clave.
Los algoritmos usados en la criptografía simétrica (por ejemplo, aes) son principalmente operaciones booleanas y de transposición. Estos algoritmos son más eficientes que los usados en la criptografía asimétrica.
En este tipo de criptografía, el emisor y el receptor no comparten una clave. Cada usuario tiene una clave que es pública, y por lo tanto conocida por todos y una clave que es privada, conocida sólo por el receptor.
Por lo tanto en este caso, utilizando distintos tipos de algoritmos (por ejemplo DSA o RSA) cualquier usuario puede cifrar un mensaje usando la clave pública de un usuario, sólo este usuario podrá descifrar el mensaje usando su clave privada.
Este tipo de criptografía es más compleja que la simétrica, por lo tanto es menos eficiente, aunque es más segura.
En al mayoría de los protocolos que usan criptografía para cifrar la información que se trasmite (por ejemplo, ssh o https) utilizamos las dos tipos de criptografía que hemos estudiado:
Antes de seguir explicando la firma digital, vamos a introducir el concepto de función de dispersión o hash. Por medio de un algoritmo (por ejemplo MD5, SHA-1,…) a partir de un fichero se obtiene un resumen de tamaño fijo. Evidentemente, a partir del resultado (hash) no podemos generar el fichero original. Podemos utilizar los hash para comprobar la integridad de un fichero. Por ejemplo, si sabemos el hash de un fichero, si lo descargamos podemos volver a ejecutar la función de dispersión para comprobar si hemos descargado un fichero corrupto. Si el hash del fichero descargado es igual que el hash del fichero original, podemos asegurar la integridad del fichero.
Una firma digital certifica un documento y le añade una marca de tiempo. Si posteriormente el documento fuera modificado en cualquier modo, el intento de verificar la firma fallaría. La utilidad de una firma digital es la misma que la de una firma escrita a mano, sólo que la digital tiene una resistencia a la falsificación. Para que un usuario firme un mensaje utilizará su clave privada, y para poder verificar dicha firma se utilizará la clave pública del usuario.
Por lo tanto firmando un mensaje estamos asegurando su autenticidad, su integridad (que no ha sido modificado) y el no repudio (garantiza al receptor que el mensaje ha sido enviado por el emisor).
Resulta computacionalmente caro encriptar mensajes largos con nuestra clave privada para firmarlos. Por lo que al firmar un documento, vamos a calcular su hash y la vamos a encriptar con la clave privada del emisor, es decir, firmamos el hash.
Como vemos en el gráfico, tenemos un fichero que queremos firmar. Para ello:
Uno de los problemas que nos podemos encontrar al usar criptografía asimétrica es poder confiar que la clave pública que estamos usando para cifrar un documento o verificar una firma digital pertenece a un usuario adecuado. Dicho de otro modo: necesitamos establecer una confianza en que la clave pública de un usuario es correcta, es decir el único que posee la clave privada correspondiente es el usuario auténtico al que pertenece. Cuanto más fiable sea el método de establecer esta confianza más seguridad tendrá el sistema.
Lo ideal sería que cada usuario comunicara (e idealmente probara) de forma directa al resto de usuarios cuál es su clave pública. Sin embargo esto no es posible en la realidad y se desarrollan distintos esquemas para aportar confianza. Existen varios modelos, pero vamos a señalar dos de ellos:
Establecimiento de una red de confianza: Los usuario recogen claves públicas de otros usuarios y aseguran su identidad si están seguros de que la clave privada correspondiente pertenece en exclusiva a ese usuario. Se pueden organizar Grupos de firmas para que los usuarios amplíen su red de confianza.
Para establecer que un usuario confía en otro, este firma digitalmente la clave pública de otro usuario (realmente firma la huella digital que es el hash de la clave pública tras aplicarle una función de dispersión). Además, dos usuarios que no se conocen pueden confiar en sus claves públicas si existe una cadena de confianza que enlace ambas partes, es decir un usuario puede confiar en otro aunque no tenga su clave pública firmada, si esta clave esta firmada por un tercer usuario en el que el primero confía.
En este modelo hay una o varias entidades emisoras de certificados (Autoridades de certificación o CA del inglés Certification Authority) que aseguran la autenticidad de la clave pública y de ciertos atributos de identidad del usuario. Para ello firman con su clave privada ciertos atributos del usuario incluyendo su clave pública generando lo que se llama certificado del usuario.
En este artículo del blog he realizado un pequeño resumen de los conceptos teóricos más importantes sobre la Criptografía. He pretendido hacer un resumen de los contenidos que se imparten en la unidad de Criptografía en el módulo de Seguridad y Alta Disponibilidad del ciclo formativo de Administración de sistemas Informáticos. En la siguiente entrada veremos cada uno de estos conceptos usando la herramienta de encriptación y firma digital GnuPGP (GNU Privacy Guard).
]]>Más específicamente, este material corresponde a un proyecto de elaboración de materiales y recursos didácticos de la Resolución de 25 de julio de 2022, de la Dirección General de Formación del Profesorado e Innovación Educativa, por la que se aprueban proyectos de investigación e innovación educativa y de elaboración de materiales curriculares para el año 2022.
Proyecto de Fibra Óptica - IES Gonzalo Nazareno
Además de acceder a los contenidos en la página web, puedes encontrar el contenido en un repositorio en GitHub y puedes bajar la documentación en distintos formatos:
]]>En este artículo vamos a estudiar otro método de despliegue de aplicaciones web que nos ofrece OpenShift V4: Construir una nueva imagen generada a partir de un fichero Dockerfile, y desplegar la aplicación desde esa imagen construida.
El esquema para ver los recursos que se crean en OpenShift al realizar un despliegue desde un fichero Dockerfile
es el siguiente:
Sigamos trabajando con el mismo repositorio que en el artículo anterior y ahora vamos a suponer que queremos ejecutar nuestra aplicación con otra imagen base y hacer una configuración extra en la creación de la imagen. Tendríamos que crear un fichero Dockerfile
para especificar los pasos de creación de la imagen. Para ello, creamos un fichero Dockerfile
en el repositorio con el siguiente contenido:
FROM bitnami/nginx
WORKDIR /app
COPY . /app
Evidentemente, este fichero puede ser más complejo si la construcción de la imagen lo requiere. Ahora guardamos el fichero en el repositorio:
git add Dockerfile
git commit -am "php"
git push
Y ahora al intentar crear una nueva aplicación, OpenShift detectará que hay un fichero Dockerfile
en el repositorio y lo utilizará para la creación automática de la imagen:
oc new-app https://github.com/josedom24/osv4_html.git --name=app1
Si queremos que la construcción se vuelva a realizar usando el mecanismo de Source-to-Image, tendremos que indicar la estrategia específicamente:
oc new-app https://github.com/josedom24/osv4_html.git --name=app2 --strategy=source
Y volverá a usar el mecanismo anterior.
Como indicábamos en el apartado anterior, ahora vamos a añadir un fichero Dockerfile
(el mismo que vimos en el apartado anterior) al repositorio. En este caso, volvemos a elegir la opción +Add y elegimos el apartado Git Repository. Y comprobaremos que ahora nos sugiere la estrategia docker para construir la nueva imagen:
Si queremos que la construcción se vuelva a realizar usando el mecanismos de Source-to-Image, tendremos que indicar la estrategia específicamente pulsando sobre la opción Edit Import Strategy:
]]>Como indicábamos en el artículo anterior, los principales métodos de despliegue de una aplicación web en OpenShift v4 son:
En este artículo vamos a desplegar aplicaciones partiendo del código fuente de la misma guardado en un repositorio GitHub. OpenShift v4 es capaz de realizar una construcción automática de una imagen Docker a partir del código fuente y una builder image (imagen constructora).
En esta estrategia de despliegue donde se va a crear una imagen de forma automática, se crean dos nuevos recursos en OpenShift:
El esquema para ver los recursos que se crean en OpenShift al realizar un despliegue desde código fuente es el siguiente:
Como vemos, el proceso es el siguiente:
Queremos construir una imagen con un servidor web a partir de un repositorio donde tenemos una página web estática, para ello ejecutaremos:
oc new-app https://github.com/josedom24/osv4_html.git --name=app1
error: No language matched the source repository
Vemos que no es posible averiguar el lenguaje con el que está escrito, por lo que tendremos que indicar la Builder Image que vamos a utilizar.
Las Builder Image están referenciadas por una Image Stream, por ejemplo, podemos buscar las Builder Image con el servidor web apache:
oc new-app -S httpd
...
Image streams ...
-----
httpd
Project: openshift
Tags: 2.4-el7, 2.4-ubi8, 2.4-ubi9, latest
Obtenemos además las distintas etiquetas que podemos usar, que corresponden a distintas versiones del servidor web. Por lo tanto si queremos generar una imagen con el código de nuestro repositorio usando de base una imagen basada en httpd (al no indicar la etiqueta se escogerá la latest que apunta a la última versión de la imagen), ejecutamos:
oc new-app httpd~https://github.com/josedom24/osv4_html.git --name=app1
...
--> Found image e54df11 (5 months old) in image stream "openshift/httpd" under tag "2.4-ubi8" for "httpd"
...
--> Creating resources ...
imagestream.image.openshift.io "app1" created
buildconfig.build.openshift.io "app1" created
deployment.apps "app1" created
service "app1" created
--> Success
Build scheduled, use 'oc logs -f buildconfig/app1' to track its progress.
Application is not exposed. You can expose services to the outside world by executing one or more of the commands below:
'oc expose service/app1'
Run 'oc status' to view your app.
Como vemos se han creado varios recursos:
app1
que apuntará a la nueva imagen que vamos a generar, y que se utilizará en la definición del despliegue.Podemos comprobar todos los recursos que se han creado, ejecutando:
oc status
...
svc/app1 - 172.30.217.224 ports 8080, 8443
deployment/app1 deploys istag/app1:latest <-
bc/app1 source builds https://github.com/josedom24/osv4_html.git on openshift/httpd:2.4-ubi8
deployment #2 running for 30 seconds - 1 pod
deployment #1 deployed about a minute ago
También podemos ver los recursos que hemos creado, ejecutando:
oc get all
NAME READY STATUS RESTARTS AGE
pod/app1-1-build 0/1 Completed 0 2m20s
pod/app1-f55dbb8d7-r4bkx 1/1 Running 0 105s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/app1 ClusterIP 172.30.217.224 <none> 8080/TCP,8443/TCP 2m21s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/app1 1/1 1 1 2m21s
NAME DESIRED CURRENT READY AGE
replicaset.apps/app1-5bc9cfc7b9 0 0 0 2m21s
replicaset.apps/app1-f55dbb8d7 1 1 1 106s
NAME TYPE FROM LATEST
buildconfig.build.openshift.io/app1 Source Git 1
NAME TYPE FROM STATUS STARTED DURATION
build.build.openshift.io/app1-1 Source Git@5ae3b13 Complete 2 minutes ago 37s
NAME IMAGE REPOSITORY TAGS UPDATED
imagestream.image.openshift.io/app1 default-route-openshift-image-registry.apps.sandbox-m3.1530.p1.openshiftapps.com/josedom24-dev/app1 latest About a minute ago
Finamente, creamos el recurso Route:
oc expose service app1
Y accedemos a la aplicación:
Si queremos crear una aplicación con la página web de nuestro repositorio con una imagen basada en nginx, podemos buscar los recursos Images Stream que tenemos en el catálogo:
oc new-app -S nginx
Y posteriormente creamos la aplicación con el comando:
oc new-app nginx~https://github.com/josedom24/osv4_html.git --name=app2
OpenShift examina el repositorio y según los ficheros que tengamos es capaz de determinar con que lenguaje está escrito y te sugiere un Builder Image .
Si añadimos un fichero index.php
a nuestro repositorio:
echo '<?php phpinfo();?>'> index.php
git add index.php
git commit -am "php"
git push
Y ahora creamos la aplicación sin indicar la Builder Image:
oc new-app https://github.com/josedom24/osv4_html.git --name=app3
--> Found image b34c3d8 (5 months old) in image stream "openshift/php" under tag "8.0-ubi8" for "php"
Apache 2.4 with PHP 8.0
-----------------------
PHP 8.0 available as container is a base platform for building and running various PHP 8.0 applications and frameworks. PHP is an HTML-embedded scripting language. PHP attempts to make it easy for developers to write dynamically generated web pages. PHP also offers built-in database integration for several commercial and non-commercial database management systems, so writing a database-enabled webpage with PHP is fairly simple. The most common use of PHP coding is probably as a replacement for CGI scripts.
Tags: builder, php, php80, php-80
* The source repository appears to match: php
* A source build using source code from https://github.com/josedom24/osv4_html.git will be created
* The resulting image will be pushed to image stream tag "app3:latest"
* Use 'oc start-build' to trigger a new build
--> Creating resources ...
imagestream.image.openshift.io "app3" created
buildconfig.build.openshift.io "app3" created
deployment.apps "app3" created
service "app3" created
--> Success
Build scheduled, use 'oc logs -f buildconfig/app3' to track its progress.
Application is not exposed. You can expose services to the outside world by executing one or more of the commands below:
'oc expose service/app3'
Run 'oc status' to view your app.
Comprobamos que ha detectado que la aplicación está escrita en PHP, y nos ha seleccionado la Builder Image php:8.0-ubi8
. Una vez desplegada la aplicación, creamos el objeto Route y accedemos al fichero index.php
:
oc expose service app3
Si queremos usar otra versión de PHP, tendríamos que indicar la versión de la builder image, para ello buscamos las distintas versiones que nos ofrecen:
oc new-app -S php
...
Image streams (oc new-app --image-stream=<image-stream> [--code=<source>])
-----
php
Project: openshift
Tags: 7.3-ubi7, 7.4-ubi8, 8.0-ubi8, 8.0-ubi9, latest
Y ahora creamos una nueva aplicación con la versión 7.3-ubi7
:
oc new-app php:7.3-ubi7~https://github.com/josedom24/osv4_html.git --name=app4
oc expose service app4
Por ejemplo, para eliminar la aplicación app1
tendríamos que eliminar todos los recursos generados:
oc delete deploy app1
oc delete service app1
oc delete route app1
oc delete is app1
oc delete bc app1
Vamos a realizar el mismo ejercicio pero desde la consola web. Para ello accedemos desde la vista Developer a la opción de +Add y elegimos el apartado Git Repository:
Queremos construir una imagen con un servidor web a partir de un repositorio donde tenemos una página web estática, para ello vamos a configurar el despliegue:
Indicamos el repositorio donde se encuentra la aplicación (https://github.com/josedom24/osv4_html.git
). Y vemos que nos detecta una Builder Image para construir la nueva imagen, pero no nos muestra la que nos recomienda. Como pasaba en el apartado anterior, OpenShift no puede determinar el lenguaje con el que está escrita la aplicación, por lo que tendremos que indicar la Builder Image que vamos a utilizar. Para ello pulsamos sobre la opción Edit Import Strategy:
Vemos que ha seleccionado la estrategia de construcción (Builder Image) pero, como indicábamos, no se ha seleccionado ninguna. Nosotros podemos seleccionar una imagen para construir la nueva imagen (en nuestro caso httpd) y podemos elegir la versión de la imagen que hemos escogido.
Una vez lo hemos hecho, seguimos con la configuración de forma similar al despliegue de una imagen:
Continuamos con la configuración:
Finalmente le damos al botón Create para crear el despliegue, esperamos unos segundos y accedemos al apartado Topology y comprobamos que se han creado los distintos recursos:
Podemos ver que tenemos varias secciones en el icono que representa el despliegue:
En la pantalla lateral, ahora tenemos una sección de Builds donde tenemos la lista de las últimas construcciones y un botón que nos permite iniciar una nueva construcción de imagen.
Se ha creado un objeto ImageStream Y un objeto BuildConfig:
Al acceder a la URL de la ruta accedemos a la aplicación:
En este caso al escoger la Builder Image elegimos la imagen de nginx:
En este caso, si creamos un fichero index.php
en el repositorio, como se indicaba en el apartado anterior, OpenShift podrá detectar la Builder Image que necesita para construir la nueva imagen:
Podríamos pulsar sobre la opción Edit Import Strategy para cambiar la Builder Image, o cambiar la versión de la imagen seleccionada.
Finalmente, hemos hecho tres despliegues agrupados en la aplicación aplicacion1
.
Si eliminamos la aplicación, se eliminarán todos los recursos que tenemos agrupados.
Ya hemos estudiado dos métodos de despliegues de aplicaciones web en OpenShift v4: Despliegue de aplicaciones desde imágenes en OpenShift v4 y el método source-2-image que nos permite a partir del código fuente guardado en repositorio, crear una nueva imagen y desplegar la aplicación. En el próximo artículo estudiaremos otra estrategia de despliegue que utiliza un fichero Dockerfile para construir la nueva imagen.
]]>Una forma de trabajar con OpenShift es a partir de la definición de los recursos en fichero con formato YAML, y utilizar la herramienta de línea de comando oc (oc apply...
) o la consola web para gestionar los recursos.
Sin embargo, lo que le da más potencialidad a OpenShift son los diferentes métodos de creación de despliegues de una forma automática. Esta funcionalidad es la que otorga a OpenShift las características de Cloud Computing PaaS, ya que nos ofrece la posibilidad de desplegar aplicaciones de una forma muy sencilla.
Los principales métodos de implementar una aplicación son:
En este artículo vamos a desplegar aplicaciones partiendo de una imagen de contenedor. En este tipo de despliegues no se utiliza directamente una imagen que está almacenada en un registro, se utiliza un recurso llamado ImageStream que nos permite referenciar a las imágenes que tenemos en un registro externo o en el registro interno de imágenes de OpenShift.
El esquema para ver los recursos que se crean en OpenShift al realizar un despliegue desde una imagen es el siguiente:
Para crear un despliegue desde la imagen josedom24/test_web:v1
que se llame test-web
ejecutamos el comando:
oc new-app josedom24/test_web:v1 --name test-web
--> Found container image 4b01a27 (12 days old) from Docker Hub for "josedom24/test_web:v1"
* An image stream tag will be created as "test-web:v1" that will track this image
--> Creating resources ...
imagestream.image.openshift.io "test-web" created
deployment.apps "test-web" created
service "test-web" created
--> Success
Application is not exposed. You can expose services to the outside world by executing one or more of the commands below:
'oc expose service/test-web'
Run 'oc status' to view your app.
Como vemos se han creado varios recursos:
josedom24/test_web:v1
en Docker Hub.Creamos el recurso Route:
oc expose service/test-web
Y comprobamos los recursos que se han creado:
oc status
...
http://test-web-josedom24-dev.apps.sandbox-m3.1530.p1.openshiftapps.com to pod port 8080-tcp (svc/test-web)
deployment/test-web deploys istag/test-web:v1
deployment #2 running for about a minute - 1 pod
deployment #1 deployed about a minute ago
También podemos ver los recursos que hemos creado, ejecutando:
oc get all
NAME READY STATUS RESTARTS AGE
pod/test-web-6f87bb78f9-9f2v5 1/1 Running 0 2m23s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/modelmesh-serving ClusterIP None <none> 8033/TCP,8008/TCP,8443/TCP,2112/TCP 19d
service/test-web ClusterIP 172.30.75.17 <none> 8080/TCP,8443/TCP 2m23s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/test-web 1/1 1 1 2m23s
NAME DESIRED CURRENT READY AGE
replicaset.apps/test-web-6c944c9765 0 0 0 2m23s
replicaset.apps/test-web-6f87bb78f9 1 1 1 2m23s
NAME IMAGE REPOSITORY TAGS UPDATED
imagestream.image.openshift.io/test-web default-route-openshift-image-registry.apps.sandbox-m3.1530.p1.openshiftapps.com/josedom24-dev/test-web v1 2 minutes ago
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
route.route.openshift.io/test-web test-web-josedom24-dev.apps.sandbox-m3.1530.p1.openshiftapps.com test-web 8080-tcp None
Veamos algunos aspectos con detalle:
Vemos como se ha creado un recurso ImageStream que apunta a la imagen que hemos indicado. Podemos ver los detalles de este recurso ejecutando:
oc describe is test-web
El recurso Deployment hace referencia en su definición al recurso ImageStream:
oc describe deploy test-web
...
Containers:
test-web:
Image: josedom24/test_web@sha256:99db6f7fdcd6aa338d80b5cd926dff8bae50062c49f82c79a3d67d048efb13a4
...
Podemos comprobar que si accedemos a la URL generada por el recurso Route, la aplicación está funcionando:
Para eliminar la aplicación, tenemos que borrar los recursos que hemos creado:
oc delete deploy test-web
oc delete service test-web
oc delete route test-web
oc delete is test-web
Vamos a realizar el mismo ejercicio pero desde la consola web. Para ello accedemos desde la vista Developer a la opción de +Add y elegimos el apartado Container Images:
A continuación vamos a configurar las propiedades del despliegue:
Continuamos con la configuración:
Finalmente le damos al botón Create para crear el despliegue, esperamos unos segundos y accedemos al apartado Topology y comprobamos que se han creado los distintos recursos:
Podemos ver que tenemos varias secciones en el icono que representa el despliegue:
Si pulsamos sobre los tres puntos verticales del cuadro del Deployment o sobre el botón Actions, obtendremos un menú con todas las acciones que podemos realizar sobre el objeto:
Nos queda por comprobar que se ha creado un objeto ImageStream, para ello en la vista Administrator, en la opción Builds -> ImageStreams la podemos encontrar:
Al acceder a la URL de la ruta accedemos a la aplicación:
Por último podemos borrar todos los recursos creados, eliminando la aplicación:
En este artículo hemos introducido la estrategia dde despliegue de aplicaciones web en OpenShift v4 utilizando una imagen de contenedor. Lo hemos hecho desde la terminal, con el cliente de línea de comandos oc
y desde el dashboard. En próximos artículos haremos la introducción a otros métodos de despliegue como por ejemplo: Source-to-Image (s2i), Dockerfile,…
OpenShift v4 es una plataforma de contenedores desarrollada por RedHat y de código abierto basada en Kubernetes que proporciona una solución completa de orquestación de contenedores y servicios de aplicaciones para desarrolladores y equipos de operaciones.
Por lo tanto nos ofrece muchas de las ventajas de usar un orquestador de contenedores como Kubernetes:
Pero lo más importante, es que podemos considerar OpenShift como una plataforma PaaS, que permite a los desarrolladores centrarse en el desarrollo del código, para que posteriormente de una manera muy sencilla y automática sean capaces de desplegar sus aplicaciones en contenedores y con las ventajas que obtenemos de tenerlos gestionados por Kubernetes.
¿Qué aspectos caracterizan a OpenShift para considerarlo una plataforma PaaS?
La versión Developer Sandbox de OpenShift Dedicated nos permite probar OpenShift v4 en una plataforma con las siguientes características:
Acceso a Red Hat OpenShift Dedicated Developer Sandbox
Podemos usar la consola web (aplicación web) para interactuar con el clúster de OpenShift y nos permite gestionar todos los recursos del clúster. La consola web, nos permite trabajar con ella usando dos vistas distintas: la de Administrador y la de Desarrollador.
En la vista Developer, los usuarios tienen acceso a herramientas para desarrollar, implementar y supervisar aplicaciones en el clúster de OpenShift.
Algunas de las opciones que tenemos disponibles en esta vista son:
En la vista Administrator, los usuarios tienen acceso a herramientas para administrar el clúster de OpenShift y las aplicaciones que se ejecutan en él.
Al estar usando un usuario sin privilegios en Red Hat OpenShift Dedicated Developer Sandbox, algunas de las opciones no nos dan todas las funcionalidades de administración.
Algunas de las opciones que tenemos disponibles en esta vista son:
La herramienta oc
nos permite gestionar los recursos de nuestro clúster de OpenShift desde la línea de comandos.
Para más información de esta herramienta puedes acceder a la documentación oficial.
Tenemos varios métodos de instalación de esta herramienta, nosotros vamos a hacerlo desde la consola web de OpenShift.
Accedemos a la consola web y escogemos el icono de ayuda en la parte superior derecha, y posteriormente elegimos la opción Command Line Tools:
Nos aparecerá una página donde podremos descargarnos las distintas versiones de la herramienta, en mi caso he escogido la versión Linux x86_64.
Nos descargamos un fichero comprimido oc.tar
, lo descomprimimos y lo copiamos con permisos de ejecución en un directorio del PATH:
tar xvf oc.tar
sudo install oc /usr/local/bin
Y comprobamos la versión que hemos instalado:
oc version
Client Version: 4.12.0-202303081116.p0.g846602e.assembly.stream-846602e
Kustomize Version: v4.5.7
Si ejecutamos cualquier comando con la herramienta oc
nos recuerda que nos tenemos que loguear:
oc get deploy
error: You must be logged in to the server (Unauthorized)
Una vez que tenemos instalado la herramienta oc
, el siguiente paso el realizar el login en nuestro clúster. En el caso de Red Hat OpenShift Dedicated Developer Sandbox, la autentificación se hace por medio de un token.
Para obtener este token accedemos al menú que aparece al pulsar sobre nuestro nombre de usuario (parte superior derecha), eligiendo la opción Copy login command:
Copiamos la instrucción de login:
Y la ejecutamos:
oc login --token=sha256~xxxxxxxxxxxxx... --server=https://api.sandbox-m3.1530.p1.openshiftapps.com:6443
Logged into "https://api.sandbox-m3.1530.p1.openshiftapps.com:6443" as "josedom24" using the token provided.
You have one project on this server: "josedom24-dev"
Using project "josedom24-dev".
Como vemos, hemos accedido con el usuario y estamos usando un proyecto, que en mi caso se llama josedom24-dev
.
Al igual que en kubernetes, la configuración de acceso se guarda en el fichero ~/.kube/config
:
apiVersion: v1
clusters:
- cluster:
server: https://api.sandbox-m3.1530.p1.openshiftapps.com:6443
name: api-sandbox-m3-1530-p1-openshiftapps-com:6443
contexts:
- context:
cluster: api-sandbox-m3-1530-p1-openshiftapps-com:6443
namespace: josedom24-dev
user: josedom24/api-sandbox-m3-1530-p1-openshiftapps-com:6443
name: josedom24-dev/api-sandbox-m3-1530-p1-openshiftapps-com:6443/josedom24
current-context: josedom24-dev/api-sandbox-m3-1530-p1-openshiftapps-com:6443/josedom24
kind: Config
preferences: {}
users:
- name: josedom24/api-sandbox-m3-1530-p1-openshiftapps-com:6443
user:
token: sha256~UDgHaWXrosaNOAJEFxjhqZMPgH8ksbREvd8LZ_7mFYw
Donde vemos que se ha creado un contexto, donde se guarda el servidor al que nos conectamos (cluster
), el namespace o proyecto que estamos usando (namespace
) y el usuario (user
). Como hemos indicado el usuario utiliza el token para autentificarse sobre el clúster.
Esta operación habrá que repetirla cada vez que el token se caduque.
Como hemos indicado anteriormente el proyecto que estamos usando se corresponde con un recurso namespace
que nos permite agrupar todos nuestros recursos. Con el usuario que usamos tenemos acceso a nuestro proyecto, pero no podemos acceder a los recurso namespaces
que están definidos en el clúster:
oc get project
NAME DISPLAY NAME STATUS
josedom24-dev josedom24-dev Active
oc get namespace
Error from server (Forbidden): namespaces is forbidden: User "josedom24" cannot list resource "namespaces" in API group "" at the cluster scope
Si pulsamos sobre el siguiente icono en la parte superior derecha de la consola web:
Nos permitirá abrir eun terminal en la consola web:
Debemos indicar el proyecto donde se creará un recurso DevWorkspace donde se creará un Deployment que creará un Pod donde se ejecutará el terminal que estamos usando:
Un proyecto permite a OpenShift agrupar distintos recursos. Es similar al recurso namespace de Kubernetes, pero guarda información adicional.
De hecho, cada vez que se crea un nuevo proyecto, se crea un recursos namespace con el mismo nombre.
En Red Hat OpenShift Dedicated Developer Sandbox, no podemos crear nuevos proyectos y se nos asigna de forma automática un proyecto con el mismo nombre que el de nuestro usuario.
Para acceder a la información de nuestro proyecto, en la Vista Administrator, escogemos la opción Home -> Projects:
Si pulsamos sobre el nombre del proyecto, obtendremos los detalles del mismo: definición, inventario, uso de recursos, métricas, cuotas, eventos,…
Tenemos varias opciones:
En esta entrada hemos estudiado las características fundamentales de la plataforma OpenShift v4. Hemos visto los dos métodos fundamentales de interactuar con ella: la consola web y la herramienta de línea de comandos oc. En el próximo articulo veremos algunos ejemplos ppara enseñar como desplegar aplicaciones en contenedores usando OpenShift v4.
]]>