macos-auth is an experimental PAM module, Linux helper, and macOS user-session agent for approving Linux authentication requests from a Mac.
This public repository contains the Linux build and packaging subset, release packaging documentation, and user-facing setup notes. The macOS agent package is distributed as a signed/notarized release asset and Homebrew cask; the macOS agent source is not part of this public source subset yet.
Public Linux-side contents include:
- Rust protocol and Linux helper crates
- C PAM shim
- Linux package build scripts and package metadata
- Linux testing and packaging documentation
Status: development-only. Do not use this as a production authentication mechanism yet.
You need two machines or VMs:
- a Mac running the macOS user-session agent
- a Linux host where you want to approve PAM authentication requests from the Mac
On macOS:
- Apple Silicon Mac is the currently tested target
- Touch ID or Apple Watch unlock configured for LocalAuthentication approval
- OpenSSH client
- Homebrew, if installing through the cask
On Linux:
- OpenSSH server
- a user account with sudo access
- a supported package target:
- Ubuntu 24.04 / 25.10,
amd64orarm64 - RHEL 9 / 10 family,
x86_64oraarch64
- Ubuntu 24.04 / 25.10,
pamtesteris strongly recommended before touchingsudoPAM configuration
Security and recovery prerequisites:
- test in a VM or disposable host first
- keep console access to the Linux machine
- keep a root shell open before editing any PAM file
- do not use this as a production authentication mechanism yet
A typical setup has the Linux host connect back to a Unix-domain socket on your Mac through SSH RemoteForward.
The Linux package provides:
/usr/bin/macos-auth-helperpam_macos_auth.so- documentation and PAM examples
The macOS package/cask follows the Apple Silicon Homebrew prefix and provides:
/opt/homebrew/bin/macos-auth-agent- LaunchAgent helper scripts under
/opt/homebrew/share/macos-auth/scripts/ - a LaunchAgent plist template
- sample config files under
/opt/homebrew/share/macos-auth/examples/
Runtime state is intentionally user-controlled:
- macOS agent config:
$HOME/Library/Application Support/macos-auth/agent-config.json - macOS agent socket: commonly under
$HOME/Library/Application Support/macos-auth/agent.sock - Linux helper config:
/etc/macos-auth/config.toml - Linux host private key:
/etc/macos-auth/host_ed25519.key - pinned macOS agent public key:
/etc/macos-auth/agents.d/*.pub - macOS samples:
/opt/homebrew/share/macos-auth/examples/agent-config.json.exampleand/opt/homebrew/share/macos-auth/examples/ssh-config.sample - Linux samples:
/usr/share/macos-auth/examples/config.toml.sampleand/usr/share/macos-auth/examples/ssh-config.sample
+----------------------+ +----------------------+
| Linux host | | macOS user session |
| | | |
| user runs sudo | | macos-auth-agent |
| | | | LocalAuthentication |
| v | | Touch ID / Watch |
| Linux PAM | | ^ |
| | | | | |
| v | signed request | | |
| pam_macos_auth.so |-------------------------+----------+ |
| | | via SSH RemoteForward Unix socket |
| v | | |
| macos-auth-helper | signed response | |
| | |<-----------------------------------------------|
| v | | |
| PAM result | | |
| - approved: success| | |
| - unavailable: | | |
| password fallback | |
| - tamper/unsafe: | | |
| hard fail | | |
+----------------------+ +----------------------+
Trust model summary:
- SSH forwarding is transport only; it is not the trust boundary.
- Linux requests are signed by a root-owned Linux host key.
- macOS responses are signed by a pinned agent key.
- The macOS agent verifies the Linux host signature before showing UI.
- Nonces, timestamps, and request hashes limit replay/substitution risk.
- Authenticator unavailable/cancel/failure can fall back to normal Linux password authentication.
- Invalid signatures, unsafe config, tampering, and protocol errors fail closed.
Run this on macOS. When the Homebrew cask is available:
brew install --cask rioriost/cask/macos-auth
The cask installs files under /opt/homebrew; it does not automatically create keys, host allowlists, or a LaunchAgent.
Prepare an agent key and config, then install the per-user LaunchAgent explicitly. The exact host allowlist setup depends on the Linux host key generated in the next step.
Useful installed commands:
/opt/homebrew/bin/macos-auth-agent --help
/opt/homebrew/share/macos-auth/scripts/install-launchagent.sh --help
/opt/homebrew/share/macos-auth/scripts/status-launchagent.sh
/opt/homebrew/share/macos-auth/scripts/uninstall-launchagent.sh --help
Run this on Linux. Download the package matching your Linux distribution family and architecture from the release assets.
Ubuntu/Debian example:
sudo dpkg -i macos-auth_0.1.0_ubuntu24.04_arm64.deb
RHEL-family example:
sudo rpm -Uvh macos-auth-0.1.0-1.rhel9.aarch64.rpm
Packages install the helper and PAM module, but they do not modify /etc/pam.d/sudo.
Run this on Linux to generate a Linux host key:
sudo mkdir -p /etc/macos-auth/agents.d
sudo /usr/bin/macos-auth-helper gen-key \
--private-key-file /etc/macos-auth/host_ed25519.key \
--public-key-file /etc/macos-auth/host_ed25519.pub
Run this on macOS to initialize a macOS agent key:
/opt/homebrew/bin/macos-auth-agent keychain-init \
--service com.macos-auth.agent \
--account default
/opt/homebrew/bin/macos-auth-agent keychain-public-key \
--service com.macos-auth.agent \
--account default > agent.pub
Copy public keys to the opposite side:
- copy the Linux host public key,
/etc/macos-auth/host_ed25519.pub, to the Mac host allowlist - copy the macOS agent public key,
agent.pub, to Linux as/etc/macos-auth/agents.d/agent.pub
On macOS, start from the installed sample if you prefer:
cp /opt/homebrew/share/macos-auth/examples/agent-config.json.example \
"$HOME/Library/Application Support/macos-auth/agent-config.json"
Example macOS agent config:
{
"socket_path": "/Users/alice/Library/Application Support/macos-auth/agent.sock",
"hosts": [
{
"host_id": "linux-host-id",
"public_key_file": "/Users/alice/Library/Application Support/macos-auth/hosts/linux-host-id.pub"
}
],
"agent_keychain_service": "com.macos-auth.agent",
"agent_keychain_account": "default",
"agent_key_id": "agent-key-1",
"allowed_future_skew_ms": 30000,
"require_confirmation": false,
"rate_limit_window_seconds": 60,
"rate_limit_max_requests": 5
}
Save it as $HOME/Library/Application Support/macos-auth/agent-config.json and make sure the hosts entry points at the copied Linux host public key. The sample uses require_confirmation=false so the normal flow has a single LocalAuthentication prompt; set it to true only if you want an additional pre-confirmation dialog before Touch ID / Apple Watch.
Run this on Linux. Start from the installed sample if you prefer:
sudo cp /usr/share/macos-auth/examples/config.toml.sample /etc/macos-auth/config.toml
Create /etc/macos-auth/config.toml with:
socket_path = "/run/user/1000/macos-auth-agent.sock"
host_key_file = "/etc/macos-auth/host_ed25519.key"
agent_pubkey_file = "/etc/macos-auth/agents.d/agent.pub"
key_id = "host-key-1"
host_id = "linux-host-id"
hostname = "linux.example.com"
service = "sudo"
timeout_ms = 15000
allowed_future_skew_ms = 30000
Important permissions:
sudo chown -R root:root /etc/macos-auth
sudo chmod 0755 /etc/macos-auth /etc/macos-auth/agents.d
sudo chmod 0600 /etc/macos-auth/host_ed25519.key
sudo chmod 0644 /etc/macos-auth/config.toml /etc/macos-auth/agents.d/agent.pub
This step is performed on macOS.
If you prefer, copy the installed sample to a temporary working file, edit HostName, User, the Linux-side UID, and the macOS-side socket path, then append it to ~/.ssh/config:
cp /opt/homebrew/share/macos-auth/examples/ssh-config.sample /tmp/macos-auth-ssh-config
$EDITOR /tmp/macos-auth-ssh-config
mkdir -p "$HOME/.ssh"
cat /tmp/macos-auth-ssh-config >> "$HOME/.ssh/config"
chmod 0600 "$HOME/.ssh/config"
Example SSH config on macOS:
Host linux-with-macos-auth
HostName linux.example.com
User alice
RemoteForward /run/user/1000/macos-auth-agent.sock /Users/alice/Library/Application Support/macos-auth/agent.sock
StreamLocalBindUnlink yes
ExitOnForwardFailure yes
Adjust /run/user/1000 to the Linux user's UID and the macOS socket path to your agent config.
If using the LaunchAgent helper, run this on macOS:
/opt/homebrew/share/macos-auth/scripts/install-launchagent.sh \
--agent-bin /opt/homebrew/bin/macos-auth-agent \
--config "$HOME/Library/Application Support/macos-auth/agent-config.json"
Check status:
/opt/homebrew/share/macos-auth/scripts/status-launchagent.sh
Run SSH from macOS using the host entry that contains RemoteForward:
ssh linux-with-macos-auth
Then, inside the resulting Linux session, confirm the forwarded socket exists:
ls -l /run/user/$(id -u)/macos-auth-agent.sock
Before using PAM, run this on Linux:
/usr/bin/macos-auth-helper request \
--config /etc/macos-auth/config.toml \
--user "$USER" \
--ruser "$USER" \
--tty "$(tty | sed 's|^/dev/||')"
Expected exits:
| Exit | Meaning | Intended PAM behavior |
|---|---|---|
0 |
approved | success |
10 |
agent unavailable | password fallback |
11 |
user cancelled | password fallback |
12 |
authentication failed | password fallback |
30 |
signature/binding/freshness failure | hard fail |
31 |
unsafe config | hard fail |
32 |
protocol error | hard fail |
Do not edit /etc/pam.d/sudo first. Run this on Linux.
Create a test service and use pamtester; see docs/pam-testing.md for distro-specific details.
Recommended control syntax:
auth [success=done authinfo_unavail=ignore default=die] pam_macos_auth.so conf=/etc/macos-auth/config.toml helper=/usr/bin/macos-auth-helper timeout_ms=25000 debug
Only after the test service works, edit /etc/pam.d/sudo on Linux and add the pam_macos_auth.so line near the top, keeping normal password authentication below it.
Always keep a separate root shell open while editing PAM.
This means the Linux helper could not find the SSH-forwarded Unix socket on the Linux side.
The macOS agent may still be running correctly. The most common cause is that the SSH RemoteForward connection is gone, so this Linux-side socket no longer exists:
/run/user/1000/macos-auth-agent.sock
Common reasons:
- the SSH session that created
RemoteForwardended - the Mac went to sleep
- the network changed or disconnected
- the background
ssh -fN ...process exited - the Linux user runtime directory under
/run/user/<uid>was cleaned up - the Linux SSH server refused remote forwarding
Check on macOS that the agent is running:
/opt/homebrew/share/macos-auth/scripts/status-launchagent.sh
ls -l "$HOME/Library/Application Support/macos-auth/agent.sock"
Check on Linux whether the forwarded socket exists:
ls -l /run/user/$(id -u)/macos-auth-agent.sock
To recreate the forwarding connection from macOS, run either an interactive SSH session:
ssh linux-with-macos-auth
or a background tunnel:
ssh -fN linux-with-macos-auth
Then re-check on Linux:
ls -l /run/user/$(id -u)/macos-auth-agent.sock
If forwarding is refused, check the Linux SSH server configuration. It must allow remote forwarding and stream-local forwarding for the user. For example:
Match User alice
AllowTcpForwarding remote
AllowStreamLocalForwarding yes
StreamLocalBindUnlink yes
Reload sshd after changing server configuration.
This is expected. sudo -n means non-interactive mode, so sudo may fail immediately instead of entering the PAM approval flow. Test with a normal TTY-backed command instead:
sudo -k
sudo true
| Component | Path | Purpose |
|---|---|---|
| Protocol crate | crates/protocol |
Signed request/response types, canonical bytes, signature verification |
| Linux helper | crates/helper |
Generates signed PAM requests, talks to agent socket, verifies responses |
| PAM shim | pam/pam_macos_auth.c |
Extracts PAM context, invokes helper, maps helper exit codes to PAM results |
| Debian packaging | packaging/linux/build-deb.sh, packaging/linux/deb/ |
Native .deb package build |
| RPM packaging | packaging/linux/build-rpm.sh, packaging/linux/rpm/ |
Native .rpm package build |
| Linux setup helpers | scripts/linux-*.sh |
Development config/install helpers for VM testing |
Debian/Ubuntu builders:
sudo apt-get update
sudo apt-get install -y build-essential cargo dpkg-dev libpam0g-dev make rustc
Fedora/RHEL-family builders:
sudo dnf install -y cargo gcc make pam-devel rpm-build rust
Using a current Rust toolchain through rustup is also acceptable on local builders.
Run before packaging or committing:
make check
This runs:
- Rust formatting check
- Rust tests
- PAM C syntax check
- shell script syntax checks
Equivalent:
scripts/check.sh
Run on a native Debian/Ubuntu builder:
make package-deb
Equivalent:
packaging/linux/build-deb.sh
The artifact is written under target/package/deb/.
Run on a native Fedora/RHEL-family builder:
make package-rpm
Equivalent:
packaging/linux/build-rpm.sh
The artifact is written under target/package/rpm/.
If Rust is installed through rustup rather than distro RPM packages, rpmbuild dependency checks may not see it. In that case, install the distro rust / cargo packages or run with explicit local-builder options, for example:
RPMBUILD_OPTS=--nodeps packaging/linux/build-rpm.sh
Linux packages install:
/usr/bin/macos-auth-helperpam_macos_auth.soin the distro PAM module directory- package documentation under
/usr/share/doc/macos-auth/ - PAM examples under
/usr/share/macos-auth/examples/ - sample Linux config:
/usr/share/macos-auth/examples/config.toml.sample - sample SSH config:
/usr/share/macos-auth/examples/ssh-config.sample
Packages must not automatically modify /etc/pam.d/sudo.
Do not edit /etc/pam.d/sudo first.
Use the test service and pamtester flow in:
docs/pam-testing.mdpam/examples/macos-auth-test
Recommended PAM control syntax:
auth [success=done authinfo_unavail=ignore default=die] pam_macos_auth.so conf=/etc/macos-auth/config.toml helper=/usr/bin/macos-auth-helper
This means:
- macOS approval succeeds immediately
- authenticator unavailable/cancel/failure falls back to password
- tampering/unsafe config/protocol errors hard fail
| Document | Purpose |
|---|---|
docs/release-packaging.md |
Release packaging plan for Linux and macOS artifacts |
docs/release-runbook.md |
Reproducible release targets and release checklist |
docs/build-farm.md |
Local/native build farm policy and builder roles |
docs/linux-vm-test-plan.md |
Manual Parallels VM test plan for Ubuntu/Debian and Fedora/RHEL |
docs/pam-testing.md |
PAM testing guide |
docs/helper.md |
Linux helper usage |
pam/README.md |
PAM shim usage |
packaging/linux/README.md |
Linux package build notes |
MIT. See LICENSE.