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

@@ -1,4 +1,4 @@
# Building ubuntu-fido from source
# Building authforge from source
## System dependencies
@@ -19,32 +19,24 @@ Rust toolchain: stable (1.78 minimum). Install via [rustup](https://rustup.rs).
cargo build --release
# 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)
make -C pam
# Full deb build (requires all of the above)
debuild -us -uc -b
# Output: ../ubuntu-fido*.deb
# Full deb build (requires all of the above + debhelper)
dpkg-buildpackage -us -uc -b
# Output: ../authforge*.deb
```
## Tests
```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
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).
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.
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).

View File

@@ -1,4 +1,4 @@
# ubuntu-fido — Design Document
# authforge — Design Document
**Date:** 2026-04-26
**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. |
| 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. |
| 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. |
| 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. |
| 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_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
```
┌─────────────────────────┐ ┌──────────────────────────┐ ┌──────────────┐
ubuntu-fido GTK app │ │ ubuntu-fidoctl (CLI) │ │ Future KDE │
authforge GTK app │ │ authforgectl (CLI) │ │ Future KDE │
│ (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 │
│ - policy read/write + lockout │
│ 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>
via pam-auth- (admin/fleet /etc/u2f_mappings (first-login flag)
update) drop-ins) (central override)
@@ -55,7 +55,7 @@ update) drop-ins) (central override)
### 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 |
|---|---|
@@ -74,18 +74,18 @@ The daemon owns all writes to `/etc/pam.d/`, `/etc/ubuntu-fido/`, and `/var/lib/
| Package | Contents | Type |
|---|---|---|
| `ubuntu-fido` | 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 |
| `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 |
| `ubuntu-fido-cli` | `/usr/bin/ubuntu-fidoctl` (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) |
| `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` | Empty metapackage. | Depends |
| `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 |
| `authforge-pam` | `/usr/lib/$DEB_HOST_MULTIARCH/security/pam_authforge_pending.so`, pam-auth-update profile at `/usr/share/pam-configs/authforge` | Hard dep |
| `authforge-cli` | `/usr/bin/authforgectl` (Rust binary, talks D-Bus) | Hard dep |
| `authforge-gui` | `/usr/bin/authforge` (GTK4/libadwaita), `.desktop` file with `Categories=Settings;Security;`, app icon | **Recommends** (so headless servers can skip) |
| `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
```bash
sudo add-apt-repository ppa:dangerousthings/ubuntu-fido
sudo apt install ubuntu-fido
sudo add-apt-repository ppa:dangerousthings/authforge
sudo apt install authforge
```
Two commands. After install:
@@ -97,20 +97,20 @@ Two commands. After install:
### Headless / fleet install
```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.
### 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
### App: `ubuntu-fido`
### App: `authforge`
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
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.
### 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).
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).
## Enrollment flows
### 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".
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.
@@ -178,49 +178,49 @@ polkit asks admin password.
**At account creation (admin side):**
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).
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):**
1. User logs into GDM with temp password.
2. PAM `passwd` module forces password change (existing Ubuntu behavior, no work for us).
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."
6. User enrolls (Flow A inside the modal).
7. On success, daemon clears pending flag.
8. Modal closes, user lands on a normal desktop.
**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."
- A watchdog inside `ubuntu-fido --first-run` calls `gnome-session-quit --logout --no-prompt` after 60 seconds of inactivity in the modal.
- 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 `authforge --first-run` calls `gnome-session-quit --logout --no-prompt` after 60 seconds of inactivity in the modal.
### Flow D: Lost key recovery
1. User can't authenticate.
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.
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.
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_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.
## PAM and policy details
### 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
Priority: 192
Auth-Type: Additional
Auth:
[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.
### 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:
@@ -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:
- 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'."
@@ -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.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. |
| **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.
>
> **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.
**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 |
| 4 | Daemon: policy apply via pam-auth-update wrapper | 4 days | Spec'd |
| 5 | Daemon: lockout simulator | 3 days | Spec'd |
| 6 | PAM module: `pam_ubuntu_fido_pending.so` (C) | 3 days | Spec'd |
| 7 | CLI: `ubuntu-fidoctl` | 4 days | Spec'd |
| 6 | PAM module: `pam_authforge_pending.so` (C) | 3 days | Spec'd |
| 7 | CLI: `authforgectl` | 4 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 |
| 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 |
| 13 | Debian packaging finalization (postinst, debconf, purge) | 4 days | Spec'd |
| 14 | Launchpad PPA build setup | 2 days | Spec'd |
| 15 | `ubuntu-fido-gnome-integration` (Users panel shortcut) | 3 days | Spec'd |
| 16 | Ansible role `dangerousthings.ubuntu_fido` | 2 days | Spec'd |
| 15 | `authforge-gnome-integration` (Users panel shortcut) | 3 days | Spec'd |
| 16 | Ansible role `dangerousthings.authforge` | 2 days | Spec'd |
| 17 | Integration test harness (Multipass / LXD VM) | 4 days | Spec'd |
| 18 | User docs + onboarding site | 3 days | Spec'd |
| **R** | **v1.0 release** | 1 day | — |
@@ -82,7 +82,7 @@ debian/.debhelper/
debian/files
debian/*.substvars
debian/*.debhelper.log
debian/ubuntu-fido*/
debian/authforge*/
build/
.vscode/
*.swp
@@ -93,14 +93,14 @@ build/
**Step 4:** Write minimal `README.md`:
```markdown
# ubuntu-fido
# authforge
Turnkey U2F / FIDO2 passkey / TOTP MFA for Ubuntu desktops.
## Install (end users)
sudo add-apt-repository ppa:dangerousthings/ubuntu-fido
sudo apt install ubuntu-fido
sudo add-apt-repository ppa:dangerousthings/authforge
sudo apt install authforge
## Build from source
@@ -137,7 +137,7 @@ version = "0.1.0"
edition = "2021"
license = "Apache-2.0"
authors = ["Dangerous Things <ops@dangerousthings.com>"]
repository = "https://github.com/dangerousthings/ubuntu-fido"
repository = "https://github.com/dangerousthings/authforge"
rust-version = "1.78"
[workspace.dependencies]
@@ -185,7 +185,7 @@ rustflags = ["-D", "warnings"]
```toml
[package]
name = "ubuntu-fido-common"
name = "authforge-common"
version.workspace = true
edition.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`.
**Step 6:** Run `cargo test -p ubuntu-fido-common`. Expected: PASS.
**Step 6:** Run `cargo test -p authforge-common`. Expected: PASS.
**Step 7:** Commit.
@@ -267,17 +267,17 @@ git commit -m "Add common crate with shared Mode and Method types"
```toml
[package]
name = "ubuntu-fidod"
name = "authforged"
version.workspace = true
edition.workspace = true
license.workspace = true
[[bin]]
name = "ubuntu-fidod"
name = "authforged"
path = "src/main.rs"
[dependencies]
ubuntu-fido-common = { path = "../common" }
authforge-common = { path = "../common" }
zbus = { workspace = true }
tokio = { workspace = true }
tracing = { workspace = true }
@@ -296,13 +296,13 @@ async fn main() -> Result<()> {
tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.init();
info!("ubuntu-fidod {} starting", env!("CARGO_PKG_VERSION"));
info!("authforged {} starting", env!("CARGO_PKG_VERSION"));
// D-Bus service registration in Phase 1.
Ok(())
}
```
**Step 3:** `cargo build -p ubuntu-fidod`. Expected: success.
**Step 3:** `cargo build -p authforged`. Expected: success.
**Step 4:** Commit.
@@ -321,17 +321,17 @@ git commit -m "Add daemon crate skeleton"
```toml
[package]
name = "ubuntu-fidoctl"
name = "authforgectl"
version.workspace = true
edition.workspace = true
license.workspace = true
[[bin]]
name = "ubuntu-fidoctl"
name = "authforgectl"
path = "src/main.rs"
[dependencies]
ubuntu-fido-common = { path = "../common" }
authforge-common = { path = "../common" }
clap = { workspace = true }
anyhow = { workspace = true }
zbus = { workspace = true }
@@ -345,7 +345,7 @@ use anyhow::Result;
use clap::Parser;
#[derive(Parser)]
#[command(name = "ubuntu-fidoctl", version, about = "Manage ubuntu-fido configuration")]
#[command(name = "authforgectl", version, about = "Manage authforge configuration")]
struct Cli {
#[command(subcommand)]
cmd: Cmd,
@@ -360,7 +360,7 @@ enum Cmd {
async fn main() -> Result<()> {
let args = Cli::parse();
match args.cmd {
Cmd::Status => println!("ubuntu-fidoctl: not yet implemented"),
Cmd::Status => println!("authforgectl: not yet implemented"),
}
Ok(())
}
@@ -379,9 +379,9 @@ git commit -m "Add cli crate skeleton"
**Files:**
- 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
#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,
int argc, const char **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 */
}
@@ -409,14 +409,14 @@ PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags,
CFLAGS ?= -Wall -Wextra -Werror -fPIC -O2
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
install: pam_ubuntu_fido_pending.so
install -D -m 0644 pam_ubuntu_fido_pending.so $(DESTDIR)$(LIBDIR)/pam_ubuntu_fido_pending.so
install: pam_authforge_pending.so
install -D -m 0644 pam_authforge_pending.so $(DESTDIR)$(LIBDIR)/pam_authforge_pending.so
clean:
rm -f pam_ubuntu_fido_pending.so
rm -f pam_authforge_pending.so
.PHONY: install clean
```
@@ -427,7 +427,7 @@ clean:
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.
@@ -441,8 +441,8 @@ git commit -m "Add PAM module stub"
**Files:**
- Create: `gui/Cargo.toml`
- Create: `gui/src/main.rs`
- Create: `gui/data/io.dangerousthings.UbuntuFido.desktop`
- Create: `gui/data/io.dangerousthings.UbuntuFido.svg` (1KB placeholder icon)
- Create: `gui/data/io.dangerousthings.AuthForge.desktop`
- Create: `gui/data/io.dangerousthings.AuthForge.svg` (1KB placeholder icon)
**Step 1:** Add `gui` to workspace members in root `Cargo.toml`.
@@ -450,19 +450,19 @@ git commit -m "Add PAM module stub"
```toml
[package]
name = "ubuntu-fido-gui"
name = "authforge-gui"
version.workspace = true
edition.workspace = true
license.workspace = true
[[bin]]
name = "ubuntu-fido"
name = "authforge"
path = "src/main.rs"
[dependencies]
gtk = { package = "gtk4", version = "0.8" }
adw = { package = "libadwaita", version = "0.6" }
ubuntu-fido-common = { path = "../common" }
authforge-common = { path = "../common" }
anyhow = { workspace = true }
```
@@ -472,7 +472,7 @@ anyhow = { workspace = true }
use adw::prelude::*;
use gtk::glib;
const APP_ID: &str = "io.dangerousthings.UbuntuFido";
const APP_ID: &str = "io.dangerousthings.AuthForge";
fn main() -> glib::ExitCode {
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 Entry]
Name=Authentication
GenericName=Security Keys & MFA
Comment=Manage U2F/FIDO2 keys and authentication policy
Exec=ubuntu-fido
Icon=io.dangerousthings.UbuntuFido
Exec=authforge
Icon=io.dangerousthings.AuthForge
Terminal=false
Type=Application
Categories=Settings;Security;
@@ -515,12 +515,12 @@ Keywords=mfa;u2f;fido2;passkey;totp;security;
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`).
```bash
cargo build -p ubuntu-fido-gui
cargo build -p authforge-gui
```
Expected: success.
@@ -541,17 +541,17 @@ git commit -m "Add GUI crate skeleton with libadwaita placeholder window"
- Create: `debian/rules`
- Create: `debian/source/format`
- Create: `debian/compat` (or use `debhelper-compat (= 13)` in control)
- Create: `debian/ubuntu-fido.install`
- Create: `debian/ubuntu-fido-daemon.install`
- Create: `debian/ubuntu-fido-daemon.service`
- Create: `debian/ubuntu-fido-pam.install`
- Create: `debian/ubuntu-fido-cli.install`
- Create: `debian/ubuntu-fido-gui.install`
- Create: `debian/authforge.install`
- Create: `debian/authforge-daemon.install`
- Create: `debian/authforge-daemon.service`
- Create: `debian/authforge-pam.install`
- Create: `debian/authforge-cli.install`
- Create: `debian/authforge-gui.install`
**Step 1:** `debian/changelog`:
```
ubuntu-fido (0.1.0-1) UNRELEASED; urgency=low
authforge (0.1.0-1) UNRELEASED; urgency=low
* 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:
```
Source: ubuntu-fido
Source: authforge
Section: admin
Priority: optional
Maintainer: Dangerous Things <ops@dangerousthings.com>
@@ -581,46 +581,46 @@ Build-Depends:
libfido2-dev,
pkg-config
Standards-Version: 4.6.2
Homepage: https://github.com/dangerousthings/ubuntu-fido
Vcs-Browser: https://github.com/dangerousthings/ubuntu-fido
Vcs-Git: https://github.com/dangerousthings/ubuntu-fido.git
Homepage: https://github.com/dangerousthings/authforge
Vcs-Browser: https://github.com/dangerousthings/authforge
Vcs-Git: https://github.com/dangerousthings/authforge.git
Package: ubuntu-fido
Package: authforge
Architecture: any
Depends: ubuntu-fido-daemon (= ${binary:Version}),
ubuntu-fido-pam (= ${binary:Version}),
ubuntu-fido-cli (= ${binary:Version}),
Depends: authforge-daemon (= ${binary:Version}),
authforge-pam (= ${binary:Version}),
authforge-cli (= ${binary:Version}),
${misc:Depends}
Recommends: ubuntu-fido-gui (= ${binary:Version})
Suggests: ubuntu-fido-gnome-integration
Recommends: authforge-gui (= ${binary:Version})
Suggests: authforge-gnome-integration
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.
Package: ubuntu-fido-daemon
Package: authforge-daemon
Architecture: any
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,
policy edits, and lockout-prevention checks.
Package: ubuntu-fido-pam
Package: authforge-pam
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, libpam-runtime
Description: PAM module backstopping ubuntu-fido first-login enrollment
Refuses authentication when /var/lib/ubuntu-fido/pending/<user> exists
Description: PAM module backstopping authforge first-login enrollment
Refuses authentication when /var/lib/authforge/pending/<user> exists
and the user has not completed first-login MFA setup.
Package: ubuntu-fido-cli
Package: authforge-cli
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, ubuntu-fido-daemon
Description: CLI for ubuntu-fido (admin and fleet)
Depends: ${shlibs:Depends}, ${misc:Depends}, authforge-daemon
Description: CLI for authforge (admin and fleet)
Configure policy, enroll on behalf of users, generate recovery codes.
Package: ubuntu-fido-gui
Package: authforge-gui
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, ubuntu-fido-daemon
Description: GTK4/libadwaita UI for ubuntu-fido
Depends: ${shlibs:Depends}, ${misc:Depends}, authforge-daemon
Description: GTK4/libadwaita UI for authforge
End-user-facing settings panel for enrollment and policy.
```
@@ -640,20 +640,20 @@ override_dh_auto_build:
override_dh_auto_install:
# Daemon
install -D -m 0755 target/release/ubuntu-fidod \
debian/ubuntu-fido-daemon/usr/sbin/ubuntu-fidod
install -D -m 0755 target/release/authforged \
debian/authforge-daemon/usr/sbin/authforged
# CLI
install -D -m 0755 target/release/ubuntu-fidoctl \
debian/ubuntu-fido-cli/usr/bin/ubuntu-fidoctl
install -D -m 0755 target/release/authforgectl \
debian/authforge-cli/usr/bin/authforgectl
# GUI
install -D -m 0755 target/release/ubuntu-fido \
debian/ubuntu-fido-gui/usr/bin/ubuntu-fido
install -D -m 0644 gui/data/io.dangerousthings.UbuntuFido.desktop \
debian/ubuntu-fido-gui/usr/share/applications/io.dangerousthings.UbuntuFido.desktop
install -D -m 0644 gui/data/io.dangerousthings.UbuntuFido.svg \
debian/ubuntu-fido-gui/usr/share/icons/hicolor/scalable/apps/io.dangerousthings.UbuntuFido.svg
install -D -m 0755 target/release/authforge \
debian/authforge-gui/usr/bin/authforge
install -D -m 0644 gui/data/io.dangerousthings.AuthForge.desktop \
debian/authforge-gui/usr/share/applications/io.dangerousthings.AuthForge.desktop
install -D -m 0644 gui/data/io.dangerousthings.AuthForge.svg \
debian/authforge-gui/usr/share/icons/hicolor/scalable/apps/io.dangerousthings.AuthForge.svg
# PAM
$(MAKE) -C pam install DESTDIR=$(CURDIR)/debian/ubuntu-fido-pam
$(MAKE) -C pam install DESTDIR=$(CURDIR)/debian/authforge-pam
override_dh_auto_test:
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).
```bash
sudo dpkg -i ../ubuntu-fido*.deb || sudo apt -f install
sudo systemctl status ubuntu-fido.service # will fail until Phase 1 ships unit
sudo apt purge ubuntu-fido*
sudo dpkg -i ../authforge*.deb || sudo apt -f install
sudo systemctl status authforge.service # will fail until Phase 1 ships unit
sudo apt purge authforge*
```
**Step 9:** Commit.
@@ -721,7 +721,7 @@ jobs:
- 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: 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.
@@ -740,9 +740,9 @@ Verify all of the following before declaring Phase 0 complete:
- [ ] `cargo build --workspace --release` succeeds.
- [ ] `cargo test --workspace` succeeds (1 test in `common`).
- [ ] `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.
- [ ] 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`.
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
**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.**
**Files to create:**
- `daemon/src/dbus.rs` — zbus `interface` definition
- `daemon/src/state.rs` — in-memory state shell
- `debian/ubuntu-fido-daemon.service` — systemd unit
- `debian/io.dangerousthings.UbuntuFido.conf` — D-Bus policy
- `debian/io.dangerousthings.UbuntuFido.service` — D-Bus activation file
- `debian/io.dangerousthings.UbuntuFido.policy` — polkit actions
- `debian/ubuntu-fido-daemon.postinst` — enable + start unit
- `debian/authforge-daemon.service` — systemd unit
- `debian/io.dangerousthings.AuthForge.conf` — D-Bus policy
- `debian/io.dangerousthings.AuthForge.service` — D-Bus activation file
- `debian/io.dangerousthings.AuthForge.policy` — polkit actions
- `debian/authforge-daemon.postinst` — enable + start unit
**D-Bus interface skeleton:**
```rust
#[zbus::interface(name = "io.dangerousthings.UbuntuFido1")]
impl UbuntuFido {
#[zbus::interface(name = "io.dangerousthings.AuthForge1")]
impl AuthForge {
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 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 |
|---|---|
| `io.dangerousthings.UbuntuFido.enroll-own` | `auth_self_keep` |
| `io.dangerousthings.UbuntuFido.remove-own` | `auth_self_keep` |
| `io.dangerousthings.UbuntuFido.enroll-other` | `auth_admin_keep` |
| `io.dangerousthings.UbuntuFido.set-policy` | `auth_admin_keep` |
| `io.dangerousthings.UbuntuFido.set-pending` | `auth_admin_keep` |
| `io.dangerousthings.UbuntuFido.clear-pending` | `auth_admin_keep` |
| `io.dangerousthings.UbuntuFido.generate-recovery` | `auth_admin_keep` |
| `io.dangerousthings.AuthForge.enroll-own` | `auth_self_keep` |
| `io.dangerousthings.AuthForge.remove-own` | `auth_self_keep` |
| `io.dangerousthings.AuthForge.enroll-other` | `auth_admin_keep` |
| `io.dangerousthings.AuthForge.set-policy` | `auth_admin_keep` |
| `io.dangerousthings.AuthForge.set-pending` | `auth_admin_keep` |
| `io.dangerousthings.AuthForge.clear-pending` | `auth_admin_keep` |
| `io.dangerousthings.AuthForge.generate-recovery` | `auth_admin_keep` |
**Tasks:**
1. Add zbus interface module with stubs returning fixture data.
2. Wire up `connection.request_name("io.dangerousthings.UbuntuFido")`.
3. Write systemd unit (`Type=dbus`, `BusName=io.dangerousthings.UbuntuFido`, `User=root`).
2. Wire up `connection.request_name("io.dangerousthings.AuthForge")`.
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).
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`.
7. Test: `busctl call io.dangerousthings.UbuntuFido /io/dangerousthings/UbuntuFido io.dangerousthings.UbuntuFido1 ListCredentials s "$USER"` returns the fixture data.
6. Wire postinst: `systemctl daemon-reload && systemctl enable --now authforge.service`.
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.
**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
**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:**
- `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/pending.rs` — read/write pending flags
- `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:**
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:**
- `daemon/src/policy_apply/mod.rs`
- `daemon/src/policy_apply/profile.rs` — generates the pam-configs file dynamically
- `debian/ubuntu-fido-pam.install` — ships the static pam-configs profile
- `debian/ubuntu-fido-daemon.postinst` — runs `pam-auth-update --package` on install
- `debian/authforge-pam.install` — ships the static pam-configs profile
- `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:**
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.
**Files:**
- `pam/pam_ubuntu_fido_pending.c` — full implementation
- `pam/pam_authforge_pending.c` — full implementation
- `pam/Makefile` — already exists from Phase 0
**Behavior:**
- `pam_sm_authenticate`:
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:
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."
4. If no flag: return `PAM_IGNORE`.
- `pam_sm_setcred`: `PAM_SUCCESS`.
@@ -928,11 +928,11 @@ Returns a list of (user, stack, reason) violations.
3. Add structured logging via `pam_syslog`.
4. Test with `pamtester`:
```
sudo touch /var/lib/ubuntu-fido/pending/alice
pamtester ubuntu-fido alice authenticate
sudo touch /var/lib/authforge/pending/alice
pamtester authforge alice authenticate
# expect: failure with our message
sudo rm /var/lib/ubuntu-fido/pending/alice
pamtester ubuntu-fido alice authenticate
sudo rm /var/lib/authforge/pending/alice
pamtester authforge alice authenticate
# 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.
@@ -951,19 +951,19 @@ Returns a list of (user, stack, reason) violations.
**Subcommands:**
```
ubuntu-fidoctl status
ubuntu-fidoctl enroll [--user USER] [--nickname NAME]
ubuntu-fidoctl list [--user USER]
ubuntu-fidoctl remove [--user USER] CRED_ID
ubuntu-fidoctl policy show
ubuntu-fidoctl policy set <stack> <mode> [--methods METHOD,...]
ubuntu-fidoctl policy apply [--force-i-know-what-im-doing]
ubuntu-fidoctl policy validate
ubuntu-fidoctl pending set USER [--methods METHOD,...]
ubuntu-fidoctl pending clear USER
ubuntu-fidoctl pending list
ubuntu-fidoctl recovery generate USER
ubuntu-fidoctl recovery list USER
authforgectl status
authforgectl enroll [--user USER] [--nickname NAME]
authforgectl list [--user USER]
authforgectl remove [--user USER] CRED_ID
authforgectl policy show
authforgectl policy set <stack> <mode> [--methods METHOD,...]
authforgectl policy apply [--force-i-know-what-im-doing]
authforgectl policy validate
authforgectl pending set USER [--methods METHOD,...]
authforgectl pending clear USER
authforgectl pending list
authforgectl recovery generate USER
authforgectl recovery list USER
```
**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:**
- `gui/src/views/firstrun.rs` — fullscreen modal mode
- `gui/data/ubuntu-fido-firstrun.desktop` — autostart entry
- `debian/ubuntu-fido-gui.install` — install autostart entry to `/etc/xdg/autostart/`
- `gui/data/authforge-firstrun.desktop` — autostart entry
- `debian/authforge-gui.install` — install autostart entry to `/etc/xdg/autostart/`
- `gui/src/main.rs` — handle `--first-run` CLI flag
**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.
3. Show welcome page + run enrollment flow.
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):**
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.
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.
@@ -1053,7 +1053,7 @@ ubuntu-fidoctl recovery list USER
- `daemon/Cargo.toml` — add `totp` feature
- `daemon/src/totp/mod.rs` — secret generation, recovery codes
- `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
**Tasks:**
@@ -1077,9 +1077,9 @@ ubuntu-fidoctl recovery list USER
- `gui/src/views/recovery.rs`
**Tasks:**
1. `generate_recovery_code(user)` writes `/var/lib/ubuntu-fido/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`.
3. CLI: `ubuntu-fidoctl recovery generate alice` outputs the 8-digit code.
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/authforge/pending/<user>` flag with `re_enroll = true`.
3. CLI: `authforgectl recovery generate alice` outputs the 8-digit code.
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.
@@ -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.
**Files:**
- `debian/ubuntu-fido-daemon.{postinst,prerm,postrm}`
- `debian/ubuntu-fido-pam.{postinst,prerm,postrm}`
- `debian/ubuntu-fido.config` — debconf script
- `debian/ubuntu-fido.templates` — debconf templates
- `debian/authforge-daemon.{postinst,prerm,postrm}`
- `debian/authforge-pam.{postinst,prerm,postrm}`
- `debian/authforge.config` — debconf script
- `debian/authforge.templates` — debconf templates
**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.
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)".
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.
**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
**Goal:** End users can `sudo add-apt-repository ppa:dangerousthings/ubuntu-fido`.
**Goal:** End users can `sudo add-apt-repository ppa:dangerousthings/authforge`.
**Tasks:**
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).
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.
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:**
- 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.
@@ -1141,13 +1141,13 @@ ubuntu-fidoctl recovery list USER
3. Test against current Ubuntu LTS GNOME version.
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
**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):**
- `ansible-role/tasks/main.yml`
@@ -1156,7 +1156,7 @@ ubuntu-fidoctl recovery list USER
- `ansible-role/meta/main.yml`
**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.
3. Examples in `examples/playbook.yml`.
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/recovery.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:**
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:
> 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:
>