- Introduced `web/templates/` for HTMX-fragmented pages (`dashboard`, `accounts`, `account_detail`, `error_fragment`, etc.). - Implemented UI routes for account CRUD, audit log display, and login/logout with CSRF protection. - Added `internal/ui/` package for handlers, CSRF manager, session validation, and token issuance. - Updated documentation to include new UI features and templates directory structure. - Security: Double-submit CSRF cookies, constant-time HMAC validation, login password/Argon2id re-verification at all steps to prevent bypass.
292 lines
6.5 KiB
Markdown
292 lines
6.5 KiB
Markdown
# MCIAS — Metacircular Identity and Access System
|
||
|
||
MCIAS is a self-hosted SSO and IAM service for personal projects.
|
||
It provides authentication (JWT/Ed25519), account management, TOTP, and
|
||
Postgres credential storage over a REST API (HTTPS) and a gRPC API (TLS).
|
||
|
||
See [ARCHITECTURE.md](ARCHITECTURE.md) for the technical design and
|
||
[PROJECT_PLAN.md](PROJECT_PLAN.md) for the implementation roadmap.
|
||
|
||
---
|
||
|
||
## Quick Start
|
||
|
||
### Option A: Install from source
|
||
|
||
**Prerequisites:** Go 1.26+, a C compiler (required by modernc.org/sqlite).
|
||
|
||
```sh
|
||
git clone https://git.wntrmute.dev/kyle/mcias
|
||
cd mcias
|
||
make build # produces bin/mciassrv, other binaries
|
||
sudo make install
|
||
```
|
||
|
||
### Option B: Docker
|
||
|
||
```sh
|
||
docker build -t mcias:latest .
|
||
# or
|
||
make docker # tags mcias:<git-describe>
|
||
```
|
||
|
||
See [Deploying with Docker](#deploying-with-docker) below.
|
||
|
||
---
|
||
|
||
## First-Run Setup
|
||
|
||
### 1. Generate a TLS certificate
|
||
|
||
**Option A: Using the cert tool**
|
||
|
||
Install the cert tool:
|
||
```sh
|
||
go install github.com/kisom/cert@latest
|
||
```
|
||
|
||
Create a certificate request configuration file:
|
||
```sh
|
||
cat > /tmp/request.yaml << EOF
|
||
subject:
|
||
common_name: auth.example.com
|
||
hosts:
|
||
- auth.example.com
|
||
- localhost
|
||
key:
|
||
algo: ecdsa
|
||
size: 521
|
||
ca:
|
||
expiry: 87600h # 10 years
|
||
EOF
|
||
```
|
||
|
||
Generate the certificate:
|
||
```sh
|
||
cert genkey -a ec -s 521 > /etc/mcias/server.key
|
||
cert selfsign -p /etc/mcias/server.key -f /tmp/request.yaml > /etc/mcias/server.crt
|
||
chmod 0640 /etc/mcias/server.key
|
||
chown root:mcias /etc/mcias/server.key
|
||
rm /tmp/request.yaml
|
||
```
|
||
|
||
**Option B: Using openssl**
|
||
|
||
```sh
|
||
openssl req -x509 -newkey ed25519 -days 3650 \
|
||
-keyout /etc/mcias/server.key \
|
||
-out /etc/mcias/server.crt \
|
||
-subj "/CN=auth.example.com" \
|
||
-nodes
|
||
chmod 0640 /etc/mcias/server.key
|
||
chown root:mcias /etc/mcias/server.key
|
||
```
|
||
|
||
### 2. Configure the server
|
||
|
||
```sh
|
||
cp dist/mcias.conf.example /etc/mcias/mcias.conf
|
||
$EDITOR /etc/mcias/mcias.conf
|
||
chmod 0640 /etc/mcias/mcias.conf
|
||
chown root:mcias /etc/mcias/mcias.conf
|
||
```
|
||
|
||
Minimum required fields:
|
||
|
||
```toml
|
||
[server]
|
||
listen_addr = "0.0.0.0:8443"
|
||
tls_cert = "/etc/mcias/server.crt"
|
||
tls_key = "/etc/mcias/server.key"
|
||
|
||
[database]
|
||
path = "/var/lib/mcias/mcias.db"
|
||
|
||
[tokens]
|
||
issuer = "https://auth.example.com"
|
||
|
||
[master_key]
|
||
passphrase_env = "MCIAS_MASTER_PASSPHRASE"
|
||
```
|
||
|
||
See `dist/mcias.conf.example` for the fully-commented reference configuration.
|
||
For local development, use `dist/mcias-dev.conf.example`.
|
||
|
||
### 3. Set the master key passphrase
|
||
|
||
```sh
|
||
cp dist/mcias.env.example /etc/mcias/env
|
||
$EDITOR /etc/mcias/env # replace the placeholder passphrase
|
||
chmod 0640 /etc/mcias/env
|
||
chown root:mcias /etc/mcias/env
|
||
```
|
||
|
||
> **Important:** Back up the passphrase to a secure offline location.
|
||
> Losing it means losing access to all encrypted data in the database.
|
||
|
||
### 4. Create the first admin account
|
||
|
||
```sh
|
||
export MCIAS_MASTER_PASSPHRASE=your-passphrase
|
||
|
||
mciasdb --config /etc/mcias/mcias.conf account create \
|
||
--username admin --type human
|
||
mciasdb --config /etc/mcias/mcias.conf account set-password --id <UUID>
|
||
mciasdb --config /etc/mcias/mcias.conf role grant --id <UUID> --role admin
|
||
```
|
||
|
||
### 5. Start the server
|
||
|
||
```sh
|
||
# systemd
|
||
systemctl enable --now mcias
|
||
|
||
# manual
|
||
MCIAS_MASTER_PASSPHRASE=your-passphrase mciassrv -config /etc/mcias/mcias.conf
|
||
```
|
||
|
||
### 6. Verify
|
||
|
||
```sh
|
||
curl -k https://localhost:8443/v1/health
|
||
# {"status":"ok"}
|
||
```
|
||
|
||
---
|
||
|
||
## Building
|
||
|
||
```sh
|
||
make build # compile all binaries to bin/
|
||
make test # go test -race ./...
|
||
make lint # golangci-lint run ./...
|
||
make generate # regenerate protobuf stubs (requires protoc)
|
||
make man # build compressed man pages
|
||
make clean # remove bin/ and generated artifacts
|
||
make dist # cross-compiled tarballs for linux/amd64 and linux/arm64
|
||
make docker # build Docker image mcias:<version>
|
||
```
|
||
|
||
---
|
||
|
||
## Admin CLI (mciasctl)
|
||
|
||
```sh
|
||
TOKEN=$(curl -sk https://localhost:8443/v1/auth/login \
|
||
-d '{"username":"admin","password":"..."}' | jq -r .token)
|
||
export MCIAS_TOKEN=$TOKEN
|
||
|
||
mciasctl -server https://localhost:8443 account list
|
||
mciasctl account create -username alice -password s3cr3t
|
||
mciasctl role set -id $UUID -roles admin
|
||
mciasctl token issue -id $SYSTEM_UUID
|
||
mciasctl pgcreds set -id $UUID -host db.example.com -port 5432 \
|
||
-db mydb -user myuser -password mypassword
|
||
```
|
||
|
||
See `man mciasctl` for the full reference.
|
||
|
||
---
|
||
|
||
## Database Maintenance Tool (mciasdb)
|
||
|
||
```sh
|
||
export MCIAS_MASTER_PASSPHRASE=your-passphrase
|
||
CONF="--config /etc/mcias/mcias.conf"
|
||
|
||
mciasdb $CONF schema verify
|
||
mciasdb $CONF account list
|
||
mciasdb $CONF account set-password --id $UUID
|
||
mciasdb $CONF role grant --id $UUID --role admin
|
||
mciasdb $CONF prune tokens
|
||
mciasdb $CONF audit tail --n 50
|
||
mciasdb $CONF audit query --account $UUID --json
|
||
```
|
||
|
||
See `man mciasdb` for the full reference, including the trust model and
|
||
safety warnings.
|
||
|
||
---
|
||
|
||
## gRPC Interface
|
||
|
||
Enable the gRPC listener in config:
|
||
|
||
```toml
|
||
[server]
|
||
listen_addr = "0.0.0.0:8443"
|
||
grpc_addr = "0.0.0.0:9443"
|
||
tls_cert = "/etc/mcias/server.crt"
|
||
tls_key = "/etc/mcias/server.key"
|
||
```
|
||
|
||
Using mciasgrpcctl:
|
||
|
||
```sh
|
||
export MCIAS_TOKEN=$ADMIN_JWT
|
||
mciasgrpcctl -server auth.example.com:9443 -cacert /etc/mcias/server.crt health
|
||
mciasgrpcctl account list
|
||
```
|
||
|
||
Using grpcurl:
|
||
|
||
```sh
|
||
grpcurl -cacert /etc/mcias/server.crt \
|
||
-H "authorization: Bearer $ADMIN_JWT" \
|
||
auth.example.com:9443 \
|
||
mcias.v1.AdminService/Health
|
||
```
|
||
|
||
See `man mciasgrpcctl` and [ARCHITECTURE.md](ARCHITECTURE.md) §17.
|
||
|
||
---
|
||
|
||
## Deploying with Docker
|
||
|
||
```sh
|
||
make docker
|
||
|
||
mkdir -p /srv/mcias/config
|
||
cp dist/mcias.conf.docker.example /srv/mcias/config/mcias.conf
|
||
$EDITOR /srv/mcias/config/mcias.conf
|
||
|
||
docker run -d \
|
||
--name mcias \
|
||
-v /srv/mcias/config:/etc/mcias:ro \
|
||
-v mcias-data:/data \
|
||
-e MCIAS_MASTER_PASSPHRASE=your-passphrase \
|
||
-p 8443:8443 \
|
||
-p 9443:9443 \
|
||
mcias:latest
|
||
|
||
curl -k https://localhost:8443/v1/health
|
||
```
|
||
|
||
The container runs as uid 10001 (mcias) with no capabilities.
|
||
TLS termination happens inside the container.
|
||
See `dist/mcias.conf.docker.example` for the config template.
|
||
|
||
---
|
||
|
||
## Man Pages
|
||
|
||
```sh
|
||
man mciassrv # server reference
|
||
man mciasctl # admin CLI (REST)
|
||
man mciasdb # database maintenance tool
|
||
man mciasgrpcctl # gRPC admin CLI
|
||
```
|
||
|
||
---
|
||
|
||
## Security Notes
|
||
|
||
- JWTs signed with Ed25519 (EdDSA); `alg` header validated before processing
|
||
to defeat algorithm confusion attacks.
|
||
- Passwords hashed with Argon2id at OWASP 2023 minimum parameters.
|
||
- All secret material encrypted at rest with AES-256-GCM using the master key.
|
||
- Credential fields never appear in any API response.
|
||
- TLS 1.2 minimum protocol version.
|
||
|
||
See [ARCHITECTURE.md](ARCHITECTURE.md) §2–3 for the full security model.
|