mirror of
https://codeberg.org/tyy/aspm
synced 2024-12-22 21:49:28 -07:00
naja-server: fix update bug, enable h2c, add logging, add docker configs; naja-lib: add insecure option when debug_assertions are enabled; nana-cli: add insecure option when debug_assertions are enabled
This commit is contained in:
parent
0c5452a1b8
commit
a6b7790107
11 changed files with 117 additions and 16 deletions
5
.dockerignore
Normal file
5
.dockerignore
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
.vscode/*
|
||||||
|
./keys
|
||||||
|
./target
|
||||||
|
./*.jwk
|
||||||
|
./README.md
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
/target
|
/target
|
||||||
*.jw[tk]
|
*.jw[tk]
|
||||||
|
/server-data
|
30
Cargo.lock
generated
30
Cargo.lock
generated
|
@ -1222,6 +1222,29 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_filter"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_logger"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"env_filter",
|
||||||
|
"humantime",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
@ -1703,6 +1726,12 @@ version = "1.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "humantime"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
|
@ -2339,6 +2368,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"clap",
|
"clap",
|
||||||
|
"env_logger",
|
||||||
"naja-lib",
|
"naja-lib",
|
||||||
"sea-orm",
|
"sea-orm",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
26
Dockerfile
Normal file
26
Dockerfile
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# Using the `rust-musl-builder` as base image, instead of
|
||||||
|
# the official Rust toolchain
|
||||||
|
FROM clux/muslrust:1.79.0-stable AS chef
|
||||||
|
USER root
|
||||||
|
# Install cargo-chef
|
||||||
|
RUN cargo install cargo-chef --locked
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
FROM chef AS planner
|
||||||
|
COPY . .
|
||||||
|
# Cache all dependencies for the server
|
||||||
|
RUN cargo chef prepare --recipe-path recipe.json --bin naja-server
|
||||||
|
|
||||||
|
FROM chef AS builder
|
||||||
|
COPY --from=planner /app/recipe.json recipe.json
|
||||||
|
# Build dependencies
|
||||||
|
RUN cargo chef cook --release --target x86_64-unknown-linux-musl --recipe-path recipe.json --package naja-server
|
||||||
|
COPY . .
|
||||||
|
# Build main binary
|
||||||
|
RUN cargo build --release --target x86_64-unknown-linux-musl --package naja-server
|
||||||
|
|
||||||
|
FROM alpine AS runtime
|
||||||
|
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/naja-server /usr/local/bin/
|
||||||
|
HEALTHCHECK --interval=10s --timeout=3s \
|
||||||
|
CMD wget --no-verbose --tries=1 --spider http://localhost:$NAJA_BIND_PORT/ || exit 1
|
||||||
|
CMD ["/usr/local/bin/naja-server"]
|
|
@ -29,12 +29,16 @@ pub struct AspeDeleteCommand {
|
||||||
/// The ASPE server to upload this profile to
|
/// The ASPE server to upload this profile to
|
||||||
#[clap(value_parser = Host::parse)]
|
#[clap(value_parser = Host::parse)]
|
||||||
server: Host,
|
server: Host,
|
||||||
|
/// (dev only) If the ASPE server is insecure (not HTTPS)
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
#[arg(long)]
|
||||||
|
insecure: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl NajaSubcommand for AspeDeleteCommand {
|
impl NajaSubcommand for AspeDeleteCommand {
|
||||||
async fn execute(self, state: crate::CliState) -> Result<(), anyhow::Error> {
|
async fn execute(self, state: crate::CliState) -> Result<(), anyhow::Error> {
|
||||||
let server = AspeServer::new(self.server)
|
let server = AspeServer::new(self.server, #[cfg(debug_assertions)] self.insecure)
|
||||||
.await
|
.await
|
||||||
.context("Unable to parse provided server into ASPE server, please verify that it is a valid ASPE server")?;
|
.context("Unable to parse provided server into ASPE server, please verify that it is a valid ASPE server")?;
|
||||||
|
|
||||||
|
|
|
@ -38,12 +38,16 @@ pub struct AspeUploadCommand {
|
||||||
/// If passed, this command will only create new keys on the server, rather than updating if the key already exists
|
/// If passed, this command will only create new keys on the server, rather than updating if the key already exists
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
no_update: bool,
|
no_update: bool,
|
||||||
|
/// (dev only) If the ASPE server is insecure (not HTTPS)
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
#[arg(long)]
|
||||||
|
insecure: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl NajaSubcommand for AspeUploadCommand {
|
impl NajaSubcommand for AspeUploadCommand {
|
||||||
async fn execute(self, state: crate::CliState) -> Result<(), anyhow::Error> {
|
async fn execute(self, state: crate::CliState) -> Result<(), anyhow::Error> {
|
||||||
let server = AspeServer::new(self.server)
|
let server = AspeServer::new(self.server, #[cfg(debug_assertions)] self.insecure)
|
||||||
.await
|
.await
|
||||||
.context("Unable to parse provided server into ASPE server, please verify that it is a valid ASPE server")?;
|
.context("Unable to parse provided server into ASPE server, please verify that it is a valid ASPE server")?;
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,10 @@ pub struct ProfilesImportCommand {
|
||||||
/// The alias to give to the imported profile
|
/// The alias to give to the imported profile
|
||||||
#[clap(trailing_var_arg = true)]
|
#[clap(trailing_var_arg = true)]
|
||||||
alias: Vec<String>,
|
alias: Vec<String>,
|
||||||
|
/// (dev only) If the ASPE server is insecure (not HTTPS)
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
#[arg(long)]
|
||||||
|
insecure: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
|
@ -32,7 +36,7 @@ impl NajaSubcommand for ProfilesImportCommand {
|
||||||
(host.to_string(), fingerprint.to_string())
|
(host.to_string(), fingerprint.to_string())
|
||||||
})
|
})
|
||||||
}) {
|
}) {
|
||||||
Some((host, fingerprint)) => match aspe::AspeServer::new(Host::parse(&host).unwrap())
|
Some((host, fingerprint)) => match aspe::AspeServer::new(Host::parse(&host).unwrap(), #[cfg(debug_assertions)] self.insecure)
|
||||||
.await?
|
.await?
|
||||||
.fetch_profile(&fingerprint)
|
.fetch_profile(&fingerprint)
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -20,6 +20,7 @@ pub struct AspeVersion {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AspeServer {
|
pub struct AspeServer {
|
||||||
pub host: Host,
|
pub host: Host,
|
||||||
|
insecure: bool, // This is here for developing with local ASPE servers, but cannot actually be set to true in release binaries
|
||||||
client: reqwest::Client,
|
client: reqwest::Client,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,16 +79,18 @@ impl From<reqwest::Error> for AspeFetchFailure {
|
||||||
|
|
||||||
impl AspeServer {
|
impl AspeServer {
|
||||||
/// Creates a new [AspeServer] instance given the specified domain. A domain and ONLY a domain should be provided (no scheme, no path, etc).
|
/// Creates a new [AspeServer] instance given the specified domain. A domain and ONLY a domain should be provided (no scheme, no path, etc).
|
||||||
pub async fn new(host: Host) -> anyhow::Result<Self> {
|
pub async fn new(host: Host, #[cfg(debug_assertions)] insecure: bool) -> anyhow::Result<Self> {
|
||||||
|
#[cfg(not(debug_assertions))] let insecure = false;
|
||||||
let server = Self {
|
let server = Self {
|
||||||
host,
|
host,
|
||||||
|
insecure,
|
||||||
client: reqwest::Client::builder()
|
client: reqwest::Client::builder()
|
||||||
.user_agent(format!(
|
.user_agent(format!(
|
||||||
"naja-lib/{version} ({repo})",
|
"naja-lib/{version} ({repo})",
|
||||||
version = env!("CARGO_PKG_VERSION"),
|
version = env!("CARGO_PKG_VERSION"),
|
||||||
repo = env!("CARGO_PKG_REPOSITORY")
|
repo = env!("CARGO_PKG_REPOSITORY")
|
||||||
))
|
))
|
||||||
.https_only(true)
|
.https_only(!insecure)
|
||||||
.build()?,
|
.build()?,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -101,7 +104,8 @@ impl AspeServer {
|
||||||
pub async fn version(&self) -> Result<(), reqwest::Error> {
|
pub async fn version(&self) -> Result<(), reqwest::Error> {
|
||||||
self.client
|
self.client
|
||||||
.get(format!(
|
.get(format!(
|
||||||
"https://{host}/.well-known/aspe/version",
|
"{scheme}://{host}/.well-known/aspe/version",
|
||||||
|
scheme = if self.insecure { "http" } else { "https" },
|
||||||
host = self.host
|
host = self.host
|
||||||
))
|
))
|
||||||
.header(
|
.header(
|
||||||
|
@ -119,7 +123,8 @@ impl AspeServer {
|
||||||
pub async fn post_request(&self, jws: impl Into<String>) -> Result<(), AspeRequestFailure> {
|
pub async fn post_request(&self, jws: impl Into<String>) -> Result<(), AspeRequestFailure> {
|
||||||
self.client
|
self.client
|
||||||
.post(format!(
|
.post(format!(
|
||||||
"https://{host}/.well-known/aspe/post",
|
"{scheme}://{host}/.well-known/aspe/post",
|
||||||
|
scheme = if self.insecure { "http" } else { "https" },
|
||||||
host = self.host
|
host = self.host
|
||||||
))
|
))
|
||||||
.header(
|
.header(
|
||||||
|
@ -141,8 +146,9 @@ impl AspeServer {
|
||||||
let res = self
|
let res = self
|
||||||
.client
|
.client
|
||||||
.get(format!(
|
.get(format!(
|
||||||
"https://{host}/.well-known/aspe/id/{fingerprint}",
|
"{scheme}://{host}/.well-known/aspe/id/{fingerprint}",
|
||||||
host = self.host,
|
host = self.host,
|
||||||
|
scheme = if self.insecure { "http" } else { "https" },
|
||||||
fingerprint = fingerprint.as_ref()
|
fingerprint = fingerprint.as_ref()
|
||||||
))
|
))
|
||||||
.header(
|
.header(
|
||||||
|
@ -167,12 +173,12 @@ mod tests {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn building_aspe_server_succeeds() {
|
async fn building_aspe_server_succeeds() {
|
||||||
AspeServer::new(Host::Domain(String::from("keyoxide.org"))).await.expect("Contructing an AspeServer should succeed");
|
AspeServer::new(Host::Domain(String::from("keyoxide.org")), false).await.expect("Contructing an AspeServer should succeed");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn building_invalid_aspe_server_fails() {
|
async fn building_invalid_aspe_server_fails() {
|
||||||
let result = AspeServer::new(Host::Domain(String::from("example.com"))).await;
|
let result = AspeServer::new(Host::Domain(String::from("example.com")), false).await;
|
||||||
assert!(result.is_err(), "Constructing an AspeServer with an invalid domain should fail")
|
assert!(result.is_err(), "Constructing an AspeServer with an invalid domain should fail")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,3 +12,4 @@ sea-orm = { version = "0.12", features = ["sqlx-sqlite", "runtime-tokio-rustls",
|
||||||
serde = { version = "1.0.204", features = ["derive"] }
|
serde = { version = "1.0.204", features = ["derive"] }
|
||||||
naja-lib = { path = "../naja-lib" }
|
naja-lib = { path = "../naja-lib" }
|
||||||
migrations = { path = "../server-migrations", package = "server-migrations" }
|
migrations = { path = "../server-migrations", package = "server-migrations" }
|
||||||
|
env_logger = "0.11.3"
|
||||||
|
|
|
@ -3,9 +3,10 @@ mod entities;
|
||||||
|
|
||||||
use std::{fs, io, path::PathBuf};
|
use std::{fs, io, path::PathBuf};
|
||||||
|
|
||||||
use actix_web::{get, http::header, post, web, App, HttpResponse, HttpServer, Responder};
|
use actix_web::{get, http::header, middleware::Logger, post, web, App, HttpResponse, HttpServer, Responder};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use entities::{prelude::*, profiles};
|
use entities::{prelude::*, profiles};
|
||||||
|
use env_logger::Env;
|
||||||
use migrations::{Migrator, MigratorTrait as _};
|
use migrations::{Migrator, MigratorTrait as _};
|
||||||
use naja_lib::{
|
use naja_lib::{
|
||||||
aspe::requests::{AspeRequest, AspeRequestVariant},
|
aspe::requests::{AspeRequest, AspeRequestVariant},
|
||||||
|
@ -13,7 +14,7 @@ use naja_lib::{
|
||||||
utils::jwt::{JwtDeserializationError, JwtSerialize},
|
utils::jwt::{JwtDeserializationError, JwtSerialize},
|
||||||
};
|
};
|
||||||
use sea_orm::{
|
use sea_orm::{
|
||||||
ActiveValue, ColumnTrait as _, Database, DatabaseConnection, DbErr, EntityTrait, QueryFilter,
|
ActiveValue, Database, DatabaseConnection, DbErr, EntityTrait,
|
||||||
};
|
};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
|
@ -69,14 +70,16 @@ async fn main() -> io::Result<()> {
|
||||||
domain: args.domain,
|
domain: args.domain,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
env_logger::init_from_env(Env::default().default_filter_or("info"));
|
||||||
HttpServer::new(move || {
|
HttpServer::new(move || {
|
||||||
App::new()
|
App::new()
|
||||||
|
.wrap(Logger::default())
|
||||||
.app_data(web::Data::new(state.clone()))
|
.app_data(web::Data::new(state.clone()))
|
||||||
.service(version)
|
.service(version)
|
||||||
.service(get_by_fingerprint)
|
.service(get_by_fingerprint)
|
||||||
.service(post_request)
|
.service(post_request)
|
||||||
})
|
})
|
||||||
.bind((args.bind_address, args.port))?
|
.bind_auto_h2c((args.bind_address, args.port))?
|
||||||
.run()
|
.run()
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
@ -164,10 +167,9 @@ async fn post_request(
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
// Must be AspeRequestvariant::Update { .. }
|
// Must be AspeRequestvariant::Update { .. }
|
||||||
match Profiles::update(profiles::ActiveModel {
|
match Profiles::update(profiles::ActiveModel {
|
||||||
|
fingerprint: ActiveValue::Unchanged(key.fingerprint.to_owned()),
|
||||||
jwt: ActiveValue::Set(profile_jws.to_owned()),
|
jwt: ActiveValue::Set(profile_jws.to_owned()),
|
||||||
..Default::default()
|
|
||||||
})
|
})
|
||||||
.filter(profiles::Column::Fingerprint.eq(&key.fingerprint))
|
|
||||||
.exec(&state.db)
|
.exec(&state.db)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
|
|
18
docker-compose.dev.yml
Normal file
18
docker-compose.dev.yml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
services:
|
||||||
|
server:
|
||||||
|
image: naja-server
|
||||||
|
build:
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
network: host
|
||||||
|
environment:
|
||||||
|
NAJA_DATA_PATH: /home/naja/.local/share/naja-server/
|
||||||
|
NAJA_BIND_ADDRESS: 0.0.0.0
|
||||||
|
NAJA_BIND_PORT: 80
|
||||||
|
NAJA_DOMAIN: localhost
|
||||||
|
RUST_LOG: actix_web=debug,actix_server=info
|
||||||
|
ports:
|
||||||
|
- 80:80
|
||||||
|
restart: always
|
||||||
|
container_name: naja-server
|
||||||
|
volumes:
|
||||||
|
- ./server-data:/home/naja/.local/share/naja-server/
|
Loading…
Reference in a new issue