mirror of
https://github.com/goauthentik/authentik.git
synced 2026-06-17 19:09:11 +03:00
root: introduce allinone mode (#21990)
This commit is contained in:
committed by
GitHub
parent
82fc2e2c80
commit
ba62507fc2
@@ -229,6 +229,11 @@ source_docs/
|
||||
|
||||
### Golang ###
|
||||
/vendor/
|
||||
server
|
||||
proxy
|
||||
ldap
|
||||
rac
|
||||
radius
|
||||
|
||||
### Docker ###
|
||||
tests/openid_conformance/exports/*.zip
|
||||
|
||||
Generated
+10
@@ -191,6 +191,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tracing",
|
||||
"uuid",
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4512,6 +4513,15 @@ dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "8.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81995fafaaaf6ae47a7d0cc83c67caf92aeb7e5331650ae6ff856f7c0c60c459"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "whoami"
|
||||
version = "1.6.1"
|
||||
|
||||
@@ -113,6 +113,7 @@ tracing-subscriber = { version = "= 0.3.23", features = [
|
||||
] }
|
||||
url = "= 2.5.8"
|
||||
uuid = { version = "= 1.23.1", features = ["serde", "v4"] }
|
||||
which = "= 8.0.2"
|
||||
|
||||
ak-axum = { package = "authentik-axum", version = "2026.5.0-rc1", path = "./packages/ak-axum" }
|
||||
ak-client = { package = "authentik-client", version = "2026.5.0-rc1", path = "./packages/client-rust" }
|
||||
@@ -282,6 +283,7 @@ sqlx = { workspace = true, optional = true }
|
||||
tokio.workspace = true
|
||||
tracing.workspace = true
|
||||
uuid.workspace = true
|
||||
which.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -109,14 +109,11 @@ i18n-extract: core-i18n-extract web-i18n-extract ## Extract strings that requir
|
||||
aws-cfn:
|
||||
cd lifecycle/aws && npm i && $(UV) run npm run aws-cfn
|
||||
|
||||
run-server: ## Run the main authentik server process
|
||||
$(UV) run ak server
|
||||
run: ## Run the main authentik server and worker processes
|
||||
$(UV) run ak allinone
|
||||
|
||||
run-worker: ## Run the main authentik worker process
|
||||
$(UV) run ak worker
|
||||
|
||||
run-worker-watch: ## Run the authentik worker, with auto reloading
|
||||
watchexec --on-busy-update=restart --stop-signal=SIGINT --exts py,rs --no-meta --notify -- $(UV) run ak worker
|
||||
run-watch: ## Run the authentik server and worker, with auto reloading
|
||||
watchexec --on-busy-update=restart --stop-signal=SIGINT --exts py,rs,go --no-meta --notify -- $(UV) run ak allinone
|
||||
|
||||
core-i18n-extract:
|
||||
$(UV) run ak makemessages \
|
||||
|
||||
@@ -26,6 +26,8 @@ var healthcheckCmd = &cobra.Command{
|
||||
exitCode := 1
|
||||
log.WithField("mode", mode).Debug("checking health")
|
||||
switch strings.ToLower(mode) {
|
||||
case "allinone":
|
||||
fallthrough
|
||||
case "server":
|
||||
exitCode = check(fmt.Sprintf("http://localhost%s-/health/live/", config.Get().Web.Path))
|
||||
case "worker":
|
||||
|
||||
+2
-2
@@ -31,7 +31,7 @@ function run_authentik {
|
||||
echo go run ./cmd/server "$@"
|
||||
fi
|
||||
;;
|
||||
worker)
|
||||
allinone | worker)
|
||||
if [[ -x "$(command -v authentik)" ]]; then
|
||||
echo authentik "$@"
|
||||
else
|
||||
@@ -105,7 +105,7 @@ elif [[ "$1" == "test-all" ]]; then
|
||||
prepare_debug
|
||||
chmod 777 /root
|
||||
check_if_root_and_run manage test authentik
|
||||
elif [[ "$1" == "server" ]] || [[ "$1" == "worker" ]]; then
|
||||
elif [[ "$1" == "allinone" ]] || [[ "$1" == "server" ]] || [[ "$1" == "worker" ]]; then
|
||||
wait_for_db
|
||||
check_if_root_and_run "$@"
|
||||
elif [[ "$1" == "healthcheck" ]]; then
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Utilities to run an axum server.
|
||||
|
||||
use std::{net, os::unix};
|
||||
use std::{net, os::unix, path::PathBuf};
|
||||
|
||||
use ak_common::arbiter::{Arbiter, Tasks};
|
||||
use axum::Router;
|
||||
@@ -21,26 +21,20 @@ async fn run_plain(
|
||||
name: &str,
|
||||
router: Router,
|
||||
addr: net::SocketAddr,
|
||||
allow_failure: bool,
|
||||
) -> Result<()> {
|
||||
info!(addr = addr.to_string(), "starting {name} server");
|
||||
|
||||
let handle = Handle::new();
|
||||
arbiter.add_net_handle(handle.clone()).await;
|
||||
|
||||
let res = axum_server::Server::bind(addr)
|
||||
axum_server::Server::bind(addr)
|
||||
.acceptor(CatchPanicAcceptor::new(
|
||||
ProxyProtocolAcceptor::new().acceptor(DefaultAcceptor::new()),
|
||||
arbiter.clone(),
|
||||
))
|
||||
.handle(handle)
|
||||
.serve(router.into_make_service_with_connect_info::<net::SocketAddr>())
|
||||
.await;
|
||||
if res.is_err() && allow_failure {
|
||||
arbiter.shutdown().await;
|
||||
return Ok(());
|
||||
}
|
||||
res?;
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -49,60 +43,59 @@ async fn run_plain(
|
||||
///
|
||||
/// `name` is only used for observability purposes and should describe which module is starting the
|
||||
/// server.
|
||||
///
|
||||
/// `allow_failure` allows the server to fail silently.
|
||||
pub fn start_plain(
|
||||
tasks: &mut Tasks,
|
||||
name: &'static str,
|
||||
router: Router,
|
||||
addr: net::SocketAddr,
|
||||
allow_failure: bool,
|
||||
) -> Result<()> {
|
||||
let arbiter = tasks.arbiter();
|
||||
tasks
|
||||
.build_task()
|
||||
.name(&format!("{}::run_plain({name}, {addr})", module_path!()))
|
||||
.spawn(run_plain(arbiter, name, router, addr, allow_failure))?;
|
||||
.spawn(run_plain(arbiter, name, router, addr))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct UnixSocketGuard(PathBuf);
|
||||
|
||||
impl Drop for UnixSocketGuard {
|
||||
fn drop(&mut self) {
|
||||
trace!(path = ?self.0, "removing socket");
|
||||
if let Err(err) = std::fs::remove_file(&self.0) {
|
||||
trace!(?err, "failed to remove socket, ignoring");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn run_unix(
|
||||
arbiter: Arbiter,
|
||||
name: &str,
|
||||
router: Router,
|
||||
addr: unix::net::SocketAddr,
|
||||
allow_failure: bool,
|
||||
) -> Result<()> {
|
||||
info!(?addr, "starting {name} server");
|
||||
|
||||
let handle = Handle::new();
|
||||
arbiter.add_unix_handle(handle.clone()).await;
|
||||
|
||||
if !allow_failure && let Some(path) = addr.as_pathname() {
|
||||
let _guard = if let Some(path) = addr.as_pathname() {
|
||||
trace!(?addr, "removing socket");
|
||||
if let Err(err) = std::fs::remove_file(path) {
|
||||
trace!(?err, "failed to remove socket, ignoring");
|
||||
}
|
||||
}
|
||||
let res = axum_server::Server::bind(addr.clone())
|
||||
Some(UnixSocketGuard(path.to_owned()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
axum_server::Server::bind(addr.clone())
|
||||
.acceptor(CatchPanicAcceptor::new(
|
||||
DefaultAcceptor::new(),
|
||||
arbiter.clone(),
|
||||
))
|
||||
.handle(handle)
|
||||
.serve(router.into_make_service())
|
||||
.await;
|
||||
if !allow_failure && let Some(path) = addr.as_pathname() {
|
||||
trace!(?addr, "removing socket");
|
||||
if let Err(err) = std::fs::remove_file(path) {
|
||||
trace!(?err, "failed to remove socket, ignoring");
|
||||
}
|
||||
}
|
||||
if res.is_err() && allow_failure {
|
||||
arbiter.shutdown().await;
|
||||
return Ok(());
|
||||
}
|
||||
res?;
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -111,20 +104,17 @@ pub(crate) async fn run_unix(
|
||||
///
|
||||
/// `name` is only used for observability purposes and should describe which module is starting the
|
||||
/// server.
|
||||
///
|
||||
/// `allow_failure` allows the server to fail silently.
|
||||
pub fn start_unix(
|
||||
tasks: &mut Tasks,
|
||||
name: &'static str,
|
||||
router: Router,
|
||||
addr: unix::net::SocketAddr,
|
||||
allow_failure: bool,
|
||||
) -> Result<()> {
|
||||
let arbiter = tasks.arbiter();
|
||||
tasks
|
||||
.build_task()
|
||||
.name(&format!("{}::run_unix({name}, {addr:?})", module_path!()))
|
||||
.spawn(run_unix(arbiter, name, router, addr, allow_failure))?;
|
||||
.spawn(run_unix(arbiter, name, router, addr))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
+27
@@ -23,10 +23,23 @@ struct Cli {
|
||||
#[derive(Debug, FromArgs, PartialEq)]
|
||||
#[argh(subcommand)]
|
||||
enum Command {
|
||||
#[cfg(feature = "core")]
|
||||
AllInOne(AllInOne),
|
||||
#[cfg(feature = "core")]
|
||||
Server(server::Cli),
|
||||
#[cfg(feature = "core")]
|
||||
Worker(worker::Cli),
|
||||
}
|
||||
|
||||
#[derive(Debug, FromArgs, PartialEq)]
|
||||
/// Run both the authentik server and worker.
|
||||
#[argh(subcommand, name = "allinone")]
|
||||
#[expect(
|
||||
clippy::empty_structs_with_brackets,
|
||||
reason = "argh doesn't support unit structs"
|
||||
)]
|
||||
pub(crate) struct AllInOne {}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let tracing_crude = ak_tracing::install_crude();
|
||||
info!(version = authentik_full_version(), "authentik is starting");
|
||||
@@ -34,6 +47,10 @@ fn main() -> Result<()> {
|
||||
let cli: Cli = argh::from_env();
|
||||
|
||||
match &cli.command {
|
||||
#[cfg(feature = "core")]
|
||||
Command::AllInOne(_) => Mode::set(Mode::AllInOne)?,
|
||||
#[cfg(feature = "core")]
|
||||
Command::Server(_) => Mode::set(Mode::Server)?,
|
||||
#[cfg(feature = "core")]
|
||||
Command::Worker(_) => Mode::set(Mode::Worker)?,
|
||||
}
|
||||
@@ -76,6 +93,16 @@ fn main() -> Result<()> {
|
||||
}
|
||||
|
||||
match cli.command {
|
||||
#[cfg(feature = "core")]
|
||||
Command::AllInOne(_) => {
|
||||
server::start(server::Cli::default(), &mut tasks).await?;
|
||||
let workers = worker::start(worker::Cli::default(), &mut tasks)?;
|
||||
metrics.workers.store(Some(workers));
|
||||
}
|
||||
#[cfg(feature = "core")]
|
||||
Command::Server(args) => {
|
||||
server::start(args, &mut tasks).await?;
|
||||
}
|
||||
#[cfg(feature = "core")]
|
||||
Command::Worker(args) => {
|
||||
let workers = worker::start(args, &mut tasks)?;
|
||||
|
||||
+11
-15
@@ -2,6 +2,7 @@ use std::{env::temp_dir, os::unix, path::PathBuf, sync::Arc};
|
||||
|
||||
use ak_axum::{router::wrap_router, server};
|
||||
use ak_common::{
|
||||
Mode,
|
||||
arbiter::{Arbiter, Tasks},
|
||||
config,
|
||||
};
|
||||
@@ -77,25 +78,20 @@ pub(crate) fn start(tasks: &mut Tasks) -> Result<Arc<Metrics>> {
|
||||
.name(&format!("{}::run_upkeep", module_path!()))
|
||||
.spawn(run_upkeep(arbiter, Arc::clone(&metrics)))?;
|
||||
|
||||
for addr in config::get().listen.metrics.iter().copied() {
|
||||
server::start_plain(
|
||||
// Only run HTTP server in worker mode, in server or allinone mode, they're handled by the
|
||||
// server.
|
||||
if Mode::get() == Mode::Worker {
|
||||
for addr in config::get().listen.metrics.iter().copied() {
|
||||
server::start_plain(tasks, "metrics", router.clone(), addr)?;
|
||||
}
|
||||
|
||||
server::start_unix(
|
||||
tasks,
|
||||
"metrics",
|
||||
router.clone(),
|
||||
addr,
|
||||
config::get().debug, /* Allow failure in case the server is running on the same
|
||||
* machine, like in dev */
|
||||
router,
|
||||
unix::net::SocketAddr::from_pathname(socket_path())?,
|
||||
)?;
|
||||
}
|
||||
|
||||
server::start_unix(
|
||||
tasks,
|
||||
"metrics",
|
||||
router,
|
||||
unix::net::SocketAddr::from_pathname(socket_path())?,
|
||||
config::get().debug, /* Allow failure in case the server is running on the same machine,
|
||||
* like in dev */
|
||||
)?;
|
||||
|
||||
Ok(metrics)
|
||||
}
|
||||
|
||||
+120
-1
@@ -1,5 +1,124 @@
|
||||
use std::{env::temp_dir, path::PathBuf};
|
||||
use std::{env::temp_dir, path::PathBuf, process::Stdio, sync::Arc};
|
||||
|
||||
use ak_common::{Arbiter, Tasks, config};
|
||||
use argh::FromArgs;
|
||||
use eyre::{Result, eyre};
|
||||
use nix::{
|
||||
sys::signal::{Signal, kill},
|
||||
unistd::Pid,
|
||||
};
|
||||
use tokio::{
|
||||
process::{Child, Command},
|
||||
sync::Mutex,
|
||||
time::{Duration, sleep, timeout},
|
||||
};
|
||||
use tracing::{info, warn};
|
||||
|
||||
#[derive(Debug, Default, FromArgs, PartialEq, Eq)]
|
||||
/// Run the authentik server.
|
||||
#[argh(subcommand, name = "server")]
|
||||
#[expect(
|
||||
clippy::empty_structs_with_brackets,
|
||||
reason = "argh doesn't support unit structs"
|
||||
)]
|
||||
pub(crate) struct Cli {}
|
||||
|
||||
pub(crate) fn socket_path() -> PathBuf {
|
||||
temp_dir().join("authentik.sock")
|
||||
}
|
||||
|
||||
pub(crate) struct Server {
|
||||
server: Mutex<Child>,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
async fn new() -> Result<Self> {
|
||||
info!("starting server");
|
||||
|
||||
let server = if config::get().debug && which::which("authentik-server").is_err() {
|
||||
let build_status = Command::new("go")
|
||||
.args(["build", "-o", "server", "./cmd/server"])
|
||||
.stdin(Stdio::null())
|
||||
.status()
|
||||
.await?;
|
||||
if !build_status.success() {
|
||||
return Err(eyre!("golang server failed to compile"));
|
||||
}
|
||||
Command::new("./server")
|
||||
.kill_on_drop(true)
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::inherit())
|
||||
.stderr(Stdio::inherit())
|
||||
.spawn()?
|
||||
} else {
|
||||
Command::new("authentik-server")
|
||||
.kill_on_drop(true)
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::inherit())
|
||||
.stderr(Stdio::inherit())
|
||||
.spawn()?
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
server: Mutex::new(server),
|
||||
})
|
||||
}
|
||||
|
||||
async fn shutdown(&self) -> Result<()> {
|
||||
info!("shutting down server");
|
||||
let mut server = self.server.lock().await;
|
||||
if let Some(id) = server.id() {
|
||||
kill(Pid::from_raw(id.cast_signed()), Signal::SIGINT)?;
|
||||
}
|
||||
timeout(Duration::from_secs(1), server.wait()).await??;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn is_alive(&self) -> bool {
|
||||
let try_wait = self.server.lock().await.try_wait();
|
||||
match try_wait {
|
||||
Ok(Some(code)) => {
|
||||
warn!(?code, "server has exited");
|
||||
false
|
||||
}
|
||||
Ok(None) => true,
|
||||
Err(err) => {
|
||||
warn!(
|
||||
?err,
|
||||
"failed to check the status of server process, ignoring"
|
||||
);
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn watch_server(arbiter: Arbiter, server: Arc<Server>) -> Result<()> {
|
||||
info!("starting server watcher");
|
||||
loop {
|
||||
tokio::select! {
|
||||
() = sleep(Duration::from_secs(5)) => {
|
||||
if !server.is_alive().await {
|
||||
return Err(eyre!("server has exited unexpectedly"));
|
||||
}
|
||||
}
|
||||
() = arbiter.shutdown() => {
|
||||
server.shutdown().await?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn start(_cli: Cli, tasks: &mut Tasks) -> Result<Arc<Server>> {
|
||||
let arbiter = tasks.arbiter();
|
||||
|
||||
let server = Arc::new(Server::new().await?);
|
||||
|
||||
tasks
|
||||
.build_task()
|
||||
.name(&format!("{}::watch_server", module_path!()))
|
||||
.spawn(watch_server(arbiter.clone(), Arc::clone(&server)))?;
|
||||
|
||||
Ok(server)
|
||||
}
|
||||
|
||||
+1
-10
@@ -343,14 +343,7 @@ pub(crate) fn start(_cli: Cli, tasks: &mut Tasks) -> Result<Arc<Workers>> {
|
||||
let router = healthcheck::build_router(Arc::clone(&workers));
|
||||
|
||||
for addr in config::get().listen.http.iter().copied() {
|
||||
ak_axum::server::start_plain(
|
||||
tasks,
|
||||
"worker",
|
||||
router.clone(),
|
||||
addr,
|
||||
config::get().debug, /* Allow failure in case the server is running on the same
|
||||
* machine, like in dev. */
|
||||
)?;
|
||||
ak_axum::server::start_plain(tasks, "worker", router.clone(), addr)?;
|
||||
}
|
||||
|
||||
ak_axum::server::start_unix(
|
||||
@@ -358,8 +351,6 @@ pub(crate) fn start(_cli: Cli, tasks: &mut Tasks) -> Result<Arc<Workers>> {
|
||||
"worker",
|
||||
router,
|
||||
unix::net::SocketAddr::from_pathname(socket_path())?,
|
||||
config::get().debug, /* Allow failure in case the server is running on the same
|
||||
* machine, like in dev. */
|
||||
)?;
|
||||
}
|
||||
|
||||
|
||||
@@ -145,30 +145,24 @@ If you ever want to start over, use `make dev-reset`, which drops and restores t
|
||||
|
||||
## 5. Running authentik
|
||||
|
||||
Now that the backend has been set up and built, you can start authentik. In two different tabs in your terminal, run the following commands from the root of your installation directory:
|
||||
Now that the backend has been set up and built, you can start authentik. Run the following command from the root of your installation directory:
|
||||
|
||||
```shell
|
||||
make run-server
|
||||
```
|
||||
|
||||
```shell
|
||||
make run-worker
|
||||
make run
|
||||
```
|
||||
|
||||
:::info
|
||||
The very first time a worker runs, it might need some time to clear the initial task queue. Adjust [`AUTHENTIK_WORKER__THREADS`](../../../install-config/configuration/#authentik_worker__threads) as required.
|
||||
The very first time authentik runs, it might need some time to clear the initial task queue. Adjust [`AUTHENTIK_WORKER__THREADS`](../../../install-config/configuration/#authentik_worker__threads) as required.
|
||||
:::
|
||||
|
||||
Both processes need to run to get a fully functioning authentik development environment.
|
||||
|
||||
### Hot-reloading
|
||||
|
||||
When `AUTHENTIK_DEBUG` is set to `true` (the default for the development environment), the authentik server automatically reloads whenever changes are made to the code.
|
||||
|
||||
For the authentik worker, install [watchexec](https://github.com/watchexec/watchexec), and run:
|
||||
Install [watchexec](https://github.com/watchexec/watchexec), and run:
|
||||
|
||||
```shell
|
||||
make run-worker-watch
|
||||
make run-watch
|
||||
```
|
||||
|
||||
## 6. Build the frontend
|
||||
|
||||
Reference in New Issue
Block a user