fleet-telemetry icon indicating copy to clipboard operation
fleet-telemetry copied to clipboard

introduce data connectors, used to validate if vin is allowed to connect

Open patrickdemers6 opened this issue 1 year ago • 2 comments

Description

Introduces the concept of data connectors which can use externally provided data to enhance server functionality.

The data connectors supported so far:

  • Redis
  • HTTP
  • File
  • gRPC

To start, data connectors are used for one capability: checking if a vin is allowed before allowing it to send data to the server. This prevents a compromised vehicle from being able to send data to a random fleet-telemetry server.

Please select all options that apply to this change:

  • [X] New feature (non-breaking change which adds functionality)
  • [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • [ ] Bug fix (non-breaking change which fixes an issue)
  • [X] Documentation update

Checklist:

Confirm you have completed the following steps:

  • [X] My code follows the style of this project.
  • [X] I have performed a self-review of my code.
  • [X] I have made corresponding updates to the documentation.
  • [X] I have added/updated unit tests to cover my changes.
  • [X] I have added/updated integration tests to cover my changes.

Other Notes

  • To generate gRPC service protos, additional dependencies are needed. The generation of protos is now handled by a Docker container.
  • General dependency upgrades.
    • Ginkgo no longer supports SlowSpecThreshold so it has been removed.

patrickdemers6 avatar Jun 10 '24 04:06 patrickdemers6

I'd like to reject connection before web socket is established but am not having success. Getting that working can be a followup if someone wants to work on it.

patrickdemers6 avatar Jun 11 '24 23:06 patrickdemers6

I'd like to reject connection before web socket is established but am not having success. Getting that working can be a followup if someone wants to work on it.

I have similar code in my server, for what it's worth I can give some information for any who may be looking at this PR or a follow up later. Vehicles connect to a telemetry server and present a client certificate. The client certificate includes the VIN in the subject DN. It is therefore possible to terminate the connection based on a VIN list during or at some point after client certificate validation.

My custom telemetry server uses nginx for client certificate validation and TLS termination, see this configuration snippet.

    server {
        .... omitting unrelated ...
        
        # SSL client verification settings for WebSocket
        ssl_client_certificate /certs/tesla-ca.pem;
        ssl_verify_client on;
        
        # WebSocket path(s) only
        location ~ ^/(ws)?$ {
        
            .... omitting unrelated ...
            
            proxy_set_header X-SSL-Client-DN $ssl_client_s_dn;
            proxy_pass http://app_servers;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
            proxy_cache_bypass $http_upgrade;
        }
    }

I am doing something similar in my server to check VIN, but for my purpose it is to use certificate validation to check vehicles only send data for their own VIN. I check that after the websocket is established, I compare the VIN in the header (which comes from the client cert) to what's in the telemetry payload to make sure they are a match.

#[get("/")]
pub async fn websocket_handler(
    req: HttpRequest,
    stream: web::Payload,
    pool: web::Data<PgPool>,
) -> Result<HttpResponse, Error> {
    // Extract client certificate DN from header
    let client_dn = req
        .headers()
        .get("X-SSL-Client-DN")
        .and_then(|h| h.to_str().ok())
        .and_then(extract_vin_from_dn)
        .ok_or_else(|| {
            actix_web::error::ErrorUnauthorized("Invalid or missing client certificate")
        })?;

    let (res, session, msg_stream) = actix_ws::handle(&req, stream)?;

    // Spawn websocket handler with the authorized VIN
    actix_web::rt::spawn(handle_websocket(session, msg_stream, pool, client_dn));

    Ok(res)
}

It seems to me like it would be possible to check the headers right before the websocket is established. Not sure if this helps anyone looking to work on this PR, and I am not sure it's applicable as I did not understand all the code in this PR. I also think it would be possible to terminate the connection even earlier than this, presumably during client certificate validation rather than after that during the HTTP part before the websocket is established.

sjmiller609 avatar Dec 07 '24 00:12 sjmiller609