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:
24
.env.example
24
.env.example
@@ -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
1
.gitignore
vendored
@@ -29,6 +29,7 @@ Thumbs.db
|
||||
# Config with secrets
|
||||
config.yaml
|
||||
*.env
|
||||
deployments/config.docker.yaml
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
|
||||
24
README.md
24
README.md
@@ -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 |
|
||||
|
||||
@@ -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:
|
||||
|
||||
35
deployments/config.dev.yaml
Normal file
35
deployments/config.dev.yaml
Normal 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
|
||||
172
deployments/docker-compose.allinone.yaml
Normal file
172
deployments/docker-compose.allinone.yaml
Normal 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
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
36
deployments/ldap/memberof.ldif
Normal file
36
deployments/ldap/memberof.ldif
Normal 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
|
||||
34
deployments/ldap/silo-groups.ldif
Normal file
34
deployments/ldap/silo-groups.ldif
Normal 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
|
||||
44
deployments/nginx/nginx-nossl.conf
Normal file
44
deployments/nginx/nginx-nossl.conf
Normal 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;
|
||||
}
|
||||
}
|
||||
103
deployments/nginx/nginx.conf
Normal file
103
deployments/nginx/nginx.conf
Normal 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;
|
||||
# }
|
||||
# }
|
||||
@@ -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
518
docs/INSTALL.md
Normal 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 |
|
||||
@@ -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
344
scripts/setup-docker.sh
Executable 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 ""
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user