Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
31 changes: 31 additions & 0 deletions ansible-infrastructure/playbooks/deployment/htpc-nas.yml
Original file line number Diff line number Diff line change
@@ -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') }}"

Original file line number Diff line number Diff line change
@@ -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('') }}"

Original file line number Diff line number Diff line change
@@ -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"
7 changes: 6 additions & 1 deletion ansible-infrastructure/roles/tailscale/defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
tailscale_exit_node_allow_lan_access: true

33 changes: 22 additions & 11 deletions ansible-infrastructure/roles/tailscale/tasks/configure.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -56,4 +67,4 @@
- name: Get final Tailscale status
command: tailscale status
register: tailscale_final_status
changed_when: false
changed_when: false
24 changes: 23 additions & 1 deletion scripts/dns-manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import requests
import json
import sys
import subprocess
from typing import Dict, List, Optional

class TechnitiumDNS:
Expand Down Expand Up @@ -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"""
Expand Down Expand Up @@ -149,6 +163,7 @@ def main():
print(" list-zones")
print(" setup-gitlab <domain> <ip>")
print(" add-record <domain> <name> <type> <value>")
print(" register-tailscale <domain> <name>")
return

# Initialize DNS client
Expand Down Expand Up @@ -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 <domain> <name>")
return
domain, name = sys.argv[2:4]
dns.add_tailscale_record(domain, name)

else:
print(f"Unknown command: {command}")

if __name__ == "__main__":
main()
main()