Rename project: ubuntu-fido -> authforge

Renames the package and all artifacts to authforge to drop the
distro-specific prefix, since the roadmap targets Ubuntu + Debian +
KDE + eventually Fedora (option C in the design).

- deb packages: authforge, authforge-{daemon,pam,cli,gui,gnome-integration}
- binaries: authforged, authforgectl, authforge (GUI)
- D-Bus name: io.dangerousthings.AuthForge
- PAM module: pam_authforge_pending.so
- Paths: /etc/authforge/, /var/lib/authforge/, /usr/share/pam-configs/authforge
- PPA: ppa:dangerousthings/authforge

Filesystem path /home/work/VSCodeProjects/ubuntu_fido/ left as-is for
historical reference; can rename later via git mv at the dir level.

Verified: cargo build/test/clippy/fmt clean, pam builds, gui builds,
all 5 debs produced.
This commit is contained in:
michael
2026-04-26 16:53:11 -07:00
parent c6a5e942c9
commit f50c7b2e82
23 changed files with 2431 additions and 289 deletions

View File

@@ -41,8 +41,8 @@ jobs:
cargo rustc cargo rustc
- run: debuild -us -uc -b - run: debuild -us -uc -b
- name: Run lintian (warnings allowed in Phase 0) - name: Run lintian (warnings allowed in Phase 0)
run: lintian --info --display-info ../ubuntu-fido*.changes || true run: lintian --info --display-info ../authforge*.changes || true
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: with:
name: deb-packages name: deb-packages
path: ../ubuntu-fido*.deb path: ../authforge*.deb

4
.gitignore vendored
View File

@@ -8,7 +8,9 @@ debian/.debhelper/
debian/files debian/files
debian/*.substvars debian/*.substvars
debian/*.debhelper.log debian/*.debhelper.log
debian/ubuntu-fido*/ debian/authforge*/
debian/debhelper-build-stamp
pam/*.so
build/ build/
.vscode/ .vscode/
*.swp *.swp

2148
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
resolver = "2" resolver = "2"
members = ["common", "daemon", "cli", "gui"] members = ["common", "daemon", "cli", "gui"]
# GUI excluded from default builds because it requires libgtk-4-dev + libadwaita-1-dev # GUI excluded from default builds because it requires libgtk-4-dev + libadwaita-1-dev
# at link time. Build it explicitly with `cargo build -p ubuntu-fido-gui`. # at link time. Build it explicitly with `cargo build -p authforge-gui`.
default-members = ["common", "daemon", "cli"] default-members = ["common", "daemon", "cli"]
[workspace.package] [workspace.package]
@@ -10,7 +10,7 @@ version = "0.1.0"
edition = "2021" edition = "2021"
license = "Apache-2.0" license = "Apache-2.0"
authors = ["Dangerous Things <ops@dangerousthings.com>"] authors = ["Dangerous Things <ops@dangerousthings.com>"]
repository = "https://github.com/dangerousthings/ubuntu-fido" repository = "https://github.com/dangerousthings/authforge"
rust-version = "1.78" rust-version = "1.78"
[workspace.dependencies] [workspace.dependencies]

View File

@@ -1,18 +1,18 @@
# ubuntu-fido # authforge
Turnkey U2F / FIDO2 passkey / TOTP MFA for Ubuntu desktops. Turnkey U2F / FIDO2 passkey / TOTP MFA for Linux desktops. v1 ships on Ubuntu LTS + GNOME; Debian and KDE Plasma are on the roadmap.
## Install (end users) ## Install (end users)
sudo add-apt-repository ppa:dangerousthings/ubuntu-fido sudo add-apt-repository ppa:dangerousthings/authforge
sudo apt install ubuntu-fido sudo apt install authforge
## Build from source ## Build from source
See `docs/BUILDING.md`. See [docs/BUILDING.md](docs/BUILDING.md).
## Status ## Status
Pre-alpha. See `docs/plans/` for design and roadmap. Pre-alpha. See [docs/plans/](docs/plans/) for design and roadmap.
Copyright 2026 Dangerous Things, LLC. Licensed under the Apache License, Version 2.0. Copyright 2026 Dangerous Things, LLC. Licensed under the Apache License, Version 2.0.

View File

@@ -1,15 +1,15 @@
[package] [package]
name = "ubuntu-fidoctl" name = "authforge-cli"
version.workspace = true version.workspace = true
edition.workspace = true edition.workspace = true
license.workspace = true license.workspace = true
[[bin]] [[bin]]
name = "ubuntu-fidoctl" name = "authforgectl"
path = "src/main.rs" path = "src/main.rs"
[dependencies] [dependencies]
ubuntu-fido-common = { path = "../common" } authforge-common = { path = "../common" }
clap = { workspace = true } clap = { workspace = true }
anyhow = { workspace = true } anyhow = { workspace = true }
zbus = { workspace = true } zbus = { workspace = true }

View File

@@ -3,9 +3,9 @@ use clap::Parser;
#[derive(Parser)] #[derive(Parser)]
#[command( #[command(
name = "ubuntu-fidoctl", name = "authforgectl",
version, version,
about = "Manage ubuntu-fido configuration" about = "Manage authforge configuration"
)] )]
struct Cli { struct Cli {
#[command(subcommand)] #[command(subcommand)]
@@ -21,7 +21,7 @@ enum Cmd {
async fn main() -> Result<()> { async fn main() -> Result<()> {
let args = Cli::parse(); let args = Cli::parse();
match args.cmd { match args.cmd {
Cmd::Status => println!("ubuntu-fidoctl: not yet implemented"), Cmd::Status => println!("authforgectl: not yet implemented"),
} }
Ok(()) Ok(())
} }

View File

@@ -1,5 +1,5 @@
[package] [package]
name = "ubuntu-fido-common" name = "authforge-common"
version.workspace = true version.workspace = true
edition.workspace = true edition.workspace = true
license.workspace = true license.workspace = true

View File

@@ -1,15 +1,15 @@
[package] [package]
name = "ubuntu-fidod" name = "authforge-daemon"
version.workspace = true version.workspace = true
edition.workspace = true edition.workspace = true
license.workspace = true license.workspace = true
[[bin]] [[bin]]
name = "ubuntu-fidod" name = "authforged"
path = "src/main.rs" path = "src/main.rs"
[dependencies] [dependencies]
ubuntu-fido-common = { path = "../common" } authforge-common = { path = "../common" }
zbus = { workspace = true } zbus = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }
tracing = { workspace = true } tracing = { workspace = true }

View File

@@ -6,7 +6,7 @@ async fn main() -> Result<()> {
tracing_subscriber::fmt() tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.init(); .init();
info!("ubuntu-fidod {} starting", env!("CARGO_PKG_VERSION")); info!("authforged {} starting", env!("CARGO_PKG_VERSION"));
// D-Bus service registration in Phase 1. // D-Bus service registration in Phase 1.
Ok(()) Ok(())
} }

4
debian/changelog vendored
View File

@@ -1,5 +1,5 @@
ubuntu-fido (0.1.0-1) UNRELEASED; urgency=low authforge (0.1.0-1) UNRELEASED; urgency=low
* Initial packaging skeleton. * Initial packaging skeleton (renamed from ubuntu-fido).
-- Dangerous Things <ops@dangerousthings.com> Sun, 26 Apr 2026 12:00:00 +0000 -- Dangerous Things <ops@dangerousthings.com> Sun, 26 Apr 2026 12:00:00 +0000

46
debian/control vendored
View File

@@ -1,4 +1,4 @@
Source: ubuntu-fido Source: authforge
Section: admin Section: admin
Priority: optional Priority: optional
Maintainer: Dangerous Things <ops@dangerousthings.com> Maintainer: Dangerous Things <ops@dangerousthings.com>
@@ -12,44 +12,44 @@ Build-Depends:
libfido2-dev, libfido2-dev,
pkg-config pkg-config
Standards-Version: 4.6.2 Standards-Version: 4.6.2
Homepage: https://github.com/dangerousthings/ubuntu-fido Homepage: https://github.com/dangerousthings/authforge
Vcs-Browser: https://github.com/dangerousthings/ubuntu-fido Vcs-Browser: https://github.com/dangerousthings/authforge
Vcs-Git: https://github.com/dangerousthings/ubuntu-fido.git Vcs-Git: https://github.com/dangerousthings/authforge.git
Package: ubuntu-fido Package: authforge
Architecture: any Architecture: any
Depends: ubuntu-fido-daemon (= ${binary:Version}), Depends: authforge-daemon (= ${binary:Version}),
ubuntu-fido-pam (= ${binary:Version}), authforge-pam (= ${binary:Version}),
ubuntu-fido-cli (= ${binary:Version}), authforge-cli (= ${binary:Version}),
${misc:Depends} ${misc:Depends}
Recommends: ubuntu-fido-gui (= ${binary:Version}) Recommends: authforge-gui (= ${binary:Version})
Suggests: ubuntu-fido-gnome-integration Suggests: authforge-gnome-integration
Description: Turnkey FIDO2/U2F/TOTP MFA for Ubuntu (metapackage) Description: Turnkey FIDO2/U2F/TOTP MFA for Linux desktops (metapackage)
Installs the full ubuntu-fido stack: daemon, PAM module, CLI, and Installs the full authforge stack: daemon, PAM module, CLI, and
(if recommended) GUI. (if recommended) GUI.
Package: ubuntu-fido-daemon Package: authforge-daemon
Architecture: any Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, libpam-u2f, libfido2-1 Depends: ${shlibs:Depends}, ${misc:Depends}, libpam-u2f, libfido2-1
Description: System daemon for ubuntu-fido MFA management Description: System daemon for authforge MFA management
Provides the privileged D-Bus service that orchestrates enrollment, Provides the privileged D-Bus service that orchestrates enrollment,
policy edits, and lockout-prevention checks. policy edits, and lockout-prevention checks.
Package: ubuntu-fido-pam Package: authforge-pam
Architecture: any Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, libpam-runtime Depends: ${shlibs:Depends}, ${misc:Depends}, libpam-runtime
Description: PAM module backstopping ubuntu-fido first-login enrollment Description: PAM module backstopping authforge first-login enrollment
Refuses authentication when /var/lib/ubuntu-fido/pending/<user> exists Refuses authentication when /var/lib/authforge/pending/<user> exists
and the user has not completed first-login MFA setup. and the user has not completed first-login MFA setup.
Package: ubuntu-fido-cli Package: authforge-cli
Architecture: any Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, ubuntu-fido-daemon Depends: ${shlibs:Depends}, ${misc:Depends}, authforge-daemon
Description: CLI for ubuntu-fido (admin and fleet) Description: CLI for authforge (admin and fleet)
Configure policy, enroll on behalf of users, generate recovery codes. Configure policy, enroll on behalf of users, generate recovery codes.
Package: ubuntu-fido-gui Package: authforge-gui
Architecture: any Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, ubuntu-fido-daemon Depends: ${shlibs:Depends}, ${misc:Depends}, authforge-daemon
Description: GTK4/libadwaita UI for ubuntu-fido Description: GTK4/libadwaita UI for authforge
End-user-facing settings panel for enrollment and policy. End-user-facing settings panel for enrollment and policy.

4
debian/copyright vendored
View File

@@ -1,7 +1,7 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: ubuntu-fido Upstream-Name: authforge
Upstream-Contact: Dangerous Things <ops@dangerousthings.com> Upstream-Contact: Dangerous Things <ops@dangerousthings.com>
Source: https://github.com/dangerousthings/ubuntu-fido Source: https://github.com/dangerousthings/authforge
Files: * Files: *
Copyright: 2026 Dangerous Things, LLC Copyright: 2026 Dangerous Things, LLC

22
debian/rules vendored
View File

@@ -11,20 +11,20 @@ override_dh_auto_build:
override_dh_auto_install: override_dh_auto_install:
# Daemon # Daemon
install -D -m 0755 target/release/ubuntu-fidod \ install -D -m 0755 target/release/authforged \
debian/ubuntu-fido-daemon/usr/sbin/ubuntu-fidod debian/authforge-daemon/usr/sbin/authforged
# CLI # CLI
install -D -m 0755 target/release/ubuntu-fidoctl \ install -D -m 0755 target/release/authforgectl \
debian/ubuntu-fido-cli/usr/bin/ubuntu-fidoctl debian/authforge-cli/usr/bin/authforgectl
# GUI # GUI
install -D -m 0755 target/release/ubuntu-fido \ install -D -m 0755 target/release/authforge \
debian/ubuntu-fido-gui/usr/bin/ubuntu-fido debian/authforge-gui/usr/bin/authforge
install -D -m 0644 gui/data/io.dangerousthings.UbuntuFido.desktop \ install -D -m 0644 gui/data/io.dangerousthings.AuthForge.desktop \
debian/ubuntu-fido-gui/usr/share/applications/io.dangerousthings.UbuntuFido.desktop debian/authforge-gui/usr/share/applications/io.dangerousthings.AuthForge.desktop
install -D -m 0644 gui/data/io.dangerousthings.UbuntuFido.svg \ install -D -m 0644 gui/data/io.dangerousthings.AuthForge.svg \
debian/ubuntu-fido-gui/usr/share/icons/hicolor/scalable/apps/io.dangerousthings.UbuntuFido.svg debian/authforge-gui/usr/share/icons/hicolor/scalable/apps/io.dangerousthings.AuthForge.svg
# PAM # PAM
$(MAKE) -C pam install DESTDIR=$(CURDIR)/debian/ubuntu-fido-pam $(MAKE) -C pam install DESTDIR=$(CURDIR)/debian/authforge-pam
override_dh_auto_test: override_dh_auto_test:
cargo test --workspace --release cargo test --workspace --release

View File

@@ -1,4 +1,4 @@
# Building ubuntu-fido from source # Building authforge from source
## System dependencies ## System dependencies
@@ -19,32 +19,24 @@ Rust toolchain: stable (1.78 minimum). Install via [rustup](https://rustup.rs).
cargo build --release cargo build --release
# GUI (requires libgtk-4-dev + libadwaita-1-dev) # GUI (requires libgtk-4-dev + libadwaita-1-dev)
cargo build -p ubuntu-fido-gui --release cargo build -p authforge-gui --release
# PAM module (requires libpam0g-dev) # PAM module (requires libpam0g-dev)
make -C pam make -C pam
# Full deb build (requires all of the above) # Full deb build (requires all of the above + debhelper)
debuild -us -uc -b dpkg-buildpackage -us -uc -b
# Output: ../ubuntu-fido*.deb # Output: ../authforge*.deb
``` ```
## Tests ## Tests
```bash ```bash
cargo test --workspace --exclude ubuntu-fido-gui cargo test --workspace --exclude authforge-gui
``` ```
The GUI crate has no automated tests in v1; manual smoke tests are documented in [docs/plans/2026-04-26-ubuntu-fido-design.md](plans/2026-04-26-ubuntu-fido-design.md#testing-strategy). The GUI crate has no automated tests in v1; manual smoke tests are documented in [docs/plans/2026-04-26-authforge-design.md](plans/2026-04-26-authforge-design.md#testing-strategy).
## Phase 0 status ## Phase 0 status
Repository scaffolding is complete (tag `v0.1.0-scaffolding`). Subsequent phases per [docs/plans/2026-04-26-ubuntu-fido-implementation.md](plans/2026-04-26-ubuntu-fido-implementation.md). Repository scaffolding is complete (tag `v0.1.0-scaffolding`, then renamed `ubuntu-fido``authforge`). Subsequent phases per [docs/plans/2026-04-26-authforge-implementation.md](plans/2026-04-26-authforge-implementation.md).
Build verifications NOT yet run on this machine (require `sudo apt install libpam0g-dev libgtk-4-dev libadwaita-1-dev libfido2-dev`):
- `make -C pam` — PAM module compile
- `cargo build -p ubuntu-fido-gui --release` — GUI link
- `debuild -us -uc -b` — full deb build
Once those packages are installed, run the three commands above to complete the Phase 0 acceptance gate.

View File

@@ -1,4 +1,4 @@
# ubuntu-fido — Design Document # authforge — Design Document
**Date:** 2026-04-26 **Date:** 2026-04-26
**Status:** Draft (brainstorm complete, validated through Section 2 with stakeholder) **Status:** Draft (brainstorm complete, validated through Section 2 with stakeholder)
@@ -22,21 +22,21 @@ This project ships a single deb package that gives end users and admins a turnke
| 2 | **v1 target: Ubuntu LTS + GNOME.** Ultimate target: Ubuntu + Debian × GNOME + KDE Plasma. | ~2530% of Linux desktop on day one, ~5060% at target state. v1 ships in months not years. | | 2 | **v1 target: Ubuntu LTS + GNOME.** Ultimate target: Ubuntu + Debian × GNOME + KDE Plasma. | ~2530% of Linux desktop on day one, ~5060% at target state. v1 ships in months not years. |
| 3 | **GUI: standalone GTK4/libadwaita app + optional gnome-control-center Users-panel shortcut deb.** | gnome-control-center has no stable plugin API. Standalone app is forward-compatible; shortcut deb adds discoverability without coupling to GNOME release cycle. | | 3 | **GUI: standalone GTK4/libadwaita app + optional gnome-control-center Users-panel shortcut deb.** | gnome-control-center has no stable plugin API. Standalone app is forward-compatible; shortcut deb adds discoverability without coupling to GNOME release cycle. |
| 4 | **Authenticators: U2F + FIDO2 passkey + TOTP.** TOTP behind a build-time feature flag. | Full scope as written; flag lets us ship a beta without TOTP if the QR/recovery-code UX slips schedule. | | 4 | **Authenticators: U2F + FIDO2 passkey + TOTP.** TOTP behind a build-time feature flag. | Full scope as written; flag lets us ship a beta without TOTP if the QR/recovery-code UX slips schedule. |
| 5 | **Policy contexts: 3 in main GUI (gdm-password, sudo, sshd) with Disabled / Optional / Required modes.** Full list (polkit, su, login, plus auto-detected stacks) under pkexec admin gate or via `/etc/ubuntu-fido/policy.d/*.conf`. | 95% case stays simple; advanced/fleet path unrestricted. Mandatory pre-commit lockout simulation. | | 5 | **Policy contexts: 3 in main GUI (gdm-password, sudo, sshd) with Disabled / Optional / Required modes.** Full list (polkit, su, login, plus auto-detected stacks) under pkexec admin gate or via `/etc/authforge/policy.d/*.conf`. | 95% case stays simple; advanced/fleet path unrestricted. Mandatory pre-commit lockout simulation. |
| 6 | **First-login enrollment: `chage -d 0` + autostart enrollment app + small `pam_ubuntu_fido_pending.so` blocking module.** | Belt-and-suspenders: leverages existing forced-password-change, adds enrollment via autostart, PAM module ensures user can't bypass by killing the modal. | | 6 | **First-login enrollment: `chage -d 0` + autostart enrollment app + small `pam_authforge_pending.so` blocking module.** | Belt-and-suspenders: leverages existing forced-password-change, adds enrollment via autostart, PAM module ensures user can't bypass by killing the modal. |
## Architecture ## Architecture
``` ```
┌─────────────────────────┐ ┌──────────────────────────┐ ┌──────────────┐ ┌─────────────────────────┐ ┌──────────────────────────┐ ┌──────────────┐
ubuntu-fido GTK app │ │ ubuntu-fidoctl (CLI) │ │ Future KDE │ authforge GTK app │ │ authforgectl (CLI) │ │ Future KDE │
│ (libadwaita panel) │ │ (admin/fleet scripting) │ │ KCM module │ │ (libadwaita panel) │ │ (admin/fleet scripting) │ │ KCM module │
└─────────────┬───────────┘ └────────────┬─────────────┘ └──────┬───────┘ └─────────────┬───────────┘ └────────────┬─────────────┘ └──────┬───────┘
│ D-Bus (org.dt.UbuntuFido) │ │ │ D-Bus (io.dangerousthings.AuthForge) │ │
└─────────────┬─────────────┴────────────────────────┘ └─────────────┬─────────────┴────────────────────────┘
┌────────────────────────────────────┐ ┌────────────────────────────────────┐
ubuntu-fidod (system daemon) │ ← polkit-mediated authforged (system daemon) │ ← polkit-mediated
│ - enrollment orchestration │ │ - enrollment orchestration │
│ - policy read/write + lockout │ │ - policy read/write + lockout │
│ safety simulation │ │ safety simulation │
@@ -47,7 +47,7 @@ This project ships a single deb package that gives end users and admins a turnke
┌─────────────────┬───────┴────────┬──────────────────────┐ ┌─────────────────┬───────┴────────┬──────────────────────┐
▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼
/etc/pam.d/* /etc/ubuntu-fido/ ~/.config/Yubico/ /var/lib/ubuntu-fido/ /etc/pam.d/* /etc/authforge/ ~/.config/Yubico/ /var/lib/authforge/
(stack edits policy.d/*.conf u2f_keys (per-user) pending/<user> (stack edits policy.d/*.conf u2f_keys (per-user) pending/<user>
via pam-auth- (admin/fleet /etc/u2f_mappings (first-login flag) via pam-auth- (admin/fleet /etc/u2f_mappings (first-login flag)
update) drop-ins) (central override) update) drop-ins) (central override)
@@ -55,7 +55,7 @@ update) drop-ins) (central override)
### Trust boundary ### Trust boundary
The daemon owns all writes to `/etc/pam.d/`, `/etc/ubuntu-fido/`, and `/var/lib/ubuntu-fido/`. GUIs never write these directly. polkit policies decide who can call which method: The daemon owns all writes to `/etc/pam.d/`, `/etc/authforge/`, and `/var/lib/authforge/`. GUIs never write these directly. polkit policies decide who can call which method:
| D-Bus method | Default polkit action | | D-Bus method | Default polkit action |
|---|---| |---|---|
@@ -74,18 +74,18 @@ The daemon owns all writes to `/etc/pam.d/`, `/etc/ubuntu-fido/`, and `/var/lib/
| Package | Contents | Type | | Package | Contents | Type |
|---|---|---| |---|---|---|
| `ubuntu-fido` | Empty metapackage. | Depends | | `authforge` | Empty metapackage. | Depends |
| `ubuntu-fido-daemon` | `/usr/sbin/ubuntu-fidod`, systemd unit (`ubuntu-fido.service`), D-Bus service file, polkit rules under `/usr/share/polkit-1/actions/` | Hard dep of metapackage | | `authforge-daemon` | `/usr/sbin/authforged`, systemd unit (`authforge.service`), D-Bus service file, polkit rules under `/usr/share/polkit-1/actions/` | Hard dep of metapackage |
| `ubuntu-fido-pam` | `/usr/lib/$DEB_HOST_MULTIARCH/security/pam_ubuntu_fido_pending.so`, pam-auth-update profile at `/usr/share/pam-configs/ubuntu-fido` | Hard dep | | `authforge-pam` | `/usr/lib/$DEB_HOST_MULTIARCH/security/pam_authforge_pending.so`, pam-auth-update profile at `/usr/share/pam-configs/authforge` | Hard dep |
| `ubuntu-fido-cli` | `/usr/bin/ubuntu-fidoctl` (Rust binary, talks D-Bus) | Hard dep | | `authforge-cli` | `/usr/bin/authforgectl` (Rust binary, talks D-Bus) | Hard dep |
| `ubuntu-fido-gui` | `/usr/bin/ubuntu-fido` (GTK4/libadwaita), `.desktop` file with `Categories=Settings;Security;`, app icon | **Recommends** (so headless servers can skip) | | `authforge-gui` | `/usr/bin/authforge` (GTK4/libadwaita), `.desktop` file with `Categories=Settings;Security;`, app icon | **Recommends** (so headless servers can skip) |
| `ubuntu-fido-gnome-integration` | Tiny shim that adds a "Configure security…" launcher button to gnome-control-center's Users panel via overlay `.desktop` extension | **Suggests** (not auto-installed) | | `authforge-gnome-integration` | Tiny shim that adds a "Configure security…" launcher button to gnome-control-center's Users panel via overlay `.desktop` extension | **Suggests** (not auto-installed) |
### Headline install ### Headline install
```bash ```bash
sudo add-apt-repository ppa:dangerousthings/ubuntu-fido sudo add-apt-repository ppa:dangerousthings/authforge
sudo apt install ubuntu-fido sudo apt install authforge
``` ```
Two commands. After install: Two commands. After install:
@@ -97,20 +97,20 @@ Two commands. After install:
### Headless / fleet install ### Headless / fleet install
```bash ```bash
apt install ubuntu-fido-daemon ubuntu-fido-pam ubuntu-fido-cli apt install authforge-daemon authforge-pam authforge-cli
``` ```
Skips the GUI metadata. Configure via `/etc/ubuntu-fido/policy.d/90-fleet.conf` or `ubuntu-fidoctl`. We additionally publish: Skips the GUI metadata. Configure via `/etc/authforge/policy.d/90-fleet.conf` or `authforgectl`. We additionally publish:
- An **Ansible role** `dangerousthings.ubuntu_fido` on Galaxy wrapping `apt + debconf + drop-in config`. - An **Ansible role** `dangerousthings.authforge` on Galaxy wrapping `apt + debconf + drop-in config`.
- A **debconf preseed schema** so `debian-installer`/cloud-init can answer questions at install time. - A **debconf preseed schema** so `debian-installer`/cloud-init can answer questions at install time.
### Clean uninstall ### Clean uninstall
`apt purge ubuntu-fido*` runs `pam-auth-update --remove` (cleanly retracts our PAM hooks), stops and disables the systemd unit, removes `/etc/ubuntu-fido/` only on `purge` (not `remove`), and leaves `~/.config/Yubico/u2f_keys` files alone (so reinstall is painless). `apt purge authforge*` runs `pam-auth-update --remove` (cleanly retracts our PAM hooks), stops and disables the systemd unit, removes `/etc/authforge/` only on `purge` (not `remove`), and leaves `~/.config/Yubico/u2f_keys` files alone (so reinstall is painless).
## GUI design ## GUI design
### App: `ubuntu-fido` ### App: `authforge`
GTK4 + libadwaita. Single-window adaptive layout, looks like a stock Settings panel. Top-level navigation via libadwaita `AdwViewStack`: GTK4 + libadwaita. Single-window adaptive layout, looks like a stock Settings panel. Top-level navigation via libadwaita `AdwViewStack`:
@@ -138,23 +138,23 @@ GTK4 + libadwaita. Single-window adaptive layout, looks like a stock Settings pa
### App: gnome-control-center Users panel shortcut ### App: gnome-control-center Users panel shortcut
Optional `ubuntu-fido-gnome-integration` package ships an overlay file under `/usr/share/gnome-control-center/users/` (or whatever the current GNOME version uses) that adds an additional row labeled **"Authentication & Security Keys…"** to each user's detail view. Clicking launches `ubuntu-fido --user <username>` with elevated D-Bus permissions if the launching user is in `sudo`/`admin` group. Optional `authforge-gnome-integration` package ships an overlay file under `/usr/share/gnome-control-center/users/` (or whatever the current GNOME version uses) that adds an additional row labeled **"Authentication & Security Keys…"** to each user's detail view. Clicking launches `authforge --user <username>` with elevated D-Bus permissions if the launching user is in `sudo`/`admin` group.
The overlay is shipped separately precisely because gnome-control-center has no stable plugin API — this package may need re-tuning every GNOME release. If it ever breaks, the standalone app keeps working; only the discoverability shortcut is lost. The overlay is shipped separately precisely because gnome-control-center has no stable plugin API — this package may need re-tuning every GNOME release. If it ever breaks, the standalone app keeps working; only the discoverability shortcut is lost.
### Add User flow extension ### Add User flow extension
When `ubuntu-fido-gnome-integration` is installed, the **gnome-control-center Users → Add User** dialog gains a new section: **Configure security**, with three radio options: When `authforge-gnome-integration` is installed, the **gnome-control-center Users → Add User** dialog gains a new section: **Configure security**, with three radio options:
1. **Enroll security credential now** — opens enrollment modal, admin must have the user's key/implant present. Sets a real password (admin-supplied). 1. **Enroll security credential now** — opens enrollment modal, admin must have the user's key/implant present. Sets a real password (admin-supplied).
2. **Require user to set up at first login** — admin sets a temp password (or accepts a generated one). We run `chage -d 0 <user>` and create `/var/lib/ubuntu-fido/pending/<user>`. 2. **Require user to set up at first login** — admin sets a temp password (or accepts a generated one). We run `chage -d 0 <user>` and create `/var/lib/authforge/pending/<user>`.
3. **No MFA** — only available if local policy permits ("Required" stacks would block this option with a tooltip). 3. **No MFA** — only available if local policy permits ("Required" stacks would block this option with a tooltip).
## Enrollment flows ## Enrollment flows
### Flow A: User self-enrollment (the common case) ### Flow A: User self-enrollment (the common case)
1. User opens `ubuntu-fido` from Activities. 1. User opens `authforge` from Activities.
2. Clicks "+ Add Security Key". 2. Clicks "+ Add Security Key".
3. Modal: "Plug in your key now or tap your NFC implant to a reader." Daemon polls `libfido2` for new device. 3. Modal: "Plug in your key now or tap your NFC implant to a reader." Daemon polls `libfido2` for new device.
4. Device detected → optional PIN/UV prompt for FIDO2 → daemon emits `pamu2fcfg`-equivalent registration line. 4. Device detected → optional PIN/UV prompt for FIDO2 → daemon emits `pamu2fcfg`-equivalent registration line.
@@ -178,49 +178,49 @@ polkit asks admin password.
**At account creation (admin side):** **At account creation (admin side):**
1. Admin picks "Require user to set up at first login", enters/accepts temp password. 1. Admin picks "Require user to set up at first login", enters/accepts temp password.
2. Daemon runs `chage -d 0 <user>` (built-in Linux mechanism: forces password change at next login). 2. Daemon runs `chage -d 0 <user>` (built-in Linux mechanism: forces password change at next login).
3. Daemon writes flag file `/var/lib/ubuntu-fido/pending/<user>` containing JSON: `{"required_methods": ["fido2"], "created": "...", "deadline": null}`. 3. Daemon writes flag file `/var/lib/authforge/pending/<user>` containing JSON: `{"required_methods": ["fido2"], "created": "...", "deadline": null}`.
**First login (user side):** **First login (user side):**
1. User logs into GDM with temp password. 1. User logs into GDM with temp password.
2. PAM `passwd` module forces password change (existing Ubuntu behavior, no work for us). 2. PAM `passwd` module forces password change (existing Ubuntu behavior, no work for us).
3. GNOME session starts. 3. GNOME session starts.
4. Our autostart entry `/etc/xdg/autostart/ubuntu-fido-firstrun.desktop` runs `ubuntu-fido --first-run`. 4. Our autostart entry `/etc/xdg/autostart/authforge-firstrun.desktop` runs `authforge --first-run`.
5. Detects pending flag, takes over the screen with a fullscreen modal: "Welcome. Before you continue, please enroll a security key." 5. Detects pending flag, takes over the screen with a fullscreen modal: "Welcome. Before you continue, please enroll a security key."
6. User enrolls (Flow A inside the modal). 6. User enrolls (Flow A inside the modal).
7. On success, daemon clears pending flag. 7. On success, daemon clears pending flag.
8. Modal closes, user lands on a normal desktop. 8. Modal closes, user lands on a normal desktop.
**Backstop (the PAM module):** **Backstop (the PAM module):**
- If user kills `ubuntu-fido --first-run` and tries to do anything sudo/login-related, `pam_ubuntu_fido_pending.so` is in their auth stack; it sees the pending flag and denies auth with a friendly message: "Account setup incomplete. Please complete enrollment in the Authentication app." - If user kills `authforge --first-run` and tries to do anything sudo/login-related, `pam_authforge_pending.so` is in their auth stack; it sees the pending flag and denies auth with a friendly message: "Account setup incomplete. Please complete enrollment in the Authentication app."
- A watchdog inside `ubuntu-fido --first-run` calls `gnome-session-quit --logout --no-prompt` after 60 seconds of inactivity in the modal. - A watchdog inside `authforge --first-run` calls `gnome-session-quit --logout --no-prompt` after 60 seconds of inactivity in the modal.
### Flow D: Lost key recovery ### Flow D: Lost key recovery
1. User can't authenticate. 1. User can't authenticate.
2. User contacts admin. 2. User contacts admin.
3. Admin runs `ubuntu-fidoctl recovery generate <user>` (or clicks button in GUI). Outputs an 8-digit one-time code valid 24 h. 3. Admin runs `authforgectl recovery generate <user>` (or clicks button in GUI). Outputs an 8-digit one-time code valid 24 h.
4. User enters code at GDM password prompt — `pam_ubuntu_fido_pending.so` recognizes recovery code prefix, lets them through with a forced re-enrollment flag set. 4. User enters code at GDM password prompt — `pam_authforge_pending.so` recognizes recovery code prefix, lets them through with a forced re-enrollment flag set.
5. User logs in, immediately sees first-login-style modal forcing them to enroll a new key before doing anything else. 5. User logs in, immediately sees first-login-style modal forcing them to enroll a new key before doing anything else.
## PAM and policy details ## PAM and policy details
### pam-auth-update profile ### pam-auth-update profile
`/usr/share/pam-configs/ubuntu-fido`: `/usr/share/pam-configs/authforge`:
``` ```
Name: Dangerous Things ubuntu-fido MFA Name: Dangerous Things authforge MFA
Default: no Default: no
Priority: 192 Priority: 192
Auth-Type: Additional Auth-Type: Additional
Auth: Auth:
[success=ok default=1 ignore=ignore] pam_u2f.so cue authfile=/etc/u2f_mappings [success=ok default=1 ignore=ignore] pam_u2f.so cue authfile=/etc/u2f_mappings
[success=ok default=die] pam_ubuntu_fido_pending.so [success=ok default=die] pam_authforge_pending.so
``` ```
Default `no` means the package install does not enable enforcement — admins / GUI explicitly turn it on per stack. Default `no` means the package install does not enable enforcement — admins / GUI explicitly turn it on per stack.
### Policy file format (`/etc/ubuntu-fido/policy.d/*.conf`) ### Policy file format (`/etc/authforge/policy.d/*.conf`)
TOML, parsed in lexical order, last-key-wins: TOML, parsed in lexical order, last-key-wins:
@@ -252,7 +252,7 @@ Daemon reloads on `SIGHUP` or when a watched file changes (uses `inotify`).
Before applying any policy change, daemon simulates the new policy against: Before applying any policy change, daemon simulates the new policy against:
- The currently logged-in interactive user (resolved via `loginctl`). - The currently logged-in interactive user (resolved via `loginctl`).
- Every user listed in `/var/lib/ubuntu-fido/users.db` (cached enrollment registry). - Every user listed in `/var/lib/authforge/users.db` (cached enrollment registry).
For each user, it runs the new policy through a dry-run PAM check. If the result would deny login *without* a way to recover (no enrolled credentials of any required method), the daemon refuses the change and returns an actionable error: "Applying this policy would lock out user `alice` from `gdm-password` (no fido2 credentials enrolled). Enroll a credential for alice or set this stack to 'optional'." For each user, it runs the new policy through a dry-run PAM check. If the result would deny login *without* a way to recover (no enrolled credentials of any required method), the daemon refuses the change and returns an actionable error: "Applying this policy would lock out user `alice` from `gdm-password` (no fido2 credentials enrolled). Enroll a credential for alice or set this stack to 'optional'."
@@ -283,7 +283,7 @@ The architecture is designed so each step here is additive — no refactor of v1
|---|---|---| |---|---|---|
| **v1.0** | Ubuntu LTS + GNOME, U2F + FIDO2 passkey + TOTP. | Reference. | | **v1.0** | Ubuntu LTS + GNOME, U2F + FIDO2 passkey + TOTP. | Reference. |
| **v1.1** | Debian stable packaging (same source, second build target). | ~2 weeks. | | **v1.1** | Debian stable packaging (same source, second build target). | ~2 weeks. |
| **v1.2** | KDE Plasma front-end as a KCM module (`kcm_ubuntu_fido`). Reuses the same daemon over D-Bus. Built with KF6/Qt6. | ~68 weeks (mostly Qt port of GUI). | | **v1.2** | KDE Plasma front-end as a KCM module (`kcm_authforge`). Reuses the same daemon over D-Bus. Built with KF6/Qt6. | ~68 weeks (mostly Qt port of GUI). |
| **v1.3** | Fedora/RHEL spec file. PAM module and daemon already work; needs `.rpm` packaging and SELinux policy. GNOME GUI works as-is. | ~34 weeks. | | **v1.3** | Fedora/RHEL spec file. PAM module and daemon already work; needs `.rpm` packaging and SELinux policy. GNOME GUI works as-is. | ~34 weeks. |
| **v2.0** | Optional: organization-wide credential sync via FreeIPA / LDAP integration. Pure backend feature. | Out of scope for now. | | **v2.0** | Optional: organization-wide credential sync via FreeIPA / LDAP integration. Pure backend feature. | Out of scope for now. |
@@ -307,4 +307,4 @@ The architecture is designed so each step here is additive — no refactor of v1
--- ---
*This design was developed via guided brainstorming on 2026-04-26. The next artifact is a detailed implementation plan in `2026-04-26-ubuntu-fido-implementation-plan.md`.* *This design was developed via guided brainstorming on 2026-04-26. The next artifact is a detailed implementation plan in `2026-04-26-authforge-implementation-plan.md`.*

View File

@@ -1,16 +1,16 @@
# ubuntu-fido Implementation Plan # authforge Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
> >
> **Note on plan structure:** This is a multi-month project. Phase 0 is fully detailed at step granularity. Phases 118 are specified with goals, deliverables, file targets, and task lists at sufficient detail to begin implementation. Each subsequent phase should re-invoke `superpowers:writing-plans` to expand its tasks to step-level granularity at the moment that phase begins (state from prior phases informs the expansion). > **Note on plan structure:** This is a multi-month project. Phase 0 is fully detailed at step granularity. Phases 118 are specified with goals, deliverables, file targets, and task lists at sufficient detail to begin implementation. Each subsequent phase should re-invoke `superpowers:writing-plans` to expand its tasks to step-level granularity at the moment that phase begins (state from prior phases informs the expansion).
**Goal:** Ship `ubuntu-fido` as an apt package on a PPA that turns U2F / FIDO2 passkey / TOTP MFA on Ubuntu desktops into a two-command install with a polished libadwaita GUI, sane policy, and a first-login enrollment flow. **Goal:** Ship `authforge` as an apt package on a PPA that turns U2F / FIDO2 passkey / TOTP MFA on Ubuntu desktops into a two-command install with a polished libadwaita GUI, sane policy, and a first-login enrollment flow.
**Architecture:** Rust system daemon (`ubuntu-fidod`) exposes a D-Bus interface. A GTK4 / libadwaita app and a CLI are thin clients. A small C PAM module (`pam_ubuntu_fido_pending.so`) backstops first-login enrollment. PAM stack edits go through `pam-auth-update`. Policy is drop-in TOML in `/etc/ubuntu-fido/policy.d/`. Full design in `docs/plans/2026-04-26-ubuntu-fido-design.md`. **Architecture:** Rust system daemon (`authforged`) exposes a D-Bus interface. A GTK4 / libadwaita app and a CLI are thin clients. A small C PAM module (`pam_authforge_pending.so`) backstops first-login enrollment. PAM stack edits go through `pam-auth-update`. Policy is drop-in TOML in `/etc/authforge/policy.d/`. Full design in `docs/plans/2026-04-26-authforge-design.md`.
**Tech Stack:** Rust 2021 edition (daemon, CLI), GTK4 + libadwaita via `gtk4-rs` and `libadwaita-rs` (GUI), C (PAM module), `libfido2` via `ctap-hid-fido2` crate, D-Bus via `zbus`, polkit, debhelper-compat 13 packaging, sbuild for clean builds, GitHub Actions CI, Launchpad PPA for distribution. **Tech Stack:** Rust 2021 edition (daemon, CLI), GTK4 + libadwaita via `gtk4-rs` and `libadwaita-rs` (GUI), C (PAM module), `libfido2` via `ctap-hid-fido2` crate, D-Bus via `zbus`, polkit, debhelper-compat 13 packaging, sbuild for clean builds, GitHub Actions CI, Launchpad PPA for distribution.
**Reference design:** [docs/plans/2026-04-26-ubuntu-fido-design.md](2026-04-26-ubuntu-fido-design.md) **Reference design:** [docs/plans/2026-04-26-authforge-design.md](2026-04-26-authforge-design.md)
--- ---
@@ -24,8 +24,8 @@
| 3 | Daemon: FIDO2 enrollment backend via `ctap-hid-fido2` | 57 days | Spec'd | | 3 | Daemon: FIDO2 enrollment backend via `ctap-hid-fido2` | 57 days | Spec'd |
| 4 | Daemon: policy apply via pam-auth-update wrapper | 4 days | Spec'd | | 4 | Daemon: policy apply via pam-auth-update wrapper | 4 days | Spec'd |
| 5 | Daemon: lockout simulator | 3 days | Spec'd | | 5 | Daemon: lockout simulator | 3 days | Spec'd |
| 6 | PAM module: `pam_ubuntu_fido_pending.so` (C) | 3 days | Spec'd | | 6 | PAM module: `pam_authforge_pending.so` (C) | 3 days | Spec'd |
| 7 | CLI: `ubuntu-fidoctl` | 4 days | Spec'd | | 7 | CLI: `authforgectl` | 4 days | Spec'd |
| 8 | GUI: app shell + Security Keys tab | 68 days | Spec'd | | 8 | GUI: app shell + Security Keys tab | 68 days | Spec'd |
| 9 | GUI: Policy tab + lockout-warning UX | 4 days | Spec'd | | 9 | GUI: Policy tab + lockout-warning UX | 4 days | Spec'd |
| 10 | First-login flow: autostart entry + fullscreen modal | 4 days | Spec'd | | 10 | First-login flow: autostart entry + fullscreen modal | 4 days | Spec'd |
@@ -33,8 +33,8 @@
| 12 | Recovery flow (codes + emergency unlock) | 4 days | Spec'd | | 12 | Recovery flow (codes + emergency unlock) | 4 days | Spec'd |
| 13 | Debian packaging finalization (postinst, debconf, purge) | 4 days | Spec'd | | 13 | Debian packaging finalization (postinst, debconf, purge) | 4 days | Spec'd |
| 14 | Launchpad PPA build setup | 2 days | Spec'd | | 14 | Launchpad PPA build setup | 2 days | Spec'd |
| 15 | `ubuntu-fido-gnome-integration` (Users panel shortcut) | 3 days | Spec'd | | 15 | `authforge-gnome-integration` (Users panel shortcut) | 3 days | Spec'd |
| 16 | Ansible role `dangerousthings.ubuntu_fido` | 2 days | Spec'd | | 16 | Ansible role `dangerousthings.authforge` | 2 days | Spec'd |
| 17 | Integration test harness (Multipass / LXD VM) | 4 days | Spec'd | | 17 | Integration test harness (Multipass / LXD VM) | 4 days | Spec'd |
| 18 | User docs + onboarding site | 3 days | Spec'd | | 18 | User docs + onboarding site | 3 days | Spec'd |
| **R** | **v1.0 release** | 1 day | — | | **R** | **v1.0 release** | 1 day | — |
@@ -82,7 +82,7 @@ debian/.debhelper/
debian/files debian/files
debian/*.substvars debian/*.substvars
debian/*.debhelper.log debian/*.debhelper.log
debian/ubuntu-fido*/ debian/authforge*/
build/ build/
.vscode/ .vscode/
*.swp *.swp
@@ -93,14 +93,14 @@ build/
**Step 4:** Write minimal `README.md`: **Step 4:** Write minimal `README.md`:
```markdown ```markdown
# ubuntu-fido # authforge
Turnkey U2F / FIDO2 passkey / TOTP MFA for Ubuntu desktops. Turnkey U2F / FIDO2 passkey / TOTP MFA for Ubuntu desktops.
## Install (end users) ## Install (end users)
sudo add-apt-repository ppa:dangerousthings/ubuntu-fido sudo add-apt-repository ppa:dangerousthings/authforge
sudo apt install ubuntu-fido sudo apt install authforge
## Build from source ## Build from source
@@ -137,7 +137,7 @@ version = "0.1.0"
edition = "2021" edition = "2021"
license = "Apache-2.0" license = "Apache-2.0"
authors = ["Dangerous Things <ops@dangerousthings.com>"] authors = ["Dangerous Things <ops@dangerousthings.com>"]
repository = "https://github.com/dangerousthings/ubuntu-fido" repository = "https://github.com/dangerousthings/authforge"
rust-version = "1.78" rust-version = "1.78"
[workspace.dependencies] [workspace.dependencies]
@@ -185,7 +185,7 @@ rustflags = ["-D", "warnings"]
```toml ```toml
[package] [package]
name = "ubuntu-fido-common" name = "authforge-common"
version.workspace = true version.workspace = true
edition.workspace = true edition.workspace = true
license.workspace = true license.workspace = true
@@ -248,7 +248,7 @@ mod tests {
This needs `serde_json` as a dev-dep; add `[dev-dependencies] serde_json = { workspace = true }` to `common/Cargo.toml`. This needs `serde_json` as a dev-dep; add `[dev-dependencies] serde_json = { workspace = true }` to `common/Cargo.toml`.
**Step 6:** Run `cargo test -p ubuntu-fido-common`. Expected: PASS. **Step 6:** Run `cargo test -p authforge-common`. Expected: PASS.
**Step 7:** Commit. **Step 7:** Commit.
@@ -267,17 +267,17 @@ git commit -m "Add common crate with shared Mode and Method types"
```toml ```toml
[package] [package]
name = "ubuntu-fidod" name = "authforged"
version.workspace = true version.workspace = true
edition.workspace = true edition.workspace = true
license.workspace = true license.workspace = true
[[bin]] [[bin]]
name = "ubuntu-fidod" name = "authforged"
path = "src/main.rs" path = "src/main.rs"
[dependencies] [dependencies]
ubuntu-fido-common = { path = "../common" } authforge-common = { path = "../common" }
zbus = { workspace = true } zbus = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }
tracing = { workspace = true } tracing = { workspace = true }
@@ -296,13 +296,13 @@ async fn main() -> Result<()> {
tracing_subscriber::fmt() tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.init(); .init();
info!("ubuntu-fidod {} starting", env!("CARGO_PKG_VERSION")); info!("authforged {} starting", env!("CARGO_PKG_VERSION"));
// D-Bus service registration in Phase 1. // D-Bus service registration in Phase 1.
Ok(()) Ok(())
} }
``` ```
**Step 3:** `cargo build -p ubuntu-fidod`. Expected: success. **Step 3:** `cargo build -p authforged`. Expected: success.
**Step 4:** Commit. **Step 4:** Commit.
@@ -321,17 +321,17 @@ git commit -m "Add daemon crate skeleton"
```toml ```toml
[package] [package]
name = "ubuntu-fidoctl" name = "authforgectl"
version.workspace = true version.workspace = true
edition.workspace = true edition.workspace = true
license.workspace = true license.workspace = true
[[bin]] [[bin]]
name = "ubuntu-fidoctl" name = "authforgectl"
path = "src/main.rs" path = "src/main.rs"
[dependencies] [dependencies]
ubuntu-fido-common = { path = "../common" } authforge-common = { path = "../common" }
clap = { workspace = true } clap = { workspace = true }
anyhow = { workspace = true } anyhow = { workspace = true }
zbus = { workspace = true } zbus = { workspace = true }
@@ -345,7 +345,7 @@ use anyhow::Result;
use clap::Parser; use clap::Parser;
#[derive(Parser)] #[derive(Parser)]
#[command(name = "ubuntu-fidoctl", version, about = "Manage ubuntu-fido configuration")] #[command(name = "authforgectl", version, about = "Manage authforge configuration")]
struct Cli { struct Cli {
#[command(subcommand)] #[command(subcommand)]
cmd: Cmd, cmd: Cmd,
@@ -360,7 +360,7 @@ enum Cmd {
async fn main() -> Result<()> { async fn main() -> Result<()> {
let args = Cli::parse(); let args = Cli::parse();
match args.cmd { match args.cmd {
Cmd::Status => println!("ubuntu-fidoctl: not yet implemented"), Cmd::Status => println!("authforgectl: not yet implemented"),
} }
Ok(()) Ok(())
} }
@@ -379,9 +379,9 @@ git commit -m "Add cli crate skeleton"
**Files:** **Files:**
- Create: `pam/Makefile` - Create: `pam/Makefile`
- Create: `pam/pam_ubuntu_fido_pending.c` - Create: `pam/pam_authforge_pending.c`
**Step 1:** `pam/pam_ubuntu_fido_pending.c`: **Step 1:** `pam/pam_authforge_pending.c`:
```c ```c
#define PAM_SM_AUTH #define PAM_SM_AUTH
@@ -392,7 +392,7 @@ git commit -m "Add cli crate skeleton"
PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags,
int argc, const char **argv) { int argc, const char **argv) {
(void)flags; (void)argc; (void)argv; (void)flags; (void)argc; (void)argv;
pam_syslog(pamh, LOG_INFO, "ubuntu_fido_pending: stub - allowing"); pam_syslog(pamh, LOG_INFO, "authforge_pending: stub - allowing");
return PAM_IGNORE; /* implemented in Phase 6 */ return PAM_IGNORE; /* implemented in Phase 6 */
} }
@@ -409,14 +409,14 @@ PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags,
CFLAGS ?= -Wall -Wextra -Werror -fPIC -O2 CFLAGS ?= -Wall -Wextra -Werror -fPIC -O2
LIBDIR ?= /usr/lib/$(shell dpkg-architecture -qDEB_HOST_MULTIARCH)/security LIBDIR ?= /usr/lib/$(shell dpkg-architecture -qDEB_HOST_MULTIARCH)/security
pam_ubuntu_fido_pending.so: pam_ubuntu_fido_pending.c pam_authforge_pending.so: pam_authforge_pending.c
$(CC) $(CFLAGS) -shared -o $@ $< -lpam $(CC) $(CFLAGS) -shared -o $@ $< -lpam
install: pam_ubuntu_fido_pending.so install: pam_authforge_pending.so
install -D -m 0644 pam_ubuntu_fido_pending.so $(DESTDIR)$(LIBDIR)/pam_ubuntu_fido_pending.so install -D -m 0644 pam_authforge_pending.so $(DESTDIR)$(LIBDIR)/pam_authforge_pending.so
clean: clean:
rm -f pam_ubuntu_fido_pending.so rm -f pam_authforge_pending.so
.PHONY: install clean .PHONY: install clean
``` ```
@@ -427,7 +427,7 @@ clean:
cd pam && make && cd .. cd pam && make && cd ..
``` ```
Expected: produces `pam/pam_ubuntu_fido_pending.so` (~10 KB). Requires `libpam0g-dev` (will be in build-deps). Expected: produces `pam/pam_authforge_pending.so` (~10 KB). Requires `libpam0g-dev` (will be in build-deps).
**Step 4:** Commit. **Step 4:** Commit.
@@ -441,8 +441,8 @@ git commit -m "Add PAM module stub"
**Files:** **Files:**
- Create: `gui/Cargo.toml` - Create: `gui/Cargo.toml`
- Create: `gui/src/main.rs` - Create: `gui/src/main.rs`
- Create: `gui/data/io.dangerousthings.UbuntuFido.desktop` - Create: `gui/data/io.dangerousthings.AuthForge.desktop`
- Create: `gui/data/io.dangerousthings.UbuntuFido.svg` (1KB placeholder icon) - Create: `gui/data/io.dangerousthings.AuthForge.svg` (1KB placeholder icon)
**Step 1:** Add `gui` to workspace members in root `Cargo.toml`. **Step 1:** Add `gui` to workspace members in root `Cargo.toml`.
@@ -450,19 +450,19 @@ git commit -m "Add PAM module stub"
```toml ```toml
[package] [package]
name = "ubuntu-fido-gui" name = "authforge-gui"
version.workspace = true version.workspace = true
edition.workspace = true edition.workspace = true
license.workspace = true license.workspace = true
[[bin]] [[bin]]
name = "ubuntu-fido" name = "authforge"
path = "src/main.rs" path = "src/main.rs"
[dependencies] [dependencies]
gtk = { package = "gtk4", version = "0.8" } gtk = { package = "gtk4", version = "0.8" }
adw = { package = "libadwaita", version = "0.6" } adw = { package = "libadwaita", version = "0.6" }
ubuntu-fido-common = { path = "../common" } authforge-common = { path = "../common" }
anyhow = { workspace = true } anyhow = { workspace = true }
``` ```
@@ -472,7 +472,7 @@ anyhow = { workspace = true }
use adw::prelude::*; use adw::prelude::*;
use gtk::glib; use gtk::glib;
const APP_ID: &str = "io.dangerousthings.UbuntuFido"; const APP_ID: &str = "io.dangerousthings.AuthForge";
fn main() -> glib::ExitCode { fn main() -> glib::ExitCode {
let app = adw::Application::builder().application_id(APP_ID).build(); let app = adw::Application::builder().application_id(APP_ID).build();
@@ -499,15 +499,15 @@ fn main() -> glib::ExitCode {
} }
``` ```
**Step 4:** `.desktop` file at `gui/data/io.dangerousthings.UbuntuFido.desktop`: **Step 4:** `.desktop` file at `gui/data/io.dangerousthings.AuthForge.desktop`:
```desktop ```desktop
[Desktop Entry] [Desktop Entry]
Name=Authentication Name=Authentication
GenericName=Security Keys & MFA GenericName=Security Keys & MFA
Comment=Manage U2F/FIDO2 keys and authentication policy Comment=Manage U2F/FIDO2 keys and authentication policy
Exec=ubuntu-fido Exec=authforge
Icon=io.dangerousthings.UbuntuFido Icon=io.dangerousthings.AuthForge
Terminal=false Terminal=false
Type=Application Type=Application
Categories=Settings;Security; Categories=Settings;Security;
@@ -515,12 +515,12 @@ Keywords=mfa;u2f;fido2;passkey;totp;security;
StartupNotify=true StartupNotify=true
``` ```
**Step 5:** Placeholder SVG icon — a simple gray key glyph; sourced from any open icon set under a compatible license. Save at `gui/data/io.dangerousthings.UbuntuFido.svg`. **Step 5:** Placeholder SVG icon — a simple gray key glyph; sourced from any open icon set under a compatible license. Save at `gui/data/io.dangerousthings.AuthForge.svg`.
**Step 6:** Build (assumes GTK4 / libadwaita dev packages installed: `libgtk-4-dev`, `libadwaita-1-dev`). **Step 6:** Build (assumes GTK4 / libadwaita dev packages installed: `libgtk-4-dev`, `libadwaita-1-dev`).
```bash ```bash
cargo build -p ubuntu-fido-gui cargo build -p authforge-gui
``` ```
Expected: success. Expected: success.
@@ -541,17 +541,17 @@ git commit -m "Add GUI crate skeleton with libadwaita placeholder window"
- Create: `debian/rules` - Create: `debian/rules`
- Create: `debian/source/format` - Create: `debian/source/format`
- Create: `debian/compat` (or use `debhelper-compat (= 13)` in control) - Create: `debian/compat` (or use `debhelper-compat (= 13)` in control)
- Create: `debian/ubuntu-fido.install` - Create: `debian/authforge.install`
- Create: `debian/ubuntu-fido-daemon.install` - Create: `debian/authforge-daemon.install`
- Create: `debian/ubuntu-fido-daemon.service` - Create: `debian/authforge-daemon.service`
- Create: `debian/ubuntu-fido-pam.install` - Create: `debian/authforge-pam.install`
- Create: `debian/ubuntu-fido-cli.install` - Create: `debian/authforge-cli.install`
- Create: `debian/ubuntu-fido-gui.install` - Create: `debian/authforge-gui.install`
**Step 1:** `debian/changelog`: **Step 1:** `debian/changelog`:
``` ```
ubuntu-fido (0.1.0-1) UNRELEASED; urgency=low authforge (0.1.0-1) UNRELEASED; urgency=low
* Initial packaging skeleton. * Initial packaging skeleton.
@@ -567,7 +567,7 @@ ubuntu-fido (0.1.0-1) UNRELEASED; urgency=low
**Step 3:** `debian/control` — declares all 5 binary packages and their relationships: **Step 3:** `debian/control` — declares all 5 binary packages and their relationships:
``` ```
Source: ubuntu-fido Source: authforge
Section: admin Section: admin
Priority: optional Priority: optional
Maintainer: Dangerous Things <ops@dangerousthings.com> Maintainer: Dangerous Things <ops@dangerousthings.com>
@@ -581,46 +581,46 @@ Build-Depends:
libfido2-dev, libfido2-dev,
pkg-config pkg-config
Standards-Version: 4.6.2 Standards-Version: 4.6.2
Homepage: https://github.com/dangerousthings/ubuntu-fido Homepage: https://github.com/dangerousthings/authforge
Vcs-Browser: https://github.com/dangerousthings/ubuntu-fido Vcs-Browser: https://github.com/dangerousthings/authforge
Vcs-Git: https://github.com/dangerousthings/ubuntu-fido.git Vcs-Git: https://github.com/dangerousthings/authforge.git
Package: ubuntu-fido Package: authforge
Architecture: any Architecture: any
Depends: ubuntu-fido-daemon (= ${binary:Version}), Depends: authforge-daemon (= ${binary:Version}),
ubuntu-fido-pam (= ${binary:Version}), authforge-pam (= ${binary:Version}),
ubuntu-fido-cli (= ${binary:Version}), authforge-cli (= ${binary:Version}),
${misc:Depends} ${misc:Depends}
Recommends: ubuntu-fido-gui (= ${binary:Version}) Recommends: authforge-gui (= ${binary:Version})
Suggests: ubuntu-fido-gnome-integration Suggests: authforge-gnome-integration
Description: Turnkey FIDO2/U2F/TOTP MFA for Ubuntu (metapackage) Description: Turnkey FIDO2/U2F/TOTP MFA for Ubuntu (metapackage)
Installs the full ubuntu-fido stack: daemon, PAM module, CLI, and Installs the full authforge stack: daemon, PAM module, CLI, and
(if recommended) GUI. (if recommended) GUI.
Package: ubuntu-fido-daemon Package: authforge-daemon
Architecture: any Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, libpam-u2f, libfido2-1 Depends: ${shlibs:Depends}, ${misc:Depends}, libpam-u2f, libfido2-1
Description: System daemon for ubuntu-fido MFA management Description: System daemon for authforge MFA management
Provides the privileged D-Bus service that orchestrates enrollment, Provides the privileged D-Bus service that orchestrates enrollment,
policy edits, and lockout-prevention checks. policy edits, and lockout-prevention checks.
Package: ubuntu-fido-pam Package: authforge-pam
Architecture: any Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, libpam-runtime Depends: ${shlibs:Depends}, ${misc:Depends}, libpam-runtime
Description: PAM module backstopping ubuntu-fido first-login enrollment Description: PAM module backstopping authforge first-login enrollment
Refuses authentication when /var/lib/ubuntu-fido/pending/<user> exists Refuses authentication when /var/lib/authforge/pending/<user> exists
and the user has not completed first-login MFA setup. and the user has not completed first-login MFA setup.
Package: ubuntu-fido-cli Package: authforge-cli
Architecture: any Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, ubuntu-fido-daemon Depends: ${shlibs:Depends}, ${misc:Depends}, authforge-daemon
Description: CLI for ubuntu-fido (admin and fleet) Description: CLI for authforge (admin and fleet)
Configure policy, enroll on behalf of users, generate recovery codes. Configure policy, enroll on behalf of users, generate recovery codes.
Package: ubuntu-fido-gui Package: authforge-gui
Architecture: any Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, ubuntu-fido-daemon Depends: ${shlibs:Depends}, ${misc:Depends}, authforge-daemon
Description: GTK4/libadwaita UI for ubuntu-fido Description: GTK4/libadwaita UI for authforge
End-user-facing settings panel for enrollment and policy. End-user-facing settings panel for enrollment and policy.
``` ```
@@ -640,20 +640,20 @@ override_dh_auto_build:
override_dh_auto_install: override_dh_auto_install:
# Daemon # Daemon
install -D -m 0755 target/release/ubuntu-fidod \ install -D -m 0755 target/release/authforged \
debian/ubuntu-fido-daemon/usr/sbin/ubuntu-fidod debian/authforge-daemon/usr/sbin/authforged
# CLI # CLI
install -D -m 0755 target/release/ubuntu-fidoctl \ install -D -m 0755 target/release/authforgectl \
debian/ubuntu-fido-cli/usr/bin/ubuntu-fidoctl debian/authforge-cli/usr/bin/authforgectl
# GUI # GUI
install -D -m 0755 target/release/ubuntu-fido \ install -D -m 0755 target/release/authforge \
debian/ubuntu-fido-gui/usr/bin/ubuntu-fido debian/authforge-gui/usr/bin/authforge
install -D -m 0644 gui/data/io.dangerousthings.UbuntuFido.desktop \ install -D -m 0644 gui/data/io.dangerousthings.AuthForge.desktop \
debian/ubuntu-fido-gui/usr/share/applications/io.dangerousthings.UbuntuFido.desktop debian/authforge-gui/usr/share/applications/io.dangerousthings.AuthForge.desktop
install -D -m 0644 gui/data/io.dangerousthings.UbuntuFido.svg \ install -D -m 0644 gui/data/io.dangerousthings.AuthForge.svg \
debian/ubuntu-fido-gui/usr/share/icons/hicolor/scalable/apps/io.dangerousthings.UbuntuFido.svg debian/authforge-gui/usr/share/icons/hicolor/scalable/apps/io.dangerousthings.AuthForge.svg
# PAM # PAM
$(MAKE) -C pam install DESTDIR=$(CURDIR)/debian/ubuntu-fido-pam $(MAKE) -C pam install DESTDIR=$(CURDIR)/debian/authforge-pam
override_dh_auto_test: override_dh_auto_test:
cargo test --workspace --release cargo test --workspace --release
@@ -680,9 +680,9 @@ Expected: produces 5 `.deb` files in the parent directory. Lintian may complain
**Step 8:** Verify install + remove on a throwaway VM (Multipass shell or LXD container). **Step 8:** Verify install + remove on a throwaway VM (Multipass shell or LXD container).
```bash ```bash
sudo dpkg -i ../ubuntu-fido*.deb || sudo apt -f install sudo dpkg -i ../authforge*.deb || sudo apt -f install
sudo systemctl status ubuntu-fido.service # will fail until Phase 1 ships unit sudo systemctl status authforge.service # will fail until Phase 1 ships unit
sudo apt purge ubuntu-fido* sudo apt purge authforge*
``` ```
**Step 9:** Commit. **Step 9:** Commit.
@@ -721,7 +721,7 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- run: sudo apt-get update && sudo apt-get install -y debhelper devscripts lintian libpam0g-dev libgtk-4-dev libadwaita-1-dev libfido2-dev pkg-config cargo rustc - run: sudo apt-get update && sudo apt-get install -y debhelper devscripts lintian libpam0g-dev libgtk-4-dev libadwaita-1-dev libfido2-dev pkg-config cargo rustc
- run: debuild -us -uc -b - run: debuild -us -uc -b
- run: lintian --info --display-info ../ubuntu-fido*.changes || true # warnings allowed in Phase 0 - run: lintian --info --display-info ../authforge*.changes || true # warnings allowed in Phase 0
``` ```
**Step 2:** Push and verify both jobs go green. **Step 2:** Push and verify both jobs go green.
@@ -740,9 +740,9 @@ Verify all of the following before declaring Phase 0 complete:
- [ ] `cargo build --workspace --release` succeeds. - [ ] `cargo build --workspace --release` succeeds.
- [ ] `cargo test --workspace` succeeds (1 test in `common`). - [ ] `cargo test --workspace` succeeds (1 test in `common`).
- [ ] `cargo clippy --workspace -- -D warnings` is clean. - [ ] `cargo clippy --workspace -- -D warnings` is clean.
- [ ] `make -C pam` produces `pam_ubuntu_fido_pending.so`. - [ ] `make -C pam` produces `pam_authforge_pending.so`.
- [ ] `debuild -us -uc -b` produces 5 `.deb` files. - [ ] `debuild -us -uc -b` produces 5 `.deb` files.
- [ ] On an Ubuntu 24.04 LXD container: `sudo dpkg -i ../ubuntu-fido*.deb` succeeds, `sudo apt purge ubuntu-fido*` cleanly removes. - [ ] On an Ubuntu 24.04 LXD container: `sudo dpkg -i ../authforge*.deb` succeeds, `sudo apt purge authforge*` cleanly removes.
- [ ] CI is green on `main`. - [ ] CI is green on `main`.
If all check, tag `v0.1.0-scaffolding`: If all check, tag `v0.1.0-scaffolding`:
@@ -755,24 +755,24 @@ git tag -a v0.1.0-scaffolding -m "Phase 0: repository scaffolding complete"
# Phase 1: Daemon — D-Bus Interface, systemd, polkit # Phase 1: Daemon — D-Bus Interface, systemd, polkit
**Goal:** `ubuntu-fidod` registers on the system bus as `io.dangerousthings.UbuntuFido`, gets started by systemd, and enforces polkit-mediated method-level authorization. Methods are stubs returning sensible test data. **Goal:** `authforged` registers on the system bus as `io.dangerousthings.AuthForge`, gets started by systemd, and enforces polkit-mediated method-level authorization. Methods are stubs returning sensible test data.
**Re-invoke `superpowers:writing-plans` at start of this phase to expand to step granularity.** **Re-invoke `superpowers:writing-plans` at start of this phase to expand to step granularity.**
**Files to create:** **Files to create:**
- `daemon/src/dbus.rs` — zbus `interface` definition - `daemon/src/dbus.rs` — zbus `interface` definition
- `daemon/src/state.rs` — in-memory state shell - `daemon/src/state.rs` — in-memory state shell
- `debian/ubuntu-fido-daemon.service` — systemd unit - `debian/authforge-daemon.service` — systemd unit
- `debian/io.dangerousthings.UbuntuFido.conf` — D-Bus policy - `debian/io.dangerousthings.AuthForge.conf` — D-Bus policy
- `debian/io.dangerousthings.UbuntuFido.service` — D-Bus activation file - `debian/io.dangerousthings.AuthForge.service` — D-Bus activation file
- `debian/io.dangerousthings.UbuntuFido.policy` — polkit actions - `debian/io.dangerousthings.AuthForge.policy` — polkit actions
- `debian/ubuntu-fido-daemon.postinst` — enable + start unit - `debian/authforge-daemon.postinst` — enable + start unit
**D-Bus interface skeleton:** **D-Bus interface skeleton:**
```rust ```rust
#[zbus::interface(name = "io.dangerousthings.UbuntuFido1")] #[zbus::interface(name = "io.dangerousthings.AuthForge1")]
impl UbuntuFido { impl AuthForge {
async fn list_credentials(&self, user: String) -> zbus::fdo::Result<Vec<Credential>> { ... } async fn list_credentials(&self, user: String) -> zbus::fdo::Result<Vec<Credential>> { ... }
async fn enroll_own(&self, user: String, nickname: String) -> zbus::fdo::Result<Credential> { ... } async fn enroll_own(&self, user: String, nickname: String) -> zbus::fdo::Result<Credential> { ... }
async fn remove_own(&self, user: String, cred_id: String) -> zbus::fdo::Result<()> { ... } async fn remove_own(&self, user: String, cred_id: String) -> zbus::fdo::Result<()> { ... }
@@ -785,26 +785,26 @@ impl UbuntuFido {
} }
``` ```
**polkit actions** (XML in `io.dangerousthings.UbuntuFido.policy`): **polkit actions** (XML in `io.dangerousthings.AuthForge.policy`):
| Action | Default for active session | | Action | Default for active session |
|---|---| |---|---|
| `io.dangerousthings.UbuntuFido.enroll-own` | `auth_self_keep` | | `io.dangerousthings.AuthForge.enroll-own` | `auth_self_keep` |
| `io.dangerousthings.UbuntuFido.remove-own` | `auth_self_keep` | | `io.dangerousthings.AuthForge.remove-own` | `auth_self_keep` |
| `io.dangerousthings.UbuntuFido.enroll-other` | `auth_admin_keep` | | `io.dangerousthings.AuthForge.enroll-other` | `auth_admin_keep` |
| `io.dangerousthings.UbuntuFido.set-policy` | `auth_admin_keep` | | `io.dangerousthings.AuthForge.set-policy` | `auth_admin_keep` |
| `io.dangerousthings.UbuntuFido.set-pending` | `auth_admin_keep` | | `io.dangerousthings.AuthForge.set-pending` | `auth_admin_keep` |
| `io.dangerousthings.UbuntuFido.clear-pending` | `auth_admin_keep` | | `io.dangerousthings.AuthForge.clear-pending` | `auth_admin_keep` |
| `io.dangerousthings.UbuntuFido.generate-recovery` | `auth_admin_keep` | | `io.dangerousthings.AuthForge.generate-recovery` | `auth_admin_keep` |
**Tasks:** **Tasks:**
1. Add zbus interface module with stubs returning fixture data. 1. Add zbus interface module with stubs returning fixture data.
2. Wire up `connection.request_name("io.dangerousthings.UbuntuFido")`. 2. Wire up `connection.request_name("io.dangerousthings.AuthForge")`.
3. Write systemd unit (`Type=dbus`, `BusName=io.dangerousthings.UbuntuFido`, `User=root`). 3. Write systemd unit (`Type=dbus`, `BusName=io.dangerousthings.AuthForge`, `User=root`).
4. Write D-Bus system policy (allow root to own; allow `at_console` to call read methods; restrict write methods). 4. Write D-Bus system policy (allow root to own; allow `at_console` to call read methods; restrict write methods).
5. Write polkit policy XML; daemon checks each method against polkit before executing. 5. Write polkit policy XML; daemon checks each method against polkit before executing.
6. Wire postinst: `systemctl daemon-reload && systemctl enable --now ubuntu-fido.service`. 6. Wire postinst: `systemctl daemon-reload && systemctl enable --now authforge.service`.
7. Test: `busctl call io.dangerousthings.UbuntuFido /io/dangerousthings/UbuntuFido io.dangerousthings.UbuntuFido1 ListCredentials s "$USER"` returns the fixture data. 7. Test: `busctl call io.dangerousthings.AuthForge /io/dangerousthings/AuthForge io.dangerousthings.AuthForge1 ListCredentials s "$USER"` returns the fixture data.
8. Test: as non-root, `SetPolicy` triggers polkit prompt. 8. Test: as non-root, `SetPolicy` triggers polkit prompt.
**Acceptance:** D-Bus introspection works (`busctl introspect ...`), all 9 methods are callable as stubs, polkit prompts appear at correct times. **Acceptance:** D-Bus introspection works (`busctl introspect ...`), all 9 methods are callable as stubs, polkit prompts appear at correct times.
@@ -813,14 +813,14 @@ impl UbuntuFido {
# Phase 2: Daemon — Storage Layer # Phase 2: Daemon — Storage Layer
**Goal:** Real I/O to `/etc/ubuntu-fido/policy.d/`, `/var/lib/ubuntu-fido/pending/`, and `~/.config/Yubico/u2f_keys` (or `/etc/u2f_mappings`). Replaces fixtures from Phase 1. **Goal:** Real I/O to `/etc/authforge/policy.d/`, `/var/lib/authforge/pending/`, and `~/.config/Yubico/u2f_keys` (or `/etc/u2f_mappings`). Replaces fixtures from Phase 1.
**Files:** **Files:**
- `common/src/policy.rs` — fully implemented TOML parser with last-key-wins merge - `common/src/policy.rs` — fully implemented TOML parser with last-key-wins merge
- `daemon/src/storage/policy.rs` — read/write policy.d/ dir, inotify watcher - `daemon/src/storage/policy.rs` — read/write policy.d/ dir, inotify watcher
- `daemon/src/storage/pending.rs` — read/write pending flags - `daemon/src/storage/pending.rs` — read/write pending flags
- `daemon/src/storage/credentials.rs` — manipulate u2f_keys files (per-user vs central) - `daemon/src/storage/credentials.rs` — manipulate u2f_keys files (per-user vs central)
- `daemon/src/storage/userdb.rs``/var/lib/ubuntu-fido/users.db` cache (sqlite via `rusqlite`) - `daemon/src/storage/userdb.rs``/var/lib/authforge/users.db` cache (sqlite via `rusqlite`)
**Tasks:** **Tasks:**
1. Implement `Policy::load_from_dir(path)` with merge semantics. TDD: write 3 test fixture dirs, assert merge order. 1. Implement `Policy::load_from_dir(path)` with merge semantics. TDD: write 3 test fixture dirs, assert merge order.
@@ -864,10 +864,10 @@ impl UbuntuFido {
**Files:** **Files:**
- `daemon/src/policy_apply/mod.rs` - `daemon/src/policy_apply/mod.rs`
- `daemon/src/policy_apply/profile.rs` — generates the pam-configs file dynamically - `daemon/src/policy_apply/profile.rs` — generates the pam-configs file dynamically
- `debian/ubuntu-fido-pam.install` — ships the static pam-configs profile - `debian/authforge-pam.install` — ships the static pam-configs profile
- `debian/ubuntu-fido-daemon.postinst` — runs `pam-auth-update --package` on install - `debian/authforge-daemon.postinst` — runs `pam-auth-update --package` on install
**Approach:** The pam-configs profile is templated. Daemon writes the appropriate variant to `/usr/share/pam-configs/ubuntu-fido` based on which stacks are required, then invokes `pam-auth-update --package`. (Alternative: ship multiple profiles, enable/disable each. The first approach is simpler.) **Approach:** The pam-configs profile is templated. Daemon writes the appropriate variant to `/usr/share/pam-configs/authforge` based on which stacks are required, then invokes `pam-auth-update --package`. (Alternative: ship multiple profiles, enable/disable each. The first approach is simpler.)
**Tasks:** **Tasks:**
1. Write template renderer. 1. Write template renderer.
@@ -904,20 +904,20 @@ Returns a list of (user, stack, reason) violations.
--- ---
# Phase 6: PAM Module — `pam_ubuntu_fido_pending.so` # Phase 6: PAM Module — `pam_authforge_pending.so`
**Goal:** The C module that backstops first-login enrollment. Replaces the Phase 0 stub. **Goal:** The C module that backstops first-login enrollment. Replaces the Phase 0 stub.
**Files:** **Files:**
- `pam/pam_ubuntu_fido_pending.c` — full implementation - `pam/pam_authforge_pending.c` — full implementation
- `pam/Makefile` — already exists from Phase 0 - `pam/Makefile` — already exists from Phase 0
**Behavior:** **Behavior:**
- `pam_sm_authenticate`: - `pam_sm_authenticate`:
1. Get `PAM_USER`. 1. Get `PAM_USER`.
2. Check for `/var/lib/ubuntu-fido/pending/<user>` (use `stat()`, root-owned, mode 0644, no setuid surprises). 2. Check for `/var/lib/authforge/pending/<user>` (use `stat()`, root-owned, mode 0644, no setuid surprises).
3. If exists: 3. If exists:
a. If recovery code provided and matches `/var/lib/ubuntu-fido/recovery/<user>` (8-digit, 24h validity), accept and re-flag for re-enrollment. a. If recovery code provided and matches `/var/lib/authforge/recovery/<user>` (8-digit, 24h validity), accept and re-flag for re-enrollment.
b. Otherwise return `PAM_AUTH_ERR` with conv message: "Account setup incomplete. Please complete enrollment in the Authentication app." b. Otherwise return `PAM_AUTH_ERR` with conv message: "Account setup incomplete. Please complete enrollment in the Authentication app."
4. If no flag: return `PAM_IGNORE`. 4. If no flag: return `PAM_IGNORE`.
- `pam_sm_setcred`: `PAM_SUCCESS`. - `pam_sm_setcred`: `PAM_SUCCESS`.
@@ -928,11 +928,11 @@ Returns a list of (user, stack, reason) violations.
3. Add structured logging via `pam_syslog`. 3. Add structured logging via `pam_syslog`.
4. Test with `pamtester`: 4. Test with `pamtester`:
``` ```
sudo touch /var/lib/ubuntu-fido/pending/alice sudo touch /var/lib/authforge/pending/alice
pamtester ubuntu-fido alice authenticate pamtester authforge alice authenticate
# expect: failure with our message # expect: failure with our message
sudo rm /var/lib/ubuntu-fido/pending/alice sudo rm /var/lib/authforge/pending/alice
pamtester ubuntu-fido alice authenticate pamtester authforge alice authenticate
# expect: success (PAM_IGNORE → other modules carry it) # expect: success (PAM_IGNORE → other modules carry it)
``` ```
@@ -940,7 +940,7 @@ Returns a list of (user, stack, reason) violations.
--- ---
# Phase 7: CLI — `ubuntu-fidoctl` # Phase 7: CLI — `authforgectl`
**Goal:** Full admin/fleet CLI. Talks D-Bus to the daemon. No magic — every command is a thin wrapper around a D-Bus call. **Goal:** Full admin/fleet CLI. Talks D-Bus to the daemon. No magic — every command is a thin wrapper around a D-Bus call.
@@ -951,19 +951,19 @@ Returns a list of (user, stack, reason) violations.
**Subcommands:** **Subcommands:**
``` ```
ubuntu-fidoctl status authforgectl status
ubuntu-fidoctl enroll [--user USER] [--nickname NAME] authforgectl enroll [--user USER] [--nickname NAME]
ubuntu-fidoctl list [--user USER] authforgectl list [--user USER]
ubuntu-fidoctl remove [--user USER] CRED_ID authforgectl remove [--user USER] CRED_ID
ubuntu-fidoctl policy show authforgectl policy show
ubuntu-fidoctl policy set <stack> <mode> [--methods METHOD,...] authforgectl policy set <stack> <mode> [--methods METHOD,...]
ubuntu-fidoctl policy apply [--force-i-know-what-im-doing] authforgectl policy apply [--force-i-know-what-im-doing]
ubuntu-fidoctl policy validate authforgectl policy validate
ubuntu-fidoctl pending set USER [--methods METHOD,...] authforgectl pending set USER [--methods METHOD,...]
ubuntu-fidoctl pending clear USER authforgectl pending clear USER
ubuntu-fidoctl pending list authforgectl pending list
ubuntu-fidoctl recovery generate USER authforgectl recovery generate USER
ubuntu-fidoctl recovery list USER authforgectl recovery list USER
``` ```
**Output:** human-readable by default; `--json` flag for machine-parseable output (essential for Ansible integration). **Output:** human-readable by default; `--json` flag for machine-parseable output (essential for Ansible integration).
@@ -1023,12 +1023,12 @@ ubuntu-fidoctl recovery list USER
**Files:** **Files:**
- `gui/src/views/firstrun.rs` — fullscreen modal mode - `gui/src/views/firstrun.rs` — fullscreen modal mode
- `gui/data/ubuntu-fido-firstrun.desktop` — autostart entry - `gui/data/authforge-firstrun.desktop` — autostart entry
- `debian/ubuntu-fido-gui.install` — install autostart entry to `/etc/xdg/autostart/` - `debian/authforge-gui.install` — install autostart entry to `/etc/xdg/autostart/`
- `gui/src/main.rs` — handle `--first-run` CLI flag - `gui/src/main.rs` — handle `--first-run` CLI flag
**Tasks:** **Tasks:**
1. Add `--first-run` flag to `ubuntu-fido` binary; alters startup to fullscreen modal mode (no header bar, no decorations, sticky-on-top). 1. Add `--first-run` flag to `authforge` binary; alters startup to fullscreen modal mode (no header bar, no decorations, sticky-on-top).
2. On startup in first-run mode, query daemon for `pending_flag(current_user)`; if absent, exit immediately. 2. On startup in first-run mode, query daemon for `pending_flag(current_user)`; if absent, exit immediately.
3. Show welcome page + run enrollment flow. 3. Show welcome page + run enrollment flow.
4. On successful enrollment, call `clear_pending_flag` via D-Bus, exit cleanly. 4. On successful enrollment, call `clear_pending_flag` via D-Bus, exit cleanly.
@@ -1037,9 +1037,9 @@ ubuntu-fidoctl recovery list USER
**Test (manual, in VM):** **Test (manual, in VM):**
1. `useradd -m testuser`, `passwd testuser` (set tempPW), `chage -d 0 testuser`. 1. `useradd -m testuser`, `passwd testuser` (set tempPW), `chage -d 0 testuser`.
2. `sudo ubuntu-fidoctl pending set testuser --methods fido2`. 2. `sudo authforgectl pending set testuser --methods fido2`.
3. Log out, log in as testuser → forced password change → enrollment modal appears → enroll Yubikey → modal closes → desktop usable. 3. Log out, log in as testuser → forced password change → enrollment modal appears → enroll Yubikey → modal closes → desktop usable.
4. Verify: subsequent SSH-as-testuser-without-key is blocked by `pam_ubuntu_fido_pending.so` if pending wasn't cleared. 4. Verify: subsequent SSH-as-testuser-without-key is blocked by `pam_authforge_pending.so` if pending wasn't cleared.
**Acceptance:** End-to-end flow C works in a VM smoke test. **Acceptance:** End-to-end flow C works in a VM smoke test.
@@ -1053,7 +1053,7 @@ ubuntu-fidoctl recovery list USER
- `daemon/Cargo.toml` — add `totp` feature - `daemon/Cargo.toml` — add `totp` feature
- `daemon/src/totp/mod.rs` — secret generation, recovery codes - `daemon/src/totp/mod.rs` — secret generation, recovery codes
- `gui/src/views/totp.rs` — QR code display, secret enrollment - `gui/src/views/totp.rs` — QR code display, secret enrollment
- `debian/ubuntu-fido-daemon.install` — conditionally install pam-configs entry for TOTP - `debian/authforge-daemon.install` — conditionally install pam-configs entry for TOTP
- `debian/control``Recommends: libpam-google-authenticator` when feature enabled - `debian/control``Recommends: libpam-google-authenticator` when feature enabled
**Tasks:** **Tasks:**
@@ -1077,9 +1077,9 @@ ubuntu-fidoctl recovery list USER
- `gui/src/views/recovery.rs` - `gui/src/views/recovery.rs`
**Tasks:** **Tasks:**
1. `generate_recovery_code(user)` writes `/var/lib/ubuntu-fido/recovery/<user>` (root-owned, 0600) containing Argon2id hash + expiry timestamp. 1. `generate_recovery_code(user)` writes `/var/lib/authforge/recovery/<user>` (root-owned, 0600) containing Argon2id hash + expiry timestamp.
2. PAM module checks recovery code at password prompt (already added in Phase 6); on success, removes recovery file and writes a `/var/lib/ubuntu-fido/pending/<user>` flag with `re_enroll = true`. 2. PAM module checks recovery code at password prompt (already added in Phase 6); on success, removes recovery file and writes a `/var/lib/authforge/pending/<user>` flag with `re_enroll = true`.
3. CLI: `ubuntu-fidoctl recovery generate alice` outputs the 8-digit code. 3. CLI: `authforgectl recovery generate alice` outputs the 8-digit code.
4. GUI: "Recovery" tab shows generate / list / revoke. 4. GUI: "Recovery" tab shows generate / list / revoke.
5. Print-PDF feature: `gtk_print_unix_dialog` with template containing user's TOTP secret (opt-in only) and recovery codes. 5. Print-PDF feature: `gtk_print_unix_dialog` with template containing user's TOTP secret (opt-in only) and recovery codes.
@@ -1092,32 +1092,32 @@ ubuntu-fidoctl recovery list USER
**Goal:** All packages install cleanly, postinst/prerm/postrm scripts handle every state transition correctly, debconf preseed works. **Goal:** All packages install cleanly, postinst/prerm/postrm scripts handle every state transition correctly, debconf preseed works.
**Files:** **Files:**
- `debian/ubuntu-fido-daemon.{postinst,prerm,postrm}` - `debian/authforge-daemon.{postinst,prerm,postrm}`
- `debian/ubuntu-fido-pam.{postinst,prerm,postrm}` - `debian/authforge-pam.{postinst,prerm,postrm}`
- `debian/ubuntu-fido.config` — debconf script - `debian/authforge.config` — debconf script
- `debian/ubuntu-fido.templates` — debconf templates - `debian/authforge.templates` — debconf templates
**Tasks:** **Tasks:**
1. postinst: enable + start daemon; run `pam-auth-update --package`; if first-time install and debconf provided answers, write initial `/etc/ubuntu-fido/policy.d/00-debconf.conf`. 1. postinst: enable + start daemon; run `pam-auth-update --package`; if first-time install and debconf provided answers, write initial `/etc/authforge/policy.d/00-debconf.conf`.
2. prerm: disable PAM enforcement (`pam-auth-update --package --remove`) so removal can't lock anyone out. 2. prerm: disable PAM enforcement (`pam-auth-update --package --remove`) so removal can't lock anyone out.
3. postrm purge: rm `/etc/ubuntu-fido/`, `/var/lib/ubuntu-fido/`. 3. postrm purge: rm `/etc/authforge/`, `/var/lib/authforge/`.
4. debconf templates: ask "Default policy? (None / Optional everywhere / Required for sudo)". 4. debconf templates: ask "Default policy? (None / Optional everywhere / Required for sudo)".
5. lintian: get all warnings down to zero or explicitly overridden with rationale. 5. lintian: get all warnings down to zero or explicitly overridden with rationale.
6. Test matrix: install → upgrade (from 0.0.x → 0.1.0) → remove → purge → reinstall, on Ubuntu 22.04 and 24.04. 6. Test matrix: install → upgrade (from 0.0.x → 0.1.0) → remove → purge → reinstall, on Ubuntu 22.04 and 24.04.
**Acceptance:** `piuparts ubuntu-fido_0.1.0-1_amd64.deb` passes. **Acceptance:** `piuparts authforge_0.1.0-1_amd64.deb` passes.
--- ---
# Phase 14: Launchpad PPA Setup # Phase 14: Launchpad PPA Setup
**Goal:** End users can `sudo add-apt-repository ppa:dangerousthings/ubuntu-fido`. **Goal:** End users can `sudo add-apt-repository ppa:dangerousthings/authforge`.
**Tasks:** **Tasks:**
1. Create Launchpad team `dangerousthings` (or use existing). 1. Create Launchpad team `dangerousthings` (or use existing).
2. Create PPA `ubuntu-fido`. 2. Create PPA `authforge`.
3. Generate signing key (gpg, store passphrase in 1Password / vault). 3. Generate signing key (gpg, store passphrase in 1Password / vault).
4. `dput ppa:dangerousthings/ubuntu-fido ubuntu-fido_0.1.0-1_source.changes` (note: Launchpad builds from source). 4. `dput ppa:dangerousthings/authforge authforge_0.1.0-1_source.changes` (note: Launchpad builds from source).
5. Wait for build, verify install on a fresh VM. 5. Wait for build, verify install on a fresh VM.
6. Document signing key fingerprint in README. 6. Document signing key fingerprint in README.
@@ -1125,13 +1125,13 @@ ubuntu-fidoctl recovery list USER
--- ---
# Phase 15: `ubuntu-fido-gnome-integration` # Phase 15: `authforge-gnome-integration`
**Goal:** Optional shortcut deb that makes ubuntu-fido discoverable from `gnome-control-center` Users panel. **Goal:** Optional shortcut deb that makes authforge discoverable from `gnome-control-center` Users panel.
**Files:** **Files:**
- New source tree under `gnome-integration/` (separate Cargo workspace member or pure-data deb) - New source tree under `gnome-integration/` (separate Cargo workspace member or pure-data deb)
- `debian/ubuntu-fido-gnome-integration.install` - `debian/authforge-gnome-integration.install`
**Approach (research required during this phase):** GNOME 46+ allows panel extensions via dbus-activated services. Ship a small JS/GJS extension or a dynamic library loaded by gnome-control-center. If neither stable approach exists, ship a `.desktop` file under `/usr/share/applications/` tagged with `X-GNOME-Settings-Panel=user-accounts` or similar — exact mechanism is GNOME-version-dependent. **Approach (research required during this phase):** GNOME 46+ allows panel extensions via dbus-activated services. Ship a small JS/GJS extension or a dynamic library loaded by gnome-control-center. If neither stable approach exists, ship a `.desktop` file under `/usr/share/applications/` tagged with `X-GNOME-Settings-Panel=user-accounts` or similar — exact mechanism is GNOME-version-dependent.
@@ -1141,13 +1141,13 @@ ubuntu-fidoctl recovery list USER
3. Test against current Ubuntu LTS GNOME version. 3. Test against current Ubuntu LTS GNOME version.
4. Pin Recommends to specific gnome-control-center major version range. 4. Pin Recommends to specific gnome-control-center major version range.
**Acceptance:** Opening Settings → Users → some-user shows a "Configure security…" link that launches `ubuntu-fido --user some-user` with proper polkit context. **Acceptance:** Opening Settings → Users → some-user shows a "Configure security…" link that launches `authforge --user some-user` with proper polkit context.
--- ---
# Phase 16: Ansible Role # Phase 16: Ansible Role
**Goal:** `dangerousthings.ubuntu_fido` role on Ansible Galaxy that handles install, policy, and enrollment for fleet deployments. **Goal:** `dangerousthings.authforge` role on Ansible Galaxy that handles install, policy, and enrollment for fleet deployments.
**Files (in a separate `ansible-role` tree, possibly its own repo):** **Files (in a separate `ansible-role` tree, possibly its own repo):**
- `ansible-role/tasks/main.yml` - `ansible-role/tasks/main.yml`
@@ -1156,7 +1156,7 @@ ubuntu-fidoctl recovery list USER
- `ansible-role/meta/main.yml` - `ansible-role/meta/main.yml`
**Tasks:** **Tasks:**
1. Role tasks: add PPA, install packages, drop policy file at `/etc/ubuntu-fido/policy.d/90-fleet.conf`, restart daemon. 1. Role tasks: add PPA, install packages, drop policy file at `/etc/authforge/policy.d/90-fleet.conf`, restart daemon.
2. Variables for: enabled stacks, modes per stack, central credential storage on/off, default firstrun methods. 2. Variables for: enabled stacks, modes per stack, central credential storage on/off, default firstrun methods.
3. Examples in `examples/playbook.yml`. 3. Examples in `examples/playbook.yml`.
4. Publish to Galaxy. 4. Publish to Galaxy.
@@ -1196,7 +1196,7 @@ Use `umockdev` to simulate USB devices in CI; require real hardware for nightly
- `docs/user/fleet-deployment.md` - `docs/user/fleet-deployment.md`
- `docs/user/recovery.md` - `docs/user/recovery.md`
- `docs/user/troubleshooting.md` - `docs/user/troubleshooting.md`
- Optional: small static site (mdBook or Docusaurus) at `https://ubuntu-fido.dangerousthings.com` - Optional: small static site (mdBook or Docusaurus) at `https://authforge.dangerousthings.com`
**Tasks:** **Tasks:**
1. Getting started: end-user view of "install + enroll my Yubikey + require it for sudo". 1. Getting started: end-user view of "install + enroll my Yubikey + require it for sudo".
@@ -1247,7 +1247,7 @@ Use `umockdev` to simulate USB devices in CI; require real hardware for nightly
When the user returns: When the user returns:
> Plan complete and saved to `docs/plans/2026-04-26-ubuntu-fido-implementation.md`. The companion design doc is at `docs/plans/2026-04-26-ubuntu-fido-design.md`. Phase 0 is fully detailed; phases 118 are spec'd to a level sufficient to begin work. Each later phase should re-invoke `superpowers:writing-plans` for step-level expansion as it begins. > Plan complete and saved to `docs/plans/2026-04-26-authforge-implementation.md`. The companion design doc is at `docs/plans/2026-04-26-authforge-design.md`. Phase 0 is fully detailed; phases 118 are spec'd to a level sufficient to begin work. Each later phase should re-invoke `superpowers:writing-plans` for step-level expansion as it begins.
> >
> Two execution options: > Two execution options:
> >

View File

@@ -1,15 +1,15 @@
[package] [package]
name = "ubuntu-fido-gui" name = "authforge-gui"
version.workspace = true version.workspace = true
edition.workspace = true edition.workspace = true
license.workspace = true license.workspace = true
[[bin]] [[bin]]
name = "ubuntu-fido" name = "authforge"
path = "src/main.rs" path = "src/main.rs"
[dependencies] [dependencies]
gtk = { package = "gtk4", version = "0.8" } gtk = { package = "gtk4", version = "0.8" }
adw = { package = "libadwaita", version = "0.6" } adw = { package = "libadwaita", version = "0.6" }
ubuntu-fido-common = { path = "../common" } authforge-common = { path = "../common" }
anyhow = { workspace = true } anyhow = { workspace = true }

View File

@@ -2,8 +2,8 @@
Name=Authentication Name=Authentication
GenericName=Security Keys & MFA GenericName=Security Keys & MFA
Comment=Manage U2F/FIDO2 keys and authentication policy Comment=Manage U2F/FIDO2 keys and authentication policy
Exec=ubuntu-fido Exec=authforge
Icon=io.dangerousthings.UbuntuFido Icon=io.dangerousthings.AuthForge
Terminal=false Terminal=false
Type=Application Type=Application
Categories=Settings;Security; Categories=Settings;Security;

View File

Before

Width:  |  Height:  |  Size: 440 B

After

Width:  |  Height:  |  Size: 440 B

View File

@@ -1,7 +1,7 @@
use adw::prelude::*; use adw::prelude::*;
use gtk::glib; use gtk::glib;
const APP_ID: &str = "io.dangerousthings.UbuntuFido"; const APP_ID: &str = "io.dangerousthings.AuthForge";
fn main() -> glib::ExitCode { fn main() -> glib::ExitCode {
let app = adw::Application::builder().application_id(APP_ID).build(); let app = adw::Application::builder().application_id(APP_ID).build();

View File

@@ -1,13 +1,13 @@
CFLAGS ?= -Wall -Wextra -Werror -fPIC -O2 CFLAGS ?= -Wall -Wextra -Werror -fPIC -O2
LIBDIR ?= /usr/lib/$(shell dpkg-architecture -qDEB_HOST_MULTIARCH)/security LIBDIR ?= /usr/lib/$(shell dpkg-architecture -qDEB_HOST_MULTIARCH)/security
pam_ubuntu_fido_pending.so: pam_ubuntu_fido_pending.c pam_authforge_pending.so: pam_authforge_pending.c
$(CC) $(CFLAGS) -shared -o $@ $< -lpam $(CC) $(CFLAGS) -shared -o $@ $< -lpam
install: pam_ubuntu_fido_pending.so install: pam_authforge_pending.so
install -D -m 0644 pam_ubuntu_fido_pending.so $(DESTDIR)$(LIBDIR)/pam_ubuntu_fido_pending.so install -D -m 0644 pam_authforge_pending.so $(DESTDIR)$(LIBDIR)/pam_authforge_pending.so
clean: clean:
rm -f pam_ubuntu_fido_pending.so rm -f pam_authforge_pending.so
.PHONY: install clean .PHONY: install clean

View File

@@ -7,7 +7,7 @@
PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags,
int argc, const char **argv) { int argc, const char **argv) {
(void)flags; (void)argc; (void)argv; (void)flags; (void)argc; (void)argv;
pam_syslog(pamh, LOG_INFO, "ubuntu_fido_pending: stub - allowing"); pam_syslog(pamh, LOG_INFO, "authforge_pending: stub - allowing");
return PAM_IGNORE; /* implemented in Phase 6 */ return PAM_IGNORE; /* implemented in Phase 6 */
} }