A lightweight, self-hosted authentication gateway that puts password + TOTP protection in front of any internal HTTP service — Adminer, Grafana, Prometheus, or anything else you run behind Docker.
AdminPass listens on one port per service you define in adminpass.yaml. Every request hitting those ports is intercepted: if the user has a valid session cookie they are proxied through transparently; if not, they see a login form. A separate admin UI on port 8777 lets you manage user accounts and edit the service list.
Browser → :8080 (AdminPass) → adminer:8080
Browser → :8010 (AdminPass) → grafana:3000
Browser → :8777 (AdminPass) → admin panel
Sessions are in-memory, expire after 8 hours, and are reaped automatically every 15 minutes. All accounts require TOTP (RFC 6238) — there is no way to create an account without 2FA.
The single config file is adminpass.yaml, placed in the working directory of the binary (or the Docker container root).
totp:
issuer: "AdminPass" # shown in the authenticator app
period: 30 # seconds per code (default 30)
digits: 6 # code length (default 6)
services:
"8080": "adminer:8080" # listen-port: upstream-host:upstream-port
"8010": "grafana:3000"The totp block is optional — all three fields have sensible defaults. The services map is the full list of proxied ports. Changes made through the admin UI are written back to this file immediately; new listeners only take effect after a process restart.
The database connection is configured entirely through environment variables. All have defaults suitable for a Docker Compose stack.
| Variable | Default | Description |
|---|---|---|
DB_HOST |
db |
MySQL hostname |
DB_PORT |
3306 |
MySQL port |
DB_NAME |
wizzl |
Database name |
DB_USER |
wizzl |
Database user |
DB_PASS |
secret |
Database password |
A minimal docker-compose.yml that wires AdminPass to a MySQL container:
services:
adminpass:
build: .
container-name: adminpass
ports:
- "8080:8080"
- "8010:8010"
- "8777:8777"
environment:
DB_HOST: db
DB_USER: wizzl
DB_PASS: changeme
DB_NAME: wizzl
volumes:
- ./adminpass.yaml:/adminpass.yaml
depends_on:
- db
db:
image: mysql:latest
environment:
MYSQL_DATABASE: wizzl
MYSQL_USER: wizzl
MYSQL_PASSWORD: changeme
MYSQL_ROOT_PASSWORD: root
volumes:
- db_data:/var/lib/mysql
volumes:
db_data:Expose only the ports you actually need. The admin panel on 8777 should never be exposed to the public internet.
- Start the stack. On first boot AdminPass runs the database migration automatically.
- Open
http://your-host:8777in a browser. You will be redirected to the First Setup screen. - Enter an email address and a password of at least 12 characters, then click Create account & generate 2FA.
- Scan the QR code with an authenticator app (Google Authenticator, Authy, 1Password, etc.). This QR code is shown exactly once — save it or store the manual key before leaving the page.
- Click Continue to login and sign in with your email, password, and the 6-digit code from your authenticator app.
The admin panel is always available at :8777 and is independent of the proxied services.
Displays every account with its email, creation date, and role. The founding admin (the first account ever created, identified by the lowest database ID) has two extra privileges:
- Can create additional admin accounts via the Create user form.
- Can delete any account other than their own.
Deleting a user immediately revokes all of their active sessions.
Lists every entry currently in adminpass.yaml. From here you can:
- Remove an existing service — takes effect immediately in the config file. The listener for that port will stop accepting new connections after a process restart.
- Add a new service by providing a listen port, an upstream host, and an upstream port. The entry is written to
adminpass.yamlat once; start a new listener by restarting the container.
Each proxied port runs its own login page at /_adminpass/login. After a successful login the session token, a display hint (base64-encoded email), and the originating port are stored in three HttpOnly cookies scoped to /. Logging out at /_adminpass/logout revokes the token server-side and clears the cookies.
The admin panel at :8777 uses its own session that is independent of the per-service sessions — you may be logged into the admin UI and a proxied service at the same time.
- HTTPS: The cookies are currently set with
Secure: false. If you terminate TLS upstream (e.g. with Caddy or Nginx), setSecure: trueinauth.goand redeploy. - Admin port: Port
8777should be firewalled or bound only to a private network interface. Exposing it publicly gives anyone a login page for your admin panel. - Database password: Change
DB_PASSfrom the defaultsecretbefore deploying anywhere. - Session storage: Sessions are held in memory. A process restart clears all active sessions and everyone will need to log in again.
Requires Go 1.25.5 or later.
git clone git https://github.com/wizzldev/adminpass.git
cd adminpass
go build -o adminpass ./...
make config
./adminpass # expects adminpass.yaml in the current directoryFor a Docker image, use the provided Dockerfile:
docker build -t adminpass .