Search code examples
dockersslrusthttpsactix-web

Rust Actix TLS error -> stream error: request parse error: invalid Header provided


I try to make a Rust Actix TLS web server.

Project layout

.
├── .env
├── Cargo.lock
├── Cargo.toml
├── certs
│   ├── cert.pem
│   └── key.pem
└── src
    ├── api_error.rs
    ├── handlers.rs
    └── main.rs

.env

RUST_LOG=debug
PORT=443
CERTIFICATE_PATH=certs/cert.pem
PRIVATE_KEY_PATH=certs/key.pem

Cargo.toml

[package]
name = "https-test"
version = "0.1.0"
edition = "2021"

[dependencies]
actix-web = { version = "4.3.1", features = ["openssl"] }
openssl = { version = "0.10.54", features = ["v110"], optional = true }
anyhow = "1.0.72"
log = "0.4.19"
env_logger = "0.10.0"
dotenvy = "0.15.7"

main.rs

mod api_error;
mod handlers;

use anyhow::{anyhow, Result};
use log::info;

#[actix_web::main]
async fn main() -> Result<()> {
    dotenvy::dotenv()?;
    env_logger::init();
    info!("Starting server");

    let port = std::env::var("PORT")?.parse::<u16>()?;

    let server = actix_web::HttpServer::new(|| {
        actix_web::App::new()
            .wrap(actix_web::middleware::Logger::default())
            .service(actix_web::web::scope("/api/v1").configure(handlers::config_app))
    });

    let server = server.bind_openssl(("0.0.0.0", port), {
        info!("Using TLS");
        let mut builder = openssl::ssl::SslAcceptor::mozilla_intermediate(openssl::ssl::SslMethod::tls())
                .unwrap();
        let private_key_path = std::env::var("PRIVATE_KEY_PATH")?;
        let certificate_path = std::env::var("CERTIFICATE_PATH")?;
        builder
            .set_private_key_file(private_key_path, openssl::ssl::SslFiletype::PEM)
            .unwrap();
        builder
            .set_certificate_chain_file(certificate_path)
            .unwrap();
        builder
    })?;

    server.run().await.map_err(|e| anyhow!(e))
}

handlers.rs

use crate::api_error::ApiError;

pub fn config_app(cfg: &mut actix_web::web::ServiceConfig) {
    cfg.service(actix_web::web::resource("/").route(actix_web::web::get().to(index)));
}

async fn index() -> Result<actix_web::HttpResponse, ApiError> {
    log::debug!("Hitting `index`");
    Ok(actix_web::HttpResponse::Ok().body("Hello world !"))
}

Note: the certificate is self-signed with a CA that I generated. I put the root CA in my system's trusted CA so I don't get the "Untrusted certificate" error in the browser every time.

When running the server, I get the expected logs:

$ cargo run --release
[INFO  https-test] Starting server
[INFO  actix_server::builder] starting 10 workers
[INFO  actix_server::server] Actix runtime found; starting in Actix runtime

Then on the browser, I go to https://localhost/, my browser can't connect to the server, in the logs I get:

[ERROR actix_http::h1::dispatcher] stream error: request parse error: invalid Header provided
[ERROR actix_http::h1::dispatcher] stream error: request parse error: invalid Header provided
[ERROR actix_http::h1::dispatcher] stream error: request parse error: invalid Header provided
[ERROR actix_http::h1::dispatcher] stream error: request parse error: invalid Header provided
[ERROR actix_http::h1::dispatcher] stream error: request parse error: invalid Header provided
[ERROR actix_http::h1::dispatcher] stream error: request parse error: invalid Header provided

Solution

  • The issue seems to be related to the HTTP/1.1 parser detecting an invalid header in the request. This can happen due to various reasons, such as:

    1. Misconfigured SSL/TLS settings
    2. Incorrect client behavior (although browsers are unlikely to send malformed headers)
    3. Interference from some proxy or firewall

    Let's explore some troubleshooting steps:

    Check SSL/TLS Configuration

    Make sure your SSL/TLS certificates are properly loaded, and the path is correct. Double-check if you can use openssl CLI to inspect them:

    openssl x509 -in certs/cert.pem -text -noout
    openssl rsa -check -in certs/key.pem
    

    Validate Environment Variables

    Ensure that CERTIFICATE_PATH and PRIVATE_KEY_PATH environment variables are correctly loaded and pointing to the correct paths.

    Examine Raw HTTP Traffic

    You can use a tool like tcpdump or Wireshark to capture the raw packets between the client and server. This can provide further clues about what's wrong with the headers:

    sudo tcpdump -i lo0 'port 443' -w capture.pcap
    

    Try With a Different Client

    Instead of a browser, try using curl to see if the error still persists. Make sure to use the -k flag to accept the self-signed certificate.

    curl -k https://localhost:443/api/v1/
    

    Increase Log Verbosity

    Modify your .env file or directly set RUST_LOG to trace to get even more detailed logs. This can provide clues into what part of the request is causing the error.

    Test Without SSL/TLS

    Try running the server without SSL/TLS to see if the issue is actually related to SSL/TLS or something else. If it works fine without SSL, it's likely an issue with your SSL configuration.

    Let me know which step gives you more information or if you have further questions.