Skip to content

Commit

Permalink
Add some long overdue docs, more cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
vswarte committed Aug 10, 2024
1 parent 2b23e25 commit 9aa8f47
Show file tree
Hide file tree
Showing 18 changed files with 222 additions and 107 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ jobs:
run: |
mkdir -p artifacts
find ./target/release -name waygate-server -exec cp {} artifacts/ \;
find ./target/release -name generate-keys -exec cp {} artifacts/ \;
find ./target/release -name waygate-generate-keys -exec cp {} artifacts/ \;
find ./target/release -name waygate-server.exe -exec cp {} artifacts/ \;
find ./target/release -name generate-keys.exe -exec cp {} artifacts/ \;
find ./target/release -name waygate-generate-keys.exe -exec cp {} artifacts/ \;
find ./target/release -name libsteam_api.so -exec cp {} artifacts/ \;
find ./target/release -name steam_api64.dll -exec cp {} artifacts/ \;
cp config.toml artifacts/
cp announcements.toml artifacts/
cp logging.toml artifacts/
cp steam_appid.txt artifacts/
shell: bash
Expand Down
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 14 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]
members = [
"server",
"crates/server",
"crates/generate-keys",
"crates/wire",
"crates/pool",
"crates/message",
Expand Down Expand Up @@ -66,6 +67,18 @@ features = [
"rustls-tls-native-roots",
]

[workspace.dependencies.libsodium-sys-stable]
version = "1"
features = [
"fetch-latest",
]

[workspace.dependencies.waygate-server]
path = "crates/server"

[workspace.dependencies.waygate-generate-keys]
path = "crates/generate-keys"

[workspace.dependencies.waygate-pool]
path = "crates/pool"

Expand Down
98 changes: 90 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,95 @@ Over the past year testing with about 30-40 people no ones has reported getting
banned from official online play, however there is no guarantee this server
is completely safe to use.

## Setup
### Server setup
The server requires a postgresql database to store messages, bloodstains, ghosts
and more. I am not going to cover how to set up a postgresql database as enough
places cover this exact procedure.

### Keys
The game uses a set of fixed keys to kick off the connection and perform the key
exchange. You will need to generate a key pair for your server and client. You
do this once during setup of the server.

In order to generate a keypair you invoke:
```bash
$ ./waygate-generate-keys
```

This will print a valid keypair alongside instructions on applying the keypair
to your setup:
```
# Pass this to your servers launch command
--client-public-key = "8oWXtzzyvMwg0DZTUxdRP/HzDnDhlw8J1ZyXiB2Giks="
--server-secret-key = "XzUteM9hPf2n/XUg8L2ImxIaRUGusUpqYVFDnEY0Egs="
# Add this to your clients's waygate.toml
client_secret_key = "VD7xDTwd6+kt9zg+3qzVaKnxfIvVIwSG8JM1cc8Eeu0="
server_public_key = "5HCUh3iOJiPwsEPvln0QFnmx9sFaQrzDI9XopTa532c="
```

### Running the server
Grab the [latest release](https://github.com/vswarte/waygate-server/releases)
for your platform and invoke it as such:

```bash
$ ./waygate-server \
--bind 0.0.0.0:10901 \
--api-bind 0.0.0.0:10902 \
--api-key <API KEY> \
--database "<DATABASE URL>" \
--client-public-key "<CLIENT PUBLIC KEY>" \
--server-secret-key "<SERVER SECRET KEY>"
```

| Parameter | Env variable | Description |
|-----------------------|-----------------------------|----------------------------------------------------|
| `--bind` | `WAYGATE_BIND` | Specifies the binding address for the game server. |
| `--api-bind` | `WAYGATE_API_BIND` | Specifies the binding address for the api server. |
| `--api-key` | `WAYGATE_API_KEY` | Specifies API authentication key. Keep secret. |
| `--database` | `WAYGATE_DATABASE` | Specifies the database URL to be used |
| `--client-public-key` | `WAYGATE_CLIENT_PUBLIC_KEY` | Specifies the KX client public key. Keep secret. |
| `--server-secret-key` | `WAYGATE_SERVER_SECRET_KEY` | Specifies the KX server secret key. Keep secret. |

#### Database URL
The `--database` parameter expects a database URL like so: `postgresql://<USERNAME>:<PASSWORD>@<HOST>/<DATABASE>`.

#### Setting up the client
// TBD

### Additional configuration
#### Logging
The logging setup is configured with `logging.toml`. Under the hood it's log4rs,
which has its [own manual](https://docs.rs/log4rs/latest/log4rs/config/index.html) on the logging options.

#### Announcements
The announcements are set in `announcements.toml`.

### API
The server also spins up a JSON HTTP API that allows people to do automated healthchecks,
broadcast messages and more in the future. This HTTP server is bound seperately
from the game server dictated by the `--api-bind` parameter.

API authentication is regulated by the `--api-key` which requires you to specify
a key that must be matched on incoming HTTP requests. You can use random.org or
a password generator to derive a secure API key.

Example healtcheck call:
```bash
$ curl -X GET http://localhost:10902/health \
--header "X-Auth-Token: <API KEY>" \
--header "Content-Type: application/json"
```

Example announcement call:
```bash
$ curl -v -X POST http://localhost:10902/notify/message \
--header "X-Auth-Token: <API KEY>" \
--header "Content-Type: application/json" \
--data '{"message":"Test Announcement"}'
```

## What's working? What needs to be done?
- [x] Summoning per sign
- [x] Quickmatches (arena)
Expand All @@ -41,13 +130,6 @@ A lot of the development happened as I was reversing the game, so a few parts
need rewriting to be more maintainable. The database needs indices as an
optimization, etc

## Installation
The server requires a postgresql database to store messages, bloodstains, ghosts
and more. I am not going to cover how to set up a postgresql database as enough
places cover this exact procedure.

// TBD

# Credits
The most important section of this README, this project took a long time to
execute and there's still a bit to go. Without these people this project
Expand All @@ -59,6 +141,6 @@ would've remained on my disk forever.
- Steelovsky for testing
- Mintal for testing
- Metalcrow for testing
- Dasaav for testing
- Shion for testing
- Dasaav for testing
- auramalexander (Dylan) for name
10 changes: 0 additions & 10 deletions config.toml

This file was deleted.

18 changes: 13 additions & 5 deletions crates/api/src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,19 @@ use actix_web::{
http::header::HeaderValue,
Error
};
use waygate_config::GENERAL;
use std::future::{ready, Ready};
use std::task::{Context, Poll};
use std::pin::Pin;

pub struct CheckKey;
pub struct CheckKey {
api_key: String,
}

impl CheckKey {
pub fn new(api_key: &str) -> Self {
Self { api_key: api_key.to_string() }
}
}

impl<S, B> Transform<S, ServiceRequest> for CheckKey
where
Expand All @@ -23,11 +30,13 @@ where
type Future = Ready<Result<Self::Transform, Self::InitError>>;

fn new_transform(&self, service: S) -> Self::Future {
ready(Ok(CheckKeyMiddleware { service }))
let api_key = self.api_key.clone();
ready(Ok(CheckKeyMiddleware { service, api_key }))
}
}

pub struct CheckKeyMiddleware<S> {
api_key: String,
service: S,
}

Expand All @@ -45,8 +54,7 @@ where
}

fn call(&self, req: ServiceRequest) -> Self::Future {
let key = GENERAL.get().unwrap().api_key.as_str();
let expected = HeaderValue::from_str(key).unwrap();
let expected = HeaderValue::from_str(&self.api_key).unwrap();
let authorized = req.headers()
.get("X-Auth-Token")
.map(|v| v == expected)
Expand Down
7 changes: 4 additions & 3 deletions crates/api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ pub enum ApiError {
Start(#[from] std::io::Error),
}

pub async fn serve_api(bind: &str, key: &str) -> Result<(), ApiError> {
pub async fn serve_api(bind: &str, api_key: &str) -> Result<(), ApiError> {
let api_key = api_key.to_string();
Ok(
HttpServer::new(|| {
HttpServer::new(move || {
App::new()
.wrap(CheckKey)
.wrap(CheckKey::new(&api_key))
.service(health)
.service(notify::notify_message)
})
Expand Down
17 changes: 0 additions & 17 deletions crates/config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,6 @@ use std::fs::read_to_string;

use serde::Deserialize;

#[derive(Debug, Deserialize)]
pub struct GeneralConfig {
pub bind: String,
pub database_url: String,
pub client_public_key: String,
pub server_secret_key: String,
pub api_bind: String,
pub api_key: String,
}

#[derive(Debug, Deserialize)]
pub struct AnnouncementConfig {
pub announcements: Vec<AnnouncementItem>,
Expand All @@ -28,16 +18,9 @@ pub struct AnnouncementItem {
pub body: String,
}

pub static GENERAL: OnceLock<GeneralConfig> = OnceLock::new();
pub static ANNOUNCEMENTS: OnceLock<AnnouncementConfig> = OnceLock::new();

pub fn init_config() -> Result<(), Box<dyn Error>>{
let general = toml::from_str(
&read_to_string("./config.toml")?
)?;
let _ = GENERAL.set(general);


let announcements = toml::from_str(
&read_to_string("./announcements.toml")?
)?;
Expand Down
5 changes: 1 addition & 4 deletions crates/connection/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@ features = [
]

[dependencies.libsodium-sys-stable]
version = "1"
features = [
"fetch-latest",
]
workspace = true

[dependencies.tracing]
workspace = true
Expand Down
22 changes: 6 additions & 16 deletions crates/connection/src/crypto.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::ffi::c_void;
use std::io::{self, Read};
use std::sync::OnceLock;
use libsodium_sys::{
crypto_kx_PUBLICKEYBYTES,
crypto_kx_SECRETKEYBYTES,
Expand All @@ -17,11 +18,8 @@ use libsodium_sys::{

use thiserror::Error;
use tokio::io::AsyncWriteExt;
use waygate_config::GENERAL;

use base64::prelude::*;

use crate::ClientError;
use crate::{ClientError, KX_CLIENT_PUBLIC_KEY, KX_SERVER_SECRET_KEY};

pub const PUBLICKEYBYTES: usize = crypto_kx_PUBLICKEYBYTES as usize;
pub const SECRETKEYBYTES: usize = crypto_kx_SECRETKEYBYTES as usize;
Expand Down Expand Up @@ -151,19 +149,15 @@ impl ClientCrypto<ClientCryptoStateParametersGenerated> {
let (nonce, mac, mut payload) = Self::split_nonce_mac_and_payload(r)
.await.map_err(ClientError::Io)?;

let config = GENERAL.get().unwrap();
let client_public_key = BASE64_STANDARD.decode(&config.client_public_key)?;
let server_secret_key = BASE64_STANDARD.decode(&config.server_secret_key)?;

let result = unsafe {
libsodium_sys::crypto_box_open_detached(
payload.as_mut_ptr(),
payload.as_ptr(),
mac.as_ptr(),
payload.len() as u64,
nonce.as_ptr(),
client_public_key.as_ptr(),
server_secret_key.as_ptr(),
KX_CLIENT_PUBLIC_KEY.get().unwrap().as_ptr(),
KX_SERVER_SECRET_KEY.get().unwrap().as_ptr(),
)
};

Expand Down Expand Up @@ -195,10 +189,6 @@ impl ClientCrypto<ClientCryptoStateParametersGenerated> {
randombytes_buf(bootstrap_nonce.as_mut_ptr() as *mut c_void, NONCEBYTES);
};

let config = GENERAL.get().unwrap();
let client_public_key = BASE64_STANDARD.decode(&config.client_public_key)?;
let server_secret_key = BASE64_STANDARD.decode(&config.server_secret_key)?;

let mut mac = [0u8; MACBYTES];
let result = unsafe {
crypto_box_detached(
Expand All @@ -207,8 +197,8 @@ impl ClientCrypto<ClientCryptoStateParametersGenerated> {
payload.as_ptr(),
payload.len() as u64,
bootstrap_nonce.as_ptr(),
client_public_key.as_ptr(),
server_secret_key.as_ptr(),
KX_CLIENT_PUBLIC_KEY.get().unwrap().as_ptr(),
KX_SERVER_SECRET_KEY.get().unwrap().as_ptr(),
)
};

Expand Down
19 changes: 19 additions & 0 deletions crates/connection/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,27 @@ mod transport;
mod client;
mod session;

use std::sync::OnceLock;

use base64::prelude::*;
pub use push::*;
pub use crypto::*;
pub use transport::*;
pub use client::*;
pub use session::*;

pub(crate) static KX_CLIENT_PUBLIC_KEY: OnceLock<Vec<u8>> = OnceLock::new();
pub(crate) static KX_SERVER_SECRET_KEY: OnceLock<Vec<u8>> = OnceLock::new();

pub fn init_crypto(
client_public_key: &str,
server_secret_key: &str,
) -> Result<(), base64::DecodeError> {
let client_public_key = BASE64_STANDARD.decode(client_public_key)?;
KX_CLIENT_PUBLIC_KEY.set(client_public_key).expect("init_crypto called twice?");

let server_secret_key = BASE64_STANDARD.decode(server_secret_key)?;
KX_SERVER_SECRET_KEY.set(server_secret_key).expect("init_crypto called twice?");

Ok(())
}
10 changes: 10 additions & 0 deletions crates/generate-keys/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "waygate-generate-keys"
version = "0.1.0"
edition = "2021"

[dependencies.base64]
workspace = true

[dependencies.libsodium-sys-stable]
workspace = true
Loading

0 comments on commit 9aa8f47

Please sign in to comment.