diff --git a/README.md b/README.md index da132e0..934a246 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,9 @@ Internal → DNS Server → Caddy → Services **Playbooks:** - **`playbooks/deployment/gitlab-stack.yml`** - Complete GitLab CE deployment with Caddy -- **`playbooks/deployment/technitium-dns-container.yml`** - DNS server deployment +- **`playbooks/deployment/technitium-dns-container.yml`** - DNS server deployment +- **`playbooks/deployment/openwebui-tailscale.yml`** - OpenWebUI deployment with Tailscale exposure +- **`playbooks/deployment/htpc-nas.yml`** - NAS stack for htpc via Docker Compose - **`playbooks/setup/initial-setup.yml`** - Server hardening and user management - **`playbooks/maintenance/hardware-assessment.yml`** - AI/ML hardware analysis diff --git a/ansible-infrastructure/playbooks/deployment/htpc-nas.yml b/ansible-infrastructure/playbooks/deployment/htpc-nas.yml new file mode 100644 index 0000000..d3a0912 --- /dev/null +++ b/ansible-infrastructure/playbooks/deployment/htpc-nas.yml @@ -0,0 +1,31 @@ +--- +# Deploy NAS services on the htpc host using Docker Compose +- name: Deploy NAS stack on htpc + hosts: htpc + become: yes + vars: + nas_compose_dir: /opt/nas + + tasks: + - name: Ensure NAS compose directory exists + file: + path: "{{ nas_compose_dir }}" + state: directory + mode: '0755' + + - name: Deploy NAS docker-compose file + template: + src: templates/htpc-nas/docker-compose.yml.j2 + dest: "{{ nas_compose_dir }}/docker-compose.yml" + + - name: Launch NAS stack + community.docker.docker_compose: + project_src: "{{ nas_compose_dir }}" + state: present + + - name: Display NAS deployment information + debug: + msg: + - "NAS stack deployed on {{ inventory_hostname }}" + - "Data directory: {{ nas_data_path | default('/srv/nas') }}" + diff --git a/ansible-infrastructure/playbooks/deployment/openwebui-tailscale.yml b/ansible-infrastructure/playbooks/deployment/openwebui-tailscale.yml new file mode 100644 index 0000000..dbcfe08 --- /dev/null +++ b/ansible-infrastructure/playbooks/deployment/openwebui-tailscale.yml @@ -0,0 +1,39 @@ +--- +# Deploy OpenWebUI container and expose it via Tailscale Serve +- name: Deploy OpenWebUI and expose via Tailscale + hosts: ai_workers + become: yes + vars: + openwebui_image: ghcr.io/open-webui/open-webui:latest + openwebui_host_port: 3000 + + roles: + - role: ../../roles/tailscale + vars: + tailscale_auth_key: "{{ lookup('env', 'TAILSCALE_AUTH_KEY') | default('') }}" + tailscale_hostname: "{{ inventory_hostname }}-openwebui" + tailscale_accept_dns: false + + tasks: + - name: Deploy OpenWebUI container + docker_container: + name: openwebui + image: "{{ openwebui_image }}" + restart_policy: unless-stopped + published_ports: + - "{{ openwebui_host_port }}:8080" + volumes: + - /opt/openwebui:/app/backend/data + + - name: Expose OpenWebUI via Tailscale Serve + command: "tailscale serve https 443 http://localhost:{{ openwebui_host_port }}" + register: tailscale_serve + changed_when: "'Serving' in tailscale_serve.stdout" + + - name: Display OpenWebUI access information + debug: + msg: + - "OpenWebUI deployed on {{ inventory_hostname }}" + - "Local URL: http://{{ ansible_default_ipv4.address }}:{{ openwebui_host_port }}" + - "Tailscale URL: https://{{ inventory_hostname }}.{{ tailscale_dns_search_domains | first | default('') }}" + diff --git a/ansible-infrastructure/playbooks/deployment/templates/htpc-nas/docker-compose.yml.j2 b/ansible-infrastructure/playbooks/deployment/templates/htpc-nas/docker-compose.yml.j2 new file mode 100644 index 0000000..3935024 --- /dev/null +++ b/ansible-infrastructure/playbooks/deployment/templates/htpc-nas/docker-compose.yml.j2 @@ -0,0 +1,15 @@ +version: "3.8" +services: + samba: + image: ghcr.io/linuxserver/samba + container_name: samba + restart: unless-stopped + environment: + - PUID={{ nas_puid | default(1000) }} + - PGID={{ nas_pgid | default(1000) }} + - TZ={{ nas_timezone | default('UTC') }} + volumes: + - {{ nas_data_path | default('/srv/nas') }}:/data + ports: + - "139:139" + - "445:445" diff --git a/ansible-infrastructure/roles/tailscale/defaults/main.yml b/ansible-infrastructure/roles/tailscale/defaults/main.yml index eba5a8e..958f67e 100644 --- a/ansible-infrastructure/roles/tailscale/defaults/main.yml +++ b/ansible-infrastructure/roles/tailscale/defaults/main.yml @@ -11,6 +11,10 @@ tailscale_advertise_routes: [] # e.g., ["10.203.0.0/16"] tailscale_accept_routes: true tailscale_accept_dns: true +# DNS integration +tailscale_dns_servers: [] # e.g., ["10.203.1.3"] +tailscale_dns_search_domains: [] # e.g., ["doofus.co"] + # Security settings tailscale_shields_up: false tailscale_ssh: true # Enable Tailscale SSH @@ -28,4 +32,5 @@ tailscale_subnet_routes: [] # e.g., ["10.203.0.0/16"] # Exit node configuration tailscale_exit_node: false -tailscale_exit_node_allow_lan_access: true \ No newline at end of file +tailscale_exit_node_allow_lan_access: true + diff --git a/ansible-infrastructure/roles/tailscale/tasks/configure.yml b/ansible-infrastructure/roles/tailscale/tasks/configure.yml index 444ad21..9896392 100644 --- a/ansible-infrastructure/roles/tailscale/tasks/configure.yml +++ b/ansible-infrastructure/roles/tailscale/tasks/configure.yml @@ -38,16 +38,27 @@ - name: Set Tailscale preferences command: > tailscale set - {% if tailscale_shields_up %} - --shields-up - {% else %} - --shields-up=false - {% endif %} - {% if tailscale_ssh %} - --ssh - {% else %} - --ssh=false - {% endif %} + {% if tailscale_shields_up %} + --shields-up + {% else %} + --shields-up=false + {% endif %} + {% if tailscale_ssh %} + --ssh + {% else %} + --ssh=false + {% endif %} + {% if tailscale_accept_dns %} + --accept-dns=true + {% else %} + --accept-dns=false + {% endif %} + {% if tailscale_dns_servers | length > 0 %} + --dns={{ tailscale_dns_servers | join(',') }} + {% endif %} + {% if tailscale_dns_search_domains | length > 0 %} + --search-domains={{ tailscale_dns_search_domains | join(',') }} + {% endif %} when: current_tailscale_config.get('BackendState') == 'Running' become: yes register: tailscale_prefs_result @@ -56,4 +67,4 @@ - name: Get final Tailscale status command: tailscale status register: tailscale_final_status - changed_when: false \ No newline at end of file + changed_when: false diff --git a/scripts/dns-manager.py b/scripts/dns-manager.py index fc3d924..8dd9ead 100755 --- a/scripts/dns-manager.py +++ b/scripts/dns-manager.py @@ -7,6 +7,7 @@ import requests import json import sys +import subprocess from typing import Dict, List, Optional class TechnitiumDNS: @@ -114,6 +115,19 @@ def add_record(self, domain: str, name: str, record_type: str, value: str, ttl: except Exception as e: print(f"Error adding record: {e}") return False + + def add_tailscale_record(self, domain: str, name: str, ttl: int = 3600) -> bool: + """Register this host's Tailscale IP as an A record""" + try: + result = subprocess.run(["tailscale", "ip", "-4"], capture_output=True, text=True, check=True) + ip_addr = result.stdout.strip() + if not ip_addr: + print("Could not determine Tailscale IP") + return False + return self.add_record(domain, name, "A", ip_addr, ttl) + except Exception as e: + print(f"Error retrieving Tailscale IP: {e}") + return False def setup_gitlab_records(self, domain: str = "doofus.co", gitlab_ip: str = "10.203.3.60") -> bool: """Setup DNS records for GitLab deployment""" @@ -149,6 +163,7 @@ def main(): print(" list-zones") print(" setup-gitlab ") print(" add-record ") + print(" register-tailscale ") return # Initialize DNS client @@ -178,9 +193,16 @@ def main(): return domain, name, record_type, value = sys.argv[2:6] dns.add_record(domain, name, record_type, value) + + elif command == "register-tailscale": + if len(sys.argv) < 4: + print("Usage: register-tailscale ") + return + domain, name = sys.argv[2:4] + dns.add_tailscale_record(domain, name) else: print(f"Unknown command: {command}") if __name__ == "__main__": - main() \ No newline at end of file + main()