Compare commits
2 commits
e894e8eb18
...
35f706b537
Author | SHA1 | Date | |
---|---|---|---|
35f706b537 | |||
74f12d0b55 |
10 changed files with 274 additions and 44 deletions
140
Cargo.lock
generated
140
Cargo.lock
generated
|
@ -252,6 +252,26 @@ version = "1.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bindgen"
|
||||||
|
version = "0.69.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cexpr",
|
||||||
|
"clang-sys",
|
||||||
|
"itertools",
|
||||||
|
"lazy_static",
|
||||||
|
"lazycell",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"rustc-hash 1.1.0",
|
||||||
|
"shlex",
|
||||||
|
"syn 2.0.79",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.6.0"
|
version = "2.6.0"
|
||||||
|
@ -297,12 +317,31 @@ dependencies = [
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cexpr"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
|
||||||
|
dependencies = [
|
||||||
|
"nom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clang-sys"
|
||||||
|
version = "1.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
|
||||||
|
dependencies = [
|
||||||
|
"glob",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.20"
|
version = "4.5.20"
|
||||||
|
@ -496,6 +535,17 @@ dependencies = [
|
||||||
"powerfmt",
|
"powerfmt",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive-debug"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e53ef7e1cf756fd5a8e74b9a0a9504ec446eddde86c3063a76ff26a13b7773b1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.7"
|
version = "0.10.7"
|
||||||
|
@ -543,6 +593,18 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "enum_dispatch"
|
||||||
|
version = "0.3.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.79",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
@ -600,10 +662,13 @@ dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"clap",
|
"clap",
|
||||||
"color-eyre",
|
"color-eyre",
|
||||||
|
"derive-debug",
|
||||||
|
"enum_dispatch",
|
||||||
"hex",
|
"hex",
|
||||||
"hmac",
|
"hmac",
|
||||||
"ldap3",
|
"ldap3",
|
||||||
"md-5",
|
"md-5",
|
||||||
|
"pam",
|
||||||
"rand",
|
"rand",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"secrecy",
|
"secrecy",
|
||||||
|
@ -779,6 +844,12 @@ version = "0.28.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glob"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.4.6"
|
version = "0.4.6"
|
||||||
|
@ -1013,6 +1084,15 @@ version = "1.70.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.11"
|
version = "1.0.11"
|
||||||
|
@ -1037,6 +1117,12 @@ dependencies = [
|
||||||
"spin 0.9.8",
|
"spin 0.9.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazycell"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lber"
|
name = "lber"
|
||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
|
@ -1308,6 +1394,40 @@ version = "3.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
|
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pam"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ab553c52103edb295d8f7d6a3b593dc22a30b1fb99643c777a8f36915e285ba"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"memchr",
|
||||||
|
"pam-macros",
|
||||||
|
"pam-sys",
|
||||||
|
"users",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pam-macros"
|
||||||
|
version = "0.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c94f3b9b97df3c6d4e51a14916639b24e02c7d15d1dba686ce9b1118277cb811"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pam-sys"
|
||||||
|
version = "1.0.0-alpha5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ce9484729b3e52c0bacdc5191cb6a6a5f31ef4c09c5e4ab1209d3340ad9e997b"
|
||||||
|
dependencies = [
|
||||||
|
"bindgen",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking"
|
name = "parking"
|
||||||
version = "2.2.1"
|
version = "2.2.1"
|
||||||
|
@ -1431,7 +1551,7 @@ dependencies = [
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"quinn-proto",
|
"quinn-proto",
|
||||||
"quinn-udp",
|
"quinn-udp",
|
||||||
"rustc-hash",
|
"rustc-hash 2.0.0",
|
||||||
"rustls 0.23.14",
|
"rustls 0.23.14",
|
||||||
"socket2",
|
"socket2",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
@ -1448,7 +1568,7 @@ dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"rand",
|
"rand",
|
||||||
"ring 0.17.8",
|
"ring 0.17.8",
|
||||||
"rustc-hash",
|
"rustc-hash 2.0.0",
|
||||||
"rustls 0.23.14",
|
"rustls 0.23.14",
|
||||||
"slab",
|
"slab",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
@ -1662,6 +1782,12 @@ version = "0.1.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-hash"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-hash"
|
name = "rustc-hash"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
|
@ -2690,6 +2816,16 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "users"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aa4227e95324a443c9fcb06e03d4d85e91aabe9a5a02aa818688b6918b6af486"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
|
|
@ -21,10 +21,13 @@ axum = "0.7.4"
|
||||||
base64 = "0.22.0"
|
base64 = "0.22.0"
|
||||||
clap = { version = "4.5.0", features = ["derive"] }
|
clap = { version = "4.5.0", features = ["derive"] }
|
||||||
color-eyre = { version = "0.6.2" }
|
color-eyre = { version = "0.6.2" }
|
||||||
|
derive-debug = "0.1.2"
|
||||||
|
enum_dispatch = "0.3.13"
|
||||||
hex = { version = "0.4.3", features = ["serde"] }
|
hex = { version = "0.4.3", features = ["serde"] }
|
||||||
hmac = "0.12.1"
|
hmac = "0.12.1"
|
||||||
ldap3 = { version = "0.11.3", default-features = false, features = ["tls-rustls"] }
|
ldap3 = { version = "0.11.3", default-features = false, features = ["tls-rustls"] }
|
||||||
md-5 = "0.10.6"
|
md-5 = "0.10.6"
|
||||||
|
pam = "0.8.0"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
reqwest = { version = "0.12.0", default-features = false, features = ["charset", "http2", "macos-system-configuration", "json", "rustls-tls-native-roots"] }
|
reqwest = { version = "0.12.0", default-features = false, features = ["charset", "http2", "macos-system-configuration", "json", "rustls-tls-native-roots"] }
|
||||||
secrecy = { version = "0.10.0", features = ["serde"] }
|
secrecy = { version = "0.10.0", features = ["serde"] }
|
||||||
|
|
|
@ -13,8 +13,8 @@ which is no good, especially for PvP scenarios! Wouldn't it be great if you coul
|
||||||
authentication server?
|
authentication server?
|
||||||
|
|
||||||
factoriauth is exactly that. It allows clients to log in as custom users provided by one of several
|
factoriauth is exactly that. It allows clients to log in as custom users provided by one of several
|
||||||
authentication backends (e.g. LDAP or a passwd-style file), and allows servers to validate that
|
authentication backends (e.g. local users via PAM, LDAP, or a passwd-style file), and allows servers
|
||||||
these custom users are properly authenticated.
|
to validate that these custom users are properly authenticated.
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
|
@ -25,12 +25,13 @@ these custom users are properly authenticated.
|
||||||
- [user token generation](https://wiki.factorio.com/Web_authentication_API) and storage (`POST
|
- [user token generation](https://wiki.factorio.com/Web_authentication_API) and storage (`POST
|
||||||
/api-login`)
|
/api-login`)
|
||||||
- LDAP authentication backend
|
- LDAP authentication backend
|
||||||
|
- PAM authentication backend
|
||||||
- server padlock proxying (to allow e.g. factorio.com users to join servers using a custom auth
|
- server padlock proxying (to allow e.g. factorio.com users to join servers using a custom auth
|
||||||
server)
|
server)
|
||||||
|
|
||||||
### Planned
|
### Planned
|
||||||
|
|
||||||
- more authentication backends: user file, PAM(?)
|
- more authentication backends: user file
|
||||||
|
|
||||||
### Unplanned
|
### Unplanned
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,14 @@ secret = ""
|
||||||
[database]
|
[database]
|
||||||
connection-string = "sqlite://sqlite.db"
|
connection-string = "sqlite://sqlite.db"
|
||||||
|
|
||||||
[[auth-backends]]
|
# uncomment to allow login as local user via PAM
|
||||||
type = "LDAP"
|
#[[auth-backends]]
|
||||||
server-address = "ldap://ldap.example.com"
|
#type = "PAM"
|
||||||
search-base = "ou=users,dc=example,dc=com"
|
#service-name = "system-auth"
|
||||||
user-filter = "(|(uid=%s)(mail=%s))"
|
|
||||||
|
# uncomment and configure to allow login via LDAP
|
||||||
|
#[[auth-backends]]
|
||||||
|
#type = "LDAP"
|
||||||
|
#server-address = "ldap://ldap.example.com"
|
||||||
|
#search-base = "ou=users,dc=example,dc=com"
|
||||||
|
#user-filter = "(|(uid=%s)(mail=%s))"
|
||||||
|
|
|
@ -5,19 +5,9 @@ use secrecy::ExposeSecret;
|
||||||
use tokio::{sync::Mutex, time::sleep};
|
use tokio::{sync::Mutex, time::sleep};
|
||||||
use tracing::{event, instrument, Level};
|
use tracing::{event, instrument, Level};
|
||||||
|
|
||||||
use crate::{config::LdapBackendConfig, secrets::Password};
|
use crate::{auth::AuthenticationError, config::LdapBackendConfig, secrets::Password};
|
||||||
|
|
||||||
use super::AuthenticationError;
|
use super::ValidateLogin;
|
||||||
|
|
||||||
pub trait ValidateLogin {
|
|
||||||
/// Validates that the given username and password combination is correct, and returns the
|
|
||||||
/// (normalized) user ID.
|
|
||||||
async fn validate_login(
|
|
||||||
&self,
|
|
||||||
username: &str,
|
|
||||||
password: &Password,
|
|
||||||
) -> Result<String, AuthenticationError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument]
|
#[instrument]
|
||||||
async fn start_ldap_connection(
|
async fn start_ldap_connection(
|
14
src/auth/backends/mod.rs
Normal file
14
src/auth/backends/mod.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
use crate::{auth::AuthenticationError, secrets::Password};
|
||||||
|
|
||||||
|
pub mod ldap_backend;
|
||||||
|
pub mod pam_backend;
|
||||||
|
|
||||||
|
pub trait ValidateLogin {
|
||||||
|
/// Validates that the given username and password combination is correct, and returns the
|
||||||
|
/// (normalized) user ID.
|
||||||
|
async fn validate_login(
|
||||||
|
&self,
|
||||||
|
username: &str,
|
||||||
|
password: &Password,
|
||||||
|
) -> Result<String, AuthenticationError>;
|
||||||
|
}
|
57
src/auth/backends/pam_backend.rs
Normal file
57
src/auth/backends/pam_backend.rs
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
use derive_debug::Dbg;
|
||||||
|
use secrecy::ExposeSecret;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
use tracing::{event, Level};
|
||||||
|
|
||||||
|
use pam::{Client, PasswordConv};
|
||||||
|
|
||||||
|
use crate::{auth::AuthenticationError, config::PamBackendConfig, secrets::Password};
|
||||||
|
|
||||||
|
use super::ValidateLogin;
|
||||||
|
|
||||||
|
#[derive(Dbg)]
|
||||||
|
pub struct PamBackend<'a> {
|
||||||
|
#[dbg(skip)]
|
||||||
|
pam: Arc<Mutex<pam::Client<'a, PasswordConv>>>,
|
||||||
|
config: PamBackendConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PamBackend<'_> {
|
||||||
|
pub fn new(config: PamBackendConfig) -> Result<Arc<Self>, AuthenticationError> {
|
||||||
|
let pam = Client::with_password(&config.service_name)?;
|
||||||
|
|
||||||
|
event!(Level::INFO, "PAM client initialized");
|
||||||
|
|
||||||
|
Ok(Arc::new(Self {
|
||||||
|
pam: Arc::new(Mutex::new(pam)),
|
||||||
|
config,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ValidateLogin for PamBackend<'_> {
|
||||||
|
async fn validate_login(
|
||||||
|
&self,
|
||||||
|
username: &str,
|
||||||
|
password: &Password,
|
||||||
|
) -> Result<String, AuthenticationError> {
|
||||||
|
let mut pam = self.pam.lock().await;
|
||||||
|
|
||||||
|
pam.conversation_mut()
|
||||||
|
.set_credentials(username, password.0.expose_secret());
|
||||||
|
|
||||||
|
if let Err(pam_error) = pam.authenticate() {
|
||||||
|
event!(
|
||||||
|
Level::INFO,
|
||||||
|
username,
|
||||||
|
pam_error = pam_error.to_string(),
|
||||||
|
"PAM authentication failed"
|
||||||
|
);
|
||||||
|
return Err(AuthenticationError::InvalidUserOrPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
event!(Level::INFO, username, "PAM authentication succeeded");
|
||||||
|
Ok(username.to_string())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use backends::pam_backend::PamBackend;
|
||||||
use base64::{prelude::BASE64_STANDARD, Engine};
|
use base64::{prelude::BASE64_STANDARD, Engine};
|
||||||
use hmac::{Hmac, Mac};
|
use hmac::{Hmac, Mac};
|
||||||
use md5::Md5;
|
use md5::Md5;
|
||||||
|
@ -24,7 +25,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::backends::{LdapBackend, ValidateLogin};
|
use self::backends::{ldap_backend::LdapBackend, ValidateLogin};
|
||||||
|
|
||||||
pub mod backends;
|
pub mod backends;
|
||||||
|
|
||||||
|
@ -38,8 +39,10 @@ pub enum AuthenticationError {
|
||||||
InvalidServerHash,
|
InvalidServerHash,
|
||||||
#[error("Database error")]
|
#[error("Database error")]
|
||||||
Database(#[from] sqlx::Error),
|
Database(#[from] sqlx::Error),
|
||||||
#[error("Authentication backend error")]
|
#[error("LDAP backend error")]
|
||||||
Backend(#[from] ldap3::LdapError),
|
LdapBackend(#[from] ldap3::LdapError),
|
||||||
|
#[error("PAM backend error")]
|
||||||
|
PamBackend(#[from] pam::PamError),
|
||||||
#[error("Padlock proxy error")]
|
#[error("Padlock proxy error")]
|
||||||
PadlockProxy(#[from] PadlockProxyError),
|
PadlockProxy(#[from] PadlockProxyError),
|
||||||
#[error("No authentication backends available")]
|
#[error("No authentication backends available")]
|
||||||
|
@ -47,19 +50,21 @@ pub enum AuthenticationError {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum AuthenticationBackend {
|
pub enum AuthenticationBackend<'a> {
|
||||||
Ldap(Arc<LdapBackend>),
|
Ldap(Arc<LdapBackend>),
|
||||||
|
Pam(Arc<PamBackend<'a>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AuthenticationBackend {
|
impl AuthenticationBackend<'_> {
|
||||||
pub async fn new(config: AuthBackendConfig) -> Result<Self, AuthenticationError> {
|
pub async fn new(config: AuthBackendConfig) -> Result<Self, AuthenticationError> {
|
||||||
match config {
|
match config {
|
||||||
AuthBackendConfig::Ldap(c) => Ok(Self::Ldap(LdapBackend::new(c).await?)),
|
AuthBackendConfig::Ldap(c) => Ok(Self::Ldap(LdapBackend::new(c).await?)),
|
||||||
|
AuthBackendConfig::Pam(c) => Ok(Self::Pam(PamBackend::new(c)?)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ValidateLogin for AuthenticationBackend {
|
impl ValidateLogin for AuthenticationBackend<'_> {
|
||||||
async fn validate_login(
|
async fn validate_login(
|
||||||
&self,
|
&self,
|
||||||
username: &str,
|
username: &str,
|
||||||
|
@ -67,22 +72,23 @@ impl ValidateLogin for AuthenticationBackend {
|
||||||
) -> Result<String, AuthenticationError> {
|
) -> Result<String, AuthenticationError> {
|
||||||
match self {
|
match self {
|
||||||
AuthenticationBackend::Ldap(b) => b.validate_login(username, password).await,
|
AuthenticationBackend::Ldap(b) => b.validate_login(username, password).await,
|
||||||
|
AuthenticationBackend::Pam(b) => b.validate_login(username, password).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct UserAuthenticator {
|
pub struct UserAuthenticator<'a> {
|
||||||
db: Arc<Mutex<Box<dyn Database + Send>>>,
|
db: Arc<Mutex<Box<dyn Database + Send>>>,
|
||||||
backends: Vec<AuthenticationBackend>,
|
backends: Vec<AuthenticationBackend<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserAuthenticator {
|
impl<'a> UserAuthenticator<'a> {
|
||||||
const TOKEN_LEN: usize = 30;
|
const TOKEN_LEN: usize = 30;
|
||||||
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
db: Arc<Mutex<Box<dyn Database + Send>>>,
|
db: Arc<Mutex<Box<dyn Database + Send>>>,
|
||||||
backends: Vec<AuthenticationBackend>,
|
backends: Vec<AuthenticationBackend<'a>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self { db, backends }
|
Self { db, backends }
|
||||||
}
|
}
|
||||||
|
@ -298,14 +304,14 @@ impl ServerPadlockGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct UserServerKeyGenerator {
|
pub struct UserServerKeyGenerator<'a> {
|
||||||
user_authenticator: Arc<UserAuthenticator>,
|
user_authenticator: Arc<UserAuthenticator<'a>>,
|
||||||
padlock_generator: Arc<ServerPadlockGenerator>,
|
padlock_generator: Arc<ServerPadlockGenerator>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserServerKeyGenerator {
|
impl<'a> UserServerKeyGenerator<'a> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
user_authenticator: Arc<UserAuthenticator>,
|
user_authenticator: Arc<UserAuthenticator<'a>>,
|
||||||
padlock_generator: Arc<ServerPadlockGenerator>,
|
padlock_generator: Arc<ServerPadlockGenerator>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
|
@ -42,6 +42,8 @@ pub struct DatabaseConfig {
|
||||||
pub enum AuthBackendConfig {
|
pub enum AuthBackendConfig {
|
||||||
#[serde(rename = "LDAP")]
|
#[serde(rename = "LDAP")]
|
||||||
Ldap(LdapBackendConfig),
|
Ldap(LdapBackendConfig),
|
||||||
|
#[serde(rename = "PAM")]
|
||||||
|
Pam(PamBackendConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
@ -53,3 +55,18 @@ pub struct LdapBackendConfig {
|
||||||
/// User filter template. All occurences of `%s` will be replaced with the username.
|
/// User filter template. All occurences of `%s` will be replaced with the username.
|
||||||
pub user_filter: String,
|
pub user_filter: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
|
#[allow(clippy::module_name_repetitions)]
|
||||||
|
pub struct PamBackendConfig {
|
||||||
|
pub service_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PamBackendConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
service_name: "system-auth".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -18,18 +18,18 @@ use crate::auth::{
|
||||||
use crate::secrets::{Password, ServerHash, UserToken};
|
use crate::secrets::{Password, ServerHash, UserToken};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct AppState {
|
struct AppState<'a> {
|
||||||
user_authenticator: Arc<UserAuthenticator>,
|
user_authenticator: Arc<UserAuthenticator<'a>>,
|
||||||
server_padlock_generator: Arc<ServerPadlockGenerator>,
|
server_padlock_generator: Arc<ServerPadlockGenerator>,
|
||||||
user_server_key_generator: Arc<UserServerKeyGenerator>,
|
user_server_key_generator: Arc<UserServerKeyGenerator<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument]
|
#[instrument]
|
||||||
pub async fn run(
|
pub async fn run(
|
||||||
listen: SocketAddr,
|
listen: SocketAddr,
|
||||||
user_authenticator: Arc<UserAuthenticator>,
|
user_authenticator: Arc<UserAuthenticator<'static>>,
|
||||||
server_padlock_generator: Arc<ServerPadlockGenerator>,
|
server_padlock_generator: Arc<ServerPadlockGenerator>,
|
||||||
user_server_key_generator: Arc<UserServerKeyGenerator>,
|
user_server_key_generator: Arc<UserServerKeyGenerator<'static>>,
|
||||||
) -> color_eyre::Result<()> {
|
) -> color_eyre::Result<()> {
|
||||||
let app_state = AppState {
|
let app_state = AppState {
|
||||||
user_authenticator,
|
user_authenticator,
|
||||||
|
@ -110,7 +110,7 @@ struct LoginResponse {
|
||||||
async fn api_login(
|
async fn api_login(
|
||||||
State(AppState {
|
State(AppState {
|
||||||
user_authenticator, ..
|
user_authenticator, ..
|
||||||
}): State<AppState>,
|
}): State<AppState<'_>>,
|
||||||
Query(ApiVersion { api_version }): Query<ApiVersion>,
|
Query(ApiVersion { api_version }): Query<ApiVersion>,
|
||||||
Form(LoginRequest { username, password }): Form<LoginRequest>,
|
Form(LoginRequest { username, password }): Form<LoginRequest>,
|
||||||
) -> ApiResult<Json<LoginResponse>> {
|
) -> ApiResult<Json<LoginResponse>> {
|
||||||
|
@ -144,7 +144,7 @@ async fn generate_user_server_key_2(
|
||||||
State(AppState {
|
State(AppState {
|
||||||
user_server_key_generator,
|
user_server_key_generator,
|
||||||
..
|
..
|
||||||
}): State<AppState>,
|
}): State<AppState<'_>>,
|
||||||
Query(ApiVersion { api_version }): Query<ApiVersion>,
|
Query(ApiVersion { api_version }): Query<ApiVersion>,
|
||||||
Form(UserServerKeyRequest {
|
Form(UserServerKeyRequest {
|
||||||
username,
|
username,
|
||||||
|
@ -175,7 +175,7 @@ async fn generate_server_padlock_2(
|
||||||
State(AppState {
|
State(AppState {
|
||||||
server_padlock_generator,
|
server_padlock_generator,
|
||||||
..
|
..
|
||||||
}): State<AppState>,
|
}): State<AppState<'_>>,
|
||||||
Query(ApiVersion { api_version }): Query<ApiVersion>,
|
Query(ApiVersion { api_version }): Query<ApiVersion>,
|
||||||
) -> ApiResult<Json<ServerPadlockResponse>> {
|
) -> ApiResult<Json<ServerPadlockResponse>> {
|
||||||
event!(Level::INFO, "Creating server padlock");
|
event!(Level::INFO, "Creating server padlock");
|
||||||
|
|
Loading…
Reference in a new issue