Merge pull request 'docs: add consolidated installation guide and helper scripts' (#62) from install-guide-and-scripts into main

Reviewed-on: #62
This commit was merged in pull request #62.
This commit is contained in:
2026-02-12 15:02:25 +00:00
18 changed files with 1394 additions and 69 deletions

View File

@@ -1,12 +1,26 @@
# Silo Environment Configuration
# Copy this file to .env and update values as needed
# Copy to .env (or deployments/.env) and update values as needed.
# For automated setup, run: ./scripts/setup-docker.sh
# PostgreSQL
POSTGRES_PASSWORD=silodev
# MinIO
MINIO_ACCESS_KEY=minioadmin
MINIO_SECRET_KEY=minioadmin
MINIO_ACCESS_KEY=silominio
MINIO_SECRET_KEY=silominiosecret
# Silo API (optional overrides)
# SILO_SERVER_PORT=8080
# OpenLDAP
LDAP_ADMIN_PASSWORD=ldapadmin
LDAP_USERS=siloadmin
LDAP_PASSWORDS=siloadmin
# Silo Authentication
SILO_SESSION_SECRET=change-me-in-production
SILO_ADMIN_USERNAME=admin
SILO_ADMIN_PASSWORD=admin
# Optional: OIDC (Keycloak)
# SILO_OIDC_CLIENT_SECRET=
# Optional: LDAP service account
# SILO_LDAP_BIND_PASSWORD=

1
.gitignore vendored
View File

@@ -29,6 +29,7 @@ Thumbs.db
# Config with secrets
config.yaml
*.env
deployments/config.docker.yaml
# Python
__pycache__/

View File

@@ -53,15 +53,20 @@ silo/
## Quick Start
```bash
# Docker Compose (quickest)
cp config.example.yaml config.yaml
# Edit config.yaml with your database, MinIO, and auth settings
make docker-up
See the **[Installation Guide](docs/INSTALL.md)** for complete setup instructions.
# Or manual setup
psql -h localhost -U silo -d silo -f migrations/*.sql
go run ./cmd/silod -config config.yaml
**Docker Compose (quickest — includes PostgreSQL, MinIO, OpenLDAP, and Silo):**
```bash
./scripts/setup-docker.sh
docker compose -f deployments/docker-compose.allinone.yaml up -d
```
**Development (local Go + Docker services):**
```bash
make docker-up # Start PostgreSQL + MinIO in Docker
make run # Run silo locally with Go
```
When auth is enabled, a default admin account is created on first startup using the credentials in `config.yaml` under `auth.local.default_admin_username` and `auth.local.default_admin_password`.
@@ -104,9 +109,10 @@ The server provides the REST API and ODS endpoints consumed by these clients.
| Document | Description |
|----------|-------------|
| [docs/INSTALL.md](docs/INSTALL.md) | Installation guide (Docker Compose and daemon) |
| [docs/SPECIFICATION.md](docs/SPECIFICATION.md) | Full design specification and API reference |
| [docs/STATUS.md](docs/STATUS.md) | Implementation status |
| [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md) | Production deployment guide |
| [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md) | Production deployment and operations guide |
| [docs/CONFIGURATION.md](docs/CONFIGURATION.md) | Configuration reference (all `config.yaml` options) |
| [docs/AUTH.md](docs/AUTH.md) | Authentication system design |
| [docs/AUTH_USER_GUIDE.md](docs/AUTH_USER_GUIDE.md) | User guide for login, tokens, and roles |

View File

@@ -8,20 +8,20 @@ server:
# read_only: false # Reject all write operations; toggle at runtime with SIGUSR1
database:
host: "psql.example.internal"
host: "localhost" # Use "postgres" for Docker Compose
port: 5432
name: "silo"
user: "silo"
password: "" # Use SILO_DB_PASSWORD env var
sslmode: "require"
sslmode: "require" # Use "disable" for Docker Compose (internal network)
max_connections: 10
storage:
endpoint: "minio.example.internal:9000"
endpoint: "localhost:9000" # Use "minio:9000" for Docker Compose
access_key: "" # Use SILO_MINIO_ACCESS_KEY env var
secret_key: "" # Use SILO_MINIO_SECRET_KEY env var
bucket: "silo-files"
use_ssl: true
use_ssl: true # Use false for Docker Compose (internal network)
region: "us-east-1"
schemas:

View File

@@ -0,0 +1,35 @@
# Silo Development Configuration
# Used by deployments/docker-compose.yaml — works with zero setup via `make docker-up`.
# For production Docker installs, run scripts/setup-docker.sh instead.
server:
host: "0.0.0.0"
port: 8080
base_url: "http://localhost:8080"
database:
host: "postgres"
port: 5432
name: "silo"
user: "silo"
password: "${POSTGRES_PASSWORD:-silodev}"
sslmode: "disable"
max_connections: 10
storage:
endpoint: "minio:9000"
access_key: "${MINIO_ACCESS_KEY:-silominio}"
secret_key: "${MINIO_SECRET_KEY:-silominiosecret}"
bucket: "silo-files"
use_ssl: false
region: "us-east-1"
schemas:
directory: "/etc/silo/schemas"
default: "kindred-rd"
freecad:
uri_scheme: "silo"
auth:
enabled: false

View File

@@ -0,0 +1,172 @@
# Silo All-in-One Stack
# PostgreSQL + MinIO + OpenLDAP + Silo API + Nginx (optional)
#
# Quick start:
# ./scripts/setup-docker.sh
# docker compose -f deployments/docker-compose.allinone.yaml up -d
#
# With nginx reverse proxy:
# docker compose -f deployments/docker-compose.allinone.yaml --profile nginx up -d
#
# View logs:
# docker compose -f deployments/docker-compose.allinone.yaml logs -f
#
# Stop:
# docker compose -f deployments/docker-compose.allinone.yaml down
#
# Stop and delete data:
# docker compose -f deployments/docker-compose.allinone.yaml down -v
services:
# ---------------------------------------------------------------------------
# PostgreSQL 16
# ---------------------------------------------------------------------------
postgres:
image: postgres:16-alpine
container_name: silo-postgres
restart: unless-stopped
environment:
POSTGRES_DB: silo
POSTGRES_USER: silo
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Run ./scripts/setup-docker.sh first}
volumes:
- postgres_data:/var/lib/postgresql/data
- ../migrations:/docker-entrypoint-initdb.d:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U silo -d silo"]
interval: 5s
timeout: 5s
retries: 5
networks:
- silo-net
# ---------------------------------------------------------------------------
# MinIO (S3-compatible object storage)
# ---------------------------------------------------------------------------
minio:
image: minio/minio:latest
container_name: silo-minio
restart: unless-stopped
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: ${MINIO_ACCESS_KEY:?Run ./scripts/setup-docker.sh first}
MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY:?Run ./scripts/setup-docker.sh first}
volumes:
- minio_data:/data
ports:
- "9001:9001" # MinIO console (remove in hardened setups)
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 10s
timeout: 5s
retries: 5
networks:
- silo-net
# ---------------------------------------------------------------------------
# OpenLDAP (user directory for LDAP authentication)
# ---------------------------------------------------------------------------
openldap:
image: bitnami/openldap:2.6
container_name: silo-openldap
restart: unless-stopped
environment:
LDAP_ROOT: "dc=silo,dc=local"
LDAP_ADMIN_USERNAME: "admin"
LDAP_ADMIN_PASSWORD: ${LDAP_ADMIN_PASSWORD:?Run ./scripts/setup-docker.sh first}
LDAP_USERS: ${LDAP_USERS:-siloadmin}
LDAP_PASSWORDS: ${LDAP_PASSWORDS:?Run ./scripts/setup-docker.sh first}
LDAP_GROUP: "silo-users"
LDAP_USER_OU: "users"
LDAP_GROUP_OU: "groups"
volumes:
- openldap_data:/bitnami/openldap
- ./ldap:/docker-entrypoint-initdb.d:ro
ports:
- "1389:1389" # LDAP access for debugging (remove in hardened setups)
healthcheck:
test: ["CMD-SHELL", "ldapsearch -x -H ldap://localhost:1389 -b dc=silo,dc=local -D cn=admin,dc=silo,dc=local -w $${LDAP_ADMIN_PASSWORD} '(objectClass=organization)' >/dev/null 2>&1"]
interval: 10s
timeout: 5s
retries: 5
networks:
- silo-net
# ---------------------------------------------------------------------------
# Silo API Server
# ---------------------------------------------------------------------------
silo:
build:
context: ..
dockerfile: build/package/Dockerfile
container_name: silo-api
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
minio:
condition: service_healthy
openldap:
condition: service_healthy
env_file:
- .env
environment:
# These override values in config.docker.yaml via the Go config loader's
# direct env var support (see internal/config/config.go).
SILO_DB_HOST: postgres
SILO_DB_NAME: silo
SILO_DB_USER: silo
SILO_DB_PASSWORD: ${POSTGRES_PASSWORD}
SILO_MINIO_ENDPOINT: minio:9000
SILO_MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY}
SILO_MINIO_SECRET_KEY: ${MINIO_SECRET_KEY}
ports:
- "${SILO_PORT:-8080}:8080"
volumes:
- ../schemas:/etc/silo/schemas:ro
- ./config.docker.yaml:/etc/silo/config.yaml:ro
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:8080/health"]
interval: 10s
timeout: 5s
retries: 3
start_period: 15s
networks:
- silo-net
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
# ---------------------------------------------------------------------------
# Nginx reverse proxy (optional — enable with --profile nginx)
# ---------------------------------------------------------------------------
nginx:
image: nginx:alpine
container_name: silo-nginx
restart: unless-stopped
profiles:
- nginx
depends_on:
silo:
condition: service_healthy
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
# Uncomment to mount TLS certificates:
# - /path/to/cert.pem:/etc/nginx/ssl/cert.pem:ro
# - /path/to/key.pem:/etc/nginx/ssl/key.pem:ro
networks:
- silo-net
volumes:
postgres_data:
minio_data:
openldap_data:
networks:
silo-net:
driver: bridge

View File

@@ -16,22 +16,22 @@ services:
restart: unless-stopped
environment:
# Database connection (psql.example.internal)
# Supported as direct env var overrides in the Go config loader:
SILO_DB_HOST: psql.example.internal
SILO_DB_PORT: 5432
SILO_DB_NAME: silo
SILO_DB_USER: silo
SILO_DB_PASSWORD: ${SILO_DB_PASSWORD:?Database password required}
SILO_DB_SSLMODE: require
# Note: SILO_DB_PORT and SILO_DB_SSLMODE are NOT supported as direct
# env var overrides. Set these in config.yaml instead, or use ${VAR}
# syntax in the YAML file. See docs/CONFIGURATION.md for details.
# MinIO storage (minio.example.internal)
# Supported as direct env var overrides:
SILO_MINIO_ENDPOINT: minio.example.internal:9000
SILO_MINIO_ACCESS_KEY: ${SILO_MINIO_ACCESS_KEY:?MinIO access key required}
SILO_MINIO_SECRET_KEY: ${SILO_MINIO_SECRET_KEY:?MinIO secret key required}
SILO_MINIO_BUCKET: silo-files
SILO_MINIO_USE_SSL: "true"
# Server settings
SILO_SERVER_BASE_URL: ${SILO_BASE_URL:-http://silo.example.internal:8080}
# Note: SILO_MINIO_BUCKET and SILO_MINIO_USE_SSL are NOT supported as
# direct env var overrides. Set these in config.yaml instead.
ports:
- "8080:8080"
volumes:

View File

@@ -69,7 +69,7 @@ services:
- "8080:8080"
volumes:
- ../schemas:/etc/silo/schemas:ro
- ../configs/config.yaml:/etc/silo/config.yaml:ro
- ./config.dev.yaml:/etc/silo/config.yaml:ro
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:8080/health"]
interval: 10s

View File

@@ -0,0 +1,36 @@
# Enable the memberOf overlay for OpenLDAP.
# When a user is added to a groupOfNames, their entry automatically
# gets a memberOf attribute pointing to the group DN.
# This is required for Silo's LDAP role mapping.
#
# Loaded automatically by bitnami/openldap from /docker-entrypoint-initdb.d/
dn: cn=module{0},cn=config
changetype: modify
add: olcModuleLoad
olcModuleLoad: memberof
dn: olcOverlay=memberof,olcDatabase={2}mdb,cn=config
changetype: add
objectClass: olcOverlayConfig
objectClass: olcMemberOf
olcOverlay: memberof
olcMemberOfRefInt: TRUE
olcMemberOfDangling: ignore
olcMemberOfGroupOC: groupOfNames
olcMemberOfMemberAD: member
olcMemberOfMemberOfAD: memberOf
# Enable refint overlay to maintain referential integrity
# (removes memberOf when a user is removed from a group)
dn: cn=module{0},cn=config
changetype: modify
add: olcModuleLoad
olcModuleLoad: refint
dn: olcOverlay=refint,olcDatabase={2}mdb,cn=config
changetype: add
objectClass: olcOverlayConfig
objectClass: olcRefintConfig
olcOverlay: refint
olcRefintAttribute: memberOf member

View File

@@ -0,0 +1,34 @@
# Create Silo role groups for LDAP-based access control.
# These groups map to Silo roles via auth.ldap.role_mapping in config.yaml.
#
# Group hierarchy:
# silo-admins -> admin role (full access)
# silo-users -> editor role (create/modify items)
# silo-viewers -> viewer role (read-only)
#
# The initial LDAP user (set via LDAP_USERS env var) is added to silo-admins.
# Additional users can be added with ldapadd or ldapmodify.
#
# Loaded automatically by bitnami/openldap from /docker-entrypoint-initdb.d/
# Note: This runs after the default tree is created (users/groups OUs exist).
# Admin group — initial user is a member
dn: cn=silo-admins,ou=groups,dc=silo,dc=local
objectClass: groupOfNames
cn: silo-admins
description: Silo administrators (full access)
member: cn=siloadmin,ou=users,dc=silo,dc=local
# Editor group
dn: cn=silo-users,ou=groups,dc=silo,dc=local
objectClass: groupOfNames
cn: silo-users
description: Silo editors (create and modify items)
member: cn=placeholder,ou=users,dc=silo,dc=local
# Viewer group
dn: cn=silo-viewers,ou=groups,dc=silo,dc=local
objectClass: groupOfNames
cn: silo-viewers
description: Silo viewers (read-only access)
member: cn=placeholder,ou=users,dc=silo,dc=local

View File

@@ -0,0 +1,44 @@
# Silo Nginx Reverse Proxy — HTTP only (no TLS)
#
# Use this when TLS is terminated by an external load balancer or when
# running on a trusted internal network without HTTPS.
upstream silo_backend {
server silo:8080;
}
server {
listen 80;
listen [::]:80;
server_name _;
location / {
proxy_pass http://silo_backend;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
# SSE support
proxy_set_header Connection "";
proxy_buffering off;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 300s;
# File uploads (CAD files can be large)
client_max_body_size 100M;
}
location /nginx-health {
access_log off;
return 200 "OK\n";
add_header Content-Type text/plain;
}
}

View File

@@ -0,0 +1,103 @@
# Silo Nginx Reverse Proxy (Docker)
#
# HTTP reverse proxy with optional HTTPS. To enable TLS:
# 1. Uncomment the ssl server block below
# 2. Mount your certificate and key in docker-compose:
# volumes:
# - /path/to/cert.pem:/etc/nginx/ssl/cert.pem:ro
# - /path/to/key.pem:/etc/nginx/ssl/key.pem:ro
# 3. Uncomment the HTTP-to-HTTPS redirect in the port 80 block
upstream silo_backend {
server silo:8080;
}
# HTTP server
server {
listen 80;
listen [::]:80;
server_name _;
# Uncomment the next line to redirect all HTTP traffic to HTTPS
# return 301 https://$host$request_uri;
location / {
proxy_pass http://silo_backend;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
# SSE support
proxy_set_header Connection "";
proxy_buffering off;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 300s;
# File uploads (CAD files can be large)
client_max_body_size 100M;
}
# Health check endpoint for monitoring
location /nginx-health {
access_log off;
return 200 "OK\n";
add_header Content-Type text/plain;
}
}
# Uncomment for HTTPS (mount certs in docker-compose volumes)
# server {
# listen 443 ssl http2;
# listen [::]:443 ssl http2;
# server_name _;
#
# ssl_certificate /etc/nginx/ssl/cert.pem;
# ssl_certificate_key /etc/nginx/ssl/key.pem;
#
# ssl_protocols TLSv1.2 TLSv1.3;
# ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
# ssl_prefer_server_ciphers off;
# ssl_session_timeout 1d;
# ssl_session_cache shared:SSL:10m;
# ssl_session_tickets off;
#
# # Security headers
# add_header X-Frame-Options "SAMEORIGIN" always;
# add_header X-Content-Type-Options "nosniff" always;
# add_header Referrer-Policy "strict-origin-when-cross-origin" always;
#
# location / {
# proxy_pass http://silo_backend;
# proxy_http_version 1.1;
#
# proxy_set_header Host $host;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Proto $scheme;
# proxy_set_header X-Forwarded-Host $host;
# proxy_set_header X-Forwarded-Port $server_port;
#
# proxy_set_header Connection "";
# proxy_buffering off;
#
# proxy_connect_timeout 60s;
# proxy_send_timeout 60s;
# proxy_read_timeout 300s;
#
# client_max_body_size 100M;
# }
#
# location /nginx-health {
# access_log off;
# return 200 "OK\n";
# add_header Content-Type text/plain;
# }
# }

View File

@@ -1,5 +1,9 @@
# Silo Production Deployment Guide
> **First-time setup?** See the [Installation Guide](INSTALL.md) for step-by-step
> instructions. This document covers ongoing maintenance and operations for an
> existing deployment.
This guide covers deploying Silo to a dedicated VM using external PostgreSQL and MinIO services.
## Table of Contents

518
docs/INSTALL.md Normal file
View File

@@ -0,0 +1,518 @@
# Installing Silo
This guide covers two installation methods:
- **[Option A: Docker Compose](#option-a-docker-compose)** — self-contained stack with all services. Recommended for evaluation, small teams, and environments where Docker is the standard.
- **[Option B: Daemon Install](#option-b-daemon-install-systemd--external-services)** — systemd service with external PostgreSQL, MinIO, and optional LDAP/nginx. Recommended for production deployments integrated with existing infrastructure.
Both methods produce the same result: a running Silo server with a web UI, REST API, and authentication.
---
## Table of Contents
- [Prerequisites](#prerequisites)
- [Option A: Docker Compose](#option-a-docker-compose)
- [A.1 Prerequisites](#a1-prerequisites)
- [A.2 Clone the Repository](#a2-clone-the-repository)
- [A.3 Run the Setup Script](#a3-run-the-setup-script)
- [A.4 Start the Stack](#a4-start-the-stack)
- [A.5 Verify the Installation](#a5-verify-the-installation)
- [A.6 LDAP Users and Groups](#a6-ldap-users-and-groups)
- [A.7 Optional: Enable Nginx Reverse Proxy](#a7-optional-enable-nginx-reverse-proxy)
- [A.8 Stopping, Starting, and Upgrading](#a8-stopping-starting-and-upgrading)
- [Option B: Daemon Install (systemd + External Services)](#option-b-daemon-install-systemd--external-services)
- [B.1 Architecture Overview](#b1-architecture-overview)
- [B.2 Prerequisites](#b2-prerequisites)
- [B.3 Set Up External Services](#b3-set-up-external-services)
- [B.4 Prepare the Host](#b4-prepare-the-host)
- [B.5 Configure Credentials](#b5-configure-credentials)
- [B.6 Deploy](#b6-deploy)
- [B.7 Set Up Nginx and TLS](#b7-set-up-nginx-and-tls)
- [B.8 Verify the Installation](#b8-verify-the-installation)
- [B.9 Upgrading](#b9-upgrading)
- [Post-Install Configuration](#post-install-configuration)
- [Further Reading](#further-reading)
---
## Prerequisites
Regardless of which method you choose:
- **Git** to clone the repository
- A machine with at least **2 GB RAM** and **10 GB free disk**
- Network access to pull container images or download Go/Node toolchains
---
## Option A: Docker Compose
A single Docker Compose file runs everything: PostgreSQL, MinIO, OpenLDAP, and Silo. An optional nginx container can be enabled for reverse proxying.
### A.1 Prerequisites
- [Docker Engine](https://docs.docker.com/engine/install/) 24+ with the [Compose plugin](https://docs.docker.com/compose/install/) (v2)
- `openssl` (used by the setup script to generate secrets)
Verify your installation:
```bash
docker --version # Docker Engine 24+
docker compose version # Docker Compose v2+
```
### A.2 Clone the Repository
```bash
git clone https://git.kindred-systems.com/kindred/silo.git
cd silo
```
### A.3 Run the Setup Script
The setup script generates credentials and configuration files:
```bash
./scripts/setup-docker.sh
```
It prompts for:
- Server domain (default: `localhost`)
- PostgreSQL password (auto-generated if you press Enter)
- MinIO credentials (auto-generated)
- OpenLDAP admin password and initial user (auto-generated)
- Silo local admin account (fallback when LDAP is unavailable)
For automated/CI environments, use non-interactive mode:
```bash
./scripts/setup-docker.sh --non-interactive
```
The script writes two files:
- `deployments/.env` — secrets for Docker Compose
- `deployments/config.docker.yaml` — Silo server configuration
### A.4 Start the Stack
```bash
docker compose -f deployments/docker-compose.allinone.yaml up -d
```
Wait for all services to become healthy:
```bash
docker compose -f deployments/docker-compose.allinone.yaml ps
```
You should see `silo-postgres`, `silo-minio`, `silo-openldap`, and `silo-api` all in a healthy state.
View logs:
```bash
# All services
docker compose -f deployments/docker-compose.allinone.yaml logs -f
# Silo only
docker compose -f deployments/docker-compose.allinone.yaml logs -f silo
```
### A.5 Verify the Installation
```bash
# Health check
curl http://localhost:8080/health
# Readiness check (includes database and storage connectivity)
curl http://localhost:8080/ready
```
Open http://localhost:8080 in your browser. Log in with either:
- **LDAP account**: the username and password shown by the setup script (default: `siloadmin`)
- **Local admin**: the local admin credentials shown by the setup script (default: `admin`)
The credentials were printed at the end of the setup script output and are stored in `deployments/.env`.
### A.6 LDAP Users and Groups
The Docker stack includes an OpenLDAP server with three preconfigured groups that map to Silo roles:
| LDAP Group | Silo Role | Access Level |
|------------|-----------|-------------|
| `cn=silo-admins,ou=groups,dc=silo,dc=local` | admin | Full access |
| `cn=silo-users,ou=groups,dc=silo,dc=local` | editor | Create and modify items |
| `cn=silo-viewers,ou=groups,dc=silo,dc=local` | viewer | Read-only |
The initial LDAP user (default: `siloadmin`) is added to `silo-admins`.
**Add a new LDAP user:**
```bash
# From the host (using the exposed port)
ldapadd -x -H ldap://localhost:1389 \
-D "cn=admin,dc=silo,dc=local" \
-w "YOUR_LDAP_ADMIN_PASSWORD" << EOF
dn: cn=jdoe,ou=users,dc=silo,dc=local
objectClass: inetOrgPerson
cn: jdoe
sn: Doe
userPassword: changeme
mail: jdoe@example.com
EOF
```
**Add a user to a group:**
```bash
ldapmodify -x -H ldap://localhost:1389 \
-D "cn=admin,dc=silo,dc=local" \
-w "YOUR_LDAP_ADMIN_PASSWORD" << EOF
dn: cn=silo-users,ou=groups,dc=silo,dc=local
changetype: modify
add: member
member: cn=jdoe,ou=users,dc=silo,dc=local
EOF
```
**List all users:**
```bash
ldapsearch -x -H ldap://localhost:1389 \
-b "ou=users,dc=silo,dc=local" \
-D "cn=admin,dc=silo,dc=local" \
-w "YOUR_LDAP_ADMIN_PASSWORD" "(objectClass=inetOrgPerson)" cn mail memberOf
```
### A.7 Optional: Enable Nginx Reverse Proxy
To place nginx in front of Silo (for TLS termination or to serve on port 80):
```bash
docker compose -f deployments/docker-compose.allinone.yaml --profile nginx up -d
```
By default nginx listens on ports 80 and 443 and proxies to the Silo container. The configuration is at `deployments/nginx/nginx.conf`.
**To enable HTTPS**, edit `deployments/docker-compose.allinone.yaml` and uncomment the TLS certificate volume mounts in the `nginx` service, then uncomment the HTTPS server block in `deployments/nginx/nginx.conf`. See the comments in those files for details.
If you already have your own reverse proxy or load balancer, skip the nginx profile and point your proxy at port 8080.
### A.8 Stopping, Starting, and Upgrading
```bash
# Stop the stack (data is preserved in Docker volumes)
docker compose -f deployments/docker-compose.allinone.yaml down
# Start again
docker compose -f deployments/docker-compose.allinone.yaml up -d
# Stop and delete all data (WARNING: destroys database, files, and LDAP data)
docker compose -f deployments/docker-compose.allinone.yaml down -v
```
**To upgrade to a newer version:**
```bash
cd silo
git pull
docker compose -f deployments/docker-compose.allinone.yaml up -d --build
```
The Silo container is rebuilt from the updated source. Database migrations in `migrations/` are applied automatically on container startup via the PostgreSQL init mechanism.
---
## Option B: Daemon Install (systemd + External Services)
This method runs Silo as a systemd service on a dedicated host, connecting to externally managed PostgreSQL, MinIO, and optionally LDAP services.
### B.1 Architecture Overview
```
┌──────────────────────┐
│ Silo Host │
│ ┌────────────────┐ │
HTTPS (443) ──►│ │ nginx │ │
│ └───────┬────────┘ │
│ │ :8080 │
│ ┌───────▼────────┐ │
│ │ silod │ │
│ │ (API server) │ │
│ └──┬─────────┬───┘ │
└─────┼─────────┼──────┘
│ │
┌───────────▼──┐ ┌───▼──────────────┐
│ PostgreSQL 16│ │ MinIO (S3) │
│ :5432 │ │ :9000 API │
└──────────────┘ │ :9001 Console │
└──────────────────┘
```
### B.2 Prerequisites
- Linux host (Debian/Ubuntu or RHEL/Fedora/AlmaLinux)
- Root or sudo access
- Network access to your PostgreSQL and MinIO servers
The setup script installs Go and other build dependencies automatically.
### B.3 Set Up External Services
#### PostgreSQL 16
Install PostgreSQL and create the Silo database:
- [PostgreSQL downloads](https://www.postgresql.org/download/)
```bash
# After installing PostgreSQL, create the database and user:
sudo -u postgres createuser silo
sudo -u postgres createdb -O silo silo
sudo -u postgres psql -c "ALTER USER silo WITH PASSWORD 'your-password';"
```
Ensure the Silo host can connect (check `pg_hba.conf` on the PostgreSQL server).
Verify:
```bash
psql -h YOUR_PG_HOST -U silo -d silo -c 'SELECT 1'
```
#### MinIO
Install MinIO and create a bucket and service account:
- [MinIO quickstart](https://min.io/docs/minio/linux/index.html)
```bash
# Using the MinIO client (mc):
mc alias set local http://YOUR_MINIO_HOST:9000 minioadmin minioadmin
mc mb local/silo-files
mc admin user add local silouser YOUR_MINIO_SECRET
mc admin policy attach local readwrite --user silouser
```
Verify:
```bash
curl -I http://YOUR_MINIO_HOST:9000/minio/health/live
```
#### LDAP / FreeIPA (Optional)
For LDAP authentication, you need an LDAP server with user and group entries. Options:
- [FreeIPA](https://www.freeipa.org/page/Quick_Start_Guide) — full identity management (recommended for organizations already using it)
- [OpenLDAP](https://www.openldap.org/doc/admin26/) — lightweight LDAP server
Silo needs:
- A base DN (e.g., `dc=example,dc=com`)
- Users under a known OU (e.g., `cn=users,cn=accounts,dc=example,dc=com`)
- Groups that map to Silo roles (`admin`, `editor`, `viewer`)
- The `memberOf` overlay enabled (so user entries have `memberOf` attributes)
See [CONFIGURATION.md — LDAP](CONFIGURATION.md#ldap--freeipa) for the full LDAP configuration reference.
### B.4 Prepare the Host
Run the setup script on the target host:
```bash
# Copy and run the script
scp scripts/setup-host.sh root@YOUR_HOST:/tmp/
ssh root@YOUR_HOST 'bash /tmp/setup-host.sh'
```
Or directly on the host:
```bash
sudo bash scripts/setup-host.sh
```
The script:
1. Installs dependencies (git, Go 1.24)
2. Creates the `silo` system user
3. Creates directories (`/opt/silo`, `/etc/silo`)
4. Clones the repository
5. Creates the environment file template
To override the default service hostnames:
```bash
SILO_DB_HOST=db.example.com SILO_MINIO_HOST=s3.example.com sudo -E bash scripts/setup-host.sh
```
### B.5 Configure Credentials
Edit the environment file with your service credentials:
```bash
sudo nano /etc/silo/silod.env
```
```bash
# Database
SILO_DB_PASSWORD=your-database-password
# MinIO
SILO_MINIO_ACCESS_KEY=silouser
SILO_MINIO_SECRET_KEY=your-minio-secret
# Authentication
SILO_SESSION_SECRET=generate-a-long-random-string
SILO_ADMIN_USERNAME=admin
SILO_ADMIN_PASSWORD=your-admin-password
```
Generate a session secret:
```bash
openssl rand -hex 32
```
Review the server configuration:
```bash
sudo nano /etc/silo/config.yaml
```
Update `database.host`, `storage.endpoint`, `server.base_url`, and authentication settings for your environment. See [CONFIGURATION.md](CONFIGURATION.md) for all options.
### B.6 Deploy
Run the deploy script:
```bash
sudo /opt/silo/src/scripts/deploy.sh
```
The script:
1. Pulls latest code from git
2. Builds the `silod` binary and React frontend
3. Installs files to `/opt/silo` and `/etc/silo`
4. Runs database migrations
5. Installs and starts the systemd service
Deploy options:
```bash
# Skip git pull (use current checkout)
sudo /opt/silo/src/scripts/deploy.sh --no-pull
# Skip build (use existing binary)
sudo /opt/silo/src/scripts/deploy.sh --no-build
# Just restart the service
sudo /opt/silo/src/scripts/deploy.sh --restart-only
# Check service status
sudo /opt/silo/src/scripts/deploy.sh --status
```
To override the target host or database host:
```bash
SILO_DEPLOY_TARGET=silo.example.com SILO_DB_HOST=db.example.com sudo -E scripts/deploy.sh
```
### B.7 Set Up Nginx and TLS
#### With FreeIPA (automated)
If your organization uses FreeIPA, the included script handles nginx setup, IPA enrollment, and certificate issuance:
```bash
sudo /opt/silo/src/scripts/setup-ipa-nginx.sh
```
Override the hostname if needed:
```bash
SILO_HOSTNAME=silo.example.com sudo -E /opt/silo/src/scripts/setup-ipa-nginx.sh
```
The script installs nginx, enrolls the host in FreeIPA, requests a TLS certificate from the IPA CA (auto-renewed by certmonger), and configures nginx as an HTTPS reverse proxy.
#### Manual nginx setup
Install nginx and create a config:
```bash
sudo apt install nginx # or: sudo dnf install nginx
```
Use the template at `deployments/nginx/nginx.conf` as a starting point. Copy it to `/etc/nginx/sites-available/silo`, update the `server_name` and certificate paths, then enable it:
```bash
sudo ln -sf /etc/nginx/sites-available/silo /etc/nginx/sites-enabled/silo
sudo nginx -t
sudo systemctl reload nginx
```
After enabling HTTPS, update `server.base_url` in `/etc/silo/config.yaml` to use `https://` and restart Silo:
```bash
sudo systemctl restart silod
```
### B.8 Verify the Installation
```bash
# Service status
sudo systemctl status silod
# Health check
curl http://localhost:8080/health
# Readiness check
curl http://localhost:8080/ready
# Follow logs
sudo journalctl -u silod -f
```
Open your configured base URL in a browser and log in.
### B.9 Upgrading
```bash
# Pull latest code and redeploy
sudo /opt/silo/src/scripts/deploy.sh
# Or deploy a specific version
cd /opt/silo/src
git fetch --all --tags
git checkout v1.2.3
sudo /opt/silo/src/scripts/deploy.sh --no-pull
```
New database migrations are applied automatically during deployment.
---
## Post-Install Configuration
After a successful installation:
- **Authentication**: Configure LDAP, OIDC, or local auth backends. See [CONFIGURATION.md — Authentication](CONFIGURATION.md#authentication).
- **Schemas**: Part numbering schemas are loaded from YAML files. See the `schemas/` directory and [CONFIGURATION.md — Schemas](CONFIGURATION.md#schemas).
- **Read-only mode**: Toggle write protection at runtime with `kill -USR1 $(pidof silod)` or by setting `server.read_only: true` in the config.
- **Ongoing maintenance**: See [DEPLOYMENT.md](DEPLOYMENT.md) for service management, log viewing, troubleshooting, and the security checklist.
---
## Further Reading
| Document | Description |
|----------|-------------|
| [CONFIGURATION.md](CONFIGURATION.md) | Complete `config.yaml` reference |
| [DEPLOYMENT.md](DEPLOYMENT.md) | Operations guide: maintenance, troubleshooting, security |
| [AUTH.md](AUTH.md) | Authentication system design |
| [AUTH_USER_GUIDE.md](AUTH_USER_GUIDE.md) | User guide for login, tokens, and roles |
| [SPECIFICATION.md](SPECIFICATION.md) | Full design specification and API reference |
| [STATUS.md](STATUS.md) | Implementation status |
| [GAP_ANALYSIS.md](GAP_ANALYSIS.md) | Gap analysis and revision control roadmap |
| [COMPONENT_AUDIT.md](COMPONENT_AUDIT.md) | Component audit tool design |

View File

@@ -1,18 +1,23 @@
#!/bin/bash
# Deploy Silo to silo.example.internal
# Deploy Silo to a target host
#
# Usage: ./scripts/deploy.sh [host]
# host defaults to silo.example.internal
# host defaults to SILO_DEPLOY_TARGET env var, or silo.example.internal
#
# Prerequisites:
# - SSH access to the target host
# - /etc/silo/silod.env must exist on target with credentials filled in
# - PostgreSQL reachable from target at psql.example.internal
# - MinIO reachable from target at minio.example.internal
# - PostgreSQL reachable from target (set SILO_DB_HOST to override)
# - MinIO reachable from target (set SILO_MINIO_HOST to override)
#
# Environment variables:
# SILO_DEPLOY_TARGET - target host (default: silo.example.internal)
# SILO_DB_HOST - PostgreSQL host (default: psql.example.internal)
set -euo pipefail
TARGET="${1:-silo.example.internal}"
TARGET="${1:-${SILO_DEPLOY_TARGET:-silo.example.internal}}"
DB_HOST="${SILO_DB_HOST:-psql.example.internal}"
DEPLOY_DIR="/opt/silo"
CONFIG_DIR="/etc/silo"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@@ -104,7 +109,7 @@ echo " Files installed to $DEPLOY_DIR"
REMOTE
echo "[6/6] Running migrations and starting service..."
ssh "$TARGET" bash -s <<'REMOTE'
ssh "$TARGET" DB_HOST="$DB_HOST" bash -s <<'REMOTE'
set -euo pipefail
DEPLOY_DIR="/opt/silo"
@@ -123,14 +128,14 @@ if command -v psql &>/dev/null && [ -n "${SILO_DB_PASSWORD:-}" ]; then
for f in "$DEPLOY_DIR/migrations/"*.sql; do
echo " $(basename "$f")"
PGPASSWORD="$SILO_DB_PASSWORD" psql \
-h psql.example.internal -p 5432 \
-h "$DB_HOST" -p 5432 \
-U silo -d silo \
-f "$f" -q 2>&1 | grep -v "already exists" || true
done
echo " Migrations complete."
else
echo " WARNING: psql not available or SILO_DB_PASSWORD not set, skipping migrations."
echo " Run migrations manually: PGPASSWORD=... psql -h psql.example.internal -U silo -d silo -f /opt/silo/migrations/NNN_name.sql"
echo " Run migrations manually: PGPASSWORD=... psql -h $DB_HOST -U silo -d silo -f /opt/silo/migrations/NNN_name.sql"
fi
# Start service

344
scripts/setup-docker.sh Executable file
View File

@@ -0,0 +1,344 @@
#!/usr/bin/env bash
#
# Silo Docker Setup Script
# Generates .env and config.docker.yaml for the all-in-one Docker Compose stack.
#
# Usage:
# ./scripts/setup-docker.sh # interactive
# ./scripts/setup-docker.sh --non-interactive # use defaults / env vars
# ./scripts/setup-docker.sh --domain silo.example.com
# ./scripts/setup-docker.sh --with-nginx
#
# Output:
# deployments/.env - Docker Compose environment variables
# deployments/config.docker.yaml - Silo server configuration
set -euo pipefail
# Colors (disabled if not a terminal)
if [[ -t 1 ]]; then
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
BOLD='\033[1m'
NC='\033[0m'
else
RED='' GREEN='' YELLOW='' BLUE='' BOLD='' NC=''
fi
log_info() { echo -e "${BLUE}[INFO]${NC} $*"; }
log_success() { echo -e "${GREEN}[OK]${NC} $*"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
log_error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
# ---------------------------------------------------------------------------
# Defaults
# ---------------------------------------------------------------------------
DOMAIN="localhost"
NON_INTERACTIVE=false
WITH_NGINX=false
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="${SCRIPT_DIR}/.."
OUTPUT_DIR="${PROJECT_DIR}/deployments"
# ---------------------------------------------------------------------------
# Parse arguments
# ---------------------------------------------------------------------------
while [[ $# -gt 0 ]]; do
case "$1" in
--non-interactive) NON_INTERACTIVE=true; shift ;;
--domain) DOMAIN="$2"; shift 2 ;;
--with-nginx) WITH_NGINX=true; shift ;;
--output-dir) OUTPUT_DIR="$2"; shift 2 ;;
-h|--help)
echo "Usage: $0 [OPTIONS]"
echo ""
echo "Options:"
echo " --non-interactive Use defaults and env vars, no prompts"
echo " --domain DOMAIN Server hostname (default: localhost)"
echo " --with-nginx Print instructions for the nginx profile"
echo " --output-dir DIR Output directory (default: ./deployments)"
echo " -h, --help Show this help"
exit 0
;;
*) log_error "Unknown option: $1"; exit 1 ;;
esac
done
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
generate_secret() {
local len="${1:-32}"
openssl rand -hex "$len" 2>/dev/null \
|| head -c "$len" /dev/urandom | od -An -tx1 | tr -d ' \n'
}
prompt() {
local var_name="$1" prompt_text="$2" default="$3"
if [[ "$NON_INTERACTIVE" == "true" ]]; then
eval "$var_name=\"$default\""
return
fi
local input
read -r -p "$(echo -e "${BOLD}${prompt_text}${NC} [${default}]: ")" input
eval "$var_name=\"${input:-$default}\""
}
prompt_secret() {
local var_name="$1" prompt_text="$2" default="$3"
if [[ "$NON_INTERACTIVE" == "true" ]]; then
eval "$var_name=\"$default\""
return
fi
local input
read -r -p "$(echo -e "${BOLD}${prompt_text}${NC} [auto-generated]: ")" input
eval "$var_name=\"${input:-$default}\""
}
# ---------------------------------------------------------------------------
# Banner
# ---------------------------------------------------------------------------
echo ""
echo -e "${BOLD}Silo Docker Setup${NC}"
echo "Generates configuration for the all-in-one Docker Compose stack."
echo ""
# Check for existing files
if [[ -f "${OUTPUT_DIR}/.env" ]]; then
log_warn "deployments/.env already exists."
if [[ "$NON_INTERACTIVE" == "false" ]]; then
read -r -p "Overwrite? [y/N]: " overwrite
if [[ "${overwrite,,}" != "y" ]]; then
log_info "Aborted."
exit 0
fi
fi
fi
# ---------------------------------------------------------------------------
# Gather configuration
# ---------------------------------------------------------------------------
log_info "Gathering configuration..."
echo ""
# Domain / base URL
prompt DOMAIN "Server domain" "$DOMAIN"
if [[ "$WITH_NGINX" == "true" ]]; then
BASE_URL="http://${DOMAIN}"
elif [[ "$DOMAIN" == "localhost" ]]; then
BASE_URL="http://localhost:8080"
else
BASE_URL="http://${DOMAIN}:8080"
fi
# PostgreSQL
PG_PASSWORD_DEFAULT="$(generate_secret 16)"
prompt_secret POSTGRES_PASSWORD "PostgreSQL password" "$PG_PASSWORD_DEFAULT"
# MinIO
MINIO_AK_DEFAULT="$(generate_secret 10)"
MINIO_SK_DEFAULT="$(generate_secret 16)"
prompt_secret MINIO_ACCESS_KEY "MinIO access key" "$MINIO_AK_DEFAULT"
prompt_secret MINIO_SECRET_KEY "MinIO secret key" "$MINIO_SK_DEFAULT"
# OpenLDAP
LDAP_ADMIN_PW_DEFAULT="$(generate_secret 16)"
prompt_secret LDAP_ADMIN_PASSWORD "LDAP admin password" "$LDAP_ADMIN_PW_DEFAULT"
prompt LDAP_USERS "LDAP initial username" "siloadmin"
LDAP_USER_PW_DEFAULT="$(generate_secret 12)"
prompt_secret LDAP_PASSWORDS "LDAP initial user password" "$LDAP_USER_PW_DEFAULT"
# Session secret
SESSION_SECRET="$(generate_secret 32)"
# Silo local admin (fallback when LDAP is unavailable)
prompt SILO_ADMIN_USERNAME "Silo local admin username" "admin"
ADMIN_PW_DEFAULT="$(generate_secret 12)"
prompt_secret SILO_ADMIN_PASSWORD "Silo local admin password" "$ADMIN_PW_DEFAULT"
echo ""
# ---------------------------------------------------------------------------
# Write .env
# ---------------------------------------------------------------------------
log_info "Writing ${OUTPUT_DIR}/.env ..."
cat > "${OUTPUT_DIR}/.env" << EOF
# Generated by setup-docker.sh on $(date +%Y-%m-%d)
# Used by: docker compose -f deployments/docker-compose.allinone.yaml
# PostgreSQL
POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
# MinIO
MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}
MINIO_SECRET_KEY=${MINIO_SECRET_KEY}
# OpenLDAP
LDAP_ADMIN_PASSWORD=${LDAP_ADMIN_PASSWORD}
LDAP_USERS=${LDAP_USERS}
LDAP_PASSWORDS=${LDAP_PASSWORDS}
# Silo
SILO_SESSION_SECRET=${SESSION_SECRET}
SILO_ADMIN_USERNAME=${SILO_ADMIN_USERNAME}
SILO_ADMIN_PASSWORD=${SILO_ADMIN_PASSWORD}
SILO_BASE_URL=${BASE_URL}
# Uncomment if using OIDC (Keycloak)
# SILO_OIDC_CLIENT_SECRET=
EOF
chmod 600 "${OUTPUT_DIR}/.env"
log_success "deployments/.env written"
# ---------------------------------------------------------------------------
# Write config.docker.yaml
# ---------------------------------------------------------------------------
log_info "Writing ${OUTPUT_DIR}/config.docker.yaml ..."
# Note: Values wrapped in ${VAR} (inside the single-quoted YAMLEOF blocks)
# are NOT expanded by bash — they are written literally into the YAML file
# and expanded at runtime by the Go config loader via os.ExpandEnv().
# The base_url and cors origin use the bash variable directly since
# SILO_SERVER_BASE_URL is not a supported direct override in the Go loader.
{
cat << 'YAMLEOF'
# Silo Configuration — Docker Compose (all-in-one)
# Generated by scripts/setup-docker.sh
#
# Values using ${VAR} syntax are expanded from environment variables at
# startup. Direct env var overrides (SILO_DB_PASSWORD, etc.) take precedence
# over YAML values — see docs/CONFIGURATION.md for the full reference.
server:
host: "0.0.0.0"
port: 8080
YAMLEOF
cat << EOF
base_url: "${BASE_URL}"
EOF
cat << 'YAMLEOF'
database:
host: "postgres"
port: 5432
name: "silo"
user: "silo"
password: "${SILO_DB_PASSWORD}"
sslmode: "disable"
max_connections: 10
storage:
endpoint: "minio:9000"
access_key: "${SILO_MINIO_ACCESS_KEY}"
secret_key: "${SILO_MINIO_SECRET_KEY}"
bucket: "silo-files"
use_ssl: false
region: "us-east-1"
schemas:
directory: "/etc/silo/schemas"
default: "kindred-rd"
freecad:
uri_scheme: "silo"
auth:
enabled: true
session_secret: "${SILO_SESSION_SECRET}"
# Local accounts (fallback when LDAP is unavailable)
local:
enabled: true
default_admin_username: "${SILO_ADMIN_USERNAME}"
default_admin_password: "${SILO_ADMIN_PASSWORD}"
# OpenLDAP (provided by the Docker Compose stack)
ldap:
enabled: true
url: "ldap://openldap:1389"
base_dn: "dc=silo,dc=local"
user_search_dn: "ou=users,dc=silo,dc=local"
user_attr: "cn"
email_attr: "mail"
display_attr: "cn"
group_attr: "memberOf"
role_mapping:
admin:
- "cn=silo-admins,ou=groups,dc=silo,dc=local"
editor:
- "cn=silo-users,ou=groups,dc=silo,dc=local"
viewer:
- "cn=silo-viewers,ou=groups,dc=silo,dc=local"
tls_skip_verify: false
oidc:
enabled: false
cors:
allowed_origins:
YAMLEOF
cat << EOF
- "${BASE_URL}"
EOF
} > "${OUTPUT_DIR}/config.docker.yaml"
log_success "deployments/config.docker.yaml written"
# ---------------------------------------------------------------------------
# Summary
# ---------------------------------------------------------------------------
echo ""
echo -e "${BOLD}============================================${NC}"
echo -e "${BOLD}Setup complete!${NC}"
echo -e "${BOLD}============================================${NC}"
echo ""
echo "Generated files:"
echo " deployments/.env - secrets and credentials"
echo " deployments/config.docker.yaml - server configuration"
echo ""
echo -e "${BOLD}Credentials:${NC}"
echo " PostgreSQL: silo / ${POSTGRES_PASSWORD}"
echo " MinIO: ${MINIO_ACCESS_KEY} / ${MINIO_SECRET_KEY}"
echo " MinIO Console: http://localhost:9001"
echo " LDAP Admin: cn=admin,dc=silo,dc=local / ${LDAP_ADMIN_PASSWORD}"
echo " LDAP User: ${LDAP_USERS} / ${LDAP_PASSWORDS}"
echo " Silo Admin: ${SILO_ADMIN_USERNAME} / ${SILO_ADMIN_PASSWORD} (local fallback)"
echo " Base URL: ${BASE_URL}"
echo ""
echo -e "${BOLD}Next steps:${NC}"
echo ""
echo " # Start the stack"
if [[ "$WITH_NGINX" == "true" ]]; then
echo " docker compose -f deployments/docker-compose.allinone.yaml --profile nginx up -d"
else
echo " docker compose -f deployments/docker-compose.allinone.yaml up -d"
fi
echo ""
echo " # Check status"
echo " docker compose -f deployments/docker-compose.allinone.yaml ps"
echo ""
echo " # View logs"
echo " docker compose -f deployments/docker-compose.allinone.yaml logs -f silo"
echo ""
echo " # Open in browser"
echo " ${BASE_URL}"
echo ""
echo " # Log in with LDAP: ${LDAP_USERS} / <password above>"
echo " # Or local admin: ${SILO_ADMIN_USERNAME} / <password above>"
echo ""
if [[ "$WITH_NGINX" != "true" ]]; then
echo " To add nginx later:"
echo " docker compose -f deployments/docker-compose.allinone.yaml --profile nginx up -d"
echo ""
fi
echo "Save these credentials somewhere safe. The passwords in deployments/.env"
echo "are the source of truth for the running stack."
echo ""

View File

@@ -28,7 +28,9 @@ REPO_URL="${SILO_REPO_URL:-https://git.kindred-systems.com/kindred/silo.git}"
REPO_BRANCH="${SILO_BRANCH:-main}"
INSTALL_DIR="/opt/silo"
CONFIG_DIR="/etc/silo"
GO_VERSION="1.23.0"
GO_VERSION="1.24.0"
DB_HOST="${SILO_DB_HOST:-psql.example.internal}"
MINIO_HOST="${SILO_MINIO_HOST:-minio.example.internal}"
log_info() { echo -e "${BLUE}[INFO]${NC} $*"; }
log_success() { echo -e "${GREEN}[OK]${NC} $*"; }
@@ -155,21 +157,28 @@ log_success "Directories created"
ENV_FILE="${CONFIG_DIR}/silod.env"
if [[ ! -f "${ENV_FILE}" ]]; then
log_info "Creating environment file..."
cat > "${ENV_FILE}" << 'EOF'
cat > "${ENV_FILE}" << EOF
# Silo daemon environment variables
# Fill in the values below
# Database credentials (psql.example.internal)
# Database credentials (${DB_HOST})
# Database: silo, User: silo
SILO_DB_PASSWORD=
# MinIO credentials (minio.example.internal)
# MinIO credentials (${MINIO_HOST})
# User: silouser
SILO_MINIO_ACCESS_KEY=silouser
SILO_MINIO_SECRET_KEY=
# Authentication
# Session secret (required when auth is enabled)
SILO_SESSION_SECRET=
# Default admin account (created on first startup if both are set)
SILO_ADMIN_USERNAME=admin
SILO_ADMIN_PASSWORD=
# Optional overrides
# SILO_SERVER_BASE_URL=http://silo.example.internal:8080
# SILO_SERVER_BASE_URL=http://\$(hostname -f):8080
EOF
chmod 600 "${ENV_FILE}"
chown root:silo "${ENV_FILE}"
@@ -214,10 +223,10 @@ echo "1. Edit ${ENV_FILE} and fill in credentials:"
echo " sudo nano ${ENV_FILE}"
echo ""
echo "2. Verify database connectivity:"
echo " psql -h psql.example.internal -U silo -d silo -c 'SELECT 1'"
echo " psql -h ${DB_HOST} -U silo -d silo -c 'SELECT 1'"
echo ""
echo "3. Verify MinIO connectivity:"
echo " curl -I http://minio.example.internal:9000/minio/health/live"
echo " curl -I http://${MINIO_HOST}:9000/minio/health/live"
echo ""
echo "4. Run the deployment:"
echo " sudo ${INSTALL_DIR}/src/scripts/deploy.sh"

View File

@@ -8,7 +8,7 @@
#
# Prerequisites:
# - FreeIPA server at ipa.example.internal
# - DNS configured for silo.example.internal
# - DNS configured for the silo host (set SILO_HOSTNAME to override default)
# - Admin credentials for IPA enrollment
set -euo pipefail
@@ -24,9 +24,9 @@ NC='\033[0m'
IPA_SERVER="${IPA_SERVER:-ipa.example.internal}"
IPA_DOMAIN="${IPA_DOMAIN:-example.internal}"
IPA_REALM="${IPA_REALM:-KINDRED.INTERNAL}"
HOSTNAME="silo.example.internal"
SILO_HOSTNAME="${SILO_HOSTNAME:-silo.example.internal}"
CERT_DIR="/etc/ssl/silo"
SILO_PORT=8080
SILO_PORT="${SILO_PORT:-8080}"
log_info() { echo -e "${BLUE}[INFO]${NC} $*"; }
log_success() { echo -e "${GREEN}[OK]${NC} $*"; }
@@ -77,8 +77,8 @@ log_success "Packages installed"
#
# Step 2: Set hostname
#
log_info "Setting hostname to ${HOSTNAME}..."
hostnamectl set-hostname "${HOSTNAME}"
log_info "Setting hostname to ${SILO_HOSTNAME}..."
hostnamectl set-hostname "${SILO_HOSTNAME}"
log_success "Hostname set"
#
@@ -95,7 +95,7 @@ else
--server="${IPA_SERVER}" \
--domain="${IPA_DOMAIN}" \
--realm="${IPA_REALM}" \
--hostname="${HOSTNAME}" \
--hostname="${SILO_HOSTNAME}" \
--mkhomedir \
--enable-dns-updates \
--unattended \
@@ -105,7 +105,7 @@ else
--server="${IPA_SERVER}" \
--domain="${IPA_DOMAIN}" \
--realm="${IPA_REALM}" \
--hostname="${HOSTNAME}" \
--hostname="${SILO_HOSTNAME}" \
--mkhomedir \
--enable-dns-updates
}
@@ -135,9 +135,9 @@ else
ipa-getcert request \
-f "${CERT_DIR}/silo.crt" \
-k "${CERT_DIR}/silo.key" \
-K "HTTP/${HOSTNAME}" \
-D "${HOSTNAME}" \
-N "CN=${HOSTNAME}" \
-K "HTTP/${SILO_HOSTNAME}" \
-D "${SILO_HOSTNAME}" \
-N "CN=${SILO_HOSTNAME}" \
-C "systemctl reload nginx"
log_info "Waiting for certificate to be issued..."
@@ -186,14 +186,14 @@ if [[ -f /etc/nginx/sites-enabled/default ]]; then
fi
# Create silo nginx config
cat > /etc/nginx/sites-available/silo << 'NGINX_EOF'
cat > /etc/nginx/sites-available/silo << NGINX_EOF
# Silo API Server - Nginx Reverse Proxy Configuration
# Redirect HTTP to HTTPS
server {
listen 80;
listen [::]:80;
server_name silo.example.internal;
server_name ${SILO_HOSTNAME};
# Allow certmonger/ACME challenges
location /.well-known/ {
@@ -201,7 +201,7 @@ server {
}
location / {
return 301 https://$server_name$request_uri;
return 301 https://\\$server_name\\$request_uri;
}
}
@@ -209,11 +209,11 @@ server {
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name silo.example.internal;
server_name ${SILO_HOSTNAME};
# SSL certificates (managed by certmonger/IPA)
ssl_certificate /etc/ssl/silo/silo.crt;
ssl_certificate_key /etc/ssl/silo/silo.key;
ssl_certificate ${CERT_DIR}/silo.crt;
ssl_certificate_key ${CERT_DIR}/silo.key;
# SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
@@ -226,7 +226,7 @@ server {
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/ssl/silo/ca.crt;
ssl_trusted_certificate ${CERT_DIR}/ca.crt;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
@@ -240,19 +240,19 @@ server {
# Proxy settings
location / {
proxy_pass http://127.0.0.1:8080;
proxy_pass http://127.0.0.1:${SILO_PORT};
proxy_http_version 1.1;
# Headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header Host \\$host;
proxy_set_header X-Real-IP \\$remote_addr;
proxy_set_header X-Forwarded-For \\$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \\$scheme;
proxy_set_header X-Forwarded-Host \\$host;
proxy_set_header X-Forwarded-Port \\$server_port;
# WebSocket support (for future use)
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Upgrade \\$http_upgrade;
proxy_set_header Connection "upgrade";
# Timeouts
@@ -343,14 +343,14 @@ echo " getcert list"
echo ""
echo "2. Update silo config to use correct base URL:"
echo " sudo nano /etc/silo/config.yaml"
echo " # Change base_url to: https://silo.example.internal"
echo " # Change base_url to: https://${SILO_HOSTNAME}"
echo ""
echo "3. Restart silo service:"
echo " sudo systemctl restart silod"
echo ""
echo "4. Test the setup:"
echo " curl -k https://silo.example.internal/health"
echo " curl https://silo.example.internal/health # after trusting IPA CA"
echo " curl -k https://${SILO_HOSTNAME}/health"
echo " curl https://${SILO_HOSTNAME}/health # after trusting IPA CA"
echo ""
echo "5. Trust IPA CA on client machines:"
echo " # The CA cert is at: ${CERT_DIR}/ca.crt"