Taula de continguts

Apache amb DNIe

Configurar el servidor web (en aquest cas Apache) per a que validi certificats de confiança (en aquest cas del DNIe) és una tasca important en les mesures de seguretat i autenticació de l'administració pública, i pot esdevenir-ho també en un futur per a la gestió privada. L'administració pública necessita uns estàndards molt alts de seguretat, sobretot en el què respecta a autenticació i validació de la identitat de l'usuari.

En realitat el que realitzarem serà una autenticació mútua entre servidor i client, essent necessaris tant a l'un com a l'altre la configuració dels certificats de la CA (Certificate Authority, pel nostre cas el «AC RAIZ DNIE 2») i, per part del client, el certificat signat per aquesta mateixa CA (present dins del DNIe).

Referències i ampliacions:

Descàrregues:

, , , , , , , , , , , , , , , , , , ,


Configuració del client

Disposar de certificats vàlids al DNIe

Primer de tot caldrà que tingueu el DNIe amb els certificats vàlids. Per solventar-ho cal que aneu a una Commissaria de la Policía Nacional i us assegureu que renoveu els certificats i coneixeu el vostre PIN.

És molt important tenir el PIN clar perquè les smart cards es bloquegen als 3 errors d'accés. Si es donessin aquests 3 accessos erronis caldria tornar a la Comissaria per renovar els certificats.

Lector de targetes

Necessitarem un lector de targetes que funcioni amb el DNIe. Aquest lector de targetes de Zoweetek, per exemple, sabem que és compatible amb el DNIe tant per a GNU/Linux com per a Windows i Mac.

Pot ser que el lector de smart cards no estigui habilitat. Per comprovar-ho en GNU/Linux podem fer:

$ pcsc_scan

Si el servei no està instal·lat, es pot fer amb:

$ sudo apt install pcsc-tools pcscd

Si continua sense funcionar pcsc_scan, es pot activar amb:

$ sudo systemctl enable pcscd
$ sudo systemctl start pcscd
$ pcsc_scan


Resolució client en Windows

Per a Windows l'executable descarregat a la web del DNIe realitza aquestes passes més o menys automàticament si no hi ha cap error.

Àrea de descàrregues a la pàgina oficial del DNIe per als diferents SO.


Resolució client en Ubuntu 22.04

A la pàgina de descàrregues del DNIe es mostra el paquet DEB per a versions fins la Ubuntu 21.10, però no la darrera Ubuntu 22.04 LTS. El problema, segons aquest post, és la versió snap (enlloc de la clàssica APT) de Firefox que Canonical fa servir des d'aquesta versió.

Per solventar-ho, podem desinstal·lar Firefox i instal·lar la versió Firefox ESR (Extended Support Release) més estable i que actualment és un estàndard d'estabilitat en distribucions com Debian.

Desinstal·lem el Firefox de snap (pot trigar una mica, mira de tancar totes les pantalles de Firefox primer):

# snap remove firefox

Afegim els repositoris de mozillateam i instal·lem firefox-esr:

# apt install software-properties-common -y
# add-apt-repository ppa:mozillateam/ppa
# apt install firefox-esr

Si es paralitza la instal·lació, tanca la finestra de Firefox que s'ha obert. Ens obriria un breu tutorial d'instal·lació dels certificats del DNIe, però de vegades no apareix i ja l'he copiat aquí a la següent secció.

Afegim un enllaç simbòlic per facilitar la comanda firefox:

# ln -s /usr/bin/firefox-esr /usr/bin/firefox

Ara ja podem seguir i instal·lar el package DEB de la web oficial del DNIe.

Caldrà que tinguem la màquina client i el navegador configurats adientment per poder utilitzar el DNIe:

Àrea de descàrregues a la pàgina oficial del DNIe per als diferents SO.

Bàsicament la instal·lació té aquestes passes:

  1. Instal·lar els drivers necessaris per accedir al lector de targetes. Normalment es fa d'aquesta manera i hauria de mostrar instruccions de com procedir amb les següents passes per completar la instal·lació:
    sudo dpkg -i arxiu.deb
    sudo apt install -f
  2. Configurar la llibreria PKCS#11 al Firefox que permeti introduir el PIN a l'accedir al DNIe. Normalment seria (revisar si coincideix amb les instruccions):
    Firefox > Paràmetres > Privadesa i seguretat > Certificats > Dispositius > Carrega
    /usr/lib/libpkcs11-dnie.so

    Si et surt l'error:

    No s'ha pogut afegir el mòdul

    Ves a la secció anterior de «Resolució amb Ubuntu 22.04» per solucionar-ho.

  3. Instal·lar el certificat arrel del DNIe al navegador local per tal que doni per bo l'accés al certificat del DNIe (no ve instal·lat per defecte):
    Firefox > Paràmetres > Privadesa i seguretat > Certificats > Mostra els certificats
  4. Comprova que tens instal·lat el certificat local del DNI anant als certificats del browser comprovant que teniu instal·lat el certificat de la «DIRECCION GENERAL DE LA POLICIA → AC DNIE». Si no hi és, importa'l clicant «Importa» i carrega l'arxiu:
    /usr/share/libpkcs11-dnie/AC RAIZ DNIE 2.crt

Per acabar de comprovar que el client funciona correctament, proveu verificar-ho a:


Configurar el servidor

Podem fer la pràctica en un servidor públic (Cloud) però també funciona en un servidor local (VM).

Versió local amb Vagrant

Podem fer una instal·lació en local, és la manera més senzilla.

Encara que els certificats del HTTPS siguin autofirmats, podem realitzar l'autenticació de client requerint el DNIE.

També és recomanable (no imprescindible) engegar una màquina Vagrant per aïllar la instal·lació i no enredar les configuracions de la màquina host. Crea el següent Vagrantfile en una carpeta per a la teva VM:

Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :
 
Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/jammy64"
  config.vm.hostname = "ubudnie"
  config.vm.network "private_network", ip: "192.168.33.10"
  config.vm.provider "virtualbox" do |vb|
    vb.name = "ubudnie"
    vb.gui = false
    vb.memory = "2048"
  end
end

Entrem a la VM amb:

$ vagrant ssh

Fem instal·lació bàsica d'Apache i PHP i activem SSL:

  1. Engegar la VM amb el Vagranfile anterior.
  2. Assignar-li una IP convenient (potser la que té ja us serveix).
  3. Instal·lar Apache2 i PHP.
  4. Activar mod_ssl d'Apache.
  5. Activar la site default-ssl.conf

Versió Cloud

La pràctica es pot muntar perfectament en la màquina local. Tot i això, perquè sigui operativa de veritat ha d'estar en un servidor públic i amb certificats vàlids.

Així, recomanem aquestes passes:

  1. Engegar una màquina al núvol (preferentment amb Ubuntu 22.04) accessible, al menys, als ports 80 i 443.
  2. Assignar-li una IP fixa (elastic IP en cas de AWS).
  3. Apuntar-hi un domini propi.
  4. Instal·lar Apache2 i PHP.
  5. Obrir els ports 80 i 443 (en cas que tingui firewall del proveidor o del propi SO com UFW).
  6. Activar mod_ssl d'Apache.
  7. Configurar certificats HTTPS correctament. La manera més senzilla és utilitzar Certbot de Let's Encrypt.

Si arribes aquí, ja tenim el server preparat per activar autenticació via DNIe al teu site.

Si estàs fent servir alguna mena de proxy invers com Cloudflare per protegir la teva site, pensa que els certificats de client no funcionaran. Segurament cal alguna configuració extra dins el teu proveïdor per redirigir les peticions de certificat de client.

Per tant, si tens configurat Cloudflare, habilita un subdomini (per exemeple dnie.domini.cat) i desactiva-hi el proxy invers per tal de que la comunicació sigui directa amb el server.


Documentació oficial (obsoleta!) per a Apache

Aquí s'explica la doble autenticació mútua entre servidor i client.

En aquesta documentació de INTECO disposem d'una guia per a configurar Apache amb els certificats necessaris per al DNIe, tot i que caldran algunes modificacions i actualitzacions:

  1. dia_2_taller_autenticacio_n_web_con_dnie.pdf descarrega't el PDF de bytes.cat.

ULL: aquesta configuració és obsoleta. Veure més avall l'actualitzada. Ho deixem com a documentació de les fonts.

El què es proposa a la documentació del DNIe és el què segueix.

dnie.conf
# Configuracio DNIe
# ULL: aquesta configuració és obsoleta. Veure més avall l'actualitzada
<Directory /var/www/sitednie/zona-privada>
    # Certificado raiz DNIe
    SSLCACertificateFile "/etc/ssl/certs/AC RAIZ DNIE 2.crt"
    # El cliente debe autenticarse obligatoriamente con el certificado
    SSLVerifyClient require
    # Nivel máximo de profundidad (según infraestructura actual, 2)
    SSLVerifyDepth  2
    # Exportar contenido de certificados y certificados (para poder usarlos con PHP)
    SSLOptions +StdEnvVars +ExportCertData
    Options Indexes FollowSymLinks MultiViews
    AllowOverride None
    Order allow,deny
    allow from all
</Directory>

La directiva més important és la que exigeix el certificat a l'usuari que intenta entrar en aquest Directory:

SSLVerifyClient require

Per saber més d'aquesta directiva d'Apache i les seves opcions consulta la doc oficial de SSLVerifyClient.

Canvis que cal fer:

  1. Treure la SSLCACertificateFile del nivell de Directory i posar-ho al nivell de VirtualHost. Apache ara no admet diferents certificats per directori (només un per VirtualHost).
  2. Canviar el Directory per Location , tal i com recomana fer-ho Apache2. Per exemple, creeu una zona reservada a Location /zona-privada.
  3. Pujar SSLOptions i Options també a nivell de VirtualHost (tot i que això no és imprescindible).


Troubleshooting

Un error que ens pot sorgir és que ens doni un 403 FORBIDDEN «Cannot perform Post-Handshake Authentication.» En aquest cas és per una incompatibilitat amb la implementació del protocol de seguretat amb el navegador. Per solventar-lo, només cal afegir aquesta directiva per forçar a fer servir el protocol TLSv1.2:

SSLProtocol -all +TLSv1.2


Configuració final Apache2

Com ja hem dit, podem realitzar aquesta instal·lació en local, tot i que és més interessant realitzar-la en un servidor públic.

Recorda que per configurar Apache2 en Debian Ubuntu hem de situar l'arxiu .conf a la carpeta sites-available i habilitar la site amb a2ensite.

Els certificats vàlids estan a la carpeta /etc/ssl/certs, en symbolic links. És interessant destacar que afegir l'arxiu a la carpeta i emprar la directiva SSLCACertificatePath no funcionarà, a la documentació s'explica que cal seguir un symbolic link amb un format de hash concret.

Necessitem descarregar els certificats arrel de l'Autoritat de Certificació (AC) del DNIe i configurar l'accés amb la directiva SSLCACertificateFile.

La configuració que emprarem serà mitjançant l'arxiu /etc/ssl/certs/ca-certificates.crt, on estan tots els certificats concatenats seguidament, i amb la directiva SSLCACertificateFile. El certificat de CA de la FNMT ja hi és per defecte, però no el del DNIe. Per tant, el descarreguem, descomprimim i annexem a ca-certificates.crt per tal d'habilitar-ho:

# cd /etc/ssl/certs
# wget https://www.dnielectronico.es/ZIP/ACRAIZ-DNIE2.zip
# unzip ACRAIZ-DNIE2.zip
# cat "AC RAIZ DNIE 2.crt" >> /etc/ssl/certs/ca-certificates.crt

Fixeu-vos que l'arxiu de certificats el configurem amb la directiva:

SSLCACertificateFile /etc/ssl/certs/ca-certificates.crt

Finalment, la configuració d'Apache per a validació del DNIe ens quedarà com segueix.

dnie.conf
<IfModule mod_ssl.c>
    <VirtualHost *:443>
        ServerAdmin webmaster@localhost
        #ServerName localhost
        DocumentRoot /var/www/html
        LogLevel debug
 
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
 
        SSLEngine on
        # Certificats per validar HTTPS (aquests son autofirmats)
        SSLCertificateFile  /etc/ssl/certs/ssl-cert-snakeoil.pem
        SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
 
        # Configuracio DNIe
        SSLCipherSuite HIGH:MEDIUM:-SSLv2
        SSLProtocol -all +TLSv1.2
 
        # Arxiu amb tots els certificats (cal afegir-hi el "AC RAIZ DNIE 2.crt")
        SSLCACertificateFile /etc/ssl/certs/ca-certificates.crt
 
        # Exportar contenido de certificados (para poder usarlos con PHP)
        SSLOptions +StdEnvVars +ExportCertData
        Options Indexes FollowSymLinks MultiViews
 
        <Location /zona-privada>
            SSLVerifyClient require
            #SSLVerifyClient optional_no_ca
            # Nivel máximo de profundidad (según infraestructura actual, 2)
            SSLVerifyDepth  2
            AllowOverride None
            Order allow,deny
            allow from all
        </Location>
 
        <FilesMatch "\.(cgi|shtml|phtml|php)$">
                SSLOptions +StdEnvVars
        </FilesMatch>
        <Directory /usr/lib/cgi-bin>
                SSLOptions +StdEnvVars
        </Directory>
 
    </VirtualHost>
</IfModule>


Accedint a les dades del certificat amb PHP

Abans de fer el codi, recordeu que cal instal·lar PHP al servidor:

$ sudo apt install libapache2-mod-php

A les variables supraglobals trobaràs les dades que el client transmet al servidor, en particular les de $_SERVER que comencen per SSL_…, com per exemple $_SERVER['SSL_CLIENT_S_DN']. Aquestes variables les aconseguim capturar dins el nostre llenguatge de programació (en aquest cas PHP) mitjançant la directiva d'Apache:

SSLOptions +StdEnvVars +ExportCertData

Si no està activada aquesta directiva no podrem veure les dades del client.

A aquesta web hi ha alguns exemples de com escanejar totes les variables supragrobals.

Les variables que podem fer servir son:

  1. $_SERVER['SSL_CLIENT_S_DN'] : les dades del DNIe (nom, cognoms, número del DNI, etc.). Caldrà fer algun parse extra per aïllar les variables adientment (per exemple, extreure el número del DNI).
  2. $_SERVER['SSL_CLIENT_S_DN_G'] : el nom de pila.
  3. $_SERVER['SSL_CLIENT_S_DN_S'] : el 1r cognom.
  4. $_SERVER['SSL_CLIENT_CERT_CHAIN_0'] : issuer certificate
  5. $_SERVER['SSL_CLIENT_CERT'] : client certificate

Amb aquest codi pode examinar totes les variables que ens arribien dins l'array supraglobal $_SERVER:

index.php
<h1>Portal DNIe</h1>
<?php
echo "Benvingut/da ".$_SERVER['SSL_CLIENT_S_DN_G'];
echo "<br><br>\n\n";
echo "\n<table>\n";
 
foreach( $_SERVER as $key => $val ) {
    echo "<tr>\n<td>$key</td><td>$val</td></tr>\n";
}
echo "\n</table>\n";
?>


Comprovació de revocació OCSP

Amb la comprovació OCSP el què fem és consultar la base de dades de la pròpia CA (autoritat certificadora, la seu del DNIe) per comprovar que el certificat que ens arriba no hagi estat revocat. És una important passa de seguretat.

El resultat de la consulta OCSP hauria de ser: good, revoked o unknown.

En aquestes pàgines tenim exemples de com fer una query OCSP (a ocsp.dnie.es) per conèixer l'estat del certificat utilitzat, ja que podria estar revocat:

  1. Wiki locolandia (còpia a archive.org)

La 1a funciona, però cal corregir un petit error en la línia de la query que es fa mitjançant OCSP. Investiga què és el què pot fallar (la manera més fàcil és activant els missatges de error d'Apache a php.ini i mostrant les variables intermitges. Mostra també la query openssl per veure què és el que fem executar a la shell i excecuta-la manualment en una shell a part per esbrinar quin problema ens estava succeint.


Altres certificats

Perquè ens funcioni amb diversos tipus de certificats com idCAT, hisenda, etc. podem fer servir l'arxiu amb tots els certificats agrupats que porta Apache, al qual afegirem el del DNIe, que per defecte no ve inclòs.

# cd /etc/ssl/certs
# cat AC_RAIZ_DNIE_2.crt >> ca-certificates.crt

I canviarem la línia de l'anterior arxiu de configuració on diu:

SSLCACertificateFile /etc/ssl/certs/AC_RAIZ_DNIE_2.crt

per:

SSLCACertificateFile /etc/ssl/certs/ca-certificates.crt