introduce data connectors, used to validate if vin is allowed to connect
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
SlowSpecThresholdso it has been removed.
- Ginkgo no longer supports
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'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.