basic auth en otros servicios web usando las passwords de Django directamente

Estos dias tenia que dar acceso a varios servicios web en desarrollo y parecia razonable, para este caso de uso, emplear los usuarios y contraseñas que ya habiamos definido en el backend de django. Pero claro, cuando uno tiene todo en desarrollo la proteccion tipica es basicAuth, la de la cabecera Authorization que gestiona el navegador, porque en los formularios todo puede estar cambiando. ¿Cómo enlazamos el nginx, que es el que esta dando esta autorizacion, con las contraseñas que ya estan en una base de datos de Django? Para colmo, en postgresql.

No queda mas remedio, creo, que tirar de modulos externos. Se me ocurrian dos caminos:

  • Con ngx_http_auth_request_module transformar la peticion de autorizacion en una subrequest, y verificarla bien con algun servicio web activado a proposito, bien con el modulo ngx_postgres
  • Con PAM, activar el http_auth_pam_module y emplear una extension de PAM pensada para postgresql, la pam-pgsql

Lo mas estandar parece ser lo de activar un servicio web. Lo del modulo ngx_postgres tambien es majo, hay quien hasta se ha montado APIs con solo ese modulo y algunos rewrites. Pero bueno, el PAM tampoco esta mal, es lo tipico cuando se trata de autenticar contra LDAP o similar, asi que es de esperar que funcione decentemente.

Sorpresa: efectivamente nginx no tiene ningun problema para llamar al servicio pam, pero el pam_pgsql, que esta mantenido irregularmente, no coje bien la configuracion y cae al default. No es buena cosa porque en algun sitio hay que calcular el PBKDF2, y se supone que podemos definirlo en una funcion de sql, pasar como parametros usuario y contraseña y que nos devuelva un booleano. No funciona. Pero mirando el codigo se ve que en el default espera que le devolvamos la contraseña, asi que un CASE nos soluciona la papeleta

select CASE WHEN coincide(%u,%p) THEN %p ELSE 'nomatch' END

Otro problemilla es que postgresql todavia no tiene, que yo sepa, el PBKDF2, y hay que definirlo como funcion ad-hoc. Afortunadamete alguien lo puso en stackoverflow.

Con esto, el /etc/pam_pgsql.conf queda:

connect = dbname=... user=...  password=...  host=... 
pw_type=function
auth_query = select CASE WHEN concat(substring(password from 1 for 33),encode(PBKDF2(substring(password from 21 for 12)::bytea,%p,36000,32,'sha256'),'base64'))=password THEN %p ELSE 'INVALIDHASH' END from auth_user where username = %u
y ya tenemos nuestra autentificacion rulando. El “function” es lo que no funciona, porque habria tenido que pillar un THEN ‘t’ ELSE ‘f’, con un boolean. Seguramente en alguna actualizacion del package se solucionará el bug y dejará de funcionar pero, bueno, la basic auth es mas bien para proteger cosas que esten en periodo de desarrollo.
Otro inconveniente, peor, de esta solucion es que la contraseña circula por todo el proceso de consulta sin hashear. No tiene solucion facil porque la salt esta en la BBDD. En teoria la orden de conexion permite pasar un sslmode a libpq.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.