En parte 1, hicimos un recorrido rápido por S/MIME, observando la firma y el cifrado de nuestros flujos de mensajes a través de una gama de clientes de correo. Parte 2 nos llevó a través de una simple herramienta de línea de comandos para firmar y cifrar correos electrónicos, y luego enviarlos a través de SparkPost. La parte 3 mostró cómo inyectar flujos de correo seguros en plataformas locales como Port25 PowerMTA y Momentum.
En esta serie, hemos visto que incluir una firma S/MIME es bastante sencillo. Enviar correos electrónicos cifrados con S/MIME es más complejo porque necesitas obtener las claves públicas de los destinatarios. Es una cosa cuando usas un cliente de correo para humanos como Thunderbird, pero ¿cómo puede funcionar eso con flujos de correo generados por aplicaciones?
Pero espera, hay otra manera de entrar en Mordor para obtener esas claves. Tu servicio puede invitar a tus clientes (vía correo, por supuesto) a enviarte de vuelta un correo firmado a una dirección de servicio al cliente conocida. Usando los poderes mágicos de los webhooks de SparkPost Inbound Relay, extraeremos y almacenaremos esa clave pública para que la uses.
Podemos resumir esto en un caso de uso simple:
Como destinatario de mensajes, proporciono a tu servicio mi firma de correo electrónico personal a través de correo, para que en el futuro, los correos puedan enviarse a mí en forma cifrada S/MIME.
De esto, derivemos algunos requisitos más detallados:
Necesitamos un servicio de correo electrónico entrante siempre activo y confiable para recibir esos correos firmados.
No debe haber requisitos especiales sobre el formato del correo, más allá de que debe llevar una firma S/MIME.
Debido a que cualquiera puede intentar enviar un correo a este servicio, debe estar diseñado defensivamente, por ejemplo, para rechazar mensajes "suplantados" de actores malintencionados. Necesitará haber varias capas de verificación.
Si todo está en orden, el servicio almacenará el certificado en un archivo, usando el conocido formato de privacidad de texto plano Privacy-Enhanced Mail (PEM).
Hay algunos requisitos no funcionales:
Los servicios de webhook entre máquinas pueden ser difíciles de observar solo a partir de las respuestas sobre lo que está sucediendo internamente. El servicio debe proporcionar extensos registros de aplicación legibles por humanos. En particular, el análisis y verificación del certificado deben ser registrados.
Agregamos casos de prueba para los internos de la aplicación, utilizando el agradable Pytest framework, y ejecutamos esas pruebas automáticamente al realizar el check-in utilizando la integración de Travis CI con GitHub.
¡Bien! – ¡comencemos!
1. Resumen de la solución
Así es como se verá la solución general.
2. Instalación, configuración y inicio de la aplicación web
Empezaremos con esta parte, para que esté completamente probada antes de realizar la conexión de los webhooks de relé entrantes.
La aplicación web está incluida en el mismo proyecto de GitHub que las partes 1 a 3, así que si has seguido esas partes, ya la tienes. Aquí están los nuevos elementos:
Programa readSMIMEsig.py – leer un correo electrónico y analizar los certificados intermedios y de usuario.
Programa webapp.py – sencilla aplicación web compatible con Flask para uso con los webhooks de SparkPost Inbound Relay.
webapp.ini – archivo de configuración para lo anterior. Un archivo de configuración permite que los mismos valores se pasen fácilmente tanto a aplicaciones de línea de comandos como a aplicaciones web.
Necesitas asegurarte de que tu host tenga el número de puerto TCP correcto abierto para solicitudes entrantes del mundo exterior para que SparkPost pueda enviar mensajes a tu aplicación. Si estás alojado en AWS EC2, por ejemplo, necesitarás configurar el Grupo de Seguridad de tu instancia.
Las instrucciones para configurar e iniciar la aplicación web se dan aquí - es bastante fácil. Para comprobar que tu aplicación está funcionando y es accesible desde el mundo exterior, puedes enviar solicitudes (en blanco) desde otro host utilizando curl, por ejemplo:
curl -X POST https://app.trymsys.net:8855/
Deberías ver una respuesta como:
{"message":"Unknown Content-Type in request headers"}
¡Esto es algo bueno! – ¡tu aplicación está en funcionamiento!
En webapp.log en tu host, verás una salida similar a esta:
2019-01-15 00:11:07,575,root,INFO,Request from 38.96.5.10,scheme=https,path=/ 2019-01-15 00:11:07,575,root,INFO,| len(headers)=3,len(body)=None 2019-01-15 00:11:07,575,root,INFO,| Unknown Content-Type: None
Para ayudarte a trabajar con datos reales en tu aplicación de inmediato, puedes importar esta solicitud de Postman específica desde el repositorio del proyecto. Esto simula lo que tu cuenta de SparkPost estará haciendo, es decir, envía un POST https que contiene un correo electrónico con un certificado específico y válido (perteneciente a una de mis cuentas de prueba) a tu aplicación.
Solo necesitas cambiar la dirección de destino en la solicitud (en el cuadro gris de arriba) para que coincida con tu instalación. Si cambiaste el valor del token en webapp.ini, ajusta el valor de cabecera en Postman para que coincida.
Si tu aplicación está funcionando, verás una respuesta de “200 OK” de vuelta en Postman. Tu archivo de registro webapp.log de host contendrá una salida como esta:
2019-01-15 00:11:48,554,root,INFO,Request from 38.96.5.10,scheme=https,path=/ 2019-01-15 00:11:48,554,root,INFO,| len(headers)=10,len(body)=14778 2019-01-15 00:11:48,555,root,INFO,| msg_from=bob.lumreeker@gmail.com,rcpt_to=secureme@inbound.thetucks.com,len(email_rfc822)=9223 2019-01-15 00:11:48,599,root,INFO,| from=bob.lumreeker@gmail.com,DKIM passed 2019-01-15 00:11:48,600,root,INFO,| content-type=multipart/signed; protocol="application/pkcs7-signature"; micalg=sha-256; boundary="------------ms010908020707040304020406",content-description=None 2019-01-15 00:11:48,600,root,INFO,| content-type=text/plain; charset=utf-8; format=flowed,content-description=None 2019-01-15 00:11:48,600,root,INFO,| content-type=application/pkcs7-signature; name="smime.p7s",content-description=S/MIME Cryptographic Signature 2019-01-15 00:11:48,600,root,INFO,| filename=smime.p7s,bytes=3998 2019-01-15 00:11:48,601,root,INFO,| Certificate: subject email_address=['bob.lumreeker@gmail.com'],not_valid_before=2018-10-03 00:00:00,not_valid_after=2019-10-03 23:59:59,hash_algorithm=sha256,key_size=2048 bytes, issuer={'countryName': 'GB', 'stateOrProvinceName': 'Greater Manchester', 'localityName': 'Salford', 'organizationName': 'COMODO CA Limited', 'commonName': 'COMODO RSA Client Authentication and Secure Email CA'} 2019-01-15 00:11:48,602,root,INFO,| Certificate: subject email_address=[],not_valid_before=2013-01-10 00:00:00,not_valid_after=2028-01-09 23:59:59,hash_algorithm=sha384,key_size=2048 bytes, issuer={'countryName': 'GB', 'stateOrProvinceName': 'Greater Manchester', 'localityName': 'Salford', 'organizationName': 'COMODO CA Limited', 'commonName': 'COMODO RSA Certification Authority'} 2019-01-15 00:11:48,616,root,INFO,| written file ./bob.lumreeker@gmail.com.crt,bytes=1870,ok=True
Para una rápida verificación de cordura, busca la última línea – si dice “archivo escrito”, entonces estás bien. El resto de esto muestra el chequeo DKIM y el proceso de validación del certificado.
3. Configuración de los webhooks de relé entrante de SparkPost
Primero, seleccionamos un dominio para usar como nuestra dirección de mensajes entrantes – aquí, será inbound.thetucks.com. Configura tu dominio siguiendo esta guía. Aquí están los pasos que utilicé en detalle:
3.1 Agregar Registros MX
Necesitarás acceso a tu cuenta de proveedor de servicios de Internet específico. Una vez hecho, puedes verificarlos con dig – aquí está el comando para mi dominio.
dig +short MX inbound.thetucks.com
Deberías ver:
10 rx3.sparkpostmail.com. 10 rx1.sparkpostmail.com. 10 rx2.sparkpostmail.com.
3.2 Crear un Dominio Entrante
Usa la colección de API de Postman de SparkPost, seleccionando la llamada Inbound Domains / Create... El cuerpo de la solicitud POST contiene tu dominio, por ejemplo:
{ "domain": "inbound.thetucks.com" }
3.3 Crear un Webhook de Relé
Crea un webhook de relé entrante usando la llamada correspondiente de Postman. El cuerpo del mensaje en mi caso contiene:
{ "name": "Webhook de Colección de Certificados", "target": "https://app.trymsys.net:8855/", "auth_token": "t0p s3cr3t t0k3n", "match": { "protocol": "SMTP", "domain": "inbound.thetucks.com" } }
Como se mencionó anteriormente, recomiendo configurar un auth_token con tu propio valor secreto, como se configura en el archivo webapp.ini en tu host.
Tu valor de “target” necesita coincidir con la dirección de tu host y el puerto TCP donde alojarás la aplicación web.
Tu valor de “domain” necesita coincidir con tus registros MX configurados en el paso 1.
¡Eso es todo! La plomería está hecha. Ahora deberías poder enviar certificados a tu dirección de entrada; serán procesados y aparecerán en tu host de aplicación web – en este caso, un archivo llamado bob.lumreeker@gmail.com.crt.
Ahora puedes enviar correos electrónicos cifrados a Bob, usando las herramientas descritas en las partes 2 & 3 de esta serie.
Puedes examinar el contenido de un certificado usando:
openssl x509 -inform PEM -in bob.lumreeker\@gmail.com.crt -text -noout
4. Internos: Verificación de DKIM, validación de certificados
La aplicación verifica que los correos electrónicos recibidos tengan DKIM válido y verifica que los certificados sean válidos, como se describe aquí. También hay notas de implementación allí, e ideas para trabajar más adelante.
Resumiendo...
Hemos visto cómo se pueden reunir fácilmente las claves públicas de los destinatarios utilizando un correo electrónico a una dirección de webhooks de relé entrante. Una vez hecho esto, esos destinatarios pueden recibir sus mensajes en forma cifrada S/MIME.
¡Eso es todo por ahora! ¡Feliz envío.