Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ to bind both IPv4 and IPv6 addresses, for example: `-a 0.0.0.0:443 -a [::]:443`.

The server needs a JSON configuration file that gives links to files containing
TLS certificate and private key are given in a JSON configuration file. The
configuration file is given with `-c` option. By default, an example
configuration file **[server-example.json](src/bin/server-example.json)** is used,
that contains link to an invalid certificate, but can be used for development
configuration file is given with `-c` option.
If no config is specified, the example configuration file from
**[server-example.json](src/bin/server-example.json)** (at build time) is used.
It points to an invalid certificate, but can be used for development
and testing, if certificate validation is disabled at client.

**Starting the example client:**
Expand All @@ -58,7 +59,7 @@ fields defining the server operation:
- **key_file**: path to the private key needed with the certificate.

- **jwt_secret**: Secret that is used to decode the JWT tokens. This should
actually be sufficiently long randomly generated string.
actually be a sufficiently long randomly generated string.

After the global parameters, there are configurations for different endpoints
that the server operates: IP tunnel endpoint (type: `IpEndpoint`), UDP proxy
Expand Down
29 changes: 14 additions & 15 deletions src/bin/psq-server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
extern crate log;

use clap::Parser;
use std::net::SocketAddr;
use std::{
net::SocketAddr,
path::{Path, PathBuf},
};

use pasque::{server::Config, PsqServer};

Expand All @@ -11,16 +14,12 @@ async fn main() {
env_logger::builder().format_timestamp_nanos().init();

let args = Args::new();
let config = match Config::read_from_file(args.config()) {
Ok(c) => c,
Err(e) => {
warn!(
"Could not read config '{}': {}. Applying default configuration.",
args.config(),
e,
);
Config::create_default()
}

let config = if let Some(config_path) = args.config_path() {
Config::read_from_file(config_path).expect("unable to read config file")
} else {
warn!("No config specified, using default configuration.");
Config::default()
};

let mut psqserver = PsqServer::start(&args.address(), &config).await.unwrap();
Expand All @@ -40,8 +39,8 @@ pub struct Args {
address: Vec<SocketAddr>,

/// Configuration file to read.
#[arg(short, long, default_value = "src/bin/server-example.json")]
config: String,
#[arg(short, long)]
config: Option<PathBuf>,
}

impl Args {
Expand All @@ -55,7 +54,7 @@ impl Args {
&self.address
}

pub fn config(&self) -> &String {
&self.config
pub fn config_path(&self) -> Option<&Path> {
self.config.as_ref().map(PathBuf::as_path)
}
}
41 changes: 23 additions & 18 deletions src/server/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! Typically read from a JSON file. Includes certificate parameters and
//! configurations for different kinds of endpoints.

use std::{fs::File, io::BufReader};
use std::{fs::File, io::BufReader, sync::LazyLock};

use serde::Deserialize;

Expand All @@ -17,21 +17,27 @@ use super::PsqServer;
/// are configured and what fields they have.
///
/// [server-example.json]: https://github.com/PasiSa/pasque/blob/main/src/bin/server-example.json
#[derive(Debug, Deserialize)]
#[derive(Clone, Debug, Deserialize)]
pub struct Config {
cert_file: String,
key_file: String,
#[serde(default = "default_jwt_secret")]
jwt_secret: String,
endpoints: Vec<Endpoint>,
}

fn default_jwt_secret() -> String {
"not-secret".to_string()
static DEFAULT_CONFIG: LazyLock<Config> = LazyLock::new(|| {
serde_json::from_slice(include_bytes!("../bin/server-example.json"))
.expect("example config must parse")
});

impl Default for Config {
fn default() -> Self {
DEFAULT_CONFIG.clone()
}
}

/// Common attributes to different endpoint types.
#[derive(Debug, Deserialize)]
#[derive(Clone, Debug, Deserialize)]
pub struct Common {
/// Path to this endpoint.
pub path: String,
Expand All @@ -41,7 +47,7 @@ pub struct Common {
pub permission: Option<String>,
}

#[derive(Debug, Deserialize)]
#[derive(Clone, Debug, Deserialize)]
#[serde(tag = "type")]
enum Endpoint {
IpEndpoint {
Expand All @@ -64,7 +70,9 @@ enum Endpoint {

impl Config {
/// Read JSON-formatted configuration from given configuration file
pub fn read_from_file(filename: &str) -> core::result::Result<Config, PsqError> {
pub fn read_from_file(
filename: impl AsRef<std::path::Path>,
) -> core::result::Result<Config, PsqError> {
let file = match File::open(filename) {
Ok(f) => f,
Err(e) => {
Expand All @@ -87,16 +95,6 @@ impl Config {
Ok(conf)
}

/// Create a default configuration. Can be applied if configuration file cannot be read.
pub fn create_default() -> Config {
Config {
cert_file: "src/bin/cert.crt".to_string(),
key_file: "src/bin/cert.key".to_string(),
jwt_secret: "not-secret".to_string(),
endpoints: Vec::new(),
}
}

/// File path that contains the PEM formatted TLS certificate.
pub fn cert_file(&self) -> &String {
&self.cert_file
Expand All @@ -112,6 +110,13 @@ impl Config {
&self.jwt_secret
}

/// Return a config without endpoints configured. Used in tests.
pub fn default_without_endpoints() -> Self {
let mut config = Self::default();
config.endpoints.clear();
config
}

/// Apply server endpoint settings from configuration.
/// See [server-example.json] for an example configuration
/// with endpoints.
Expand Down
3 changes: 2 additions & 1 deletion src/stream/iptunnel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -904,7 +904,8 @@ mod tests {
let (tunnel, mut tester) = UnixStream::pair().unwrap();

let server = tokio::spawn(async move {
let config = Config::create_default();
let config = Config::default_without_endpoints();

let mut psqserver =
PsqServer::start(&vec![SocketAddr::from_str(addr).unwrap()], &config)
.await
Expand Down
3 changes: 2 additions & 1 deletion tests/endpoints.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@
"type": "Files",
"root": "."
}
]
],
"jwt_secret": "not-secret"
}
6 changes: 3 additions & 3 deletions tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use pasque::{
async fn test_get_request() {
init_logger();
let addr = "127.0.0.1:8888";
let config = Config::create_default();
let config = Config::default_without_endpoints();
let server = tokio::spawn(async move {
let mut psqserver = PsqServer::start(&vec![SocketAddr::from_str(addr).unwrap()], &config)
.await
Expand Down Expand Up @@ -72,7 +72,7 @@ async fn test_get_request() {
}

async fn run_server(addr: &str, shutdown: Arc<Notify>) {
let config = Config::create_default();
let config = Config::default_without_endpoints();
let mut psqserver = PsqServer::start(&vec![SocketAddr::from_str(addr).unwrap()], &config)
.await
.unwrap();
Expand Down Expand Up @@ -186,7 +186,7 @@ async fn tunnel_closing() {
init_logger();
let addr = "127.0.0.1:9003";
let server = tokio::spawn(async move {
let config = Config::create_default();
let config = Config::default_without_endpoints();
let mut psqserver = PsqServer::start(&vec![SocketAddr::from_str(addr).unwrap()], &config)
.await
.unwrap();
Expand Down
3 changes: 2 additions & 1 deletion tests/testconfig1.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"cert_file": "src/bin/cert.crt",
"key_file": "src/bin/cert.keys",
"endpoints": [ ]
"endpoints": [ ],
"jwt_secret": "not-secret"
}