PAM-based authentication proxy for integrating UGOS NAS with authentik SSO.
This service enables authentik to authenticate UGOS NAS users without storing passwords and supports automatic user provisioning. Each login attempt is validated in real-time via PAM (reading /etc/shadow).
Key Features:
- π No password caching β every login is validated in real-time
- β‘ Instant sync β password changes in UGOS apply immediately to SSO
- π€ Auto-provisioning β new users are automatically created in authentik on first login
- π‘οΈ Simple & reliable β PAM-based authentication
βββββββββββββββββββ ββββββββββββββββββββββββ βββββββββββββββββββ
β Browser ββββββΆβ authentik ββββββΆβ ugos-auth-proxy β
β β β (Expression Policy) β β (PAM + passwd) β
βββββββββββββββββββ ββββββββββββββββββββββββ βββββββββββββββββββ
β
βΌ
ββββββββββββββββββ
β /etc/shadow β
β /etc/passwd β
β /etc/group β
β (UGOS NAS) β
ββββββββββββββββββ
Flow:
- User enters credentials in authentik
- authentik's Expression Policy calls ugos-auth-proxy
/validate - If user doesn't exist in authentik, policy calls
/user-infoto get user details - Policy creates user in authentik with UGOS attributes
- User is logged in
# Clone the repository
git clone https://github.com/smgladkovskiy/ugos-auth-proxy.git
cd ugos-auth-proxy
# Start the container
docker compose up -d
# Verify it's running
curl http://localhost:8180/health# Copy files
sudo mkdir -p /opt/ugos-auth-proxy
sudo cp main.py /opt/ugos-auth-proxy/
sudo cp ugos-auth-proxy.service /etc/systemd/system/
# Enable and start
sudo systemctl daemon-reload
sudo systemctl enable --now ugos-auth-proxy
# Check status
sudo systemctl status ugos-auth-proxyValidates user credentials via PAM.
Request:
{
"username": "ugadmin",
"password": "secret"
}Success Response (200):
{
"valid": true,
"username": "ugadmin"
}Failure Response (200):
{
"valid": false,
"error": "Invalid credentials"
}Retrieves user information from /etc/passwd and /etc/group. Used for auto-provisioning users in authentik.
Query Parameters:
username(required) β UGOS username
Success Response (200):
{
"exists": true,
"username": "ugadmin",
"uid": 1026,
"gid": 100,
"full_name": "Admin User",
"email": "admin@example.com",
"home": "/var/services/homes/ugadmin",
"shell": "/bin/sh",
"groups": ["administrators", "users", "http"]
}User Not Found Response (200):
{
"exists": false,
"error": "User not found"
}Health check endpoint for monitoring and load balancers.
{
"status": "healthy",
"service": "ugos-auth-proxy",
"version": "0.2.0"
}Readiness probe for Kubernetes.
{
"status": "ready"
}Create the following groups in authentik (Directory -> Groups):
| Group Name | Description |
|---|---|
UGOS Users |
All auto-provisioned UGOS users |
admins |
Administrators (mapped from UGOS) |
family |
Family members (mapped from UGOS) |
Create a flow named ugos-authentication with the following stages:
| Order | Stage | Type | Configuration |
|---|---|---|---|
| 10 | ugos-find-user-stage | IdentificationStage | pretend_user_exists=True |
| 20 | ugos-password-prompt-stage | PromptStage | Password field only |
| 25 | ugos-password-deny-stage | DenyStage | Policy binding with negate=True |
| 100 | ugos-user-login-stage | UserLoginStage | Default settings |
Important: Set
pretend_user_exists=Trueon IdentificationStage to enable auto-provisioning for new users.
Create a policy named ugos-password-validation-policy and bind it to ugos-password-deny-stage with negate=True.
This policy handles both password validation and auto-provisioning.
π Copy the policy code from: authentik-policy.py
Configuration: Edit the variables at the top of the policy:
UGOS_AUTH_PROXY_URLβ your ugos-auth-proxy addressDEFAULT_EMAIL_DOMAINβ default email domain for usersGROUP_MAPβ UGOS to authentik group mapping
The policy automatically maps UGOS groups to authentik groups:
| UGOS Group | authentik Group | Description |
|---|---|---|
admin |
admins |
NAS administrators |
family |
family |
Family members |
All auto-provisioned users are added to UGOS Users group.
Auto-provisioned users will have these attributes:
{
"ugos_user": true,
"ugos_uid": 1005,
"ugos_gid": 10,
"ugos_groups": ["admin", "users", "ughomeusers"],
"ugos_home": "/home/ugadmin",
"auto_provisioned": true
}In Brand settings (System -> Brands), set ugos-authentication as the default authentication flow.
For emergency access with local admin (e.g., akadmin), use:
https://your-authentik-url/if/flow/default-authentication-flow/
| Variable | Default | Description |
|---|---|---|
PORT |
8080 |
Service port |
# Successful authentication
curl -X POST http://localhost:8180/validate \
-H "Content-Type: application/json" \
-d '{"username":"ugadmin","password":"correct_password"}'
# Failed authentication
curl -X POST http://localhost:8180/validate \
-H "Content-Type: application/json" \
-d '{"username":"ugadmin","password":"wrong_password"}'
# Get user info
curl "http://localhost:8180/user-info?username=ugadmin"
# Health check
curl http://localhost:8180/health- The container requires access to
/etc/shadow,/etc/passwd,/etc/groupβ mounted as read-only - Do not expose the port externally β use internal Docker network only
- Consider using HTTPS between authentik and proxy (via reverse proxy)
- The
/user-infoendpoint only exposes public user data (no passwords)
UGOS uses a complex authentication scheme:
- Dynamic RSA keys β new key pair generated for each session
- Password encryption β password is encrypted with RSA public key
- Multi-step process β multiple API calls for a single login
- Circular dependency β public key is only available after login
PAM-based authentication bypasses these complexities and works directly with /etc/shadow, which stays in sync with
UGOS passwords.
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
Sergey Gladkovskiy - @smgladkovskiy
If you find this project useful, please consider giving it a β!