homelab infrastructure services

first tinsnip host with lldap service and boot ordering

+918
+29
.gitignore
··· 1 + # OS files 2 + .DS_Store 3 + Thumbs.db 4 + 5 + # IDE 6 + .vscode/ 7 + .idea/ 8 + 9 + # Logs 10 + *.log 11 + 12 + # Docker data (contains secrets) 13 + services/*/data/ 14 + */data/ 15 + 16 + # Temporary files 17 + /tmp/ 18 + *.tmp 19 + *.swp 20 + *~ 21 + 22 + # Service-specific ignores 23 + # LLDAP 24 + services/lldap/data/ 25 + lldap_data/ 26 + 27 + # Test files 28 + test-* 29 + *-test.*
+90
CLAUDE.md
··· 1 + # CLAUDE.md 2 + 3 + This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. 4 + 5 + ## Project Overview 6 + 7 + tinsnip provides shared infrastructure services for homelab environments: 8 + - Runs all services under a dedicated `tinsnip` user (UID 1010) 9 + - Uses rootless Docker for container isolation 10 + - Currently provides LLDAP for identity management 11 + - Designed to add Redis and Prometheus in the future 12 + 13 + ## Project Structure 14 + 15 + ``` 16 + tinsnip/ 17 + ├── install.sh # Downloads and installs tinsnip 18 + ├── setup.sh # Main setup orchestrator 19 + ├── scripts/ 20 + │ ├── create_tinsnip_user.sh # Creates tinsnip user and system users 21 + │ ├── setup_rootless_docker.sh # Installs rootless Docker for tinsnip 22 + │ └── deploy_service.sh # Generic service deployment script 23 + └── services/ 24 + └── lldap/ # LLDAP identity service 25 + ├── docker-compose.yml # Container configuration 26 + └── setup.sh # LLDAP-specific setup 27 + ``` 28 + 29 + ## Key Commands 30 + 31 + ### Install tinsnip 32 + ```bash 33 + curl -fsSL "https://tangled.sh/dynamicalsystem.com/tinsnip/raw/main/install.sh?$(date +%s)" | bash 34 + cd ~/.local/opt/tinsnip 35 + ./setup.sh 36 + ``` 37 + 38 + ### Manage services 39 + ```bash 40 + # Switch to tinsnip user 41 + sudo -u tinsnip -i 42 + 43 + # Check service status 44 + cd ~/services/lldap 45 + docker compose ps 46 + 47 + # View logs 48 + docker compose logs -f 49 + ``` 50 + 51 + ### Add new service 52 + 1. Create `services/<service-name>/` directory 53 + 2. Add `docker-compose.yml` for the service 54 + 3. Optional: Add `setup.sh` for service-specific setup 55 + 4. Deploy with: `./scripts/deploy_service.sh <service-name>` 56 + 57 + ## Architecture Notes 58 + 59 + 1. **User Separation**: 60 + - `tinsnip` (UID 1010) - Regular user that runs rootless Docker 61 + - `lldap` (UID 999) - System user for LLDAP container 62 + - Services run isolated from regular system users 63 + 64 + 2. **Service Pattern**: 65 + - Each service has its own directory under `services/` 66 + - All services run under tinsnip's rootless Docker 67 + - Systemd services created for auto-start 68 + 69 + 3. **Network**: 70 + - Services share the `tinsnip_network` Docker network 71 + - Ports are exposed on all interfaces for homelab access 72 + 73 + 4. **Storage**: 74 + - Service data stored in `./data` relative to service directory 75 + - Owned by appropriate system user (e.g., 999 for LLDAP) 76 + 77 + ## Security Considerations 78 + 79 + - Never run setup.sh as root or as the tinsnip user 80 + - The tinsnip user has no sudo access 81 + - Each service runs as a system user inside containers 82 + - Secrets are generated per-service and stored with restrictive permissions 83 + 84 + ## Adding New Services 85 + 86 + When adding Redis or Prometheus: 87 + 1. Follow the existing LLDAP pattern 88 + 2. Create appropriate system users if needed 89 + 3. Use the shared tinsnip_network 90 + 4. Document the ports and configuration
+72
README.md
··· 1 + # tinsnip - Homelab Infrastructure Services 2 + 3 + Shared infrastructure services for homelab environments, deployed with proper isolation and security. 4 + 5 + ## Overview 6 + 7 + tinsnip provides essential infrastructure services that multiple homelab systems can share: 8 + - **Identity Management** (LLDAP) 9 + - More services coming soon... 10 + 11 + ## Architecture 12 + 13 + **Host Level:** 14 + - User: `tinsnip` (UID 1010) - Runs rootless Docker 15 + - Manages all tinsnip service containers 16 + - Complete isolation from regular user accounts 17 + 18 + **Service: LLDAP** 19 + - Container runs under tinsnip's rootless Docker 20 + - Inside container: runs as system user `lldap` (UID 999) 21 + - Ports: 3890 (LDAP), 17170 (Web UI) 22 + - Base DN: `dc=home,dc=local` 23 + 24 + ## Quick Start 25 + 26 + ```bash 27 + # Install tinsnip 28 + curl -fsSL "https://tangled.sh/dynamicalsystem.com/tinsnip/raw/main/install.sh?$(date +%s)" | bash 29 + 30 + # Or clone and run 31 + git clone git@tangled.sh:dynamicalsystem.com/tinsnip 32 + cd tinsnip 33 + ./setup.sh 34 + ``` 35 + 36 + ## Service Isolation Model 37 + 38 + ``` 39 + User 'tinsnip' (1010) → Runs rootless Docker → Service containers 40 + ↓ ↓ 41 + Isolated from regular users Each runs as appropriate user 42 + ``` 43 + 44 + ## Integration Guide 45 + 46 + ### For Ubuntu/Debian Systems 47 + ```bash 48 + # Configure LDAP client 49 + apt install sssd-ldap 50 + # Point to tinsnip:3890 51 + ``` 52 + 53 + ### For Docker Services 54 + ```yaml 55 + environment: 56 + - LDAP_HOST=tinsnip 57 + - LDAP_PORT=3890 58 + - LDAP_BASE_DN=dc=home,dc=local 59 + ``` 60 + 61 + ## Planned Services 62 + 63 + - [x] LLDAP - Identity Management 64 + - [ ] Redis - Caching/queuing 65 + - [ ] Prometheus - Metrics collection 66 + 67 + ## Design Principles 68 + 69 + 1. **Service Isolation** - All services run under dedicated `tinsnip` user 70 + 2. **Rootless Docker** - No root daemon required 71 + 3. **Easy Integration** - Standard protocols and ports 72 + 4. **Homelab Focused** - Optimized for home infrastructure
+95
install.sh
··· 1 + #!/bin/bash 2 + 3 + set -euo pipefail 4 + 5 + # tinsnip installer - Downloads and sets up tinsnip infrastructure services 6 + 7 + REPO_URL="https://tangled.sh/dynamicalsystem.com/tinsnip" 8 + BRANCH="main" 9 + INSTALL_DIR="${INSTALL_DIR:-$HOME/.local/opt/tinsnip}" 10 + USE_GIT="${USE_GIT:-false}" 11 + 12 + log() { 13 + echo "[Installer] $*" 14 + } 15 + 16 + error() { 17 + log "ERROR: $*" >&2 18 + exit 1 19 + } 20 + 21 + download_file() { 22 + local file_path="$1" 23 + local dest_path="$2" 24 + local url="${REPO_URL}/raw/${BRANCH}/${file_path}" 25 + 26 + log "Downloading $file_path..." 27 + 28 + if command -v curl &> /dev/null; then 29 + if curl -fsSL "$url" -o "$dest_path" 2>/dev/null; then 30 + chmod +x "$dest_path" 2>/dev/null || true 31 + return 0 32 + fi 33 + fi 34 + 35 + error "Failed to download $file_path" 36 + } 37 + 38 + clone_with_git() { 39 + log "Cloning repository with git..." 40 + if ! command -v git &> /dev/null; then 41 + log "Git not found, installing..." 42 + sudo apt-get update -qq && sudo apt-get install -y git 43 + fi 44 + 45 + if ! git clone "git@tangled.sh:dynamicalsystem.com/tinsnip" "$INSTALL_DIR"; then 46 + error "Failed to clone repository. Make sure you have SSH access to git@tangled.sh" 47 + fi 48 + } 49 + 50 + main() { 51 + log "tinsnip Infrastructure Installer" 52 + log "================================" 53 + 54 + cd ~ || error "Failed to change to home directory" 55 + 56 + if [[ ! -f /etc/os-release ]] || ! grep -q "Ubuntu" /etc/os-release; then 57 + error "This installer requires Ubuntu" 58 + fi 59 + 60 + if [[ -d "$INSTALL_DIR" ]]; then 61 + log "Directory $INSTALL_DIR already exists. Removing for fresh installation..." 62 + rm -rf "$INSTALL_DIR" 63 + fi 64 + 65 + if [[ "$USE_GIT" == "true" ]]; then 66 + mkdir -p "$(dirname "$INSTALL_DIR")" 67 + clone_with_git 68 + else 69 + log "Creating installation directory at $INSTALL_DIR..." 70 + mkdir -p "$INSTALL_DIR"/{scripts,services/lldap,config} 71 + 72 + log "Downloading setup files..." 73 + 74 + # Download main files 75 + download_file "setup.sh" "$INSTALL_DIR/setup.sh" 76 + download_file "README.md" "$INSTALL_DIR/README.md" 77 + 78 + # Download scripts 79 + download_file "scripts/create_tinsnip_user.sh" "$INSTALL_DIR/scripts/create_tinsnip_user.sh" 80 + download_file "scripts/setup_rootless_docker.sh" "$INSTALL_DIR/scripts/setup_rootless_docker.sh" 81 + download_file "scripts/deploy_service.sh" "$INSTALL_DIR/scripts/deploy_service.sh" 82 + 83 + # Download LLDAP service files 84 + download_file "services/lldap/docker-compose.yml" "$INSTALL_DIR/services/lldap/docker-compose.yml" 85 + download_file "services/lldap/setup.sh" "$INSTALL_DIR/services/lldap/setup.sh" 86 + fi 87 + 88 + log "Installation complete!" 89 + log "" 90 + log "Next steps:" 91 + log "1. cd $INSTALL_DIR" 92 + log "2. ./setup.sh" 93 + } 94 + 95 + main "$@"
+123
scripts/configure_boot_order.sh
··· 1 + #!/bin/bash 2 + 3 + # Configure boot ordering to ensure tinsnip services start first 4 + 5 + set -euo pipefail 6 + 7 + log() { 8 + echo "[Boot Order] $*" 9 + } 10 + 11 + create_tinsnip_target() { 12 + log "Creating tinsnip systemd target..." 13 + 14 + # Create a systemd target that groups all tinsnip services 15 + sudo tee /etc/systemd/system/tinsnip.target > /dev/null << 'EOF' 16 + [Unit] 17 + Description=tinsnip Infrastructure Services 18 + After=network-online.target 19 + Wants=network-online.target 20 + 21 + [Install] 22 + WantedBy=multi-user.target 23 + EOF 24 + 25 + # Update tinsnip service files to be part of this target 26 + for service in /etc/systemd/system/tinsnip-*.service; do 27 + if [[ -f "$service" ]]; then 28 + log "Updating $service to use tinsnip.target..." 29 + sudo sed -i '/\[Install\]/,/WantedBy=/ s/WantedBy=.*/WantedBy=tinsnip.target/' "$service" 30 + fi 31 + done 32 + 33 + sudo systemctl daemon-reload 34 + sudo systemctl enable tinsnip.target 35 + } 36 + 37 + create_wait_for_tinsnip() { 38 + log "Creating wait-for-tinsnip service..." 39 + 40 + # Create a service that waits for tinsnip services to be ready 41 + sudo tee /etc/systemd/system/wait-for-tinsnip.service > /dev/null << 'EOF' 42 + [Unit] 43 + Description=Wait for tinsnip services to be ready 44 + After=tinsnip.target 45 + Wants=tinsnip.target 46 + 47 + [Service] 48 + Type=oneshot 49 + RemainAfterExit=yes 50 + ExecStart=/bin/bash -c 'until nc -z localhost 3890; do echo "Waiting for LDAP..."; sleep 2; done; echo "LDAP is ready"' 51 + 52 + [Install] 53 + WantedBy=multi-user.target 54 + EOF 55 + 56 + sudo systemctl daemon-reload 57 + sudo systemctl enable wait-for-tinsnip.service 58 + } 59 + 60 + configure_user_dependencies() { 61 + log "Configuring user session dependencies..." 62 + 63 + # Create a drop-in for user@.service to depend on tinsnip 64 + sudo mkdir -p /etc/systemd/system/user@.service.d 65 + 66 + # This affects ALL user sessions 67 + sudo tee /etc/systemd/system/user@.service.d/wait-for-tinsnip.conf > /dev/null << 'EOF' 68 + [Unit] 69 + After=wait-for-tinsnip.service 70 + Wants=wait-for-tinsnip.service 71 + EOF 72 + 73 + # For more specific control, create a drop-in just for specific users 74 + for uid in 1003 1004 1005; do # Add your regular user UIDs here 75 + if id -u >/dev/null 2>&1; then 76 + sudo mkdir -p "/etc/systemd/system/user@${uid}.service.d" 77 + sudo tee "/etc/systemd/system/user@${uid}.service.d/wait-for-tinsnip.conf" > /dev/null << 'EOF' 78 + [Unit] 79 + After=wait-for-tinsnip.service 80 + Wants=wait-for-tinsnip.service 81 + EOF 82 + fi 83 + done 84 + 85 + sudo systemctl daemon-reload 86 + } 87 + 88 + create_machine_dependencies() { 89 + log "Creating machine service dependencies..." 90 + 91 + # If machine repo has systemd services, update them 92 + # This is a template - adjust based on actual machine services 93 + cat > /tmp/machine-services-dependency.conf << 'EOF' 94 + [Unit] 95 + After=wait-for-tinsnip.service 96 + Wants=wait-for-tinsnip.service 97 + EOF 98 + 99 + log "Template created at /tmp/machine-services-dependency.conf" 100 + log "Apply this to any machine systemd services that need LDAP" 101 + } 102 + 103 + main() { 104 + log "Configuring boot order for tinsnip priority..." 105 + 106 + create_tinsnip_target 107 + create_wait_for_tinsnip 108 + configure_user_dependencies 109 + create_machine_dependencies 110 + 111 + log "" 112 + log "Boot order configuration complete!" 113 + log "" 114 + log "Boot sequence will be:" 115 + log "1. System boot" 116 + log "2. tinsnip.target (all tinsnip services)" 117 + log "3. wait-for-tinsnip.service (confirms LDAP is ready)" 118 + log "4. Regular user sessions (including machine services)" 119 + log "" 120 + log "To test: sudo systemctl list-dependencies tinsnip.target" 121 + } 122 + 123 + main "$@"
+65
scripts/create_tinsnip_user.sh
··· 1 + #!/bin/bash 2 + 3 + # Create dedicated tinsnip user for running infrastructure services 4 + 5 + set -euo pipefail 6 + 7 + log() { 8 + echo "[User Setup] $*" 9 + } 10 + 11 + create_tinsnip_user() { 12 + local username="tinsnip" 13 + local uid=1010 14 + 15 + log "Creating dedicated user '$username' with UID $uid..." 16 + 17 + if id "$username" &>/dev/null; then 18 + log "User $username already exists" 19 + return 0 20 + fi 21 + 22 + # Create regular user (not system user) for rootless Docker 23 + sudo useradd -m -u "$uid" -s /bin/bash -c "tinsnip Infrastructure Services" "$username" 24 + log "Created user $username with UID $uid" 25 + 26 + # Add subuid/subgid ranges for rootless Docker 27 + echo "$username:100000:65536" | sudo tee -a /etc/subuid 28 + echo "$username:100000:65536" | sudo tee -a /etc/subgid 29 + log "Added subuid/subgid mappings for rootless containers" 30 + 31 + # Enable lingering for systemd user sessions 32 + sudo loginctl enable-linger "$username" 33 + log "Enabled systemd lingering for $username" 34 + 35 + # Create directory structure 36 + sudo -u "$username" mkdir -p /home/"$username"/{services,config,data} 37 + log "Created directory structure" 38 + } 39 + 40 + create_system_users() { 41 + log "Creating system users for services..." 42 + 43 + # Create lldap system user (will be used inside containers) 44 + if ! id "lldap" &>/dev/null; then 45 + sudo useradd -r -u 999 -s /bin/false -d /nonexistent -c "LLDAP Service" lldap 46 + log "Created system user 'lldap' with UID 999" 47 + else 48 + log "System user 'lldap' already exists" 49 + fi 50 + } 51 + 52 + main() { 53 + log "Setting up tinsnip user environment..." 54 + 55 + create_tinsnip_user 56 + create_system_users 57 + 58 + log "User setup complete!" 59 + log "" 60 + log "Created users:" 61 + log " - tinsnip (UID 1010): Runs rootless Docker and all services" 62 + log " - lldap (UID 999): System user for LLDAP container" 63 + } 64 + 65 + main "$@"
+105
scripts/deploy_service.sh
··· 1 + #!/bin/bash 2 + 3 + # Deploy a tinsnip service 4 + 5 + set -euo pipefail 6 + 7 + SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 8 + SERVICES_DIR="$(dirname "$SCRIPT_DIR")/services" 9 + 10 + log() { 11 + echo "[Deploy] $*" 12 + } 13 + 14 + error() { 15 + log "ERROR: $*" >&2 16 + exit 1 17 + } 18 + 19 + deploy_service() { 20 + local service_name="$1" 21 + local service_dir="$SERVICES_DIR/$service_name" 22 + 23 + if [[ ! -d "$service_dir" ]]; then 24 + error "Service directory not found: $service_dir" 25 + fi 26 + 27 + log "Deploying service: $service_name" 28 + 29 + # Copy service files to tinsnip user's home 30 + local target_dir="/home/tinsnip/services/$service_name" 31 + sudo -u tinsnip mkdir -p "$target_dir" 32 + 33 + # Copy all files 34 + sudo cp -r "$service_dir"/* "$target_dir/" 35 + sudo chown -R tinsnip:tinsnip "$target_dir" 36 + 37 + # Make scripts executable 38 + find "$target_dir" -name "*.sh" -exec sudo chmod +x {} \; 39 + 40 + # Run service-specific setup if it exists 41 + if [[ -f "$target_dir/setup.sh" ]]; then 42 + log "Running $service_name setup..." 43 + sudo -u tinsnip -i bash -c "cd ~/services/$service_name && ./setup.sh" 44 + else 45 + # Generic docker-compose deployment 46 + log "Starting $service_name with docker-compose..." 47 + sudo -u tinsnip -i bash -c "cd ~/services/$service_name && docker compose up -d" 48 + fi 49 + 50 + # Create systemd service for auto-start 51 + create_systemd_service "$service_name" 52 + } 53 + 54 + create_systemd_service() { 55 + local service_name="$1" 56 + local systemd_file="/etc/systemd/system/tinsnip-${service_name}.service" 57 + 58 + log "Creating systemd service for $service_name..." 59 + 60 + sudo tee "$systemd_file" > /dev/null << EOF 61 + [Unit] 62 + Description=tinsnip $service_name service 63 + After=network-online.target 64 + Wants=network-online.target 65 + Requires=user@1010.service 66 + 67 + [Service] 68 + Type=simple 69 + User=tinsnip 70 + WorkingDirectory=/home/tinsnip/services/$service_name 71 + ExecStart=/usr/bin/sudo -u tinsnip docker compose up 72 + ExecStop=/usr/bin/sudo -u tinsnip docker compose down 73 + Restart=on-failure 74 + RestartSec=10 75 + 76 + [Install] 77 + WantedBy=multi-user.target 78 + EOF 79 + 80 + sudo systemctl daemon-reload 81 + sudo systemctl enable "tinsnip-${service_name}.service" 82 + log "Systemd service created and enabled: tinsnip-${service_name}.service" 83 + } 84 + 85 + main() { 86 + if [[ $# -lt 1 ]]; then 87 + error "Usage: $0 <service-name>" 88 + fi 89 + 90 + local service_name="$1" 91 + 92 + # Check if tinsnip user exists 93 + if ! id "tinsnip" &>/dev/null; then 94 + error "tinsnip user does not exist. Run setup.sh first." 95 + fi 96 + 97 + deploy_service "$service_name" 98 + 99 + log "" 100 + log "Service $service_name deployed successfully!" 101 + log "To check status: sudo -u tinsnip docker compose -f /home/tinsnip/services/$service_name/docker-compose.yml ps" 102 + log "To view logs: sudo -u tinsnip docker compose -f /home/tinsnip/services/$service_name/docker-compose.yml logs" 103 + } 104 + 105 + main "$@"
+112
scripts/setup_rootless_docker.sh
··· 1 + #!/bin/bash 2 + 3 + # Setup rootless Docker for tinsnip user 4 + 5 + set -euo pipefail 6 + 7 + log() { 8 + echo "[Docker Setup] $*" 9 + } 10 + 11 + install_docker_rootless() { 12 + local username="tinsnip" 13 + 14 + log "Installing rootless Docker for $username..." 15 + 16 + # Check if Docker is already installed for the user 17 + if sudo -u "$username" -i bash -c "command -v docker &>/dev/null"; then 18 + log "Docker already installed for $username" 19 + return 0 20 + fi 21 + 22 + # Install dependencies 23 + log "Installing Docker dependencies..." 24 + sudo apt-get update -qq 25 + sudo apt-get install -y \ 26 + uidmap \ 27 + dbus-user-session \ 28 + systemd-container \ 29 + fuse-overlayfs \ 30 + slirp4netns 31 + 32 + # Install Docker rootless as tinsnip user 33 + log "Installing Docker rootless mode..." 34 + sudo -u "$username" -i bash << 'EOF' 35 + # Download and run rootless installer 36 + curl -fsSL https://get.docker.com/rootless | sh 37 + 38 + # Add Docker binaries to PATH 39 + echo 'export PATH=$HOME/bin:$PATH' >> ~/.bashrc 40 + echo 'export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock' >> ~/.bashrc 41 + 42 + # Source the new PATH 43 + source ~/.bashrc 44 + 45 + # Enable Docker service 46 + systemctl --user enable docker.service 47 + systemctl --user start docker.service 48 + 49 + # Wait for Docker to start 50 + sleep 5 51 + 52 + # Test Docker 53 + if docker version &>/dev/null; then 54 + echo "Docker rootless installation successful" 55 + else 56 + echo "Docker installation may have issues" 57 + exit 1 58 + fi 59 + EOF 60 + 61 + if [[ $? -eq 0 ]]; then 62 + log "Docker rootless installation completed successfully" 63 + else 64 + log "Warning: Docker installation may have issues" 65 + fi 66 + } 67 + 68 + configure_docker() { 69 + local username="tinsnip" 70 + 71 + log "Configuring Docker for $username..." 72 + 73 + # Create Docker config directory 74 + sudo -u "$username" mkdir -p /home/"$username"/.docker 75 + 76 + # Create daemon.json with useful defaults 77 + sudo -u "$username" tee /home/"$username"/.docker/daemon.json > /dev/null << 'EOF' 78 + { 79 + "log-driver": "json-file", 80 + "log-opts": { 81 + "max-size": "10m", 82 + "max-file": "3" 83 + }, 84 + "storage-driver": "overlay2" 85 + } 86 + EOF 87 + 88 + # Restart Docker to apply config 89 + sudo -u "$username" systemctl --user restart docker.service 90 + 91 + log "Docker configuration complete" 92 + } 93 + 94 + main() { 95 + log "Setting up rootless Docker for tinsnip..." 96 + 97 + # Check if tinsnip user exists 98 + if ! id "tinsnip" &>/dev/null; then 99 + log "ERROR: tinsnip user does not exist. Run create_tinsnip_user.sh first." 100 + exit 1 101 + fi 102 + 103 + install_docker_rootless 104 + configure_docker 105 + 106 + log "Rootless Docker setup complete!" 107 + log "" 108 + log "Docker is now running for user 'tinsnip'" 109 + log "To verify: sudo -u tinsnip docker version" 110 + } 111 + 112 + main "$@"
+28
services/lldap/docker-compose.yml
··· 1 + services: 2 + lldap: 3 + image: nitnelave/lldap:stable 4 + container_name: lldap 5 + restart: unless-stopped 6 + user: "999:999" # Run as system user lldap 7 + ports: 8 + - "3890:3890" # LDAP port 9 + - "17170:17170" # Web UI port 10 + volumes: 11 + - ./data:/data 12 + - /etc/timezone:/etc/timezone:ro 13 + - /etc/localtime:/etc/localtime:ro 14 + environment: 15 + - TZ=Europe/London 16 + - LLDAP_JWT_SECRET_FILE=/data/jwt_secret 17 + - LLDAP_KEY_SEED_FILE=/data/key_seed 18 + - LLDAP_LDAP_BASE_DN=dc=home,dc=local 19 + - LLDAP_LDAP_USER_DN=admin 20 + - LLDAP_LDAP_USER_PASS_FILE=/data/admin_password 21 + - LLDAP_DATABASE_URL=sqlite:///data/users.db 22 + networks: 23 + - tinsnip 24 + 25 + networks: 26 + tinsnip: 27 + driver: bridge 28 + name: tinsnip_network
+108
services/lldap/setup.sh
··· 1 + #!/bin/bash 2 + 3 + # Setup LLDAP service 4 + 5 + set -euo pipefail 6 + 7 + SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 8 + 9 + log() { 10 + echo "[LLDAP Setup] $*" 11 + } 12 + 13 + setup_directories() { 14 + log "Creating data directory..." 15 + mkdir -p "$SCRIPT_DIR/data" 16 + 17 + # Set ownership to lldap system user 18 + sudo chown -R 999:999 "$SCRIPT_DIR/data" 19 + sudo chmod 750 "$SCRIPT_DIR/data" 20 + } 21 + 22 + generate_secrets() { 23 + log "Generating secrets..." 24 + 25 + # Generate JWT secret 26 + if [[ ! -f "$SCRIPT_DIR/data/jwt_secret" ]]; then 27 + openssl rand -base64 32 | sudo tee "$SCRIPT_DIR/data/jwt_secret" > /dev/null 28 + sudo chown 999:999 "$SCRIPT_DIR/data/jwt_secret" 29 + sudo chmod 600 "$SCRIPT_DIR/data/jwt_secret" 30 + fi 31 + 32 + # Generate key seed 33 + if [[ ! -f "$SCRIPT_DIR/data/key_seed" ]]; then 34 + openssl rand -base64 32 | sudo tee "$SCRIPT_DIR/data/key_seed" > /dev/null 35 + sudo chown 999:999 "$SCRIPT_DIR/data/key_seed" 36 + sudo chmod 600 "$SCRIPT_DIR/data/key_seed" 37 + fi 38 + 39 + # Set admin password 40 + if [[ ! -f "$SCRIPT_DIR/data/admin_password" ]]; then 41 + read -s -p "Enter LLDAP admin password: " admin_pass 42 + echo 43 + echo -n "$admin_pass" | sudo tee "$SCRIPT_DIR/data/admin_password" > /dev/null 44 + sudo chown 999:999 "$SCRIPT_DIR/data/admin_password" 45 + sudo chmod 600 "$SCRIPT_DIR/data/admin_password" 46 + fi 47 + } 48 + 49 + start_lldap() { 50 + log "Starting LLDAP..." 51 + cd "$SCRIPT_DIR" 52 + 53 + docker compose up -d 54 + 55 + log "Waiting for LLDAP to start..." 56 + sleep 5 57 + 58 + if docker compose ps | grep -q "Up\|running"; then 59 + log "LLDAP started successfully!" 60 + log "" 61 + log "Access the web UI at: http://$(hostname -f):17170" 62 + log "Login: admin" 63 + log "Password: (what you just set)" 64 + log "" 65 + log "LDAP endpoint: ldap://$(hostname -f):3890" 66 + log "Base DN: dc=home,dc=local" 67 + log "Bind DN: uid=admin,ou=people,dc=home,dc=local" 68 + else 69 + log "ERROR: LLDAP failed to start" 70 + docker compose logs 71 + exit 1 72 + fi 73 + } 74 + 75 + create_initial_users_guide() { 76 + log "" 77 + log "=== Initial Setup Guide ===" 78 + log "" 79 + log "1. Create your first LDAP user:" 80 + log " - Open http://$(hostname -f):17170" 81 + log " - Login as admin" 82 + log " - Go to Users → Create User" 83 + log " - Username: your-username" 84 + log " - Set a secure password" 85 + log "" 86 + log "2. For rootless Docker integration:" 87 + log " - Create users with UIDs matching your system users" 88 + log " - Example: If your Ubuntu user has UID 1003, create LDAP user with same UID" 89 + log "" 90 + log "3. Configure client systems:" 91 + log " - Server: $(hostname -f)" 92 + log " - Port: 3890" 93 + log " - Base DN: dc=home,dc=local" 94 + } 95 + 96 + main() { 97 + log "Setting up LLDAP identity service..." 98 + 99 + setup_directories 100 + generate_secrets 101 + start_lldap 102 + create_initial_users_guide 103 + 104 + log "" 105 + log "LLDAP setup complete!" 106 + } 107 + 108 + main "$@"
+91
setup.sh
··· 1 + #!/bin/bash 2 + 3 + set -euo pipefail 4 + 5 + SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 6 + LOG_FILE="/tmp/tinsnip-setup-$(date +%Y%m%d-%H%M%S).log" 7 + 8 + log() { 9 + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE" 10 + } 11 + 12 + error() { 13 + log "ERROR: $*" >&2 14 + exit 1 15 + } 16 + 17 + check_ubuntu() { 18 + if [[ ! -f /etc/os-release ]] || ! grep -q "Ubuntu" /etc/os-release; then 19 + error "This script requires Ubuntu" 20 + fi 21 + } 22 + 23 + check_current_user() { 24 + if [[ "$USER" == "tinsnip" ]]; then 25 + error "Do not run this script as the tinsnip user. Run as a regular admin user." 26 + fi 27 + 28 + if [[ $EUID -eq 0 ]]; then 29 + error "Do not run this script as root. Run as a regular user with sudo access." 30 + fi 31 + 32 + if ! groups | grep -q sudo; then 33 + error "Current user must have sudo access" 34 + fi 35 + } 36 + 37 + main() { 38 + log "tinsnip Infrastructure Setup" 39 + log "============================" 40 + log "Log file: $LOG_FILE" 41 + 42 + check_ubuntu 43 + check_current_user 44 + 45 + log "This will set up tinsnip infrastructure services on this host." 46 + log "A dedicated 'tinsnip' user will be created to run all services." 47 + echo 48 + read -p "Continue? (y/N): " response 49 + if [[ ! "$response" =~ ^[Yy]$ ]]; then 50 + log "Setup cancelled by user" 51 + exit 0 52 + fi 53 + 54 + # Step 1: Create tinsnip user 55 + log "Step 1: Creating tinsnip user..." 56 + if ! "$SCRIPT_DIR/scripts/create_tinsnip_user.sh"; then 57 + error "Failed to create tinsnip user" 58 + fi 59 + 60 + # Step 2: Setup rootless Docker for tinsnip 61 + log "Step 2: Setting up rootless Docker..." 62 + if ! "$SCRIPT_DIR/scripts/setup_rootless_docker.sh"; then 63 + error "Failed to setup rootless Docker" 64 + fi 65 + 66 + # Step 3: Deploy services 67 + log "Step 3: Deploying services..." 68 + 69 + # Deploy LLDAP 70 + log "Deploying LLDAP identity service..." 71 + if ! "$SCRIPT_DIR/scripts/deploy_service.sh" lldap; then 72 + error "Failed to deploy LLDAP" 73 + fi 74 + 75 + log "" 76 + log "Setup completed successfully!" 77 + log "" 78 + log "Services deployed:" 79 + log " - LLDAP: http://$(hostname):17170 (Web UI)" 80 + log " - LLDAP: ldap://$(hostname):3890 (LDAP endpoint)" 81 + log "" 82 + log "Default credentials:" 83 + log " - Username: admin" 84 + log " - Password: (set during LLDAP setup)" 85 + log "" 86 + log "To manage services:" 87 + log " sudo -u tinsnip -i" 88 + log " cd ~/services/lldap && docker compose ps" 89 + } 90 + 91 + main "$@"