commit eb458d687b4f204afbf7b9abfd1a84946436f3e6 Author: Fedor Date: Mon Feb 16 18:18:13 2026 +0300 init diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..ec58a48 --- /dev/null +++ b/.env.example @@ -0,0 +1,10 @@ +# PostgreSQL для n8n (не коммитить .env в репозиторий) +POSTGRES_USER=n8n +POSTGRES_PASSWORD=<сгенерировать_надёжный_пароль> +POSTGRES_DB=n8n + +# Внешний прокси: только домены из proxy/squid.conf идут через него +EXTERNAL_PROXY_HOST=proxy.example.com +EXTERNAL_PROXY_PORT=3128 +EXTERNAL_PROXY_USER= +EXTERNAL_PROXY_PASS= diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c49bd7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.env diff --git a/README.md b/README.md new file mode 100644 index 0000000..c3c7c43 --- /dev/null +++ b/README.md @@ -0,0 +1,54 @@ +# n8n на n8n.clientright.ru + +Развёртывание n8n в Docker с PostgreSQL, SSL (Let's Encrypt) и выборочным прокси для части доменов (OpenAI, Anthropic и др.). + +## Состав + +- **n8n** (n8nio/n8n:2.7.5) — за Nginx по HTTPS +- **PostgreSQL 16** — хранилище n8n +- **Squid** — локальный прокси: часть доменов идёт через внешний прокси, остальное — напрямую + +## Требования + +- Docker и Docker Compose +- Nginx как reverse proxy с SSL для `n8n.clientright.ru` +- Файл `.env` (см. ниже) + +## Быстрый старт + +1. Скопировать `.env.example` в `.env` и заполнить значения (пароли, внешний прокси при необходимости). +2. Запуск: + ```bash + docker compose up -d + ``` +3. Открыть https://n8n.clientright.ru и создать первого пользователя. + +## Переменные окружения (.env) + +Файл `.env` не коммитится в репозиторий. Пример структуры — в `.env.example`. + +- **POSTGRES_*** — пользователь, пароль и БД для PostgreSQL. +- **EXTERNAL_PROXY_*** — внешний HTTP-прокси (host, port, при необходимости user/pass). Через него идут только домены из `proxy/squid.conf` (по умолчанию openai.com, anthropic.com, googleapis.com). +- Остальной трафик n8n выходит в интернет напрямую. + +## Прокси + +- n8n использует только локальный Squid (`http://proxy:3128`). +- В `proxy/squid.conf` заданы домены (acl `to_parent_proxy`), которые маршрутизируются во внешний прокси; для них включён `never_direct allow to_parent_proxy`. +- Добавить домен: дописать в `proxy/squid.conf` строку вида `acl to_parent_proxy dstdomain .example.com`, пересобрать и перезапустить proxy: + `docker compose build proxy && docker compose up -d proxy`. + +## Обновление n8n + +Изменить тег образа в `docker-compose.yml` (например, `n8nio/n8n:2.7.6`), затем: + +```bash +docker compose pull n8n +docker compose up -d n8n +``` + +## Файлы + +- `docker-compose.yml` — сервисы n8n, postgres, proxy +- `proxy/` — образ и конфиг Squid (Dockerfile, squid.conf, entrypoint.sh) +- `n8n.clientright.ru.conf` — пример конфига Nginx (реальный конфиг на сервере в `/etc/nginx/conf.d/`) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..6e0330a --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,59 @@ +services: + proxy: + build: ./proxy + restart: always + env_file: .env + environment: + EXTERNAL_PROXY_HOST: ${EXTERNAL_PROXY_HOST:-} + EXTERNAL_PROXY_PORT: ${EXTERNAL_PROXY_PORT:-} + EXTERNAL_PROXY_USER: ${EXTERNAL_PROXY_USER:-} + EXTERNAL_PROXY_PASS: ${EXTERNAL_PROXY_PASS:-} + # Порт только внутри Docker-сети, наружу не светим + + postgres: + image: postgres:16-alpine + restart: always + env_file: .env + environment: + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: ${POSTGRES_DB} + volumes: + - n8n_postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -h localhost -U ${POSTGRES_USER:-n8n} -d ${POSTGRES_DB:-n8n}"] + interval: 5s + timeout: 5s + retries: 10 + + n8n: + image: n8nio/n8n:2.7.5 + restart: always + depends_on: + postgres: + condition: service_healthy + proxy: + condition: service_started + env_file: .env + environment: + DB_TYPE: postgresdb + DB_POSTGRESDB_HOST: postgres + DB_POSTGRESDB_PORT: 5432 + DB_POSTGRESDB_DATABASE: ${POSTGRES_DB} + DB_POSTGRESDB_USER: ${POSTGRES_USER} + DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD} + N8N_PROTOCOL: https + N8N_HOST: n8n.clientright.ru + WEBHOOK_URL: https://n8n.clientright.ru/ + # Весь трафик n8n — через локальный Squid; выборочно домены идут во внешний прокси + HTTP_PROXY: http://proxy:3128 + HTTPS_PROXY: http://proxy:3128 + NO_PROXY: postgres,127.0.0.1,localhost,n8n.clientright.ru + ports: + - "127.0.0.1:5678:5678" + volumes: + - n8n_data:/home/node/.n8n + +volumes: + n8n_postgres_data: + n8n_data: diff --git a/n8n.clientright.ru.conf b/n8n.clientright.ru.conf new file mode 100644 index 0000000..8fe1b9e --- /dev/null +++ b/n8n.clientright.ru.conf @@ -0,0 +1,38 @@ +# n8n.clientright.ru +server { + listen 147.45.146.17:80; + server_name n8n.clientright.ru; + + location /.well-known/acme-challenge/ { + root /var/www/fastuser/data/www/n8n.clientright.ru; + } + + location / { + return 301 https://$host$request_uri; + } +} + +server { + listen 147.45.146.17:443 ssl http2; + server_name n8n.clientright.ru; + + ssl_certificate /etc/letsencrypt/live/n8n.clientright.ru/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/n8n.clientright.ru/privkey.pem; + ssl_trusted_certificate /etc/letsencrypt/live/n8n.clientright.ru/chain.pem; + + add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; + + location / { + proxy_pass http://127.0.0.1:5678; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_read_timeout 300; + proxy_connect_timeout 60; + proxy_buffering off; + } +} diff --git a/proxy/Dockerfile b/proxy/Dockerfile new file mode 100644 index 0000000..38d1c41 --- /dev/null +++ b/proxy/Dockerfile @@ -0,0 +1,9 @@ +FROM alpine:3.19 +RUN apk add --no-cache squid \ + && mkdir -p /var/log/squid /var/cache/squid /var/run/squid \ + && chown -R squid:squid /var/log/squid /var/cache/squid /var/run/squid +COPY squid.conf /etc/squid/squid.conf +COPY entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh +EXPOSE 3128 +ENTRYPOINT ["/entrypoint.sh"] diff --git a/proxy/entrypoint.sh b/proxy/entrypoint.sh new file mode 100644 index 0000000..b581fc2 --- /dev/null +++ b/proxy/entrypoint.sh @@ -0,0 +1,15 @@ +#!/bin/sh +set -e +mkdir -p /etc/squid/conf.d +# Родительский прокси: host и port из .env (генерируем одну строку конфига) +if [ -n "$EXTERNAL_PROXY_HOST" ] && [ -n "$EXTERNAL_PROXY_PORT" ]; then + _auth="" + if [ -n "$EXTERNAL_PROXY_USER" ] && [ -n "$EXTERNAL_PROXY_PASS" ]; then + _auth=" login=${EXTERNAL_PROXY_USER}:${EXTERNAL_PROXY_PASS}" + fi + echo "cache_peer ${EXTERNAL_PROXY_HOST} parent ${EXTERNAL_PROXY_PORT} 0 no-query default name=parent${_auth}" > /etc/squid/conf.d/parent.conf +else + echo "# Внешний прокси не задан (EXTERNAL_PROXY_HOST/PORT). Заполни .env и перезапусти." > /etc/squid/conf.d/parent.conf + echo "cache_peer no-proxy.invalid parent 1 0 no-query default name=parent" >> /etc/squid/conf.d/parent.conf +fi +exec squid -f /etc/squid/squid.conf -N diff --git a/proxy/squid.conf b/proxy/squid.conf new file mode 100644 index 0000000..0e307be --- /dev/null +++ b/proxy/squid.conf @@ -0,0 +1,39 @@ +# Локальный прокси для n8n: часть доменов — через внешний прокси, остальное — напрямую +# Внешний прокси задаётся в .env: EXTERNAL_PROXY_HOST, EXTERNAL_PROXY_PORT +# Домены для внешнего прокси — список ниже (acl to_parent_proxy) + +http_port 3128 + +# Доступ только из Docker-сети (n8n) +acl localnet src 10.0.0.0/8 +acl localnet src 172.16.0.0/12 +acl localnet src 192.168.0.0/16 + +acl SSL_ports port 443 +acl Safe_ports port 80 443 +acl CONNECT method CONNECT + +# Подключаем сгенерированный конфиг с cache_peer (host/port из env) +include /etc/squid/conf.d/parent.conf + +# Домены, которые идут через внешний прокси (остальные — напрямую) +acl to_parent_proxy dstdomain .openai.com +acl to_parent_proxy dstdomain .anthropic.com +acl to_parent_proxy dstdomain .googleapis.com +# Добавь при необходимости: .azure.com, .aws.amazon.com и т.д. + +# Родительский прокси только для этих доменов +cache_peer_access parent allow to_parent_proxy +cache_peer_access parent deny all + +# Важно: CONNECT (HTTPS) по умолчанию Squid может слать напрямую, а не в parent. +# Запрещаем прямой доступ для этих доменов — только через parent. +never_direct allow to_parent_proxy + +# Таймауты при работе через родительский прокси (увеличить при медленном прокси) +connect_timeout 30 seconds +read_timeout 60 seconds + +# Разрешаем запросы из нашей сети +http_access allow localnet +http_access deny all