mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
add local auth to registry
This commit is contained in:
15
core/Cargo.lock
generated
15
core/Cargo.lock
generated
@@ -5290,7 +5290,7 @@ dependencies = [
|
||||
"nix 0.30.1",
|
||||
"patch-db-macro",
|
||||
"serde",
|
||||
"serde_cbor",
|
||||
"serde_cbor 0.11.1",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@@ -6477,7 +6477,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rpc-toolkit"
|
||||
version = "0.3.2"
|
||||
source = "git+https://github.com/Start9Labs/rpc-toolkit.git?rev=068db90#068db905ee38a7da97cc4a43b806409204e73723"
|
||||
source = "git+https://github.com/Start9Labs/rpc-toolkit.git#81d18147fd0ca9725b820c010c006e8a2cada322"
|
||||
dependencies = [
|
||||
"async-stream",
|
||||
"async-trait",
|
||||
@@ -6494,6 +6494,7 @@ dependencies = [
|
||||
"pin-project",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_cbor 0.11.2",
|
||||
"serde_json",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
@@ -6933,6 +6934,16 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_cbor"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5"
|
||||
dependencies = [
|
||||
"half 1.8.3",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
|
||||
@@ -212,8 +212,8 @@ reqwest = { version = "0.12.25", features = [
|
||||
] }
|
||||
reqwest_cookie_store = "0.9.0"
|
||||
rpassword = "7.2.0"
|
||||
rpc-toolkit = { git = "https://github.com/Start9Labs/rpc-toolkit.git", rev = "068db90" }
|
||||
rust-argon2 = "3.0.0"
|
||||
rpc-toolkit = { git = "https://github.com/Start9Labs/rpc-toolkit.git" }
|
||||
safelog = { version = "0.4.8", git = "https://github.com/Start9Labs/arti.git", branch = "patch/disable-exit", optional = true }
|
||||
semver = { version = "1.0.20", features = ["serde"] }
|
||||
serde = { version = "1.0", features = ["derive", "rc"] }
|
||||
|
||||
@@ -14,8 +14,8 @@ use tracing::instrument;
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
use crate::middleware::auth::{
|
||||
AsLogoutSessionId, AuthContext, HasLoggedOutSessions, HashSessionToken, LoginRes,
|
||||
use crate::middleware::auth::session::{
|
||||
AsLogoutSessionId, HasLoggedOutSessions, HashSessionToken, LoginRes, SessionAuthContext,
|
||||
};
|
||||
use crate::prelude::*;
|
||||
use crate::util::crypto::EncryptedWire;
|
||||
@@ -110,7 +110,7 @@ impl std::str::FromStr for PasswordType {
|
||||
})
|
||||
}
|
||||
}
|
||||
pub fn auth<C: Context, AC: AuthContext>() -> ParentHandler<C>
|
||||
pub fn auth<C: Context, AC: SessionAuthContext>() -> ParentHandler<C>
|
||||
where
|
||||
CliContext: CallRemote<AC>,
|
||||
{
|
||||
@@ -173,7 +173,7 @@ fn gen_pwd() {
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
async fn cli_login<C: AuthContext>(
|
||||
async fn cli_login<C: SessionAuthContext>(
|
||||
HandlerArgs {
|
||||
context: ctx,
|
||||
parent_method,
|
||||
@@ -227,7 +227,7 @@ pub struct LoginParams {
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub async fn login_impl<C: AuthContext>(
|
||||
pub async fn login_impl<C: SessionAuthContext>(
|
||||
ctx: C,
|
||||
LoginParams {
|
||||
password,
|
||||
@@ -283,7 +283,7 @@ pub struct LogoutParams {
|
||||
session: InternedString,
|
||||
}
|
||||
|
||||
pub async fn logout<C: AuthContext>(
|
||||
pub async fn logout<C: SessionAuthContext>(
|
||||
ctx: C,
|
||||
LogoutParams { session }: LogoutParams,
|
||||
) -> Result<Option<HasLoggedOutSessions>, Error> {
|
||||
@@ -312,7 +312,7 @@ pub struct SessionList {
|
||||
sessions: Sessions,
|
||||
}
|
||||
|
||||
pub fn session<C: Context, AC: AuthContext>() -> ParentHandler<C>
|
||||
pub fn session<C: Context, AC: SessionAuthContext>() -> ParentHandler<C>
|
||||
where
|
||||
CliContext: CallRemote<AC>,
|
||||
{
|
||||
@@ -379,7 +379,7 @@ pub struct ListParams {
|
||||
|
||||
// #[command(display(display_sessions))]
|
||||
#[instrument(skip_all)]
|
||||
pub async fn list<C: AuthContext>(
|
||||
pub async fn list<C: SessionAuthContext>(
|
||||
ctx: C,
|
||||
ListParams { session, .. }: ListParams,
|
||||
) -> Result<SessionList, Error> {
|
||||
@@ -418,7 +418,10 @@ pub struct KillParams {
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub async fn kill<C: AuthContext>(ctx: C, KillParams { ids }: KillParams) -> Result<(), Error> {
|
||||
pub async fn kill<C: SessionAuthContext>(
|
||||
ctx: C,
|
||||
KillParams { ids }: KillParams,
|
||||
) -> Result<(), Error> {
|
||||
HasLoggedOutSessions::new(ids.into_iter().map(KillSessionId::new), &ctx).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ use crate::db::model::{Database, DatabaseModel};
|
||||
use crate::disk::mount::backup::BackupMountGuard;
|
||||
use crate::disk::mount::filesystem::ReadWrite;
|
||||
use crate::disk::mount::guard::{GenericMountGuard, TmpMountGuard};
|
||||
use crate::middleware::auth::AuthContext;
|
||||
use crate::middleware::auth::session::SessionAuthContext;
|
||||
use crate::notifications::{NotificationLevel, notify};
|
||||
use crate::prelude::*;
|
||||
use crate::util::io::{AtomicFile, dir_copy};
|
||||
|
||||
@@ -24,7 +24,7 @@ use super::setup::CURRENT_SECRET;
|
||||
use crate::context::config::{ClientConfig, local_config_path};
|
||||
use crate::context::{DiagnosticContext, InitContext, InstallContext, RpcContext, SetupContext};
|
||||
use crate::developer::{OS_DEVELOPER_KEY_PATH, default_developer_key_path};
|
||||
use crate::middleware::auth::AuthContext;
|
||||
use crate::middleware::auth::local::LocalAuthContext;
|
||||
use crate::prelude::*;
|
||||
use crate::rpc_continuations::Guid;
|
||||
use crate::util::io::read_file_to_string;
|
||||
@@ -307,7 +307,7 @@ impl CallRemote<RpcContext> for CliContext {
|
||||
)
|
||||
.with_kind(crate::ErrorKind::Network)?;
|
||||
}
|
||||
crate::middleware::signature::call_remote(
|
||||
crate::middleware::auth::signature::call_remote(
|
||||
self,
|
||||
self.rpc_url.clone(),
|
||||
HeaderMap::new(),
|
||||
@@ -320,7 +320,7 @@ impl CallRemote<RpcContext> for CliContext {
|
||||
}
|
||||
impl CallRemote<DiagnosticContext> for CliContext {
|
||||
async fn call_remote(&self, method: &str, params: Value, _: Empty) -> Result<Value, RpcError> {
|
||||
crate::middleware::signature::call_remote(
|
||||
crate::middleware::auth::signature::call_remote(
|
||||
self,
|
||||
self.rpc_url.clone(),
|
||||
HeaderMap::new(),
|
||||
@@ -333,7 +333,7 @@ impl CallRemote<DiagnosticContext> for CliContext {
|
||||
}
|
||||
impl CallRemote<InitContext> for CliContext {
|
||||
async fn call_remote(&self, method: &str, params: Value, _: Empty) -> Result<Value, RpcError> {
|
||||
crate::middleware::signature::call_remote(
|
||||
crate::middleware::auth::signature::call_remote(
|
||||
self,
|
||||
self.rpc_url.clone(),
|
||||
HeaderMap::new(),
|
||||
@@ -346,7 +346,7 @@ impl CallRemote<InitContext> for CliContext {
|
||||
}
|
||||
impl CallRemote<SetupContext> for CliContext {
|
||||
async fn call_remote(&self, method: &str, params: Value, _: Empty) -> Result<Value, RpcError> {
|
||||
crate::middleware::signature::call_remote(
|
||||
crate::middleware::auth::signature::call_remote(
|
||||
self,
|
||||
self.rpc_url.clone(),
|
||||
HeaderMap::new(),
|
||||
@@ -359,7 +359,7 @@ impl CallRemote<SetupContext> for CliContext {
|
||||
}
|
||||
impl CallRemote<InstallContext> for CliContext {
|
||||
async fn call_remote(&self, method: &str, params: Value, _: Empty) -> Result<Value, RpcError> {
|
||||
crate::middleware::signature::call_remote(
|
||||
crate::middleware::auth::signature::call_remote(
|
||||
self,
|
||||
self.rpc_url.clone(),
|
||||
HeaderMap::new(),
|
||||
|
||||
@@ -20,7 +20,7 @@ use crate::db::model::Database;
|
||||
use crate::db::model::public::ServerStatus;
|
||||
use crate::developer::OS_DEVELOPER_KEY_PATH;
|
||||
use crate::hostname::Hostname;
|
||||
use crate::middleware::auth::AuthContext;
|
||||
use crate::middleware::auth::local::LocalAuthContext;
|
||||
use crate::net::gateway::UpgradableListener;
|
||||
use crate::net::net_controller::{NetController, NetService};
|
||||
use crate::net::socks::DEFAULT_SOCKS_LISTEN;
|
||||
|
||||
101
core/startos/src/middleware/auth/local.rs
Normal file
101
core/startos/src/middleware/auth/local.rs
Normal file
@@ -0,0 +1,101 @@
|
||||
use base64::Engine;
|
||||
use basic_cookies::Cookie;
|
||||
use http::HeaderValue;
|
||||
use http::header::COOKIE;
|
||||
use rand::random;
|
||||
use rpc_toolkit::yajrc::{RpcError, RpcResponse};
|
||||
use rpc_toolkit::{Context, Empty, Middleware};
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::process::Command;
|
||||
|
||||
use crate::context::RpcContext;
|
||||
use crate::prelude::*;
|
||||
use crate::util::Invoke;
|
||||
use crate::util::io::{create_file_mod, read_file_to_string};
|
||||
use crate::util::serde::BASE64;
|
||||
|
||||
pub trait LocalAuthContext: Context {
|
||||
const LOCAL_AUTH_COOKIE_PATH: &str;
|
||||
const LOCAL_AUTH_COOKIE_OWNERSHIP: &str;
|
||||
fn init_auth_cookie() -> impl Future<Output = Result<(), Error>> + Send {
|
||||
async {
|
||||
let mut file = create_file_mod(Self::LOCAL_AUTH_COOKIE_PATH, 0o640).await?;
|
||||
file.write_all(BASE64.encode(random::<[u8; 32]>()).as_bytes())
|
||||
.await?;
|
||||
file.sync_all().await?;
|
||||
drop(file);
|
||||
Command::new("chown")
|
||||
.arg(Self::LOCAL_AUTH_COOKIE_OWNERSHIP)
|
||||
.arg(Self::LOCAL_AUTH_COOKIE_PATH)
|
||||
.invoke(crate::ErrorKind::Filesystem)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalAuthContext for RpcContext {
|
||||
const LOCAL_AUTH_COOKIE_PATH: &str = "/run/startos/rpc.authcookie";
|
||||
const LOCAL_AUTH_COOKIE_OWNERSHIP: &str = "root:startos";
|
||||
}
|
||||
|
||||
fn unauthorized() -> Error {
|
||||
Error::new(eyre!("UNAUTHORIZED"), crate::ErrorKind::Authorization)
|
||||
}
|
||||
|
||||
async fn check_from_header<C: LocalAuthContext>(header: Option<&HeaderValue>) -> Result<(), Error> {
|
||||
if let Some(cookie_header) = header {
|
||||
let cookies = Cookie::parse(
|
||||
cookie_header
|
||||
.to_str()
|
||||
.with_kind(crate::ErrorKind::Authorization)?,
|
||||
)
|
||||
.with_kind(crate::ErrorKind::Authorization)?;
|
||||
if let Some(cookie) = cookies.iter().find(|c| c.get_name() == "local") {
|
||||
return check_cookie::<C>(cookie).await;
|
||||
}
|
||||
}
|
||||
Err(unauthorized())
|
||||
}
|
||||
|
||||
async fn check_cookie<C: LocalAuthContext>(local: &Cookie<'_>) -> Result<(), Error> {
|
||||
if let Ok(token) = read_file_to_string(C::LOCAL_AUTH_COOKIE_PATH).await {
|
||||
if local.get_value() == &*token {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
Err(unauthorized())
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LocalAuth {
|
||||
cookie: Option<HeaderValue>,
|
||||
}
|
||||
impl LocalAuth {
|
||||
pub fn new() -> Self {
|
||||
Self { cookie: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: LocalAuthContext> Middleware<C> for LocalAuth {
|
||||
type Metadata = Empty;
|
||||
async fn process_http_request(
|
||||
&mut self,
|
||||
_: &C,
|
||||
request: &mut axum::extract::Request,
|
||||
) -> Result<(), axum::response::Response> {
|
||||
self.cookie = request.headers().get(COOKIE).cloned();
|
||||
Ok(())
|
||||
}
|
||||
async fn process_rpc_request(
|
||||
&mut self,
|
||||
_: &C,
|
||||
_: Self::Metadata,
|
||||
_: &mut rpc_toolkit::RpcRequest,
|
||||
) -> Result<(), rpc_toolkit::RpcResponse> {
|
||||
check_from_header::<C>(self.cookie.as_ref())
|
||||
.await
|
||||
.map_err(|e| RpcResponse::from(RpcError::from(e)))
|
||||
}
|
||||
}
|
||||
112
core/startos/src/middleware/auth/mod.rs
Normal file
112
core/startos/src/middleware/auth/mod.rs
Normal file
@@ -0,0 +1,112 @@
|
||||
use axum::extract::Request;
|
||||
use axum::response::Response;
|
||||
use rpc_toolkit::{Context, DynMiddleware, Middleware, RpcRequest, RpcResponse};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::context::RpcContext;
|
||||
use crate::db::model::Database;
|
||||
use crate::middleware::auth::local::{LocalAuth, LocalAuthContext};
|
||||
use crate::middleware::auth::session::{SessionAuth, SessionAuthContext};
|
||||
use crate::middleware::auth::signature::{SignatureAuth, SignatureAuthContext};
|
||||
use crate::prelude::*;
|
||||
use crate::util::serde::const_true;
|
||||
|
||||
pub mod local;
|
||||
pub mod session;
|
||||
pub mod signature;
|
||||
|
||||
pub trait DbContext: Context {
|
||||
type Database: HasModel<Model = Model<Self::Database>> + Send + Sync;
|
||||
fn db(&self) -> &TypedPatchDb<Self::Database>;
|
||||
}
|
||||
impl DbContext for RpcContext {
|
||||
type Database = Database;
|
||||
fn db(&self) -> &TypedPatchDb<Self::Database> {
|
||||
&self.db
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Metadata {
|
||||
#[serde(default = "const_true")]
|
||||
authenticated: bool,
|
||||
}
|
||||
|
||||
pub struct Auth<C: Context>(Vec<DynMiddleware<C>>);
|
||||
impl<C: Context> Clone for Auth<C> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
impl<C: Context> Auth<C> {
|
||||
pub fn new() -> Self {
|
||||
Self(Vec::new())
|
||||
}
|
||||
}
|
||||
impl<C: LocalAuthContext> Auth<C> {
|
||||
pub fn with_local_auth(mut self) -> Self {
|
||||
self.0.push(DynMiddleware::new(LocalAuth::new()));
|
||||
self
|
||||
}
|
||||
}
|
||||
impl<C: SignatureAuthContext> Auth<C> {
|
||||
pub fn with_signature_auth(mut self) -> Self {
|
||||
self.0.push(DynMiddleware::new(SignatureAuth::new()));
|
||||
self
|
||||
}
|
||||
}
|
||||
impl<C: SessionAuthContext> Auth<C> {
|
||||
pub fn with_session_auth(mut self) -> Self {
|
||||
self.0.push(DynMiddleware::new(SessionAuth::new()));
|
||||
self
|
||||
}
|
||||
}
|
||||
impl<C: Context> Middleware<C> for Auth<C> {
|
||||
type Metadata = Value;
|
||||
async fn process_http_request(
|
||||
&mut self,
|
||||
context: &C,
|
||||
request: &mut Request,
|
||||
) -> Result<(), Response> {
|
||||
for middleware in self.0.iter_mut() {
|
||||
middleware.process_http_request(context, request).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
async fn process_rpc_request(
|
||||
&mut self,
|
||||
context: &C,
|
||||
metadata: Self::Metadata,
|
||||
request: &mut RpcRequest,
|
||||
) -> Result<(), RpcResponse> {
|
||||
let m: Metadata =
|
||||
from_value(metadata.clone()).map_err(|e| RpcResponse::from_result(Err(e)))?;
|
||||
if m.authenticated {
|
||||
let mut err = None;
|
||||
for middleware in self.0.iter_mut() {
|
||||
if let Err(e) = middleware
|
||||
.process_rpc_request(context, metadata.clone(), request)
|
||||
.await
|
||||
{
|
||||
err = Some(e);
|
||||
} else {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
if let Some(e) = err {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
async fn process_rpc_response(&mut self, context: &C, response: &mut RpcResponse) {
|
||||
for middleware in self.0.iter_mut() {
|
||||
middleware.process_rpc_response(context, response).await;
|
||||
}
|
||||
}
|
||||
async fn process_http_response(&mut self, context: &C, response: &mut Response) {
|
||||
for middleware in self.0.iter_mut() {
|
||||
middleware.process_http_response(context, response).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,23 @@
|
||||
use std::borrow::Borrow;
|
||||
use std::collections::BTreeSet;
|
||||
use std::future::Future;
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use axum::extract::Request;
|
||||
use axum::response::Response;
|
||||
use base64::Engine;
|
||||
use basic_cookies::Cookie;
|
||||
use chrono::Utc;
|
||||
use color_eyre::eyre::eyre;
|
||||
use digest::Digest;
|
||||
use http::HeaderValue;
|
||||
use http::header::{COOKIE, USER_AGENT};
|
||||
use imbl_value::{InternedString, json};
|
||||
use rand::random;
|
||||
use rpc_toolkit::yajrc::INTERNAL_ERROR;
|
||||
use rpc_toolkit::{Middleware, RpcRequest, RpcResponse};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::Sha256;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::process::Command;
|
||||
use tokio::sync::Mutex;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
use crate::auth::{Sessions, check_password, write_shadow};
|
||||
use crate::context::RpcContext;
|
||||
use crate::middleware::signature::{SignatureAuth, SignatureAuthContext};
|
||||
use crate::middleware::auth::DbContext;
|
||||
use crate::prelude::*;
|
||||
use crate::rpc_continuations::OpenAuthedContinuations;
|
||||
use crate::util::Invoke;
|
||||
@@ -34,24 +25,7 @@ use crate::util::io::{create_file_mod, read_file_to_string};
|
||||
use crate::util::serde::{BASE64, const_true};
|
||||
use crate::util::sync::SyncMutex;
|
||||
|
||||
pub trait AuthContext: SignatureAuthContext {
|
||||
const LOCAL_AUTH_COOKIE_PATH: &str;
|
||||
const LOCAL_AUTH_COOKIE_OWNERSHIP: &str;
|
||||
fn init_auth_cookie() -> impl Future<Output = Result<(), Error>> + Send {
|
||||
async {
|
||||
let mut file = create_file_mod(Self::LOCAL_AUTH_COOKIE_PATH, 0o640).await?;
|
||||
file.write_all(BASE64.encode(random::<[u8; 32]>()).as_bytes())
|
||||
.await?;
|
||||
file.sync_all().await?;
|
||||
drop(file);
|
||||
Command::new("chown")
|
||||
.arg(Self::LOCAL_AUTH_COOKIE_OWNERSHIP)
|
||||
.arg(Self::LOCAL_AUTH_COOKIE_PATH)
|
||||
.invoke(crate::ErrorKind::Filesystem)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
pub trait SessionAuthContext: DbContext {
|
||||
fn ephemeral_sessions(&self) -> &SyncMutex<Sessions>;
|
||||
fn open_authed_continuations(&self) -> &OpenAuthedContinuations<Option<InternedString>>;
|
||||
fn access_sessions(db: &mut Model<Self::Database>) -> &mut Model<Sessions>;
|
||||
@@ -62,9 +36,7 @@ pub trait AuthContext: SignatureAuthContext {
|
||||
}
|
||||
}
|
||||
|
||||
impl AuthContext for RpcContext {
|
||||
const LOCAL_AUTH_COOKIE_PATH: &str = "/run/startos/rpc.authcookie";
|
||||
const LOCAL_AUTH_COOKIE_OWNERSHIP: &str = "root:startos";
|
||||
impl SessionAuthContext for RpcContext {
|
||||
fn ephemeral_sessions(&self) -> &SyncMutex<Sessions> {
|
||||
&self.ephemeral_sessions
|
||||
}
|
||||
@@ -103,7 +75,7 @@ pub trait AsLogoutSessionId {
|
||||
pub struct HasLoggedOutSessions(());
|
||||
|
||||
impl HasLoggedOutSessions {
|
||||
pub async fn new<C: AuthContext>(
|
||||
pub async fn new<C: SessionAuthContext>(
|
||||
sessions: impl IntoIterator<Item = impl AsLogoutSessionId>,
|
||||
ctx: &C,
|
||||
) -> Result<Self, Error> {
|
||||
@@ -134,90 +106,6 @@ impl HasLoggedOutSessions {
|
||||
}
|
||||
}
|
||||
|
||||
/// Used when we need to know that we have logged in with a valid user
|
||||
#[derive(Clone)]
|
||||
pub struct HasValidSession(SessionType);
|
||||
|
||||
#[derive(Clone)]
|
||||
enum SessionType {
|
||||
Local,
|
||||
Session(HashSessionToken),
|
||||
}
|
||||
|
||||
impl HasValidSession {
|
||||
pub async fn from_header<C: AuthContext>(
|
||||
header: Option<&HeaderValue>,
|
||||
ctx: &C,
|
||||
) -> Result<Self, Error> {
|
||||
if let Some(cookie_header) = header {
|
||||
let cookies = Cookie::parse(
|
||||
cookie_header
|
||||
.to_str()
|
||||
.with_kind(crate::ErrorKind::Authorization)?,
|
||||
)
|
||||
.with_kind(crate::ErrorKind::Authorization)?;
|
||||
if let Some(cookie) = cookies.iter().find(|c| c.get_name() == "local") {
|
||||
if let Ok(s) = Self::from_local::<C>(cookie).await {
|
||||
return Ok(s);
|
||||
}
|
||||
}
|
||||
if let Some(cookie) = cookies.iter().find(|c| c.get_name() == "session") {
|
||||
if let Ok(s) = Self::from_session(HashSessionToken::from_cookie(cookie), ctx).await
|
||||
{
|
||||
return Ok(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(Error::new(
|
||||
eyre!("UNAUTHORIZED"),
|
||||
crate::ErrorKind::Authorization,
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn from_session<C: AuthContext>(
|
||||
session_token: HashSessionToken,
|
||||
ctx: &C,
|
||||
) -> Result<Self, Error> {
|
||||
let session_hash = session_token.hashed();
|
||||
if !ctx.ephemeral_sessions().mutate(|s| {
|
||||
if let Some(session) = s.0.get_mut(session_hash) {
|
||||
session.last_active = Utc::now();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}) {
|
||||
ctx.db()
|
||||
.mutate(|db| {
|
||||
C::access_sessions(db)
|
||||
.as_idx_mut(session_hash)
|
||||
.ok_or_else(|| {
|
||||
Error::new(eyre!("UNAUTHORIZED"), crate::ErrorKind::Authorization)
|
||||
})?
|
||||
.mutate(|s| {
|
||||
s.last_active = Utc::now();
|
||||
Ok(())
|
||||
})
|
||||
})
|
||||
.await
|
||||
.result?;
|
||||
}
|
||||
Ok(Self(SessionType::Session(session_token)))
|
||||
}
|
||||
|
||||
pub async fn from_local<C: AuthContext>(local: &Cookie<'_>) -> Result<Self, Error> {
|
||||
let token = read_file_to_string(C::LOCAL_AUTH_COOKIE_PATH).await?;
|
||||
if local.get_value() == &*token {
|
||||
Ok(Self(SessionType::Local))
|
||||
} else {
|
||||
Err(Error::new(
|
||||
eyre!("UNAUTHORIZED"),
|
||||
crate::ErrorKind::Authorization,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// When we have a need to create a new session,
|
||||
/// Or when we are using internal valid authenticated service.
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -312,51 +200,97 @@ impl Borrow<str> for HashSessionToken {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ValidSessionToken(pub HashSessionToken);
|
||||
impl ValidSessionToken {
|
||||
pub async fn from_header<C: SessionAuthContext>(
|
||||
header: Option<&HeaderValue>,
|
||||
ctx: &C,
|
||||
) -> Result<Self, Error> {
|
||||
if let Some(cookie_header) = header {
|
||||
let cookies = Cookie::parse(
|
||||
cookie_header
|
||||
.to_str()
|
||||
.with_kind(crate::ErrorKind::Authorization)?,
|
||||
)
|
||||
.with_kind(crate::ErrorKind::Authorization)?;
|
||||
if let Some(cookie) = cookies.iter().find(|c| c.get_name() == "session") {
|
||||
if let Ok(s) = Self::from_session(HashSessionToken::from_cookie(cookie), ctx).await
|
||||
{
|
||||
return Ok(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(Error::new(
|
||||
eyre!("UNAUTHORIZED"),
|
||||
crate::ErrorKind::Authorization,
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn from_session<C: SessionAuthContext>(
|
||||
session_token: HashSessionToken,
|
||||
ctx: &C,
|
||||
) -> Result<Self, Error> {
|
||||
let session_hash = session_token.hashed();
|
||||
if !ctx.ephemeral_sessions().mutate(|s| {
|
||||
if let Some(session) = s.0.get_mut(session_hash) {
|
||||
session.last_active = Utc::now();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}) {
|
||||
ctx.db()
|
||||
.mutate(|db| {
|
||||
C::access_sessions(db)
|
||||
.as_idx_mut(session_hash)
|
||||
.ok_or_else(|| {
|
||||
Error::new(eyre!("UNAUTHORIZED"), crate::ErrorKind::Authorization)
|
||||
})?
|
||||
.mutate(|s| {
|
||||
s.last_active = Utc::now();
|
||||
Ok(())
|
||||
})
|
||||
})
|
||||
.await
|
||||
.result?;
|
||||
}
|
||||
Ok(Self(session_token))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Metadata {
|
||||
#[serde(default = "const_true")]
|
||||
authenticated: bool,
|
||||
#[serde(default)]
|
||||
login: bool,
|
||||
#[serde(default)]
|
||||
get_session: bool,
|
||||
#[serde(default)]
|
||||
get_signer: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Auth {
|
||||
rate_limiter: Arc<Mutex<(usize, Instant)>>,
|
||||
cookie: Option<HeaderValue>,
|
||||
pub struct SessionAuth {
|
||||
rate_limiter: Arc<SyncMutex<(usize, Instant)>>,
|
||||
is_login: bool,
|
||||
cookie: Option<HeaderValue>,
|
||||
set_cookie: Option<HeaderValue>,
|
||||
user_agent: Option<HeaderValue>,
|
||||
signature_auth: SignatureAuth,
|
||||
}
|
||||
impl Auth {
|
||||
impl SessionAuth {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
rate_limiter: Arc::new(Mutex::new((0, Instant::now()))),
|
||||
cookie: None,
|
||||
rate_limiter: Arc::new(SyncMutex::new((0, Instant::now()))),
|
||||
is_login: false,
|
||||
cookie: None,
|
||||
set_cookie: None,
|
||||
user_agent: None,
|
||||
signature_auth: SignatureAuth::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<C: AuthContext> Middleware<C> for Auth {
|
||||
|
||||
impl<C: SessionAuthContext> Middleware<C> for SessionAuth {
|
||||
type Metadata = Metadata;
|
||||
async fn process_http_request(
|
||||
&mut self,
|
||||
context: &C,
|
||||
request: &mut Request,
|
||||
) -> Result<(), Response> {
|
||||
self.cookie = request.headers_mut().remove(COOKIE);
|
||||
self.user_agent = request.headers_mut().remove(USER_AGENT);
|
||||
self.signature_auth
|
||||
.process_http_request(context, request)
|
||||
.await?;
|
||||
async fn process_http_request(&mut self, _: &C, request: &mut Request) -> Result<(), Response> {
|
||||
self.cookie = request.headers().get(COOKIE).cloned();
|
||||
self.user_agent = request.headers().get(USER_AGENT).cloned();
|
||||
Ok(())
|
||||
}
|
||||
async fn process_rpc_request(
|
||||
@@ -368,56 +302,37 @@ impl<C: AuthContext> Middleware<C> for Auth {
|
||||
async {
|
||||
if metadata.login {
|
||||
self.is_login = true;
|
||||
let guard = self.rate_limiter.lock().await;
|
||||
if guard.1.elapsed() < Duration::from_secs(20) && guard.0 >= 3 {
|
||||
return Err(Error::new(
|
||||
eyre!("Please limit login attempts to 3 per 20 seconds."),
|
||||
crate::ErrorKind::RateLimited,
|
||||
));
|
||||
}
|
||||
self.rate_limiter.mutate(|(count, time)| {
|
||||
if time.elapsed() < Duration::from_secs(20) && *count >= 3 {
|
||||
Err(Error::new(
|
||||
eyre!("Please limit login attempts to 3 per 20 seconds."),
|
||||
crate::ErrorKind::RateLimited,
|
||||
))
|
||||
} else {
|
||||
*count += 1;
|
||||
*time = Instant::now();
|
||||
Ok(())
|
||||
}
|
||||
})?;
|
||||
if let Some(user_agent) = self.user_agent.as_ref().and_then(|h| h.to_str().ok()) {
|
||||
request.params["__Auth_userAgent"] =
|
||||
Value::String(Arc::new(user_agent.to_owned()))
|
||||
// TODO: will this panic?
|
||||
}
|
||||
} else if metadata.authenticated {
|
||||
if self
|
||||
.signature_auth
|
||||
.process_rpc_request(
|
||||
context,
|
||||
from_value(json!({
|
||||
"get_signer": metadata.get_signer
|
||||
}))?,
|
||||
request,
|
||||
)
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
match HasValidSession::from_header(self.cookie.as_ref(), context).await? {
|
||||
HasValidSession(SessionType::Session(s)) if metadata.get_session => {
|
||||
request.params["__Auth_session"] =
|
||||
Value::String(Arc::new(s.hashed().deref().to_owned()));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
} else {
|
||||
let ValidSessionToken(s) =
|
||||
ValidSessionToken::from_header(self.cookie.as_ref(), context).await?;
|
||||
if metadata.get_session {
|
||||
request.params["__Auth_session"] =
|
||||
Value::String(Arc::new(s.hashed().deref().to_owned()));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
Ok::<_, Error>(())
|
||||
}
|
||||
.await
|
||||
.map_err(|e| RpcResponse::from_result(Err(e)))
|
||||
}
|
||||
async fn process_rpc_response(&mut self, _: &C, response: &mut RpcResponse) {
|
||||
if self.is_login {
|
||||
let mut guard = self.rate_limiter.lock().await;
|
||||
if guard.1.elapsed() < Duration::from_secs(20) {
|
||||
if response.result.is_err() {
|
||||
guard.0 += 1;
|
||||
}
|
||||
} else {
|
||||
guard.0 = 0;
|
||||
}
|
||||
guard.1 = Instant::now();
|
||||
if response.result.is_ok() {
|
||||
let res = std::mem::replace(&mut response.result, Err(INTERNAL_ERROR));
|
||||
response.result = async {
|
||||
@@ -8,14 +8,14 @@ use axum::extract::Request;
|
||||
use http::{HeaderMap, HeaderValue};
|
||||
use reqwest::Client;
|
||||
use rpc_toolkit::yajrc::RpcError;
|
||||
use rpc_toolkit::{Context, Middleware, RpcRequest, RpcResponse};
|
||||
use rpc_toolkit::{Middleware, RpcRequest, RpcResponse};
|
||||
use serde::Deserialize;
|
||||
use serde::de::DeserializeOwned;
|
||||
use tokio::sync::Mutex;
|
||||
use url::Url;
|
||||
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
use crate::db::model::Database;
|
||||
use crate::middleware::auth::DbContext;
|
||||
use crate::prelude::*;
|
||||
use crate::sign::commitment::Commitment;
|
||||
use crate::sign::commitment::request::RequestCommitment;
|
||||
@@ -25,11 +25,9 @@ use crate::util::serde::Base64;
|
||||
|
||||
pub const AUTH_SIG_HEADER: &str = "X-StartOS-Auth-Sig";
|
||||
|
||||
pub trait SignatureAuthContext: Context {
|
||||
type Database: HasModel<Model = Model<Self::Database>> + Send + Sync;
|
||||
pub trait SignatureAuthContext: DbContext {
|
||||
type AdditionalMetadata: DeserializeOwned + Send;
|
||||
type CheckPubkeyRes: Send;
|
||||
fn db(&self) -> &TypedPatchDb<Self::Database>;
|
||||
fn sig_context(
|
||||
&self,
|
||||
) -> impl Future<Output = impl IntoIterator<Item = Result<impl AsRef<str> + Send, Error>> + Send>
|
||||
@@ -47,12 +45,8 @@ pub trait SignatureAuthContext: Context {
|
||||
}
|
||||
|
||||
impl SignatureAuthContext for RpcContext {
|
||||
type Database = Database;
|
||||
type AdditionalMetadata = ();
|
||||
type CheckPubkeyRes = ();
|
||||
fn db(&self) -> &TypedPatchDb<Self::Database> {
|
||||
&self.db
|
||||
}
|
||||
async fn sig_context(
|
||||
&self,
|
||||
) -> impl IntoIterator<Item = Result<impl AsRef<str> + Send, Error>> + Send {
|
||||
@@ -96,7 +90,7 @@ impl SignatureAuthContext for RpcContext {
|
||||
}
|
||||
|
||||
Err(Error::new(
|
||||
eyre!("Developer Key is not authorized"),
|
||||
eyre!("Key is not authorized"),
|
||||
ErrorKind::IncorrectPassword,
|
||||
))
|
||||
}
|
||||
@@ -2,4 +2,3 @@ pub mod auth;
|
||||
pub mod connect_info;
|
||||
pub mod cors;
|
||||
pub mod db;
|
||||
pub mod signature;
|
||||
|
||||
@@ -32,7 +32,8 @@ use url::Url;
|
||||
|
||||
use crate::context::{DiagnosticContext, InitContext, InstallContext, RpcContext, SetupContext};
|
||||
use crate::hostname::Hostname;
|
||||
use crate::middleware::auth::{Auth, HasValidSession};
|
||||
use crate::middleware::auth::Auth;
|
||||
use crate::middleware::auth::session::ValidSessionToken;
|
||||
use crate::middleware::cors::Cors;
|
||||
use crate::middleware::db::SyncDb;
|
||||
use crate::net::gateway::GatewayInfo;
|
||||
@@ -79,7 +80,12 @@ impl UiContext for RpcContext {
|
||||
fn middleware(server: Server<Self>) -> HttpServer<Self> {
|
||||
server
|
||||
.middleware(Cors::new())
|
||||
.middleware(Auth::new())
|
||||
.middleware(
|
||||
Auth::new()
|
||||
.with_local_auth()
|
||||
.with_signature_auth()
|
||||
.with_session_auth(),
|
||||
)
|
||||
.middleware(SyncDb::new())
|
||||
}
|
||||
fn extend_router(self, router: Router) -> Router {
|
||||
@@ -404,8 +410,9 @@ async fn if_authorized<
|
||||
f: F,
|
||||
) -> Result<Response, Error> {
|
||||
if let Err(e) =
|
||||
HasValidSession::from_header(request.headers().get(http::header::COOKIE), ctx).await
|
||||
ValidSessionToken::from_header(request.headers().get(http::header::COOKIE), ctx).await
|
||||
{
|
||||
// TODO: other auth methods
|
||||
Ok(unauthorized(e, request.uri().path()))
|
||||
} else {
|
||||
f(request).await
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub use color_eyre::eyre::eyre;
|
||||
pub use imbl_value::InternedString;
|
||||
pub use lazy_format::lazy_format;
|
||||
pub use tracing::instrument;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::sync::Arc;
|
||||
|
||||
use chrono::Utc;
|
||||
use clap::Parser;
|
||||
use cookie::{Cookie, Expiration, SameSite};
|
||||
use http::HeaderMap;
|
||||
use imbl_value::InternedString;
|
||||
use patch_db::PatchDb;
|
||||
@@ -21,7 +22,9 @@ use url::Url;
|
||||
|
||||
use crate::context::config::{CONFIG_PATH, ContextConfig};
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
use crate::middleware::signature::SignatureAuthContext;
|
||||
use crate::middleware::auth::DbContext;
|
||||
use crate::middleware::auth::local::LocalAuthContext;
|
||||
use crate::middleware::auth::signature::SignatureAuthContext;
|
||||
use crate::prelude::*;
|
||||
use crate::registry::RegistryDatabase;
|
||||
use crate::registry::device_info::{DEVICE_INFO_HEADER, DeviceInfo};
|
||||
@@ -29,7 +32,7 @@ use crate::registry::migrations::run_migrations;
|
||||
use crate::registry::signer::SignerInfo;
|
||||
use crate::rpc_continuations::RpcContinuations;
|
||||
use crate::sign::AnyVerifyingKey;
|
||||
use crate::util::io::append_file;
|
||||
use crate::util::io::{append_file, read_file_to_string};
|
||||
|
||||
const DEFAULT_REGISTRY_LISTEN: SocketAddr =
|
||||
SocketAddr::new(std::net::IpAddr::V4(Ipv4Addr::LOCALHOST), 5959);
|
||||
@@ -104,6 +107,8 @@ impl RegistryContext {
|
||||
}
|
||||
db.mutate(|db| run_migrations(db)).await.result?;
|
||||
|
||||
Self::init_auth_cookie().await?;
|
||||
|
||||
let tor_proxy_url = config
|
||||
.tor_proxy
|
||||
.clone()
|
||||
@@ -169,9 +174,26 @@ impl CallRemote<RegistryContext> for CliContext {
|
||||
params: Value,
|
||||
_: Empty,
|
||||
) -> Result<Value, RpcError> {
|
||||
let mut has_cookie = false;
|
||||
if let Ok(local) = read_file_to_string(RegistryContext::LOCAL_AUTH_COOKIE_PATH).await {
|
||||
self.cookie_store
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert_raw(
|
||||
&Cookie::build(("local", local))
|
||||
.domain("localhost")
|
||||
.expires(Expiration::Session)
|
||||
.same_site(SameSite::Strict)
|
||||
.build(),
|
||||
&"http://localhost".parse()?,
|
||||
)
|
||||
.with_kind(crate::ErrorKind::Network)?;
|
||||
has_cookie = true;
|
||||
}
|
||||
|
||||
let url = if let Some(url) = self.registry_url.clone() {
|
||||
url
|
||||
} else if !self.registry_hostname.is_empty() {
|
||||
} else if has_cookie || !self.registry_hostname.is_empty() {
|
||||
let mut url: Url = format!(
|
||||
"http://{}",
|
||||
self.registry_listen.unwrap_or(DEFAULT_REGISTRY_LISTEN)
|
||||
@@ -196,7 +218,7 @@ impl CallRemote<RegistryContext> for CliContext {
|
||||
.cloned()
|
||||
.or_else(|| url.host().as_ref().map(InternedString::from_display));
|
||||
|
||||
crate::middleware::signature::call_remote(
|
||||
crate::middleware::auth::signature::call_remote(
|
||||
self,
|
||||
url,
|
||||
HeaderMap::new(),
|
||||
@@ -230,7 +252,7 @@ impl CallRemote<RegistryContext, RegistryUrlParams> for RpcContext {
|
||||
method = method.strip_prefix("registry.").unwrap_or(method);
|
||||
let sig_context = registry.host_str().map(InternedString::from);
|
||||
|
||||
crate::middleware::signature::call_remote(
|
||||
crate::middleware::auth::signature::call_remote(
|
||||
self,
|
||||
registry,
|
||||
headers,
|
||||
@@ -257,13 +279,19 @@ pub struct AdminLogRecord {
|
||||
pub key: AnyVerifyingKey,
|
||||
}
|
||||
|
||||
impl SignatureAuthContext for RegistryContext {
|
||||
impl DbContext for RegistryContext {
|
||||
type Database = RegistryDatabase;
|
||||
type AdditionalMetadata = RegistryAuthMetadata;
|
||||
type CheckPubkeyRes = Option<(AnyVerifyingKey, SignerInfo)>;
|
||||
fn db(&self) -> &TypedPatchDb<Self::Database> {
|
||||
&self.db
|
||||
}
|
||||
}
|
||||
impl LocalAuthContext for RegistryContext {
|
||||
const LOCAL_AUTH_COOKIE_PATH: &str = "/run/startos/registry.authcookie";
|
||||
const LOCAL_AUTH_COOKIE_OWNERSHIP: &str = "root:root";
|
||||
}
|
||||
impl SignatureAuthContext for RegistryContext {
|
||||
type AdditionalMetadata = RegistryAuthMetadata;
|
||||
type CheckPubkeyRes = Option<(AnyVerifyingKey, SignerInfo)>;
|
||||
async fn sig_context(
|
||||
&self,
|
||||
) -> impl IntoIterator<Item = Result<impl AsRef<str> + Send, Error>> + Send {
|
||||
|
||||
@@ -8,8 +8,8 @@ use serde::{Deserialize, Serialize};
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::context::CliContext;
|
||||
use crate::middleware::auth::Auth;
|
||||
use crate::middleware::cors::Cors;
|
||||
use crate::middleware::signature::SignatureAuth;
|
||||
use crate::net::static_server::{bad_request, not_found, server_error};
|
||||
use crate::prelude::*;
|
||||
use crate::registry::context::RegistryContext;
|
||||
@@ -108,7 +108,7 @@ pub fn registry_router(ctx: RegistryContext) -> Router {
|
||||
any(
|
||||
Server::new(move || ready(Ok(ctx.clone())), registry_api())
|
||||
.middleware(Cors::new())
|
||||
.middleware(SignatureAuth::new())
|
||||
.middleware(Auth::new().with_local_auth().with_signature_auth())
|
||||
.middleware(DeviceInfoMiddleware::new()),
|
||||
)
|
||||
})
|
||||
|
||||
@@ -46,6 +46,7 @@ pub fn package_api<C: Context>() -> ParentHandler<C> {
|
||||
.subcommand(
|
||||
"get",
|
||||
from_fn_async(get::get_package)
|
||||
.with_metadata("authenticated", Value::Bool(false))
|
||||
.with_metadata("get_device_info", Value::Bool(true))
|
||||
.with_display_serializable()
|
||||
.with_custom_display_fn(|handle, result| {
|
||||
|
||||
@@ -9,8 +9,10 @@ use ts_rs::TS;
|
||||
|
||||
use crate::auth::{Sessions, check_password};
|
||||
use crate::context::CliContext;
|
||||
use crate::middleware::auth::AuthContext;
|
||||
use crate::middleware::signature::SignatureAuthContext;
|
||||
use crate::middleware::auth::DbContext;
|
||||
use crate::middleware::auth::local::LocalAuthContext;
|
||||
use crate::middleware::auth::session::SessionAuthContext;
|
||||
use crate::middleware::auth::signature::SignatureAuthContext;
|
||||
use crate::prelude::*;
|
||||
use crate::rpc_continuations::OpenAuthedContinuations;
|
||||
use crate::sign::AnyVerifyingKey;
|
||||
@@ -19,13 +21,15 @@ use crate::tunnel::db::TunnelDatabase;
|
||||
use crate::util::serde::{HandlerExtSerde, display_serializable};
|
||||
use crate::util::sync::SyncMutex;
|
||||
|
||||
impl SignatureAuthContext for TunnelContext {
|
||||
impl DbContext for TunnelContext {
|
||||
type Database = TunnelDatabase;
|
||||
type AdditionalMetadata = ();
|
||||
type CheckPubkeyRes = ();
|
||||
fn db(&self) -> &TypedPatchDb<Self::Database> {
|
||||
&self.db
|
||||
}
|
||||
}
|
||||
impl SignatureAuthContext for TunnelContext {
|
||||
type AdditionalMetadata = ();
|
||||
type CheckPubkeyRes = ();
|
||||
async fn sig_context(
|
||||
&self,
|
||||
) -> impl IntoIterator<Item = Result<impl AsRef<str> + Send, Error>> + Send {
|
||||
@@ -93,9 +97,11 @@ impl SignatureAuthContext for TunnelContext {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl AuthContext for TunnelContext {
|
||||
const LOCAL_AUTH_COOKIE_PATH: &str = "/run/start-tunnel/rpc.authcookie";
|
||||
impl LocalAuthContext for TunnelContext {
|
||||
const LOCAL_AUTH_COOKIE_PATH: &str = "/run/startos/tunnel.authcookie";
|
||||
const LOCAL_AUTH_COOKIE_OWNERSHIP: &str = "root:root";
|
||||
}
|
||||
impl SessionAuthContext for TunnelContext {
|
||||
fn access_sessions(db: &mut Model<Self::Database>) -> &mut Model<crate::auth::Sessions> {
|
||||
db.as_sessions_mut()
|
||||
}
|
||||
|
||||
@@ -24,7 +24,8 @@ use crate::auth::Sessions;
|
||||
use crate::context::config::ContextConfig;
|
||||
use crate::context::{CliContext, RpcContext};
|
||||
use crate::db::model::public::{NetworkInterfaceInfo, NetworkInterfaceType};
|
||||
use crate::middleware::auth::{Auth, AuthContext};
|
||||
use crate::middleware::auth::Auth;
|
||||
use crate::middleware::auth::local::LocalAuthContext;
|
||||
use crate::middleware::cors::Cors;
|
||||
use crate::net::forward::{PortForwardController, add_iptables_rule};
|
||||
use crate::net::static_server::{EMPTY_DIR, UiContext};
|
||||
@@ -279,7 +280,7 @@ impl CallRemote<TunnelContext> for CliContext {
|
||||
|
||||
method = method.strip_prefix("tunnel.").unwrap_or(method);
|
||||
|
||||
crate::middleware::signature::call_remote(
|
||||
crate::middleware::auth::signature::call_remote(
|
||||
self,
|
||||
url,
|
||||
HeaderMap::new(),
|
||||
@@ -308,7 +309,7 @@ impl CallRemote<TunnelContext, TunnelUrlParams> for RpcContext {
|
||||
|
||||
let sig_ctx = url.host_str().map(InternedString::from_display);
|
||||
|
||||
crate::middleware::signature::call_remote(
|
||||
crate::middleware::auth::signature::call_remote(
|
||||
self,
|
||||
url,
|
||||
HeaderMap::new(),
|
||||
@@ -331,6 +332,11 @@ impl UiContext for TunnelContext {
|
||||
tunnel_api()
|
||||
}
|
||||
fn middleware(server: rpc_toolkit::Server<Self>) -> rpc_toolkit::HttpServer<Self> {
|
||||
server.middleware(Cors::new()).middleware(Auth::new())
|
||||
server.middleware(Cors::new()).middleware(
|
||||
Auth::new()
|
||||
.with_local_auth()
|
||||
.with_signature_auth()
|
||||
.with_session_auth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user