403 lines
16 KiB
YAML
403 lines
16 KiB
YAML
---
|
||
- name: Развернуть nginx web gateway на Proxmox host
|
||
hosts: proxmox
|
||
become: true
|
||
gather_facts: true
|
||
|
||
vars:
|
||
proxmox_web_gateway_public_hostname: "detmir.example.local"
|
||
proxmox_web_gateway_root: "/var/www/proxmox-web-gateway"
|
||
proxmox_web_gateway_site_name: "proxmox-web-gateway.conf"
|
||
proxmox_web_gateway_site_path: "/etc/nginx/sites-available/{{ proxmox_web_gateway_site_name }}"
|
||
proxmox_web_gateway_site_enabled_path: "/etc/nginx/sites-enabled/{{ proxmox_web_gateway_site_name }}"
|
||
proxmox_web_gateway_tls_dir: "/etc/nginx/ssl/proxmox-web-gateway"
|
||
proxmox_web_gateway_tls_cert_path: "{{ proxmox_web_gateway_tls_dir }}/fullchain.pem"
|
||
proxmox_web_gateway_tls_key_path: "{{ proxmox_web_gateway_tls_dir }}/privkey.pem"
|
||
proxmox_web_gateway_health_path: "/healthz"
|
||
proxmox_web_gateway_auth_realm: "AWatch-rus portal 2026-06-06"
|
||
proxmox_web_gateway_auth_user: "detmir"
|
||
proxmox_web_gateway_auth_file: "/etc/nginx/proxmox-web-gateway.htpasswd"
|
||
proxmox_web_gateway_credentials_file: "/etc/detmir/proxmox-web-gateway.credentials"
|
||
proxmox_web_gateway_routes:
|
||
- slug: "proxmox-gui"
|
||
title: "Proxmox VE"
|
||
category: "Host"
|
||
description: "Основная панель управления Proxmox VE."
|
||
target_url: "https://192.0.2.2:8006/"
|
||
external_enabled: false
|
||
- slug: "file1c-brief"
|
||
title: "1C Executive Brief"
|
||
category: "Management"
|
||
description: "Сводка по предприятиям и рискам 1С."
|
||
target_url: "http://192.0.2.2:8710/manager/brief"
|
||
proxy_path: "/r/file1c/brief"
|
||
proxy_target_url: "http://192.0.2.2:8710/manager/brief"
|
||
external_enabled: true
|
||
- slug: "file1c-actions"
|
||
title: "1C Management Actions"
|
||
category: "Management"
|
||
description: "Очередь действий по предприятиям в 1С."
|
||
target_url: "http://192.0.2.2:8710/manager/actions"
|
||
proxy_path: "/r/file1c/actions"
|
||
proxy_target_url: "http://192.0.2.2:8710/manager/actions"
|
||
external_enabled: true
|
||
- slug: "file1c-finance"
|
||
title: "1C Financial Reporting"
|
||
category: "Management"
|
||
description: "Первый financial board по файловой 1С с разделением ledger/proxy."
|
||
target_url: "http://192.0.2.11:3000/d/1c-file-finance/1c-file-financial-reporting?orgId=1"
|
||
proxy_path: "/d/1c-file-finance/1c-file-financial-reporting?orgId=1"
|
||
external_enabled: true
|
||
- slug: "file1c-telemetry"
|
||
title: "1C Telemetry Board"
|
||
category: "Dashboards"
|
||
description: "Read-only telemetry экран по состоянию файловых баз, reglog и host."
|
||
target_url: "http://192.0.2.11:3000/d/1c-file-telemetry/1c-file-telemetry-board?orgId=1"
|
||
proxy_path: "/d/1c-file-telemetry/1c-file-telemetry-board?orgId=1"
|
||
external_enabled: true
|
||
- slug: "grafana-1c"
|
||
title: "Grafana 1C"
|
||
category: "Dashboards"
|
||
description: "Рабочий file-1c dashboard contour в внешней Grafana."
|
||
target_url: "http://192.0.2.11:3000/d/1c-file-mgmt/1c-file-management-board?orgId=1"
|
||
proxy_path: "/d/1c-file-mgmt/1c-file-management-board?orgId=1"
|
||
external_enabled: true
|
||
- slug: "clickhouse-http"
|
||
title: "ClickHouse HTTP"
|
||
category: "Data"
|
||
description: "HTTP endpoint ClickHouse для file-1C analytics."
|
||
target_url: "http://192.0.2.2:8123/"
|
||
external_enabled: false
|
||
- slug: "influxdb"
|
||
title: "InfluxDB"
|
||
category: "Data"
|
||
description: "InfluxDB LXC на CT 200."
|
||
target_url: "http://192.0.2.10:8086/"
|
||
external_enabled: false
|
||
- slug: "grafana-core"
|
||
title: "Grafana Core"
|
||
category: "Dashboards"
|
||
description: "Отдельный Grafana CT 201."
|
||
target_url: "http://192.0.2.11:3000/"
|
||
proxy_path: "/dashboards"
|
||
external_enabled: true
|
||
- slug: "loki-alloy"
|
||
title: "Grafana Alloy"
|
||
category: "Logs"
|
||
description: "Web UI Alloy на CT 202."
|
||
target_url: "http://192.0.2.12:12345/"
|
||
external_enabled: false
|
||
- slug: "aw-ui"
|
||
title: "AW-rus UI"
|
||
category: "Operations"
|
||
description: "Основной ActivityWatch-Russian Web UI на CT 203."
|
||
target_url: "http://192.0.2.13:5600/"
|
||
proxy_path: "/r/aw/"
|
||
external_enabled: true
|
||
- slug: "aw-worktime"
|
||
title: "AW-rus Management Report"
|
||
category: "Operations"
|
||
description: "Управленческий worktime/report API на CT 203."
|
||
target_url: "http://192.0.2.13:5610/reports/worktime/management?format=html&host=HOST-EXAMPLE"
|
||
proxy_path: "/r/aw-worktime"
|
||
proxy_target_url: "http://192.0.2.13:5610/reports/worktime/management?format=html&host=HOST-EXAMPLE"
|
||
external_enabled: true
|
||
- slug: "detmir-portal"
|
||
title: "DetMir Portal"
|
||
category: "Operations"
|
||
description: "Единый read-only портал для оператора, руководителя и владельца."
|
||
target_url: "http://127.0.0.1:8720/"
|
||
proxy_path: "/portal/"
|
||
external_enabled: true
|
||
|
||
tasks:
|
||
- name: Установить nginx
|
||
ansible.builtin.package:
|
||
name:
|
||
- nginx
|
||
- openssl
|
||
state: present
|
||
|
||
- name: Создать web root gateway
|
||
ansible.builtin.file:
|
||
path: "{{ proxmox_web_gateway_root }}"
|
||
state: directory
|
||
owner: root
|
||
group: root
|
||
mode: "0755"
|
||
|
||
- name: Создать каталог TLS gateway
|
||
ansible.builtin.file:
|
||
path: "{{ proxmox_web_gateway_tls_dir }}"
|
||
state: directory
|
||
owner: root
|
||
group: root
|
||
mode: "0700"
|
||
|
||
- name: Сгенерировать self-signed TLS сертификат gateway
|
||
ansible.builtin.command:
|
||
argv:
|
||
- openssl
|
||
- req
|
||
- -x509
|
||
- -nodes
|
||
- -newkey
|
||
- rsa:2048
|
||
- -sha256
|
||
- -days
|
||
- "3650"
|
||
- -keyout
|
||
- "{{ proxmox_web_gateway_tls_key_path }}"
|
||
- -out
|
||
- "{{ proxmox_web_gateway_tls_cert_path }}"
|
||
- -subj
|
||
- "/CN={{ proxmox_web_gateway_public_hostname }}"
|
||
- -addext
|
||
- "subjectAltName=DNS:{{ proxmox_web_gateway_public_hostname }}"
|
||
args:
|
||
creates: "{{ proxmox_web_gateway_tls_cert_path }}"
|
||
|
||
- name: Проверить наличие gateway Basic Auth файла
|
||
ansible.builtin.stat:
|
||
path: "{{ proxmox_web_gateway_auth_file }}"
|
||
register: proxmox_web_gateway_auth_stat
|
||
|
||
- name: Проверить наличие gateway credential файла
|
||
ansible.builtin.stat:
|
||
path: "{{ proxmox_web_gateway_credentials_file }}"
|
||
register: proxmox_web_gateway_credentials_stat
|
||
|
||
- name: Создать согласованные gateway Basic Auth credential
|
||
ansible.builtin.shell: |
|
||
set -eu
|
||
umask 077
|
||
password="$(openssl rand -base64 24 | tr -d '\n')"
|
||
hash="$(openssl passwd -apr1 "$password")"
|
||
printf '%s:%s\n' '{{ proxmox_web_gateway_auth_user }}' "$hash" > '{{ proxmox_web_gateway_auth_file }}'
|
||
printf 'url=https://{{ proxmox_web_gateway_public_hostname }}/\nuser={{ proxmox_web_gateway_auth_user }}\npassword=%s\n' "$password" > '{{ proxmox_web_gateway_credentials_file }}'
|
||
args:
|
||
executable: /bin/sh
|
||
no_log: true
|
||
when: >
|
||
not (proxmox_web_gateway_auth_stat.stat.exists | default(false))
|
||
or not (proxmox_web_gateway_credentials_stat.stat.exists | default(false))
|
||
|
||
- name: Зафиксировать права gateway Basic Auth файла
|
||
ansible.builtin.file:
|
||
path: "{{ proxmox_web_gateway_auth_file }}"
|
||
owner: root
|
||
group: www-data
|
||
mode: "0640"
|
||
|
||
- name: Зафиксировать права файла с gateway credential
|
||
ansible.builtin.file:
|
||
path: "{{ proxmox_web_gateway_credentials_file }}"
|
||
owner: root
|
||
group: root
|
||
mode: "0600"
|
||
|
||
- name: Прочитать gateway credential для локальной проверки
|
||
ansible.builtin.slurp:
|
||
src: "{{ proxmox_web_gateway_credentials_file }}"
|
||
register: proxmox_web_gateway_credentials_slurp
|
||
no_log: true
|
||
|
||
- name: Подготовить gateway auth password для проверок
|
||
ansible.builtin.set_fact:
|
||
proxmox_web_gateway_auth_password: "{{ (proxmox_web_gateway_credentials_slurp.content | b64decode).split('password=')[1].split('\n')[0] }}"
|
||
no_log: true
|
||
|
||
- name: Развернуть index.html gateway
|
||
ansible.builtin.template:
|
||
src: "templates/proxmox-web-gateway-index.html.j2"
|
||
dest: "{{ proxmox_web_gateway_root }}/index.html"
|
||
owner: root
|
||
group: root
|
||
mode: "0644"
|
||
|
||
- name: Развернуть nginx site gateway
|
||
ansible.builtin.template:
|
||
src: "templates/proxmox-web-gateway.conf.j2"
|
||
dest: "{{ proxmox_web_gateway_site_path }}"
|
||
owner: root
|
||
group: root
|
||
mode: "0644"
|
||
notify: Reload nginx
|
||
|
||
- name: Отключить default nginx site
|
||
ansible.builtin.file:
|
||
path: "/etc/nginx/sites-enabled/default"
|
||
state: absent
|
||
notify: Reload nginx
|
||
|
||
- name: Включить proxmox web gateway site
|
||
ansible.builtin.file:
|
||
src: "{{ proxmox_web_gateway_site_path }}"
|
||
dest: "{{ proxmox_web_gateway_site_enabled_path }}"
|
||
state: link
|
||
notify: Reload nginx
|
||
|
||
- name: Включить и запустить nginx
|
||
ansible.builtin.systemd:
|
||
name: nginx
|
||
enabled: true
|
||
state: started
|
||
|
||
- name: Применить handlers до проверок
|
||
ansible.builtin.meta: flush_handlers
|
||
|
||
- name: Проверить синтаксис nginx после выкладки site
|
||
ansible.builtin.command: "/usr/sbin/nginx -t"
|
||
register: proxmox_web_gateway_nginx_test
|
||
changed_when: false
|
||
|
||
- name: Проверить локальный HTTP redirect на HTTPS
|
||
ansible.builtin.uri:
|
||
url: "http://127.0.0.1/"
|
||
headers:
|
||
Host: "{{ proxmox_web_gateway_public_hostname }}"
|
||
follow_redirects: none
|
||
status_code: 301
|
||
register: proxmox_web_gateway_http_redirect
|
||
failed_when:
|
||
- proxmox_web_gateway_http_redirect.status != 301
|
||
- proxmox_web_gateway_http_redirect.location != "https://{{ proxmox_web_gateway_public_hostname }}/"
|
||
changed_when: false
|
||
|
||
- name: Проверить старый /go путь без auth закрыт
|
||
ansible.builtin.uri:
|
||
url: "https://127.0.0.1/go/proxmox-gui"
|
||
headers:
|
||
Host: "{{ proxmox_web_gateway_public_hostname }}"
|
||
validate_certs: false
|
||
follow_redirects: none
|
||
status_code: 401
|
||
register: proxmox_web_gateway_redirect
|
||
failed_when:
|
||
- proxmox_web_gateway_redirect.status != 401
|
||
changed_when: false
|
||
|
||
- name: Проверить index без auth закрыт
|
||
ansible.builtin.uri:
|
||
url: "https://127.0.0.1/"
|
||
headers:
|
||
Host: "{{ proxmox_web_gateway_public_hostname }}"
|
||
validate_certs: false
|
||
status_code: 401
|
||
changed_when: false
|
||
|
||
- name: Проверить index по public hostname с auth
|
||
ansible.builtin.uri:
|
||
url: "https://127.0.0.1/"
|
||
headers:
|
||
Host: "{{ proxmox_web_gateway_public_hostname }}"
|
||
url_username: "{{ proxmox_web_gateway_auth_user }}"
|
||
url_password: "{{ proxmox_web_gateway_auth_password }}"
|
||
force_basic_auth: true
|
||
return_content: true
|
||
validate_certs: false
|
||
register: proxmox_web_gateway_named_index
|
||
failed_when:
|
||
- proxmox_web_gateway_named_index.status != 200
|
||
- proxmox_web_gateway_public_hostname not in proxmox_web_gateway_named_index.content
|
||
changed_when: false
|
||
|
||
- name: Проверить reverse proxy к 1C brief с auth
|
||
ansible.builtin.uri:
|
||
url: "https://127.0.0.1/r/file1c/brief"
|
||
headers:
|
||
Host: "{{ proxmox_web_gateway_public_hostname }}"
|
||
url_username: "{{ proxmox_web_gateway_auth_user }}"
|
||
url_password: "{{ proxmox_web_gateway_auth_password }}"
|
||
force_basic_auth: true
|
||
validate_certs: false
|
||
status_code: 200
|
||
changed_when: false
|
||
|
||
- name: Проверить reverse proxy к Grafana health с auth
|
||
ansible.builtin.uri:
|
||
url: "https://127.0.0.1/r/grafana/api/health"
|
||
headers:
|
||
Host: "{{ proxmox_web_gateway_public_hostname }}"
|
||
url_username: "{{ proxmox_web_gateway_auth_user }}"
|
||
url_password: "{{ proxmox_web_gateway_auth_password }}"
|
||
force_basic_auth: true
|
||
validate_certs: false
|
||
status_code: 200
|
||
changed_when: false
|
||
|
||
- name: Проверить reverse proxy к AW info с auth
|
||
ansible.builtin.uri:
|
||
url: "https://127.0.0.1/r/aw/api/0/info"
|
||
headers:
|
||
Host: "{{ proxmox_web_gateway_public_hostname }}"
|
||
url_username: "{{ proxmox_web_gateway_auth_user }}"
|
||
url_password: "{{ proxmox_web_gateway_auth_password }}"
|
||
force_basic_auth: true
|
||
validate_certs: false
|
||
status_code: 200
|
||
changed_when: false
|
||
|
||
- name: Проверить reverse proxy к DetMir Portal health с auth
|
||
ansible.builtin.uri:
|
||
url: "https://127.0.0.1/portal/api/health"
|
||
headers:
|
||
Host: "{{ proxmox_web_gateway_public_hostname }}"
|
||
url_username: "{{ proxmox_web_gateway_auth_user }}"
|
||
url_password: "{{ proxmox_web_gateway_auth_password }}"
|
||
force_basic_auth: true
|
||
validate_certs: false
|
||
status_code: 200
|
||
return_content: true
|
||
register: proxmox_web_gateway_portal_health
|
||
failed_when:
|
||
- proxmox_web_gateway_portal_health.status != 200
|
||
- "'sources' not in proxmox_web_gateway_portal_health.content"
|
||
changed_when: false
|
||
|
||
- name: Проверить browser-origin AW API query без 403
|
||
ansible.builtin.uri:
|
||
url: "https://127.0.0.1/api/0/query/"
|
||
method: POST
|
||
headers:
|
||
Host: "{{ proxmox_web_gateway_public_hostname }}"
|
||
Origin: "https://{{ proxmox_web_gateway_public_hostname }}"
|
||
Referer: "https://{{ proxmox_web_gateway_public_hostname }}/r/aw/"
|
||
Content-Type: "application/json"
|
||
body_format: json
|
||
body:
|
||
timeperiods:
|
||
- "1970-01-01T00:00:00+00:00/1970-01-01T00:01:00+00:00"
|
||
query:
|
||
- "RETURN = [];"
|
||
url_username: "{{ proxmox_web_gateway_auth_user }}"
|
||
url_password: "{{ proxmox_web_gateway_auth_password }}"
|
||
force_basic_auth: true
|
||
validate_certs: false
|
||
status_code: 200
|
||
return_content: true
|
||
register: proxmox_web_gateway_aw_query
|
||
failed_when:
|
||
- proxmox_web_gateway_aw_query.status != 200
|
||
- proxmox_web_gateway_aw_query.content != "[[]]"
|
||
changed_when: false
|
||
|
||
- name: Проверить локальный HTTPS health endpoint gateway
|
||
ansible.builtin.uri:
|
||
url: "https://127.0.0.1{{ proxmox_web_gateway_health_path }}"
|
||
headers:
|
||
Host: "{{ proxmox_web_gateway_public_hostname }}"
|
||
return_content: true
|
||
validate_certs: false
|
||
register: proxmox_web_gateway_https_health
|
||
failed_when:
|
||
- proxmox_web_gateway_https_health.status != 200
|
||
- "'ok' not in proxmox_web_gateway_https_health.content"
|
||
changed_when: false
|
||
|
||
handlers:
|
||
- name: Reload nginx
|
||
ansible.builtin.systemd:
|
||
name: nginx
|
||
state: reloaded
|