Implement Phase 8: operational artifacts
- Makefile: build/test/lint/generate/man/install/clean/dist/docker; CGO_ENABLED=1 throughout; VERSION from git describe --tags --always - Dockerfile: multi-stage (golang:1.26-bookworm builder -> debian:bookworm-slim runtime); non-root uid 10001 (mcias), VOLUME /data, EXPOSE 8443/9443; no toolchain in final image - dist/mcias.service: hardened systemd unit (ProtectSystem=strict, ProtectHome, PrivateTmp, NoNewPrivileges, MemoryDenyWriteExecute, CapabilityBoundingSet= empty, EnvironmentFile, LimitNOFILE=65536) - dist/mcias.env.example: passphrase env file template - dist/mcias.conf.example: fully-commented production TOML config - dist/mcias-dev.conf.example: local dev config (/tmp, short expiry) - dist/mcias.conf.docker.example: container config template - dist/install.sh: POSIX sh idempotent installer; creates mcias user/group, installs binaries, /etc/mcias, /var/lib/mcias, systemd unit, man pages; prints post-install instructions - man/man1/mciassrv.1: mdoc synopsis/config/API/signals/files - man/man1/mciasctl.1: mdoc all subcommands/env/examples - man/man1/mciasdb.1: mdoc trust model/safety/all subcommands - man/man1/mciasgrpcctl.1: mdoc gRPC commands/grpcurl example - README.md: user-facing quick-start, first-run setup, build instructions, CLI references, Docker deployment, security notes - .gitignore: added /bin/, dist/mcias_*.tar.gz, man/man1/*.gz
This commit is contained in:
14
.gitignore
vendored
14
.gitignore
vendored
@@ -1,32 +1,24 @@
|
||||
# Build output (root-level binaries only)
|
||||
/mciassrv
|
||||
/mciasctl
|
||||
/mciasdb
|
||||
/mciasgrpcctl
|
||||
/bin/
|
||||
*.exe
|
||||
|
||||
# Database files
|
||||
*.db
|
||||
*.db-wal
|
||||
*.db-shm
|
||||
|
||||
# Test artifacts
|
||||
*.out
|
||||
*.test
|
||||
coverage.html
|
||||
coverage.txt
|
||||
|
||||
# Config files with secrets (keep example configs)
|
||||
mcias.toml
|
||||
|
||||
# Editor artifacts
|
||||
.DS_Store
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# Go workspace files
|
||||
go.work
|
||||
go.work.sum
|
||||
dist/mcias_*.tar.gz
|
||||
man/man1/*.gz
|
||||
|
||||
99
Dockerfile
Normal file
99
Dockerfile
Normal file
@@ -0,0 +1,99 @@
|
||||
# Dockerfile — MCIAS multi-stage container image
|
||||
#
|
||||
# Stage 1 (builder): Compiles all four MCIAS binaries.
|
||||
# Stage 2 (runtime): Minimal Debian image containing only the binaries.
|
||||
#
|
||||
# The final image:
|
||||
# - Runs as non-root uid 10001 (mcias)
|
||||
# - Exposes port 8443 (REST/TLS) and 9443 (gRPC/TLS)
|
||||
# - Declares VOLUME /data for the SQLite database
|
||||
# - Does NOT contain the Go toolchain, source code, or build cache
|
||||
#
|
||||
# Build:
|
||||
# docker build -t mcias:$(git describe --tags --always) .
|
||||
#
|
||||
# Run:
|
||||
# docker run -d \
|
||||
# --name mcias \
|
||||
# -v /path/to/config:/etc/mcias:ro \
|
||||
# -v mcias-data:/data \
|
||||
# -e MCIAS_MASTER_PASSPHRASE=your-passphrase \
|
||||
# -p 8443:8443 \
|
||||
# -p 9443:9443 \
|
||||
# mcias:latest
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Stage 1 — builder
|
||||
# ---------------------------------------------------------------------------
|
||||
FROM golang:1.26-bookworm AS builder
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
# Download dependencies first for layer caching.
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
# Copy source.
|
||||
COPY . .
|
||||
|
||||
# CGO_ENABLED=1 is required by modernc.org/sqlite (pure-Go CGo-free SQLite).
|
||||
# -trimpath removes local file system paths from the binary.
|
||||
# -ldflags="-s -w" strips the DWARF debug info and symbol table to reduce
|
||||
# image size.
|
||||
RUN CGO_ENABLED=1 go build -trimpath -ldflags="-s -w" -o /out/mciassrv ./cmd/mciassrv && \
|
||||
CGO_ENABLED=1 go build -trimpath -ldflags="-s -w" -o /out/mciasctl ./cmd/mciasctl && \
|
||||
CGO_ENABLED=1 go build -trimpath -ldflags="-s -w" -o /out/mciasdb ./cmd/mciasdb && \
|
||||
CGO_ENABLED=1 go build -trimpath -ldflags="-s -w" -o /out/mciasgrpcctl ./cmd/mciasgrpcctl
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Stage 2 — runtime
|
||||
# ---------------------------------------------------------------------------
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
# Install runtime dependencies.
|
||||
# ca-certificates: required to validate external TLS certificates.
|
||||
# libc6: required by CGo-compiled binaries (sqlite).
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
ca-certificates \
|
||||
libc6 && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create a non-root user for the service.
|
||||
# uid/gid 10001 is chosen to be well above the range typically assigned to
|
||||
# system users (1–999) and human users (1000+), reducing the chance of
|
||||
# collision with existing uids on the host when using host networking.
|
||||
RUN groupadd --gid 10001 mcias && \
|
||||
useradd --uid 10001 --gid 10001 --no-create-home --shell /usr/sbin/nologin mcias
|
||||
|
||||
# Copy compiled binaries from the builder stage.
|
||||
COPY --from=builder /out/mciassrv /usr/local/bin/mciassrv
|
||||
COPY --from=builder /out/mciasctl /usr/local/bin/mciasctl
|
||||
COPY --from=builder /out/mciasdb /usr/local/bin/mciasdb
|
||||
COPY --from=builder /out/mciasgrpcctl /usr/local/bin/mciasgrpcctl
|
||||
|
||||
# Create the config and data directories.
|
||||
# /etc/mcias is mounted read-only by the operator with the config file,
|
||||
# TLS cert, and TLS key.
|
||||
# /data is the SQLite database mount point.
|
||||
RUN mkdir -p /etc/mcias /data && \
|
||||
chown mcias:mcias /data && \
|
||||
chmod 0750 /data
|
||||
|
||||
# Declare /data as a volume so the operator must explicitly mount it.
|
||||
# The SQLite database must persist across container restarts.
|
||||
VOLUME /data
|
||||
|
||||
# REST/TLS port and gRPC/TLS port. These are documentation only; the actual
|
||||
# ports are set in the config file. Override by mounting a different config.
|
||||
EXPOSE 8443
|
||||
EXPOSE 9443
|
||||
|
||||
# Run as the non-root mcias user.
|
||||
USER mcias
|
||||
|
||||
# Default entry point and config path.
|
||||
# The operator mounts /etc/mcias/mcias.conf from the host or a volume.
|
||||
# See dist/mcias.conf.docker.example for a suitable template.
|
||||
ENTRYPOINT ["mciassrv"]
|
||||
CMD ["-config", "/etc/mcias/mcias.conf"]
|
||||
151
Makefile
Normal file
151
Makefile
Normal file
@@ -0,0 +1,151 @@
|
||||
# Makefile — MCIAS build, test, lint, and release targets
|
||||
#
|
||||
# Usage:
|
||||
# make build — compile all binaries to bin/
|
||||
# make test — run tests with race detector
|
||||
# make lint — run golangci-lint
|
||||
# make generate — regenerate protobuf stubs (requires protoc)
|
||||
# make man — build compressed man pages
|
||||
# make install — run dist/install.sh (requires root)
|
||||
# make clean — remove bin/ and generated artifacts
|
||||
# make dist — build release tarballs for linux/amd64 and linux/arm64
|
||||
# make docker — build Docker image tagged mcias:$(VERSION)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Variables
|
||||
# ---------------------------------------------------------------------------
|
||||
MODULE := git.wntrmute.dev/kyle/mcias
|
||||
BINARIES := mciassrv mciasctl mciasdb mciasgrpcctl
|
||||
BIN_DIR := bin
|
||||
MAN_DIR := man/man1
|
||||
MAN_PAGES := $(MAN_DIR)/mciassrv.1 $(MAN_DIR)/mciasctl.1 \
|
||||
$(MAN_DIR)/mciasdb.1 $(MAN_DIR)/mciasgrpcctl.1
|
||||
|
||||
# Version: prefer git describe; fall back to "dev" when git is unavailable
|
||||
# or the tree has no tags.
|
||||
VERSION := $(shell git describe --tags --always 2>/dev/null || echo dev)
|
||||
|
||||
# Build flags: trim paths from binaries and strip DWARF/symbol table.
|
||||
# CGO_ENABLED=1 is required for modernc.org/sqlite.
|
||||
GO := go
|
||||
GOFLAGS := -trimpath
|
||||
LDFLAGS := -s -w -X main.version=$(VERSION)
|
||||
CGO := CGO_ENABLED=1
|
||||
|
||||
# Platforms for cross-compiled dist tarballs.
|
||||
DIST_PLATFORMS := linux/amd64 linux/arm64
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Default target
|
||||
# ---------------------------------------------------------------------------
|
||||
.PHONY: all
|
||||
all: build
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# build — compile all binaries to bin/
|
||||
# ---------------------------------------------------------------------------
|
||||
.PHONY: build
|
||||
build:
|
||||
@mkdir -p $(BIN_DIR)
|
||||
@for bin in $(BINARIES); do \
|
||||
echo " GO BUILD cmd/$$bin"; \
|
||||
$(CGO) $(GO) build $(GOFLAGS) -ldflags "$(LDFLAGS)" \
|
||||
-o $(BIN_DIR)/$$bin ./cmd/$$bin; \
|
||||
done
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# test — run all tests with race detector
|
||||
# ---------------------------------------------------------------------------
|
||||
.PHONY: test
|
||||
test:
|
||||
$(CGO) $(GO) test -race ./...
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# lint — run golangci-lint
|
||||
# ---------------------------------------------------------------------------
|
||||
.PHONY: lint
|
||||
lint:
|
||||
golangci-lint run ./...
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# generate — regenerate protobuf stubs from proto/ definitions
|
||||
# Requires: protoc, protoc-gen-go, protoc-gen-go-grpc
|
||||
# ---------------------------------------------------------------------------
|
||||
.PHONY: generate
|
||||
generate:
|
||||
$(GO) generate ./...
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# man — build compressed man pages
|
||||
# ---------------------------------------------------------------------------
|
||||
.PHONY: man
|
||||
man: $(patsubst %.1,%.1.gz,$(MAN_PAGES))
|
||||
|
||||
%.1.gz: %.1
|
||||
gzip -kf $<
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# install — run the installation script (requires root)
|
||||
# ---------------------------------------------------------------------------
|
||||
.PHONY: install
|
||||
install: build
|
||||
sh dist/install.sh
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# clean — remove build artifacts
|
||||
# ---------------------------------------------------------------------------
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf $(BIN_DIR)
|
||||
rm -f $(patsubst %.1,%.1.gz,$(MAN_PAGES))
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# dist — cross-compiled release tarballs for linux/amd64 and linux/arm64
|
||||
#
|
||||
# Output files: dist/mcias_<version>_<os>_<arch>.tar.gz
|
||||
# Each tarball contains: mciassrv, mciasctl, mciasdb, mciasgrpcctl,
|
||||
# man pages, and dist/ files.
|
||||
# ---------------------------------------------------------------------------
|
||||
.PHONY: dist
|
||||
dist: man
|
||||
@for platform in $(DIST_PLATFORMS); do \
|
||||
os=$$(echo $$platform | cut -d/ -f1); \
|
||||
arch=$$(echo $$platform | cut -d/ -f2); \
|
||||
outdir=dist/mcias_$$(echo $(VERSION) | tr -d 'v')_$${os}_$${arch}; \
|
||||
echo " DIST $$platform -> $$outdir.tar.gz"; \
|
||||
mkdir -p $$outdir/bin; \
|
||||
for bin in $(BINARIES); do \
|
||||
CGO_ENABLED=1 GOOS=$$os GOARCH=$$arch $(GO) build \
|
||||
$(GOFLAGS) -ldflags "$(LDFLAGS)" \
|
||||
-o $$outdir/bin/$$bin ./cmd/$$bin; \
|
||||
done; \
|
||||
cp -r man $$outdir/; \
|
||||
cp dist/mcias.conf.example dist/mcias-dev.conf.example \
|
||||
dist/mcias.env.example dist/mcias.service \
|
||||
dist/install.sh $$outdir/; \
|
||||
tar -czf $$outdir.tar.gz -C dist mcias_$$(echo $(VERSION) | tr -d 'v')_$${os}_$${arch}; \
|
||||
rm -rf $$outdir; \
|
||||
done
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# docker — build the Docker image
|
||||
# ---------------------------------------------------------------------------
|
||||
.PHONY: docker
|
||||
docker:
|
||||
docker build -t mcias:$(VERSION) .
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Help
|
||||
# ---------------------------------------------------------------------------
|
||||
.PHONY: help
|
||||
help:
|
||||
@echo "Available targets:"
|
||||
@echo " build Compile all binaries to bin/"
|
||||
@echo " test Run tests with race detector"
|
||||
@echo " lint Run golangci-lint"
|
||||
@echo " generate Regenerate protobuf stubs"
|
||||
@echo " man Build compressed man pages"
|
||||
@echo " install Install to /usr/local/bin (requires root)"
|
||||
@echo " clean Remove build artifacts"
|
||||
@echo " dist Build release tarballs for Linux amd64/arm64"
|
||||
@echo " docker Build Docker image mcias:$(VERSION)"
|
||||
58
PROGRESS.md
58
PROGRESS.md
@@ -1,16 +1,10 @@
|
||||
# MCIAS Progress
|
||||
|
||||
Source of truth for current development state.
|
||||
|
||||
---
|
||||
|
||||
## Current Status: Phase 7 Complete — Phases 8–9 Planned
|
||||
|
||||
137 tests pass with zero race conditions. Phase 7 (gRPC dual-stack) is
|
||||
complete. Phases 8–9 are designed and documented; implementation not yet started.
|
||||
|
||||
### Completed Phases
|
||||
|
||||
137 tests pass with zero race conditions. Phase 8 (operational artifacts) is
|
||||
complete. Phase 9 (client libraries) is designed and documented; implementation
|
||||
not yet started.
|
||||
- [x] Phase 0: Repository bootstrap (go.mod, .gitignore, docs)
|
||||
- [x] Phase 1: Foundational packages (model, config, crypto, db)
|
||||
- [x] Phase 2: Auth core (auth, token, middleware)
|
||||
@@ -19,16 +13,46 @@ complete. Phases 8–9 are designed and documented; implementation not yet start
|
||||
- [x] Phase 5: E2E tests, security hardening, commit
|
||||
- [x] Phase 6: mciasdb — direct SQLite maintenance tool
|
||||
- [x] Phase 7: gRPC interface (alternate transport; dual-stack with REST)
|
||||
|
||||
### Planned Phases
|
||||
|
||||
- [ ] Phase 8: Operational artifacts (systemd unit, man pages, Makefile, install script)
|
||||
- [x] Phase 8: Operational artifacts (Makefile, Dockerfile, systemd, man pages, install script)
|
||||
- [ ] Phase 9: Client libraries (Go, Rust, Common Lisp, Python)
|
||||
|
||||
---
|
||||
|
||||
## Implementation Log
|
||||
|
||||
**Makefile**
|
||||
- Targets: build, test, lint, generate, man, install, clean, dist, docker
|
||||
- build: compiles all four binaries to bin/ with CGO_ENABLED=1 and
|
||||
-trimpath -ldflags="-s -w"
|
||||
- dist: cross-compiled tarballs for linux/amd64 and linux/arm64
|
||||
- docker: builds image tagged mcias:$(git describe --tags --always)
|
||||
- VERSION derived from git describe --tags --always
|
||||
**Dockerfile** (multi-stage)
|
||||
- Build stage: golang:1.26-bookworm with CGO_ENABLED=1
|
||||
- Runtime stage: debian:bookworm-slim with only ca-certificates and libc6;
|
||||
no Go toolchain, no source, no build cache in final image
|
||||
- Non-root user mcias (uid/gid 10001)
|
||||
- EXPOSE 8443 (REST/TLS) and EXPOSE 9443 (gRPC/TLS)
|
||||
- VOLUME /data for the SQLite database mount point
|
||||
- ENTRYPOINT ["mciassrv"] CMD ["-config", "/etc/mcias/mcias.conf"]
|
||||
**dist/ artifacts**
|
||||
- dist/mcias.service: hardened systemd unit with ProtectSystem=strict,
|
||||
ProtectHome=true, PrivateTmp=true, NoNewPrivileges=true,
|
||||
CapabilityBoundingSet= (no capabilities), ReadWritePaths=/var/lib/mcias,
|
||||
EnvironmentFile=/etc/mcias/env, Restart=on-failure, LimitNOFILE=65536
|
||||
- dist/mcias.env.example: passphrase env file template
|
||||
- dist/mcias.conf.example: fully-commented production TOML config reference
|
||||
- dist/mcias-dev.conf.example: local dev config (127.0.0.1, short expiry)
|
||||
- dist/mcias.conf.docker.example: container config template
|
||||
- dist/install.sh: idempotent POSIX sh installer; creates user/group,
|
||||
installs binaries, creates /etc/mcias and /var/lib/mcias, installs
|
||||
systemd unit and man pages; existing configs not overwritten (placed .new)
|
||||
**man/ pages** (mdoc format)
|
||||
- man/man1/mciassrv.1: synopsis, options, config, REST API, signals, files
|
||||
- man/man1/mciasctl.1: all subcommands, env vars, examples
|
||||
- man/man1/mciasdb.1: trust model warnings, all subcommands, examples
|
||||
- man/man1/mciasgrpcctl.1: gRPC subcommands, grpcurl examples
|
||||
**Documentation**
|
||||
- README.md: replaced dev-workflow notes with user-facing docs; quick-start,
|
||||
first-run setup, build instructions, CLI references, Docker deployment,
|
||||
man page index, security notes
|
||||
- .gitignore: added /bin/, dist/mcias_*.tar.gz, man/man1/*.gz
|
||||
### 2026-03-11 — Phase 7: gRPC dual-stack
|
||||
|
||||
**proto/mcias/v1/**
|
||||
|
||||
277
README.md
277
README.md
@@ -1,32 +1,257 @@
|
||||
The project notes are in PROJECT.md.
|
||||
# MCIAS — Metacircular Identity and Access System
|
||||
|
||||
Before starting the project, read the project description and develop a
|
||||
system architecture document (ARCHITECTURE.md). It should describe the
|
||||
technical design of the system. Then, PROJECT_PLAN.md should be written
|
||||
that describes how to build this system in discrete steps.
|
||||
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).
|
||||
|
||||
Commit these files to Git after human review. For this project, all git
|
||||
commits should have a single first line, no more than 55 characters,
|
||||
that succinctly describes an overview of the changes. As necessary, the
|
||||
body of the commit message should contain bullets describing the work
|
||||
that was done.
|
||||
See [ARCHITECTURE.md](ARCHITECTURE.md) for the technical design and
|
||||
[PROJECT_PLAN.md](PROJECT_PLAN.md) for the implementation roadmap.
|
||||
|
||||
Then, create PROGRESS.md. This should be initialized with the first
|
||||
steps for the project.
|
||||
---
|
||||
|
||||
The development process for this should generally be:
|
||||
## Quick Start
|
||||
|
||||
1. Determine the current state of the project. Track the progress in
|
||||
PROGRESS.md, explicitly stating what was done and next steps.
|
||||
2. Develop reasonable, discrete next steps to move forward with
|
||||
actionable acceptance criteria.
|
||||
3. Complete the next steps.
|
||||
+ You should adversarially check whether any outputs are correct.
|
||||
+ All code units should be thoroughly unit tested.
|
||||
+ All subsystems should be thoroughly integration tested.
|
||||
+ Where appropriate, end-to-end tests to validate the system should
|
||||
be developed.
|
||||
+ All code changes must pass golangci-lint checks.
|
||||
4. After each phase, checkpoint your work, committing it to git.
|
||||
### Option A: Install from source
|
||||
|
||||
Repeat this cycle until the system is in the desired end state.
|
||||
**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
|
||||
|
||||
```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<<3C>--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.
|
||||
|
||||
225
dist/install.sh
vendored
Normal file
225
dist/install.sh
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
#!/bin/sh
|
||||
# install.sh — MCIAS first-time and upgrade installer
|
||||
#
|
||||
# Usage: sh dist/install.sh
|
||||
#
|
||||
# This script must be run as root. It:
|
||||
# 1. Creates the mcias system user and group (idempotent).
|
||||
# 2. Copies binaries to /usr/local/bin/.
|
||||
# 3. Creates /etc/mcias/ and /var/lib/mcias/ with correct permissions.
|
||||
# 4. Installs the systemd service unit.
|
||||
# 5. Prints post-install instructions.
|
||||
#
|
||||
# The script does NOT start or enable the service automatically. Review the
|
||||
# configuration files and set the master key passphrase before starting.
|
||||
#
|
||||
# Idempotent: safe to re-run after upgrades. Existing config files are not
|
||||
# overwritten; new example files are placed alongside them with a .new suffix
|
||||
# so you can review and merge changes.
|
||||
#
|
||||
# POSIX sh compatible — no bash-isms.
|
||||
|
||||
set -eu
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Configuration
|
||||
# ---------------------------------------------------------------------------
|
||||
BIN_DIR="/usr/local/bin"
|
||||
CONF_DIR="/etc/mcias"
|
||||
DATA_DIR="/var/lib/mcias"
|
||||
MAN_DIR="/usr/share/man/man1"
|
||||
SYSTEMD_DIR="/etc/systemd/system"
|
||||
SERVICE_USER="mcias"
|
||||
SERVICE_GROUP="mcias"
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
REPO_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
info() {
|
||||
printf '==> %s\n' "$*"
|
||||
}
|
||||
|
||||
warn() {
|
||||
printf 'WARNING: %s\n' "$*" >&2
|
||||
}
|
||||
|
||||
die() {
|
||||
printf 'ERROR: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
require_root() {
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
die "This script must be run as root."
|
||||
fi
|
||||
}
|
||||
|
||||
# install_file SRC DST MODE OWNER
|
||||
# Installs SRC to DST. If DST already exists, installs SRC as DST.new
|
||||
# so the operator can review changes.
|
||||
install_file() {
|
||||
src="$1"
|
||||
dst="$2"
|
||||
mode="$3"
|
||||
owner="$4"
|
||||
|
||||
if [ -e "$dst" ]; then
|
||||
info "Existing file found: $dst (installing as $dst.new)"
|
||||
install -m "$mode" -o "$owner" "$src" "$dst.new"
|
||||
else
|
||||
install -m "$mode" -o "$owner" "$src" "$dst"
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Main
|
||||
# ---------------------------------------------------------------------------
|
||||
require_root
|
||||
|
||||
# Step 1: Create system user and group.
|
||||
info "Creating system user and group: $SERVICE_USER"
|
||||
if getent group "$SERVICE_GROUP" > /dev/null 2>&1; then
|
||||
info "Group $SERVICE_GROUP already exists — skipping."
|
||||
else
|
||||
groupadd --system "$SERVICE_GROUP"
|
||||
fi
|
||||
|
||||
if getent passwd "$SERVICE_USER" > /dev/null 2>&1; then
|
||||
info "User $SERVICE_USER already exists — skipping."
|
||||
else
|
||||
useradd \
|
||||
--system \
|
||||
--gid "$SERVICE_GROUP" \
|
||||
--no-create-home \
|
||||
--shell /usr/sbin/nologin \
|
||||
--comment "MCIAS authentication server" \
|
||||
"$SERVICE_USER"
|
||||
fi
|
||||
|
||||
# Step 2: Install binaries.
|
||||
info "Installing binaries to $BIN_DIR"
|
||||
for bin in mciassrv mciasctl mciasdb mciasgrpcctl; do
|
||||
src="$REPO_ROOT/$bin"
|
||||
if [ ! -f "$src" ]; then
|
||||
# Try bin/ subdirectory (Makefile build output).
|
||||
src="$REPO_ROOT/bin/$bin"
|
||||
fi
|
||||
if [ ! -f "$src" ]; then
|
||||
warn "Binary not found: $bin — skipping. Run 'make build' first."
|
||||
continue
|
||||
fi
|
||||
info " Installing $bin"
|
||||
install -m 0755 -o root -g root "$src" "$BIN_DIR/$bin"
|
||||
done
|
||||
|
||||
# Step 3: Create configuration directory.
|
||||
info "Creating $CONF_DIR"
|
||||
install -d -m 0750 -o root -g "$SERVICE_GROUP" "$CONF_DIR"
|
||||
|
||||
# Install example config files; never overwrite existing configs.
|
||||
for f in mcias.conf.example mcias.env.example; do
|
||||
src="$SCRIPT_DIR/$f"
|
||||
dst="$CONF_DIR/$f"
|
||||
if [ -f "$src" ]; then
|
||||
install -m 0640 -o root -g "$SERVICE_GROUP" "$src" "$dst" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
|
||||
# Step 4: Create data directory.
|
||||
info "Creating $DATA_DIR"
|
||||
install -d -m 0750 -o "$SERVICE_USER" -g "$SERVICE_GROUP" "$DATA_DIR"
|
||||
|
||||
# Step 5: Install systemd service unit.
|
||||
if [ -d "$SYSTEMD_DIR" ]; then
|
||||
info "Installing systemd service unit to $SYSTEMD_DIR"
|
||||
install -m 0644 -o root -g root "$SCRIPT_DIR/mcias.service" "$SYSTEMD_DIR/mcias.service"
|
||||
info "Reloading systemd daemon"
|
||||
systemctl daemon-reload 2>/dev/null || warn "systemctl not available; reload manually."
|
||||
else
|
||||
warn "systemd not found at $SYSTEMD_DIR; skipping service unit installation."
|
||||
fi
|
||||
|
||||
# Step 6: Install man pages.
|
||||
if [ -d "$REPO_ROOT/man/man1" ]; then
|
||||
install -d -m 0755 -o root -g root "$MAN_DIR"
|
||||
info "Installing man pages to $MAN_DIR"
|
||||
for page in "$REPO_ROOT/man/man1"/*.1.gz; do
|
||||
[ -f "$page" ] || continue
|
||||
install -m 0644 -o root -g root "$page" "$MAN_DIR/"
|
||||
done
|
||||
# Also install uncompressed pages if no gz versions exist.
|
||||
for page in "$REPO_ROOT/man/man1"/*.1; do
|
||||
[ -f "$page" ] || continue
|
||||
gzname="${MAN_DIR}/$(basename "$page").gz"
|
||||
if [ ! -f "$gzname" ]; then
|
||||
gzip -c "$page" > "$gzname"
|
||||
chmod 0644 "$gzname"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Post-install instructions
|
||||
# ---------------------------------------------------------------------------
|
||||
cat <<EOF
|
||||
|
||||
==========================================================================
|
||||
MCIAS installed successfully.
|
||||
==========================================================================
|
||||
|
||||
Next steps:
|
||||
|
||||
1. Generate a TLS certificate and key:
|
||||
|
||||
# Self-signed (development / personal use):
|
||||
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. Copy and edit the configuration file:
|
||||
|
||||
cp /etc/mcias/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
|
||||
|
||||
3. Set the master key passphrase:
|
||||
|
||||
cp /etc/mcias/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. Enable and start the service:
|
||||
|
||||
systemctl enable mcias
|
||||
systemctl start mcias
|
||||
systemctl status mcias
|
||||
|
||||
5. Create the first admin account using mciasdb (while the server is
|
||||
running, or before first start):
|
||||
|
||||
MCIAS_MASTER_PASSPHRASE=\$(grep MCIAS_MASTER_PASSPHRASE /etc/mcias/env | cut -d= -f2) \\
|
||||
mciasdb --config /etc/mcias/mcias.conf account create \\
|
||||
--username admin --type human
|
||||
|
||||
Then set a password:
|
||||
MCIAS_MASTER_PASSPHRASE=... mciasdb --config /etc/mcias/mcias.conf \\
|
||||
account set-password --id <UUID>
|
||||
|
||||
And grant the admin role:
|
||||
MCIAS_MASTER_PASSPHRASE=... mciasdb --config /etc/mcias/mcias.conf \\
|
||||
role grant --id <UUID> --role admin
|
||||
|
||||
For full documentation, see: man mciassrv
|
||||
==========================================================================
|
||||
EOF
|
||||
42
dist/mcias-dev.conf.example
vendored
Normal file
42
dist/mcias-dev.conf.example
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
# mcias-dev.conf — Local development configuration for mciassrv
|
||||
#
|
||||
# Suitable for running mciassrv on a developer workstation.
|
||||
# DO NOT use this configuration in production:
|
||||
# - Tokens expire quickly (for rapid test iteration).
|
||||
# - The master key passphrase is trivial.
|
||||
# - TLS paths point to local self-signed certificates.
|
||||
#
|
||||
# Generate a self-signed certificate for local development:
|
||||
# openssl req -x509 -newkey ed25519 -days 365 \
|
||||
# -keyout /tmp/mcias-dev.key -out /tmp/mcias-dev.crt \
|
||||
# -subj "/CN=localhost" -nodes
|
||||
#
|
||||
# Set the master passphrase:
|
||||
# export MCIAS_MASTER_PASSPHRASE=devpassphrase
|
||||
#
|
||||
# Start the server:
|
||||
# mciassrv -config /path/to/mcias-dev.conf
|
||||
|
||||
[server]
|
||||
listen_addr = "127.0.0.1:8443"
|
||||
grpc_addr = "127.0.0.1:9443"
|
||||
tls_cert = "/tmp/mcias-dev.crt"
|
||||
tls_key = "/tmp/mcias-dev.key"
|
||||
|
||||
[database]
|
||||
path = "/tmp/mcias-dev.db"
|
||||
|
||||
[tokens]
|
||||
issuer = "https://localhost:8443"
|
||||
default_expiry = "1h"
|
||||
admin_expiry = "30m"
|
||||
service_expiry = "24h"
|
||||
|
||||
[argon2]
|
||||
# OWASP minimums maintained even in dev; do not reduce further.
|
||||
time = 2
|
||||
memory = 65536
|
||||
threads = 4
|
||||
|
||||
[master_key]
|
||||
passphrase_env = "MCIAS_MASTER_PASSPHRASE"
|
||||
48
dist/mcias.conf.docker.example
vendored
Normal file
48
dist/mcias.conf.docker.example
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
# mcias.conf.docker.example — Config template for container deployment
|
||||
#
|
||||
# Mount this file into the container at /etc/mcias/mcias.conf:
|
||||
#
|
||||
# docker run -d \
|
||||
# --name mcias \
|
||||
# -v /path/to/mcias.conf:/etc/mcias/mcias.conf:ro \
|
||||
# -v /path/to/certs:/etc/mcias:ro \
|
||||
# -v mcias-data:/data \
|
||||
# -e MCIAS_MASTER_PASSPHRASE=your-passphrase \
|
||||
# -p 8443:8443 \
|
||||
# -p 9443:9443 \
|
||||
# mcias:latest
|
||||
#
|
||||
# The container runs as uid 10001 (mcias). Ensure that:
|
||||
# - /data volume is writable by uid 10001
|
||||
# - TLS cert and key are readable by uid 10001
|
||||
#
|
||||
# TLS: The server performs TLS termination inside the container; there is no
|
||||
# plain-text mode. Mount your certificate and key under /etc/mcias/.
|
||||
# For Let's Encrypt certificates, mount the live/ directory read-only.
|
||||
|
||||
[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"
|
||||
|
||||
[database]
|
||||
# VOLUME /data is declared in the Dockerfile; map a named volume here.
|
||||
path = "/data/mcias.db"
|
||||
|
||||
[tokens]
|
||||
issuer = "https://auth.example.com"
|
||||
default_expiry = "720h"
|
||||
admin_expiry = "8h"
|
||||
service_expiry = "8760h"
|
||||
|
||||
[argon2]
|
||||
time = 3
|
||||
memory = 65536
|
||||
threads = 4
|
||||
|
||||
[master_key]
|
||||
# Pass the passphrase via the MCIAS_MASTER_PASSPHRASE environment variable.
|
||||
# Set it with: docker run -e MCIAS_MASTER_PASSPHRASE=your-passphrase ...
|
||||
# or with a Docker secret / Kubernetes secret.
|
||||
passphrase_env = "MCIAS_MASTER_PASSPHRASE"
|
||||
110
dist/mcias.conf.example
vendored
Normal file
110
dist/mcias.conf.example
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
# mcias.conf — Reference configuration for mciassrv
|
||||
#
|
||||
# Copy this file to /etc/mcias/mcias.conf and adjust the values for your
|
||||
# deployment. All fields marked REQUIRED must be set before the server will
|
||||
# start. Fields marked OPTIONAL can be omitted to use defaults.
|
||||
#
|
||||
# File permissions: mode 0640, owner root:mcias.
|
||||
# chmod 0640 /etc/mcias/mcias.conf
|
||||
# chown root:mcias /etc/mcias/mcias.conf
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# [server] — Network listener configuration
|
||||
# ---------------------------------------------------------------------------
|
||||
[server]
|
||||
|
||||
# REQUIRED. Address and port for the HTTPS REST listener.
|
||||
# Format: "host:port". Use "0.0.0.0" to listen on all interfaces.
|
||||
# Ports > 1024 do not require elevated privileges.
|
||||
listen_addr = "0.0.0.0:8443"
|
||||
|
||||
# OPTIONAL. Address and port for the gRPC/TLS listener.
|
||||
# If omitted, the gRPC listener is disabled and only REST is served.
|
||||
# Format: "host:port".
|
||||
# grpc_addr = "0.0.0.0:9443"
|
||||
|
||||
# REQUIRED. Path to the TLS certificate (PEM format).
|
||||
# Self-signed certificates work fine for personal deployments; for
|
||||
# public-facing deployments consider a certificate from Let's Encrypt.
|
||||
tls_cert = "/etc/mcias/server.crt"
|
||||
|
||||
# REQUIRED. Path to the TLS private key (PEM format).
|
||||
# Permissions: mode 0640, owner root:mcias.
|
||||
tls_key = "/etc/mcias/server.key"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# [database] — SQLite database
|
||||
# ---------------------------------------------------------------------------
|
||||
[database]
|
||||
|
||||
# REQUIRED. Path to the SQLite database file.
|
||||
# The directory must be writable by the mcias user. WAL mode is enabled
|
||||
# automatically; expect three files: mcias.db, mcias.db-wal, mcias.db-shm.
|
||||
path = "/var/lib/mcias/mcias.db"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# [tokens] — JWT issuance policy
|
||||
# ---------------------------------------------------------------------------
|
||||
[tokens]
|
||||
|
||||
# REQUIRED. Issuer claim embedded in every JWT. Relying parties should
|
||||
# validate this claim matches the expected value.
|
||||
# Use the base URL of your MCIAS server (without trailing slash).
|
||||
issuer = "https://auth.example.com"
|
||||
|
||||
# OPTIONAL. Default token expiry for interactive (human) logins.
|
||||
# Go duration string: "h" hours, "m" minutes, "s" seconds.
|
||||
# Default: 720h (30 days). Reduce for higher-security deployments.
|
||||
default_expiry = "720h"
|
||||
|
||||
# OPTIONAL. Expiry for admin tokens (tokens with the "admin" role).
|
||||
# Should be shorter than default_expiry to limit the blast radius of
|
||||
# a leaked admin credential.
|
||||
# Default: 8h.
|
||||
admin_expiry = "8h"
|
||||
|
||||
# OPTIONAL. Expiry for system account tokens (machine-to-machine).
|
||||
# System accounts have no interactive login; their tokens are long-lived.
|
||||
# Default: 8760h (365 days).
|
||||
service_expiry = "8760h"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# [argon2] — Password hashing parameters (Argon2id)
|
||||
# ---------------------------------------------------------------------------
|
||||
[argon2]
|
||||
|
||||
# OWASP 2023 minimums: time >= 2, memory >= 65536 KiB (64 MB).
|
||||
# Increasing these values improves resistance to brute-force attacks but
|
||||
# increases CPU and memory usage at login time.
|
||||
|
||||
# OPTIONAL. Time cost (number of passes over memory). Default: 3.
|
||||
time = 3
|
||||
|
||||
# OPTIONAL. Memory cost in KiB. Default: 65536 (64 MB).
|
||||
memory = 65536
|
||||
|
||||
# OPTIONAL. Parallelism (number of threads). Default: 4.
|
||||
threads = 4
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# [master_key] — AES-256 master key derivation
|
||||
# ---------------------------------------------------------------------------
|
||||
[master_key]
|
||||
|
||||
# REQUIRED. Exactly ONE of passphrase_env or keyfile must be set.
|
||||
|
||||
# Option A: Passphrase mode. The passphrase is read from the named environment
|
||||
# variable at startup, then cleared. The Argon2id KDF salt is stored in the
|
||||
# database on first run and reused on subsequent runs so the same passphrase
|
||||
# always produces the same master key.
|
||||
#
|
||||
# Set the passphrase in /etc/mcias/env (loaded by the systemd EnvironmentFile
|
||||
# directive). See dist/mcias.env.example for the template.
|
||||
passphrase_env = "MCIAS_MASTER_PASSPHRASE"
|
||||
|
||||
# Option B: Key file mode. The file must contain exactly 32 bytes of raw key
|
||||
# material (AES-256). Generate with: openssl rand -out /etc/mcias/master.key 32
|
||||
# Permissions: mode 0640, owner root:mcias.
|
||||
#
|
||||
# Uncomment and comment out passphrase_env to switch modes.
|
||||
# keyfile = "/etc/mcias/master.key"
|
||||
17
dist/mcias.env.example
vendored
Normal file
17
dist/mcias.env.example
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# /etc/mcias/env — Environment file for mciassrv (systemd EnvironmentFile).
|
||||
#
|
||||
# This file is loaded by the mcias.service unit before the server starts.
|
||||
# It must be readable only by root and the mcias service account:
|
||||
#
|
||||
# chmod 0640 /etc/mcias/env
|
||||
# chown root:mcias /etc/mcias/env
|
||||
#
|
||||
# SECURITY: This file contains the master key passphrase. Treat it with
|
||||
# the same care as a private key. Do not commit it to version control.
|
||||
# Back it up to a secure offline location — losing this passphrase means
|
||||
# losing access to all encrypted data in the database.
|
||||
|
||||
# Master key passphrase. Used to derive the AES-256 master key via Argon2id.
|
||||
# Choose a long, random passphrase (e.g., generated by `openssl rand -base64 32`).
|
||||
# This must match the passphrase_env setting in mcias.conf.
|
||||
MCIAS_MASTER_PASSPHRASE=change-me-to-a-long-random-passphrase
|
||||
51
dist/mcias.service
vendored
Normal file
51
dist/mcias.service
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
[Unit]
|
||||
Description=MCIAS Authentication Server
|
||||
Documentation=man:mciassrv(1)
|
||||
After=network.target
|
||||
# Require network to be available before starting.
|
||||
# Remove if you bind only to loopback.
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=mcias
|
||||
Group=mcias
|
||||
|
||||
# Configuration and secrets.
|
||||
# /etc/mcias/env must contain MCIAS_MASTER_PASSPHRASE=<passphrase>
|
||||
# See dist/mcias.env.example for the template.
|
||||
EnvironmentFile=/etc/mcias/env
|
||||
|
||||
ExecStart=/usr/local/bin/mciassrv -config /etc/mcias/mcias.conf
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
|
||||
# File descriptor limit. mciassrv keeps one fd per open connection plus
|
||||
# the SQLite WAL files; 65536 is generous headroom for a personal server.
|
||||
LimitNOFILE=65536
|
||||
|
||||
# Sandboxing. mcias does not need capabilities; it listens on ports > 1024.
|
||||
# If you need port 443 or 8443 on a privileged port (< 1024), either:
|
||||
# a) use a reverse proxy (recommended), or
|
||||
# b) grant CAP_NET_BIND_SERVICE with: AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
CapabilityBoundingSet=
|
||||
|
||||
# Filesystem restrictions.
|
||||
# mciassrv reads /etc/mcias (config, TLS cert/key) and writes /var/lib/mcias (DB).
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
PrivateTmp=true
|
||||
ReadWritePaths=/var/lib/mcias
|
||||
|
||||
# Additional hardening.
|
||||
NoNewPrivileges=true
|
||||
PrivateDevices=true
|
||||
ProtectKernelTunables=true
|
||||
ProtectKernelModules=true
|
||||
ProtectControlGroups=true
|
||||
RestrictNamespaces=true
|
||||
RestrictRealtime=true
|
||||
LockPersonality=true
|
||||
MemoryDenyWriteExecute=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
124
man/man1/mciasctl.1
Normal file
124
man/man1/mciasctl.1
Normal file
@@ -0,0 +1,124 @@
|
||||
.Dd March 11, 2026
|
||||
.Dt MCIASCTL 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm mciasctl
|
||||
.Nd MCIAS admin CLI (REST)
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl server Ar url
|
||||
.Op Fl token Ar jwt
|
||||
.Op Fl cacert Ar path
|
||||
.Ar command
|
||||
.Op Ar subcommand
|
||||
.Op Ar flags
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
is the administrator command-line interface for MCIAS.
|
||||
It connects to a running
|
||||
.Xr mciassrv 1
|
||||
instance via the REST API over HTTPS and provides subcommands for managing
|
||||
accounts, roles, tokens, and Postgres credentials.
|
||||
.Pp
|
||||
Authentication is performed using a bearer JWT.
|
||||
The token must have the
|
||||
.Qq admin
|
||||
role for most operations.
|
||||
Pass the token with the
|
||||
.Fl token
|
||||
flag or by setting the
|
||||
.Ev MCIAS_TOKEN
|
||||
environment variable.
|
||||
.Sh OPTIONS
|
||||
.Bl -tag -width Ds
|
||||
.It Fl server Ar url
|
||||
Base URL of the mciassrv instance.
|
||||
Default:
|
||||
.Qq https://localhost:8443 .
|
||||
Can also be set with the
|
||||
.Ev MCIAS_SERVER
|
||||
environment variable.
|
||||
.It Fl token Ar jwt
|
||||
Bearer token for authentication.
|
||||
Can also be set with the
|
||||
.Ev MCIAS_TOKEN
|
||||
environment variable.
|
||||
.It Fl cacert Ar path
|
||||
Path to a CA certificate in PEM format for TLS verification.
|
||||
Useful when mciassrv uses a self-signed certificate.
|
||||
If omitted, the system certificate pool is used.
|
||||
.El
|
||||
.Sh COMMANDS
|
||||
.Ss account
|
||||
.Bl -tag -width Ds
|
||||
.It Nm Ic account Ic list
|
||||
Lists all accounts.
|
||||
Credential fields are never included in the output.
|
||||
.It Nm Ic account Ic create Fl username Ar name Fl password Ar pass Op Fl type Ar human|system
|
||||
Creates a new account.
|
||||
.Fl type
|
||||
defaults to
|
||||
.Qq human .
|
||||
.It Nm Ic account Ic get Fl id Ar uuid
|
||||
Returns the account with the given UUID.
|
||||
.It Nm Ic account Ic update Fl id Ar uuid Op Fl status Ar active|inactive
|
||||
Updates account fields.
|
||||
Currently only status can be updated.
|
||||
.It Nm Ic account Ic delete Fl id Ar uuid
|
||||
Soft-deletes the account and revokes all its tokens.
|
||||
.El
|
||||
.Ss role
|
||||
.Bl -tag -width Ds
|
||||
.It Nm Ic role Ic list Fl id Ar uuid
|
||||
Lists the roles assigned to the account.
|
||||
.It Nm Ic role Ic set Fl id Ar uuid Fl roles Ar role1,role2,...
|
||||
Replaces the role set for the account with the comma-separated list.
|
||||
.El
|
||||
.Ss token
|
||||
.Bl -tag -width Ds
|
||||
.It Nm Ic token Ic issue Fl id Ar uuid
|
||||
Issues a new service token for a system account.
|
||||
.It Nm Ic token Ic revoke Fl jti Ar jti
|
||||
Revokes the token with the given JTI.
|
||||
.El
|
||||
.Ss pgcreds
|
||||
.Bl -tag -width Ds
|
||||
.It Nm Ic pgcreds Ic set Fl id Ar uuid Fl host Ar host Fl port Ar port Fl db Ar db Fl user Ar user Fl password Ar pass
|
||||
Sets Postgres credentials for the account.
|
||||
The credentials are encrypted with AES-256-GCM using the server master key.
|
||||
.It Nm Ic pgcreds Ic get Fl id Ar uuid
|
||||
Retrieves and prints the Postgres credentials.
|
||||
The password is included in plaintext; treat the output as sensitive.
|
||||
.El
|
||||
.Sh ENVIRONMENT
|
||||
.Bl -tag -width Ds
|
||||
.It Ev MCIAS_TOKEN
|
||||
Bearer token used for authentication when
|
||||
.Fl token
|
||||
is not specified.
|
||||
.It Ev MCIAS_SERVER
|
||||
Base URL of the mciassrv instance when
|
||||
.Fl server
|
||||
is not specified.
|
||||
.El
|
||||
.Sh EXAMPLES
|
||||
List all accounts:
|
||||
.Bd -literal -offset indent
|
||||
mciasctl -server https://auth.example.com -token $ADMIN_TOKEN account list
|
||||
.Ed
|
||||
.Pp
|
||||
Create a human account:
|
||||
.Bd -literal -offset indent
|
||||
mciasctl account create -username alice -password s3cr3t
|
||||
.Ed
|
||||
.Pp
|
||||
Grant the admin role:
|
||||
.Bd -literal -offset indent
|
||||
mciasctl role set -id $UUID -roles admin
|
||||
.Ed
|
||||
.Sh EXIT STATUS
|
||||
.Ex -std
|
||||
.Sh SEE ALSO
|
||||
.Xr mciassrv 1 ,
|
||||
.Xr mciasdb 1 ,
|
||||
.Xr mciasgrpcctl 1
|
||||
203
man/man1/mciasdb.1
Normal file
203
man/man1/mciasdb.1
Normal file
@@ -0,0 +1,203 @@
|
||||
.Dd March 11, 2026
|
||||
.Dt MCIASDB 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm mciasdb
|
||||
.Nd MCIAS database maintenance tool
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl config Ar path
|
||||
.Ar command
|
||||
.Op Ar subcommand
|
||||
.Op Ar flags
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
is the MCIAS database maintenance tool.
|
||||
It operates
|
||||
.Em directly
|
||||
on the SQLite database file, bypassing the mciassrv REST API.
|
||||
.Pp
|
||||
Use
|
||||
.Nm
|
||||
for:
|
||||
.Bl -bullet -compact
|
||||
.It
|
||||
Break-glass recovery when the server is unavailable.
|
||||
.It
|
||||
Offline account and role management before first server start.
|
||||
.It
|
||||
Schema verification and migration.
|
||||
.It
|
||||
Token pruning and audit log inspection.
|
||||
.El
|
||||
.Pp
|
||||
.Nm
|
||||
requires the same master key configuration as
|
||||
.Xr mciassrv 1
|
||||
to decrypt secrets at rest.
|
||||
.Sh TRUST MODEL AND SAFETY WARNINGS
|
||||
.Nm
|
||||
has
|
||||
.Em direct write access
|
||||
to the database and can modify any record without going through the server's
|
||||
validation logic.
|
||||
Use it only when necessary.
|
||||
.Bl -bullet
|
||||
.It
|
||||
Run
|
||||
.Nm
|
||||
on the same host as the database file.
|
||||
Never copy the database to an untrusted host.
|
||||
.It
|
||||
The master key passphrase (or keyfile) must be available.
|
||||
Set the environment variable named by
|
||||
.Sy passphrase_env
|
||||
in the config, or ensure the keyfile is accessible.
|
||||
.It
|
||||
All write operations append an audit log entry tagged
|
||||
.Qq actor:mciasdb .
|
||||
.It
|
||||
Avoid running
|
||||
.Nm
|
||||
while mciassrv is under heavy write load; SQLite WAL mode provides some
|
||||
isolation, but schema migrations acquire an exclusive lock.
|
||||
.El
|
||||
.Sh OPTIONS
|
||||
.Bl -tag -width Ds
|
||||
.It Fl config Ar path
|
||||
Path to the TOML configuration file.
|
||||
Defaults to
|
||||
.Pa mcias.toml
|
||||
in the current directory.
|
||||
.El
|
||||
.Sh COMMANDS
|
||||
.Ss schema
|
||||
.Bl -tag -width Ds
|
||||
.It Nm Ic schema Ic verify
|
||||
Reports the current schema version.
|
||||
Exits 0 if the schema is up to date; exits 1 if migrations are pending.
|
||||
.It Nm Ic schema Ic migrate
|
||||
Applies any pending migrations and reports each one applied.
|
||||
Exits 0 on success.
|
||||
.El
|
||||
.Ss account
|
||||
.Bl -tag -width Ds
|
||||
.It Nm Ic account Ic list
|
||||
Prints UUID, username, type, and status for all accounts.
|
||||
.It Nm Ic account Ic get Fl -id Ar uuid
|
||||
Prints the account record for the given UUID.
|
||||
.It Nm Ic account Ic create Fl -username Ar name Fl -type Ar human|system
|
||||
Inserts a new account row and prints the new UUID.
|
||||
.It Nm Ic account Ic set-password Fl -id Ar uuid
|
||||
Prompts for a new password twice (confirmation).
|
||||
Re-hashes with Argon2id and updates the row.
|
||||
No
|
||||
.Fl -password
|
||||
flag is provided; the password is always read interactively.
|
||||
.It Nm Ic account Ic set-status Fl -id Ar uuid Fl -status Ar STATUS
|
||||
Updates the account status.
|
||||
Valid statuses:
|
||||
.Qq active ,
|
||||
.Qq inactive ,
|
||||
.Qq deleted .
|
||||
.It Nm Ic account Ic reset-totp Fl -id Ar uuid
|
||||
Clears the TOTP secret and disables TOTP for the account.
|
||||
The user will be able to log in with password only until they re-enrol.
|
||||
.El
|
||||
.Ss role
|
||||
.Bl -tag -width Ds
|
||||
.It Nm Ic role Ic list Fl -id Ar uuid
|
||||
Prints the roles assigned to the account.
|
||||
.It Nm Ic role Ic grant Fl -id Ar uuid Fl -role Ar role
|
||||
Adds a role to the account.
|
||||
.It Nm Ic role Ic revoke Fl -id Ar uuid Fl -role Ar role
|
||||
Removes a role from the account.
|
||||
.El
|
||||
.Ss token
|
||||
.Bl -tag -width Ds
|
||||
.It Nm Ic token Ic list Fl -id Ar uuid
|
||||
Prints JTI, issued_at, expires_at, and revoked_at for all tokens associated
|
||||
with the account.
|
||||
.It Nm Ic token Ic revoke Fl -jti Ar jti
|
||||
Sets revoked_at to the current time on the specified token row.
|
||||
.It Nm Ic token Ic revoke-all Fl -id Ar uuid
|
||||
Revokes all non-revoked tokens for the account.
|
||||
A no-op (exits 0) if no active tokens exist.
|
||||
.El
|
||||
.Ss prune
|
||||
.Bl -tag -width Ds
|
||||
.It Nm Ic prune Ic tokens
|
||||
Deletes rows from the token_revocation table where expires_at is in the past.
|
||||
Prints the number of rows removed.
|
||||
.El
|
||||
.Ss audit
|
||||
.Bl -tag -width Ds
|
||||
.It Nm Ic audit Ic tail Op Fl -n Ar N
|
||||
Prints the last N audit events, newest last.
|
||||
Default: 50.
|
||||
.It Nm Ic audit Ic query Op Fl -account Ar uuid Op Fl -type Ar event_type Op Fl -since Ar timestamp Op Fl -json
|
||||
Queries the audit log.
|
||||
Filters are combinable (AND semantics).
|
||||
.Fl -since
|
||||
accepts an RFC 3339 timestamp.
|
||||
.Fl -json
|
||||
emits newline-delimited JSON instead of tabular output.
|
||||
.El
|
||||
.Ss pgcreds
|
||||
.Bl -tag -width Ds
|
||||
.It Nm Ic pgcreds Ic get Fl -id Ar uuid
|
||||
Decrypts and prints the Postgres credentials for the account.
|
||||
A warning header is printed before the output because the password is
|
||||
displayed in plaintext.
|
||||
.It Nm Ic pgcreds Ic set Fl -id Ar uuid Fl -host Ar host Fl -port Ar port Fl -db Ar db Fl -user Ar user
|
||||
Prompts for the Postgres password interactively (no
|
||||
.Fl -password
|
||||
flag).
|
||||
Encrypts with AES-256-GCM using the master key and stores the record.
|
||||
.El
|
||||
.Sh EXAMPLES
|
||||
Verify schema after upgrade:
|
||||
.Bd -literal -offset indent
|
||||
MCIAS_MASTER_PASSPHRASE=... mciasdb --config /etc/mcias/mcias.conf schema verify
|
||||
.Ed
|
||||
.Pp
|
||||
Create an admin account before first server start:
|
||||
.Bd -literal -offset indent
|
||||
MCIAS_MASTER_PASSPHRASE=... mciasdb --config /etc/mcias/mcias.conf \\
|
||||
account create --username admin --type human
|
||||
MCIAS_MASTER_PASSPHRASE=... mciasdb --config /etc/mcias/mcias.conf \\
|
||||
account set-password --id <UUID>
|
||||
MCIAS_MASTER_PASSPHRASE=... mciasdb --config /etc/mcias/mcias.conf \\
|
||||
role grant --id <UUID> --role admin
|
||||
.Ed
|
||||
.Pp
|
||||
Tail the last 20 audit events:
|
||||
.Bd -literal -offset indent
|
||||
MCIAS_MASTER_PASSPHRASE=... mciasdb --config /etc/mcias/mcias.conf \\
|
||||
audit tail --n 20
|
||||
.Ed
|
||||
.Sh ENVIRONMENT
|
||||
The environment variable named by
|
||||
.Sy passphrase_env
|
||||
in the configuration file must be set to the master key passphrase, unless
|
||||
.Sy keyfile
|
||||
is used instead.
|
||||
.Pp
|
||||
Example (with default config):
|
||||
.Bd -literal -offset indent
|
||||
export MCIAS_MASTER_PASSPHRASE=your-passphrase
|
||||
mciasdb --config /etc/mcias/mcias.conf account list
|
||||
.Ed
|
||||
.Sh EXIT STATUS
|
||||
.Ex -std
|
||||
.Sh FILES
|
||||
.Bl -tag -width Ds
|
||||
.It Pa /etc/mcias/mcias.conf
|
||||
Default configuration file location.
|
||||
.It Pa /var/lib/mcias/mcias.db
|
||||
SQLite database.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr mciassrv 1 ,
|
||||
.Xr mciasctl 1 ,
|
||||
.Xr mciasgrpcctl 1
|
||||
122
man/man1/mciasgrpcctl.1
Normal file
122
man/man1/mciasgrpcctl.1
Normal file
@@ -0,0 +1,122 @@
|
||||
.Dd March 11, 2026
|
||||
.Dt MCIASGRPCCTL 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm mciasgrpcctl
|
||||
.Nd MCIAS gRPC admin CLI
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl server Ar addr
|
||||
.Op Fl token Ar jwt
|
||||
.Op Fl cacert Ar path
|
||||
.Ar command
|
||||
.Op Ar subcommand
|
||||
.Op Ar flags
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
is the gRPC companion to
|
||||
.Xr mciasctl 1 .
|
||||
It connects to the gRPC/TLS listener of a running
|
||||
.Xr mciassrv 1
|
||||
instance and provides subcommands mirroring the REST admin CLI.
|
||||
.Pp
|
||||
The gRPC listener must be enabled in the mciassrv configuration
|
||||
.Pq Sy grpc_addr
|
||||
for
|
||||
.Nm
|
||||
to connect.
|
||||
.Pp
|
||||
Authentication is performed using a bearer JWT passed as gRPC metadata.
|
||||
The token must have the
|
||||
.Qq admin
|
||||
role for most operations.
|
||||
.Sh OPTIONS
|
||||
.Bl -tag -width Ds
|
||||
.It Fl server Ar addr
|
||||
gRPC server address in
|
||||
.Ar host:port
|
||||
format.
|
||||
Default:
|
||||
.Qq localhost:9443 .
|
||||
.It Fl token Ar jwt
|
||||
Bearer token for authentication.
|
||||
Can also be set with the
|
||||
.Ev MCIAS_TOKEN
|
||||
environment variable.
|
||||
.It Fl cacert Ar path
|
||||
Path to a CA certificate in PEM format for TLS verification.
|
||||
Useful when mciassrv uses a self-signed certificate.
|
||||
.El
|
||||
.Sh COMMANDS
|
||||
.Ss Informational (no authentication required)
|
||||
.Bl -tag -width Ds
|
||||
.It Nm Ic health
|
||||
Calls the Health RPC.
|
||||
Prints
|
||||
.Qq ok
|
||||
and exits 0 if the server is healthy.
|
||||
.It Nm Ic pubkey
|
||||
Returns the server's Ed25519 public key as a JWK.
|
||||
.El
|
||||
.Ss account
|
||||
.Bl -tag -width Ds
|
||||
.It Nm Ic account Ic list
|
||||
Lists all accounts.
|
||||
.It Nm Ic account Ic create Fl username Ar name Fl password Ar pass Op Fl type Ar human|system
|
||||
Creates a new account.
|
||||
.It Nm Ic account Ic get Fl id Ar uuid
|
||||
Returns the account with the given UUID.
|
||||
.It Nm Ic account Ic update Fl id Ar uuid Fl status Ar active|inactive
|
||||
Updates account status.
|
||||
.It Nm Ic account Ic delete Fl id Ar uuid
|
||||
Soft-deletes the account and revokes all its tokens.
|
||||
.El
|
||||
.Ss role
|
||||
.Bl -tag -width Ds
|
||||
.It Nm Ic role Ic list Fl id Ar uuid
|
||||
Lists roles for the account.
|
||||
.It Nm Ic role Ic set Fl id Ar uuid Fl roles Ar role1,role2,...
|
||||
Replaces the role set for the account.
|
||||
.El
|
||||
.Ss token
|
||||
.Bl -tag -width Ds
|
||||
.It Nm Ic token Ic validate Fl token Ar jwt
|
||||
Validates the given token and prints its claims.
|
||||
.It Nm Ic token Ic issue Fl id Ar uuid
|
||||
Issues a new service token for a system account.
|
||||
.It Nm Ic token Ic revoke Fl jti Ar jti
|
||||
Revokes the token with the given JTI.
|
||||
.El
|
||||
.Ss pgcreds
|
||||
.Bl -tag -width Ds
|
||||
.It Nm Ic pgcreds Ic get Fl id Ar uuid
|
||||
Returns the Postgres credentials for the account.
|
||||
.It Nm Ic pgcreds Ic set Fl id Ar uuid Fl host Ar host Op Fl port Ar port Fl db Ar db Fl user Ar user Fl password Ar pass
|
||||
Sets Postgres credentials for the account.
|
||||
.El
|
||||
.Sh ENVIRONMENT
|
||||
.Bl -tag -width Ds
|
||||
.It Ev MCIAS_TOKEN
|
||||
Bearer token used for authentication when
|
||||
.Fl token
|
||||
is not specified.
|
||||
.El
|
||||
.Sh EXAMPLES
|
||||
Check server health over gRPC:
|
||||
.Bd -literal -offset indent
|
||||
mciasgrpcctl -server auth.example.com:9443 -cacert /etc/mcias/ca.crt health
|
||||
.Ed
|
||||
.Pp
|
||||
Using grpcurl as an alternative client:
|
||||
.Bd -literal -offset indent
|
||||
grpcurl -cacert /etc/mcias/ca.crt \\
|
||||
-H "authorization: Bearer $TOKEN" \\
|
||||
auth.example.com:9443 \\
|
||||
mcias.v1.AdminService/Health
|
||||
.Ed
|
||||
.Sh EXIT STATUS
|
||||
.Ex -std
|
||||
.Sh SEE ALSO
|
||||
.Xr mciassrv 1 ,
|
||||
.Xr mciasctl 1 ,
|
||||
.Xr mciasdb 1
|
||||
228
man/man1/mciassrv.1
Normal file
228
man/man1/mciassrv.1
Normal file
@@ -0,0 +1,228 @@
|
||||
.Dd March 11, 2026
|
||||
.Dt MCIASSRV 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm mciassrv
|
||||
.Nd MCIAS authentication server
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Fl config
|
||||
.Ar path
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
is the MCIAS (Metacircular Identity and Access System) authentication server.
|
||||
It provides a single-sign-on (SSO) and identity and access management (IAM)
|
||||
service for personal applications.
|
||||
.Pp
|
||||
.Nm
|
||||
reads a TOML configuration file, derives the AES-256 master encryption key,
|
||||
loads or generates an Ed25519 signing key, opens the SQLite database, applies
|
||||
any pending schema migrations, and starts an HTTPS listener.
|
||||
.Pp
|
||||
If the
|
||||
.Sy grpc_addr
|
||||
field is set in the configuration file, a gRPC/TLS listener is also started
|
||||
on the specified address.
|
||||
Both listeners share the same signing key, database connection, and
|
||||
configuration.
|
||||
.Sh OPTIONS
|
||||
.Bl -tag -width Ds
|
||||
.It Fl config Ar path
|
||||
Path to the TOML configuration file.
|
||||
Defaults to
|
||||
.Pa mcias.toml
|
||||
in the current directory.
|
||||
.El
|
||||
.Sh CONFIGURATION
|
||||
The configuration file is in TOML format.
|
||||
See
|
||||
.Pa /etc/mcias/mcias.conf
|
||||
(or
|
||||
.Pa dist/mcias.conf.example
|
||||
in the source tree) for a fully-commented reference configuration.
|
||||
.Pp
|
||||
The following sections are recognised:
|
||||
.Bl -tag -width Ds
|
||||
.It Sy [server]
|
||||
.Bl -tag -width Ds
|
||||
.It Sy listen_addr
|
||||
.Pq required
|
||||
Host and port for the HTTPS REST listener.
|
||||
Example:
|
||||
.Qq 0.0.0.0:8443 .
|
||||
.It Sy grpc_addr
|
||||
.Pq optional
|
||||
Host and port for the gRPC/TLS listener.
|
||||
If omitted, gRPC is disabled.
|
||||
Example:
|
||||
.Qq 0.0.0.0:9443 .
|
||||
.It Sy tls_cert
|
||||
.Pq required
|
||||
Path to the TLS certificate file in PEM format.
|
||||
.It Sy tls_key
|
||||
.Pq required
|
||||
Path to the TLS private key file in PEM format.
|
||||
.El
|
||||
.It Sy [database]
|
||||
.Bl -tag -width Ds
|
||||
.It Sy path
|
||||
.Pq required
|
||||
Path to the SQLite database file.
|
||||
WAL mode and foreign key enforcement are enabled automatically.
|
||||
.El
|
||||
.It Sy [tokens]
|
||||
.Bl -tag -width Ds
|
||||
.It Sy issuer
|
||||
.Pq required
|
||||
Issuer claim embedded in every JWT.
|
||||
Use the base URL of your MCIAS server.
|
||||
.It Sy default_expiry
|
||||
.Pq optional, default 720h
|
||||
Token expiry for interactive logins.
|
||||
Go duration string.
|
||||
.It Sy admin_expiry
|
||||
.Pq optional, default 8h
|
||||
Token expiry for tokens with the
|
||||
.Qq admin
|
||||
role.
|
||||
.It Sy service_expiry
|
||||
.Pq optional, default 8760h
|
||||
Token expiry for system account tokens.
|
||||
.El
|
||||
.It Sy [argon2]
|
||||
.Bl -tag -width Ds
|
||||
.It Sy time
|
||||
.Pq optional, default 3
|
||||
Argon2id time cost.
|
||||
Must be >= 2 (OWASP 2023 minimum).
|
||||
.It Sy memory
|
||||
.Pq optional, default 65536
|
||||
Argon2id memory cost in KiB.
|
||||
Must be >= 65536 (64 MB, OWASP 2023 minimum).
|
||||
.It Sy threads
|
||||
.Pq optional, default 4
|
||||
Argon2id parallelism.
|
||||
.El
|
||||
.It Sy [master_key]
|
||||
Exactly one of the following must be set:
|
||||
.Bl -tag -width Ds
|
||||
.It Sy passphrase_env
|
||||
Name of the environment variable containing the master key passphrase.
|
||||
The passphrase is read at startup and the environment variable is immediately
|
||||
cleared.
|
||||
.It Sy keyfile
|
||||
Path to a file containing exactly 32 bytes of raw AES-256 key material.
|
||||
.El
|
||||
.El
|
||||
.Sh REST API
|
||||
.Nm
|
||||
exposes the following REST endpoints over HTTPS:
|
||||
.Bl -tag -width Ds
|
||||
.It GET /v1/health
|
||||
Returns
|
||||
.Qq {"status":"ok"}
|
||||
with HTTP 200.
|
||||
No authentication required.
|
||||
.It GET /v1/keys/public
|
||||
Returns the Ed25519 public key as a JWK.
|
||||
No authentication required.
|
||||
.It POST /v1/auth/login
|
||||
Authenticates a user and issues a JWT.
|
||||
Body: JSON with
|
||||
.Sy username ,
|
||||
.Sy password ,
|
||||
and optionally
|
||||
.Sy totp_code .
|
||||
Returns
|
||||
.Sy token
|
||||
and
|
||||
.Sy expires_at .
|
||||
.It POST /v1/auth/logout
|
||||
Revokes the current JWT.
|
||||
Requires a valid Bearer token.
|
||||
Returns HTTP 204.
|
||||
.It POST /v1/auth/renew
|
||||
Renews the current JWT, revoking the old one.
|
||||
Requires a valid Bearer token.
|
||||
Returns
|
||||
.Sy token
|
||||
and
|
||||
.Sy expires_at .
|
||||
.It POST /v1/token/validate
|
||||
Validates a submitted token and returns its claims.
|
||||
.It DELETE /v1/token/{jti}
|
||||
Revokes a token by JTI.
|
||||
Requires admin role.
|
||||
.It POST /v1/accounts
|
||||
Creates a new account.
|
||||
Requires admin role.
|
||||
.It GET /v1/accounts
|
||||
Lists all accounts (no credential fields in response).
|
||||
Requires admin role.
|
||||
.It GET /v1/accounts/{id}
|
||||
Returns a single account.
|
||||
Requires admin role.
|
||||
.It PATCH /v1/accounts/{id}
|
||||
Updates an account.
|
||||
Requires admin role.
|
||||
.It DELETE /v1/accounts/{id}
|
||||
Soft-deletes an account and revokes all its tokens.
|
||||
Requires admin role.
|
||||
.It GET /v1/accounts/{id}/roles
|
||||
Returns the role set for an account.
|
||||
Requires admin role.
|
||||
.It PUT /v1/accounts/{id}/roles
|
||||
Replaces the role set for an account.
|
||||
Requires admin role.
|
||||
.It POST /v1/auth/totp/enroll
|
||||
Generates a TOTP secret and returns the otpauth URI.
|
||||
Requires authentication.
|
||||
.It POST /v1/auth/totp/confirm
|
||||
Confirms TOTP enrollment.
|
||||
Requires authentication.
|
||||
.It DELETE /v1/auth/totp
|
||||
Removes TOTP from an account.
|
||||
Requires admin role.
|
||||
.It GET /v1/accounts/{id}/pgcreds
|
||||
Returns the Postgres credentials for an account (decrypted).
|
||||
Requires admin role.
|
||||
.It PUT /v1/accounts/{id}/pgcreds
|
||||
Sets Postgres credentials for an account.
|
||||
Requires admin role.
|
||||
.El
|
||||
.Sh SIGNALS
|
||||
.Bl -tag -width Ds
|
||||
.It Dv SIGINT , SIGTERM
|
||||
Initiate graceful shutdown.
|
||||
In-flight requests are drained for up to 15 seconds before the server exits.
|
||||
.El
|
||||
.Sh EXIT STATUS
|
||||
.Ex -std
|
||||
.Sh FILES
|
||||
.Bl -tag -width Ds
|
||||
.It Pa /etc/mcias/mcias.conf
|
||||
Default configuration file location.
|
||||
.It Pa /etc/mcias/server.crt
|
||||
TLS certificate (operator-supplied).
|
||||
.It Pa /etc/mcias/server.key
|
||||
TLS private key (operator-supplied).
|
||||
.It Pa /var/lib/mcias/mcias.db
|
||||
SQLite database.
|
||||
.It Pa /etc/systemd/system/mcias.service
|
||||
systemd service unit.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr mciasctl 1 ,
|
||||
.Xr mciasdb 1 ,
|
||||
.Xr mciasgrpcctl 1
|
||||
.Sh SECURITY
|
||||
.Nm
|
||||
enforces TLS 1.2 as the minimum protocol version.
|
||||
All JWTs are signed with Ed25519; the
|
||||
.Sy alg
|
||||
header is validated before any other processing to defeat algorithm confusion
|
||||
attacks.
|
||||
Password hashing uses Argon2id with parameters meeting or exceeding the
|
||||
OWASP 2023 recommendations.
|
||||
Credential fields (password hashes, TOTP secrets, Postgres passwords) are
|
||||
never included in any API response.
|
||||
Reference in New Issue
Block a user