Subscribe to receive notifications of new posts:

Pingora de código abierto: nuestro marco Rust para el desarrollo de servicios de red programables

02/28/2024

7 min read
Open sourcing Pingora: our Rust framework for building programmable network services

Hoy estamos orgullosos de lanzar Pingora de código abierto, el marco Rust que hemos estado utilizando para desarrollar los servicios que impulsan una parte significativa del tráfico en Cloudflare. Pingora se ofrece bajo la licencia Apache versión 2.0.

Como mencionamos en nuestra publicación anterior del blog, Pingora es un marco multiproceso asíncrono de Rust que nos ayuda a construir servicios de proxy HTTP. Desde nuestra última publicación del blog, Pingora ha gestionado casi un cuatrillón de solicitudes de Internet en nuestra red global.

Publicamos Pingora en código abierto para ayudar a mejorar Internet y su seguridad más allá de nuestra propia infraestructura. Queremos proporcionar herramientas, ideas e inspiración a nuestros clientes, nuestros usuarios y a otras personas para que creen su propia infraestructura de Internet utilizando un marco seguro para la memoria. Disponer de un marco de este tipo es crucial dada la creciente concienciación sobre la importancia de la seguridad de la memoria en todo el sector y en el seno del gobierno de Estados Unidos. Con este objetivo común, colaboramos con Internet Security Research Group (ISRG) en el marco de su proyecto Prossimo para ayudar a hacer avanzar la adopción de Pingora en la infraestructura más crítica de Internet.

En nuestra publicación anterior del blog, comentamos por qué y cómo desarrollamos Pingora. En esta, hablaremos de por qué y cómo podrías utilizar Pingora.

Pingora ofrece elementos esenciales no solo para los proxies, sino también para los clientes y los servidores. Junto con estos componentes, también proporcionamos algunas bibliotecas de utilidades que implementan lógica común, como el recuento de eventos, la gestión de errores y el almacenamiento en caché.

Qué ofrece la solución

Pingora proporciona bibliotecas y API para desarrollar servicios sobre HTTP/1 y HTTP/2, TLS o simplemente TCP/UDP. Como proxy, admite HTTP/1 y HTTP/2 de extremo a extremo, gRPC y redireccionamiento de websockets mediante proxy. (La compatibilidad con HTTP/3 está en la hoja de ruta). También incluye estrategias personalizables de equilibrio de carga y de conmutación por error. En cuanto a la conformidad y la seguridad, es compatible con las bibliotecas OpenSSL y BoringSSL habitualmente utilizadas, que cumplen la normativa FIPS y ofrecen criptografía poscuántica.

Además de estas funciones, Pingora ofrece filtros y devoluciones de llamada para permitir a sus usuarios personalizar por completo cómo el servicio debe procesar, transformar y reenviar las solicitudes. Estas API resultan especialmente familiares para los usuarios de OpenResty y NGINX, ya que muchas se relacionan intuitivamente con las devoluciones de llamada "*_by_lua" de OpenResty.

Desde el punto de vista operativo, Pingora proporciona reinicios sin tiempo de inactividad para actualizarse sin interrumpir ni una sola solicitud entrante. Syslog, Prometheus, Sentry, OpenTelemetry y otras herramientas de observabilidad imprescindibles también se integran fácilmente con Pingora.

Quién puede beneficiarse de Pingora

Deberías considerar Pingora si:

La seguridad es tu máxima prioridad: Pingora es una alternativa más segura en cuanto a memoria para los servicios escritos en C/C++. Aunque algunos podrían aducir acerca de la seguridad de la memoria en los lenguajes de programación, nuestra experiencia práctica indica que somos mucho menos propensos a cometer errores de codificación que provoquen problemas de seguridad de la memoria. Además, puesto que dedicamos menos tiempo a resolver estos problemas, somos más productivos implementando nuevas funciones.

Tu servicio depende del rendimiento: Pingora es una solución rápida y eficiente. Como explicamos en nuestra publicación anterior del blog, hemos ahorrado muchos recursos de CPU y memoria gracias a la arquitectura multiproceso de Pingora. El ahorro de tiempo y de recursos podría resultar interesante en el caso de cargas de trabajo para las que el coste o la velocidad del sistema (o ambos) sean importantes.

Tu servicio requiere una gran personalización: las API que proporciona el marco de proxy de Pingora son muy programables. Para los usuarios que deseen crear una puerta de enlace o un equilibrador de carga personalizados y avanzados, Pingora ofrece formas eficaces pero sencillas de implementarlo. Proporcionamos ejemplos en la siguiente sección.

Desarrollemos un equilibrador de carga

Exploremos la API programable de Pingora desarrollando un equilibrador de carga sencillo. El equilibrador de carga seleccionará entre https://1.1.1.1/ y https://1.0.0.1/ para que sea un proceso ascendente round-robin.

En primer lugar, crearemos un proxy HTTP en blanco.

pub struct LB();

#[async_trait]
impl ProxyHttp for LB {
    async fn upstream_peer(...) -> Result<Box<HttpPeer>> {
        todo!()
    }
}

Cualquier objeto que implemente el rasgo ProxyHttp (similar al concepto de una interfaz en C++ o Java) es un proxy HTTP. El único método necesario es upstream_peer(), al que se llama para cada solicitud. Esta función debe devolver un HttpPeer que contenga la dirección IP de origen a la que conectarse y cómo conectarse.

A continuación, implementemos la selección round robin. El marco de Pingora ya proporciona al LoadBalancer algoritmos de selección comunes, como round robin y hash, así que vamos a utilizarlo. Si el caso de uso requiere una lógica de selección de servidor más sofisticada o personalizada, los usuarios pueden simplemente implementarla ellos mismos en esta función.

pub struct LB(Arc<LoadBalancer<RoundRobin>>);

#[async_trait]
impl ProxyHttp for LB {
    async fn upstream_peer(...) -> Result<Box<HttpPeer>> {
        let upstream = self.0
            .select(b"", 256) // hash doesn't matter for round robin
            .unwrap();

        // Set SNI to one.one.one.one
        let peer = Box::new(HttpPeer::new(upstream, true, "one.one.one.one".to_string()));
        Ok(peer)
    }
}

Como nos estamos conectando a un servidor HTTPS, también es necesario configurar la SNI. Si es necesario, los certificados, los tiempos de espera y otras opciones de conexión también se pueden configurar aquí en el objeto HttpPeer.

Por último, pongamos el servicio en práctica. En este ejemplo, codificamos las direcciones IP del servidor de origen. En las cargas de trabajo reales, las direcciones IP del servidor de origen también se pueden descubrir dinámicamente al llamar a upstream_peer() o en segundo plano. Una vez creado el servicio, simplemente le indicamos al servicio LB que esté a la escucha de 127.0.0.1:6188. Al final hemos creamos un servidor de Pingora, y el servidor será el proceso que ejecute el servicio de equilibrio de carga.

fn main() {
    let mut upstreams = LoadBalancer::try_from_iter(["1.1.1.1:443", "1.0.0.1:443"]).unwrap();

    let mut lb = pingora_proxy::http_proxy_service(&my_server.configuration, LB(upstreams));
    lb.add_tcp("127.0.0.1:6188");

    let mut my_server = Server::new(None).unwrap();
    my_server.add_service(lb);
    my_server.run_forever();
}

Probémoslo:

curl 127.0.0.1:6188 -svo /dev/null
> GET / HTTP/1.1
> Host: 127.0.0.1:6188
> User-Agent: curl/7.88.1
> Accept: */*
> 
< HTTP/1.1 403 Forbidden

Podemos ver que el proxy funciona, pero el servidor de origen nos rechaza con un error 403. Esto se debe a que nuestro servicio simplemente redirecciona el encabezado del host, 127.0.0.1:6188, establecido por curl, lo que no le gusta al servidor de origen. ¿Cómo hacemos que el proxy corrija eso? Para ello, podemos simplemente añadir otro filtro llamado upstream_request_filter. Este filtro se ejecuta en cada solicitud después de que se conecte el servidor de origen y antes de que se envíe cualquier solicitud HTTP. Podemos añadir, eliminar o cambiar los encabezados de solicitud http en este filtro.

async fn upstream_request_filter(…, upstream_request: &mut RequestHeader, …) -> Result<()> {
    upstream_request.insert_header("Host", "one.one.one.one")
}

Intentémoslo de nuevo:

curl 127.0.0.1:6188 -svo /dev/null
< HTTP/1.1 200 OK

¡Esta vez funciona! Encontrarás el ejemplo completo aquí.

A continuación se muestra un diagrama muy sencillo de cómo fluye esta solicitud a través de la devolución de llamada y el filtro que utilizamos en este ejemplo. El marco de proxy de Pingora actualmente proporciona más filtros y devoluciones de llamada en distintas etapas de una solicitud para permitir a los usuarios modificar, rechazar, enrutar y/o registrar la solicitud (y la respuesta).

En segundo plano, el marco de proxy de Pingora se encarga de la agrupación de conexiones, los protocolos de enlace TLS, la lectura, la escritura, el análisis de las solicitudes y otras tareas habituales del proxy para que los usuarios puedan centrarse en la lógica que les interesa.

Código abierto, presente y futuro

Pingora es una biblioteca y un conjunto de herramientas, no un binario ejecutable. En otras palabras, Pingora es el motor que impulsa un coche, no el coche en sí. Aunque Pingora está lista para producción para su uso en el sector, entendemos que mucha gente quiere un servicio web listo para usar con todo incluido, con opciones de configuración que apenas requieran código o que no lo requieran. Desarrollar esa aplicación sobre Pingora será el objetivo de nuestra colaboración con ISRG para ampliar el alcance de Pingora. No te pierdas las próximas novedades sobre ese proyecto.

Otras advertencias a tener en cuenta:

  • Hoy en día, la estabilidad de las API no está garantizada. Aunque intentaremos minimizar la frecuencia con la que realicemos cambios importantes, nos reservamos el derecho de añadir, eliminar o cambiar componentes como los filtros de solicitudes y respuestas a medida que evolucione la biblioteca, especialmente durante este periodo previo a la versión 1.0.
  • La compatibilidad con sistemas operativos no basados en Unix no está actualmente en la hoja de ruta. No tenemos planes inmediatos para ofrecer compatibilidad con estos sistemas, aunque esto podría cambiar en el futuro.

Cómo contribuir

No dudes en plantear informes de errores, problemas de la documentación o solicitudes de funciones en nuestro rastreador de problemas de GitHub. Antes de abrir una solicitud de extracción, te recomendamos encarecidamente que eches un vistazo a nuestra guía de contribuciones.

Conclusión

En esta publicación, hemos anunciado la publicación del código abierto de nuestro marco Pingora. Demostramos que las entidades y la infraestructura de Internet pueden beneficiarse de la seguridad, el rendimiento y la personalización de Pingora. También demostramos lo fácil que es utilizar Pingora y lo personalizable que es.

Tanto si desarrollas servicios web de producción como si experimentas con tecnologías de red, esperamos que Pindora te resulte útil. Ha sido un largo recorrido, pero compartir este proyecto con la comunidad de código abierto ha sido nuestro objetivo desde el principio. Nos gustaría dar las gracias a la comunidad de Rust, ya que Pingora se ha desarrollado con muchos paquetes Rust de código abierto que nos han sido de gran utilidad. Migrar a una red de Internet segura para la memoria puede parecer un camino imposible, pero esperamos que te unas a nosotros.

We protect entire corporate networks, help customers build Internet-scale applications efficiently, accelerate any website or Internet application, ward off DDoS attacks, keep hackers at bay, and can help you on your journey to Zero Trust.

Visit 1.1.1.1 from any device to get started with our free app that makes your Internet faster and safer.

To learn more about our mission to help build a better Internet, start here. If you're looking for a new career direction, check out our open positions.
Developer Platform (ES)Developers (ES)Rust (ES)Open Source (ES)Internet Performance (ES)

Follow on X

Cloudflare|@cloudflare

Related posts

May 22, 2024 1:00 PM

AI Gateway, interfaz unificada para gestionar y escalar tus cargas de trabajo de IA generativa, ya disponible de forma general

AI Gateway es una plataforma de operaciones de IA que mejora la velocidad, la fiabilidad y la observabilidad de tus aplicaciones de IA. Con una sola línea de código, puedes acceder a funciones eficaces como la limitación de velocidad, el almacenamiento en caché personalizado, los registros...

April 05, 2024 1:01 PM

Disponibilidad general de la API Browser Rendering, implementación de Cloudflare Snippets, SWR y, por último, Workers for Platforms, que ya está al alcance de todos los usuarios

La API Browser Rendering ya está disponible para todos los clientes de pago de Workers y hemos mejorado la gestión de sesiones...

April 03, 2024 1:30 PM

R2 añade notificaciones de eventos, compatibilidad para migraciones desde Google Cloud Storage y un nivel de almacenamiento de acceso ocasional

Nos complace anunciar tres nuevas funciones de Cloudflare R2: notificaciones de eventos, compatibilidad para migraciones desde Google Cloud Storage y un nivel de almacenamiento de acceso ocasional...

April 02, 2024 1:01 PM

Optimización de Workers AI: disponibilidad general y nuevas funciones

Hoy nos complace anunciar una serie de novedades como la disponibilidad general de Workers AI, la plataforma de inferencia de Cloudflare, y la compatibilidad de modelos ajustados con los protocolos LoRA y las implementaciones en un solo clic desde HuggingFace. Cloudflare Workers ya es compatible con...