From 36a187d96e916d22371e9ab64ee709ae778bd887 Mon Sep 17 00:00:00 2001
From: Dutchie <54616262+dutchie032@users.noreply.github.com>
Date: Sat, 28 Sep 2024 08:30:11 +0200
Subject: [PATCH] Add API Key Authentication (#269)
Added API Key authentication.
Due to a recurring question about authentication of clients I've implemented a Interceptor layer to the tonic server to check all calls for valid api keys.
example config:
```
auth.enabled = false --defaults to false
auth.tokens = {
{ client = "test", token = "Sometest" },
{ client = "another client", token = "Some other test" }
}
```
`auth.tokens` is a table of auth keys with their client name.
There can be a "default" client or a single key for all clients, but this is up to configuration.
There can be as many client keys as needed.
In the debug log the client that authenticates is logged.
### Performance
Performance wise there is no notable difference.
### Possible future features
* Possibly in the future "expiration_date" can be added to automatically revoke issues, but I didn't think that was needed for a first implementation.
* Add per client `eval` authorisation
---
CHANGELOG.md | 1 +
Cargo.lock | 27 ++++++++++++-----
Cargo.toml | 1 +
README.md | 55 +++++++++++++++++++++++++++++++++++
lua/DCS-gRPC/grpc-mission.lua | 1 +
lua/DCS-gRPC/grpc.lua | 1 +
lua/Hooks/DCS-gRPC.lua | 1 +
src/authentication.rs | 39 +++++++++++++++++++++++++
src/config.rs | 17 +++++++++++
src/lib.rs | 1 +
src/server.rs | 23 +++++++++++----
11 files changed, 154 insertions(+), 13 deletions(-)
create mode 100644 src/authentication.rs
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3383bb5e..450e623f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added `GetClients` to `SrsService`, which retrieves a list of units that are connected to SRS and the frequencies they are connected to.
- Added `SrsConnectEvent` and `SrsDisconnectEvent` events
- Added `GetDrawArgumentValue` API for units, which returns the value for drawing. (useful for "hook down", "doors open" checks)
+- Added Authentication Interceptor. This enables authentication on a per client basis.
### Fixed
- Fixed `MarkAddEvent`, `MarkChangeEvent` and `MarkRemoveEvent` position
diff --git a/Cargo.lock b/Cargo.lock
index 158d0e31..60996af2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -440,6 +440,7 @@ dependencies = [
"tokio",
"tokio-stream",
"tonic",
+ "tonic-middleware",
"walkdir",
]
@@ -918,9 +919,9 @@ dependencies = [
[[package]]
name = "hyper"
-version = "1.3.1"
+version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d"
+checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
dependencies = [
"bytes",
"futures-channel",
@@ -958,7 +959,7 @@ checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c"
dependencies = [
"futures-util",
"http 1.1.0",
- "hyper 1.3.1",
+ "hyper 1.4.1",
"hyper-util",
"rustls 0.22.4",
"rustls-pki-types",
@@ -981,16 +982,16 @@ dependencies = [
[[package]]
name = "hyper-util"
-version = "0.1.3"
+version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa"
+checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9"
dependencies = [
"bytes",
"futures-channel",
"futures-util",
"http 1.1.0",
"http-body 1.0.0",
- "hyper 1.3.1",
+ "hyper 1.4.1",
"pin-project-lite",
"socket2",
"tokio",
@@ -1639,7 +1640,7 @@ dependencies = [
"http 1.1.0",
"http-body 1.0.0",
"http-body-util",
- "hyper 1.3.1",
+ "hyper 1.4.1",
"hyper-rustls 0.26.0",
"hyper-util",
"ipnet",
@@ -2361,6 +2362,18 @@ dependencies = [
"syn 2.0.66",
]
+[[package]]
+name = "tonic-middleware"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92d34dab0f18194ddb9164685a3d8cf777ff35042752aba2be208b1384d7a304"
+dependencies = [
+ "async-trait",
+ "futures-util",
+ "tonic",
+ "tower",
+]
+
[[package]]
name = "tower"
version = "0.4.13"
diff --git a/Cargo.toml b/Cargo.toml
index c0fb3404..bd9e10c2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -65,6 +65,7 @@ time = { version = "0.3", features = ["formatting", "parsing"] }
tokio.workspace = true
tokio-stream.workspace = true
tonic.workspace = true
+tonic-middleware = "0.1.4"
[build-dependencies]
walkdir = "2.3"
diff --git a/README.md b/README.md
index 423d40f3..df2acda4 100644
--- a/README.md
+++ b/README.md
@@ -81,6 +81,15 @@ throughputLimit = 600
-- Whether the integrity check, meant to spot installation issues, is disabled.
integrityCheckDisabled = false
+-- Whether or not authentication is required
+auth.enabled = false
+-- Authentication tokens table with client names and their tokens for split tokens.
+auth.tokens = {
+ -- client => clientName, token => Any token. Advice to use UTF-8 only. Length not limited explicitly
+ { client = "SomeClient", token = "SomeToken" },
+ { client = "SomeClient2", token = "SomeOtherToken" }
+}
+
-- The default TTS provider to use if a TTS request does not explicitly specify another one.
tts.defaultProvider = "win"
@@ -217,6 +226,52 @@ In order to develop clients for `DCS-gRPC` you must be familiar with gRPC concep
The gRPC .proto files are available in the `Docs/DCS-gRPC` folder and also available in the Github repo
+### Client Authentication
+
+If authentication is enabled on the server you will have to add `X-API-Key` to the metadata/headers.
+Below are some example on what it could look like in your code.
+
+#### Examples
+
+dotnet / c#
+
+You can either set the `Metadata` for each request or you can create a `GrpcChannel` with an interceptor that will set the key each time.
+
+For a single request:
+
+```c#
+var client = new MissionService.MissionServiceClient(channel);
+
+Metadata metadata = new Metadata()
+{
+ { "X-API-Key", "