feat: add PHP ini management system and update infrastructure configs

- Add PHP ini management classes (Access, IniDirective, IniKey, PhpIni)
- Update deployment configurations (Wireguard, Traefik, Monitoring)
- Add DNS stack and Ansible role
- Add deployment debugging playbooks
- Update framework components (FilePath, RedisConnectionPool)
- Update .gitignore and documentation
This commit is contained in:
2025-11-02 15:29:41 +01:00
parent e628d30fa0
commit edcf509a4f
29 changed files with 926 additions and 39 deletions

View File

@@ -10,6 +10,7 @@
wireguard_config_file: "{{ wireguard_config_path }}/{{ wireguard_interface }}.conf"
wireguard_client_configs_path: "/etc/wireguard/clients"
wireguard_local_client_configs_dir: "{{ playbook_dir }}/../wireguard-clients"
wireguard_dns_servers: []
pre_tasks:
- name: Set WireGuard network
@@ -61,6 +62,11 @@
set_fact:
server_vpn_ip: "{{ (wireguard_server_config_read.content | b64decode | regex_search('Address = ([0-9.]+)', '\\1')) | first | default('10.8.0.1') }}"
- name: Set default DNS servers if not provided
set_fact:
wireguard_dns_servers: "{{ [server_vpn_ip] }}"
when: wireguard_dns_servers | length == 0
- name: Extract WireGuard server IP octets
set_fact:
wireguard_server_ip_octets: "{{ server_vpn_ip.split('.') }}"

View File

@@ -0,0 +1,147 @@
---
- name: Check Production Server Status
hosts: production
gather_facts: yes
become: no
tasks:
- name: Check server uptime and basic info
shell: |
echo "=== Server Uptime ==="
uptime
echo ""
echo "=== Disk Space ==="
df -h
echo ""
echo "=== Memory Usage ==="
free -h
echo ""
echo "=== Docker Status ==="
docker --version || echo "Docker not found"
docker ps || echo "Docker not running"
args:
executable: /bin/bash
register: server_info
ignore_errors: yes
failed_when: false
- name: Display server info
debug:
msg: "{{ server_info.stdout_lines }}"
- name: Check all Docker stacks status
shell: |
echo "=== Traefik Stack ==="
cd ~/deployment/stacks/traefik && docker compose ps 2>&1 || echo "Traefik stack not found or not running"
echo ""
echo "=== Application Stack ==="
cd ~/deployment/stacks/application && docker compose ps 2>&1 || echo "Application stack not found or not running"
echo ""
echo "=== PostgreSQL Stack ==="
cd ~/deployment/stacks/postgresql && docker compose ps 2>&1 || echo "PostgreSQL stack not found or not running"
echo ""
echo "=== Monitoring Stack ==="
cd ~/deployment/stacks/monitoring && docker compose ps 2>&1 || echo "Monitoring stack not found or not running"
echo ""
echo "=== Gitea Stack ==="
cd ~/deployment/stacks/gitea && docker compose ps 2>&1 || echo "Gitea stack not found or not running"
echo ""
echo "=== Registry Stack ==="
cd ~/deployment/stacks/registry && docker compose ps 2>&1 || echo "Registry stack not found or not running"
args:
executable: /bin/bash
register: stacks_status
ignore_errors: yes
failed_when: false
- name: Display stacks status
debug:
msg: "{{ stacks_status.stdout_lines }}"
- name: Check Traefik logs for errors
shell: |
cd ~/deployment/stacks/traefik
echo "=== Traefik Logs (Last 30 lines) ==="
docker compose logs --tail=30 traefik 2>&1 | tail -30 || echo "Could not read Traefik logs"
args:
executable: /bin/bash
register: traefik_logs
ignore_errors: yes
failed_when: false
- name: Display Traefik logs
debug:
msg: "{{ traefik_logs.stdout_lines }}"
- name: Check Application stack logs
shell: |
cd ~/deployment/stacks/application
echo "=== Application Nginx Logs (Last 20 lines) ==="
docker compose logs --tail=20 web 2>&1 | tail -20 || echo "Could not read web logs"
echo ""
echo "=== Application PHP Logs (Last 20 lines) ==="
docker compose logs --tail=20 php 2>&1 | tail -20 || echo "Could not read PHP logs"
args:
executable: /bin/bash
register: app_logs
ignore_errors: yes
failed_when: false
- name: Display application logs
debug:
msg: "{{ app_logs.stdout_lines }}"
- name: Test HTTP connectivity
shell: |
echo "=== Testing HTTP Connectivity ==="
echo "Test 1: HTTPS to michaelschiemer.de"
curl -k -H "User-Agent: Mozilla/5.0" -s -o /dev/null -w "HTTP %{http_code}\n" https://michaelschiemer.de/health || echo "Connection failed"
echo ""
echo "Test 2: Direct localhost"
curl -k -H "User-Agent: Mozilla/5.0" -s -o /dev/null -w "HTTP %{http_code}\n" https://localhost/health || echo "Connection failed"
args:
executable: /bin/bash
register: http_tests
ignore_errors: yes
failed_when: false
- name: Display HTTP test results
debug:
msg: "{{ http_tests.stdout_lines }}"
- name: Check network connectivity
shell: |
echo "=== Network Interfaces ==="
ip addr show | grep -E "(inet |state)" | head -10
echo ""
echo "=== Docker Networks ==="
docker network ls
echo ""
echo "=== Traefik Network Connectivity ==="
docker network inspect traefik-public 2>&1 | grep -E "(Name|Subnet|Containers)" | head -10 || echo "Traefik network not found"
args:
executable: /bin/bash
register: network_info
ignore_errors: yes
failed_when: false
- name: Display network info
debug:
msg: "{{ network_info.stdout_lines }}"
- name: Check firewall status
shell: |
echo "=== Firewall Status ==="
sudo ufw status || echo "UFW not installed or not configured"
echo ""
echo "=== Listening Ports ==="
sudo netstat -tlnp | grep -E "(80|443|8080|3000)" | head -10 || ss -tlnp | grep -E "(80|443|8080|3000)" | head -10 || echo "Could not check listening ports"
args:
executable: /bin/bash
register: firewall_info
ignore_errors: yes
failed_when: false
- name: Display firewall info
debug:
msg: "{{ firewall_info.stdout_lines }}"

View File

@@ -0,0 +1,49 @@
---
- name: Check Staging 500 Error
hosts: production
gather_facts: yes
become: no
tasks:
- name: Get recent PHP errors from staging-app
shell: |
cd ~/deployment/stacks/staging
echo "=== Recent PHP errors (last 50 lines) ==="
docker compose exec -T staging-app tail -100 /var/www/html/storage/logs/php-errors.log 2>&1 | tail -50
args:
executable: /bin/bash
register: php_errors
ignore_errors: yes
failed_when: false
- name: Display PHP errors
debug:
msg: "{{ php_errors.stdout_lines }}"
- name: Get docker compose logs for staging-app
shell: |
cd ~/deployment/stacks/staging
echo "=== Recent staging-app container logs ==="
docker compose logs --tail=50 staging-app 2>&1 | tail -50
args:
executable: /bin/bash
register: container_logs
ignore_errors: yes
failed_when: false
- name: Display container logs
debug:
msg: "{{ container_logs.stdout_lines }}"
- name: Test health endpoint
shell: |
curl -H "User-Agent: Mozilla/5.0" -s https://staging.michaelschiemer.de/health 2>&1
args:
executable: /bin/bash
register: health_test
ignore_errors: yes
failed_when: false
- name: Display health endpoint result
debug:
msg: "{{ health_test.stdout }}"

View File

@@ -0,0 +1,125 @@
---
- name: Debug Grafana 403 Error
hosts: production
gather_facts: yes
become: no
# This playbook requires the production inventory file
# Run with: ansible-playbook -i ../inventory/production.yml debug-grafana-403.yml
tasks:
- name: Check Traefik logs for recent Grafana access attempts
shell: |
cd ~/deployment/stacks/traefik
echo "=== Recent Traefik Access Logs (last 50 lines with grafana) ==="
docker compose logs --tail=100 traefik 2>&1 | grep -i grafana | tail -50 || echo "No grafana entries found"
args:
executable: /bin/bash
register: traefik_logs
ignore_errors: yes
failed_when: false
- name: Display Traefik logs
debug:
msg: "{{ traefik_logs.stdout_lines }}"
- name: Check Traefik access log file
shell: |
cd ~/deployment/stacks/traefik
echo "=== Recent Traefik Access Log (last 50 lines) ==="
tail -50 logs/access.log 2>&1 | tail -50 || echo "Access log not found"
args:
executable: /bin/bash
register: access_log
ignore_errors: yes
failed_when: false
- name: Display access log
debug:
msg: "{{ access_log.stdout_lines }}"
- name: Check Grafana container status
shell: |
cd ~/deployment/stacks/monitoring
docker compose ps grafana
args:
executable: /bin/bash
register: grafana_status
ignore_errors: yes
failed_when: false
- name: Display Grafana status
debug:
msg: "{{ grafana_status.stdout_lines }}"
- name: Check Grafana Traefik labels
shell: |
cd ~/deployment/stacks/monitoring
docker compose config | grep -A 20 "grafana:" | grep -E "(ipwhitelist|middleware|sourcerange)" || echo "No IP whitelist labels found"
args:
executable: /bin/bash
register: grafana_labels
ignore_errors: yes
failed_when: false
- name: Display Grafana labels
debug:
msg: "{{ grafana_labels.stdout_lines }}"
- name: Check CoreDNS configuration
shell: |
cd ~/deployment/stacks/dns
echo "=== CoreDNS Corefile ==="
cat Corefile 2>&1 || echo "Corefile not found"
args:
executable: /bin/bash
register: coredns_config
ignore_errors: yes
failed_when: false
- name: Display CoreDNS configuration
debug:
msg: "{{ coredns_config.stdout_lines }}"
- name: Check monitoring stack environment variables
shell: |
cd ~/deployment/stacks/monitoring
echo "=== MONITORING_VPN_IP_WHITELIST ==="
grep MONITORING_VPN_IP_WHITELIST .env 2>&1 || echo "Variable not found in .env"
args:
executable: /bin/bash
register: monitoring_env
ignore_errors: yes
failed_when: false
- name: Display monitoring environment
debug:
msg: "{{ monitoring_env.stdout_lines }}"
- name: Test DNS resolution for grafana.michaelschiemer.de
shell: |
echo "=== DNS Resolution Test ==="
dig +short grafana.michaelschiemer.de @10.8.0.1 2>&1 || echo "DNS resolution failed"
args:
executable: /bin/bash
register: dns_test
ignore_errors: yes
failed_when: false
- name: Display DNS test result
debug:
msg: "{{ dns_test.stdout_lines }}"
- name: Check WireGuard interface status
shell: |
echo "=== WireGuard Interface Status ==="
sudo wg show 2>&1 || echo "WireGuard not running or no permissions"
args:
executable: /bin/bash
register: wg_status
ignore_errors: yes
failed_when: false
- name: Display WireGuard status
debug:
msg: "{{ wg_status.stdout_lines }}"

View File

@@ -0,0 +1,142 @@
---
- name: Fix Traefik Configuration
hosts: production
gather_facts: no
become: no
tasks:
- name: Backup current traefik.yml
shell: |
cd ~/deployment/stacks/traefik
cp traefik.yml traefik.yml.backup.$(date +%Y%m%d_%H%M%S)
args:
executable: /bin/bash
- name: Create correct traefik.yml
copy:
content: |
# Static Configuration for Traefik
# Global Configuration
global:
checkNewVersion: true
sendAnonymousUsage: false
# API and Dashboard
# Note: insecure: false means API is only accessible via HTTPS (through Traefik itself)
# No port 8080 needed - dashboard accessible via HTTPS at traefik.michaelschiemer.de
api:
dashboard: true
insecure: false
# Dashboard accessible via HTTPS router (no separate HTTP listener needed)
# Entry Points
entryPoints:
web:
address: ":80"
# No global redirect - ACME challenges need HTTP access
# Redirects are handled per-router via middleware
websecure:
address: ":443"
http:
tls:
certResolver: letsencrypt
domains:
- main: michaelschiemer.de
sans:
- "*.michaelschiemer.de"
# Certificate Resolvers
certificatesResolvers:
letsencrypt:
acme:
email: kontakt@michaelschiemer.de
storage: /acme.json
caServer: https://acme-v02.api.letsencrypt.org/directory
# Use HTTP-01 challenge (requires port 80 accessible)
httpChallenge:
entryPoint: web
# Uncomment for DNS challenge (requires DNS provider)
# dnsChallenge:
# provider: cloudflare
# delayBeforeCheck: 30
# Providers
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
# Network mode is 'host', so we don't specify a network here
# Traefik can reach containers directly via their IPs in host network mode
watch: true
file:
directory: /dynamic
watch: true
# Forwarded Headers Configuration
# This ensures Traefik correctly identifies the real client IP
# Important for VPN access where requests come from WireGuard interface
forwardedHeaders:
trustedIPs:
- "10.8.0.0/24" # WireGuard VPN network
- "127.0.0.1/32" # Localhost
- "172.17.0.0/16" # Docker bridge network
- "172.18.0.0/16" # Docker user-defined networks
insecure: false
# Logging
log:
level: INFO
filePath: /logs/traefik.log
format: json
# Access Logs
accessLog:
filePath: /logs/access.log
format: json
bufferingSize: 100
filters:
statusCodes:
- "400-499"
- "500-599"
# Metrics
metrics:
prometheus:
addEntryPointsLabels: true
addRoutersLabels: true
addServicesLabels: true
# Ping
ping:
entryPoint: web
dest: ~/deployment/stacks/traefik/traefik.yml
mode: '0644'
- name: Validate YAML syntax
command: python3 -c "import yaml; yaml.safe_load(open('traefik.yml')); print('YAML valid')"
args:
chdir: ~/deployment/stacks/traefik
changed_when: false
- name: Restart Traefik
command: docker compose up -d traefik
args:
chdir: ~/deployment/stacks/traefik
register: traefik_restart
- name: Wait for Traefik to start
pause:
seconds: 5
- name: Check Traefik status
command: docker compose ps traefik
args:
chdir: ~/deployment/stacks/traefik
register: traefik_status
- name: Display Traefik status
debug:
msg: "{{ traefik_status.stdout_lines }}"

View File

@@ -59,17 +59,22 @@
import_role:
name: registry
# 4. Deploy MinIO (Object Storage)
# 4. Deploy DNS (CoreDNS for WireGuard clients)
- name: Deploy DNS stack
import_role:
name: dns
# 5. Deploy MinIO (Object Storage)
- name: Deploy MinIO stack
import_role:
name: minio
# 5. Deploy Gitea (CRITICAL - Git Server + MySQL + Redis)
# 6. Deploy Gitea (CRITICAL - Git Server + MySQL + Redis)
- name: Deploy Gitea stack
import_role:
name: gitea
# 6. Deploy Monitoring (Portainer + Grafana + Prometheus)
# 7. Deploy Monitoring (Portainer + Grafana + Prometheus)
- name: Deploy Monitoring stack
import_role:
name: monitoring
@@ -98,7 +103,7 @@
debug:
msg: "Gitea HTTPS check: {{ 'SUCCESS' if gitea_http_check.status == 200 else 'FAILED - Status: ' + (gitea_http_check.status|string) }}"
# 7. Deploy Application Stack
# 8. Deploy Application Stack
- name: Deploy Application Stack
import_role:
name: application
@@ -131,6 +136,7 @@
- "Traefik: {{ 'Deployed' if traefik_stack_changed else 'Already running' }}"
- "PostgreSQL: {{ 'Deployed' if postgresql_stack_changed else 'Already running' }}"
- "Docker Registry: {{ 'Deployed' if registry_stack_changed else 'Already running' }}"
- "DNS: {{ 'Deployed' if dns_stack_changed else 'Already running' }}"
- "MinIO: {{ 'Deployed' if minio_stack_changed else 'Already running' }}"
- "Gitea: {{ 'Deployed' if gitea_stack_changed else 'Already running' }}"
- "Monitoring: {{ 'Deployed' if monitoring_stack_changed else 'Already running' }}"