From 034096d16d214a40ab23e8b6dd6e59f619be72c0 Mon Sep 17 00:00:00 2001 From: Repinoid Date: Mon, 28 Sep 2026 08:04:52 +0300 Subject: [PATCH] =?UTF-8?q?chore:=20=D0=B1=D1=8D=D0=BA=D0=B0=D0=BF=20FPipe?= =?UTF-8?q?Gmail=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B4=20=D0=B4=D0=BE=D0=B1?= =?UTF-8?q?=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=D0=BC=20=D0=92=D0=9C?= =?UTF-8?q?=20(=D0=B1=D0=B5=D0=B7=20tfvars/tfstate=20=E2=80=94=20=D0=BE?= =?UTF-8?q?=D0=BD=D0=B8=20=D0=B2=20.gitignore)=20+=20HISTORY=20=D0=BF?= =?UTF-8?q?=D0=BE=20vpn-transit-213?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HISTORY/2026-09-28_vpn-transit-213-vultr.md | 96 +++++++++++ TMP/backup_2026-09-28/FPipeGmail/edge.tf | 28 +++ TMP/backup_2026-09-28/FPipeGmail/modifiers.tf | 60 +++++++ TMP/backup_2026-09-28/FPipeGmail/outputs.tf | 29 ++++ TMP/backup_2026-09-28/FPipeGmail/provider.tf | 4 + TMP/backup_2026-09-28/FPipeGmail/shturval.tf | 161 ++++++++++++++++++ TMP/backup_2026-09-28/FPipeGmail/variables.tf | 116 +++++++++++++ TMP/backup_2026-09-28/FPipeGmail/vdc.tf | 20 +++ TMP/backup_2026-09-28/FPipeGmail/versions.tf | 10 ++ 9 files changed, 524 insertions(+) create mode 100644 HISTORY/2026-09-28_vpn-transit-213-vultr.md create mode 100644 TMP/backup_2026-09-28/FPipeGmail/edge.tf create mode 100644 TMP/backup_2026-09-28/FPipeGmail/modifiers.tf create mode 100644 TMP/backup_2026-09-28/FPipeGmail/outputs.tf create mode 100644 TMP/backup_2026-09-28/FPipeGmail/provider.tf create mode 100644 TMP/backup_2026-09-28/FPipeGmail/shturval.tf create mode 100644 TMP/backup_2026-09-28/FPipeGmail/variables.tf create mode 100644 TMP/backup_2026-09-28/FPipeGmail/vdc.tf create mode 100644 TMP/backup_2026-09-28/FPipeGmail/versions.tf diff --git a/HISTORY/2026-09-28_vpn-transit-213-vultr.md b/HISTORY/2026-09-28_vpn-transit-213-vultr.md new file mode 100644 index 0000000..3dfaa86 --- /dev/null +++ b/HISTORY/2026-09-28_vpn-transit-213-vultr.md @@ -0,0 +1,96 @@ +# VPN transit via VM 213 and Vultr + +Date: 2026-09-27 to 2026-09-28 + +## Goal + +Provide access from Russian residential/mobile networks to services restricted by Russian network filtering, while retaining the existing foreign egress on Vultr. + +## Verified network facts + +- Test host `3060`: `46.39.251.163`, connection from Khimki / Iskratelecom. +- Transit VM `213`: `5.172.178.213`, public egress observed as `5.172.178.65`; hosted in NUBES data centre. +- Vultr addresses: primary `95.179.252.111`; secondary `104.238.177.67`. +- `3060 -> 213`: ICMP approximately 3 ms, 0% loss. +- `213 -> Vultr`: ICMP approximately 34 ms, 0% loss; HTTPS response returned in about 0.07-0.11 s. +- Direct `213 -> Vultr` test file transfer: 10 MiB in 1.59 s, about 6.27 MiB/s / 50.2 Mbit/s. +- Direct `3060 -> Vultr` test file transfer timed out / was throttled. +- Direct `213 -> OVH proof endpoint`: 10 MiB in 1.18 s, about 8.5 MiB/s. +- Direct access from `213` to YouTube and Telegram failed with `HTTP=000` and timeout/SSL errors, while OVH and Google returned HTTP 200. Therefore a foreign egress remains required for those services. + +## Persistent changes on VM 213 + +- Created backup: + - `/etc/nginx/sites-available/check.kube5s.ru.bak_vpn` +- Modified: + - `/etc/nginx/sites-available/check.kube5s.ru` +- Added an Nginx `/ws` reverse-proxy location with: + - upstream `https://95.179.252.111:443` + - SNI `vipien.kube5s.ru` + - upstream Host header `vipien.kube5s.ru` + - WebSocket upgrade headers + - 3600-second proxy timeouts +- Ran `nginx -t` successfully and reloaded Nginx. +- Existing unrelated Nginx warnings about duplicate `contracts.kube5s.ru` server names remained. + +## Persistent/previously existing changes on Vultr + +The following configuration was read or used during validation: + +- `/etc/nginx/conf.d/vipien.conf`: TLS/WebSocket endpoint for `vipien.kube5s.ru`. +- `/etc/v2ray-agent/xray/conf/08_VLESS_ws_inbound.json`: VLESS WebSocket inbound on `127.0.0.1:10086`, path `/ws`. +- `/etc/systemd/system/hysteria-server.service`: Hysteria service was stopped and disabled; it was not changed in this work. +- Xray service was confirmed active. +- Nginx service was confirmed active. +- Cloudflared tunnel configuration was inspected earlier, but it is not used by the final working route. +- A temporary 10 MiB test file was created on Vultr and removed after testing. + +## Temporary files on test VM 3060 + +The following temporary client files were created under `/tmp/xray-test/` for validation and are not repository files: + +- `client-cf.json` +- `client-213.json` +- `client-directip.json` +- temporary log/test artifacts where applicable + +The files contained test Xray client configurations. They were used only to verify the route from `3060`; no permanent system service was installed there. + +## Final tested route + +`client in Russia -> 5.172.178.213:443 -> Nginx WebSocket proxy -> 95.179.252.111:443 -> Xray -> Internet` + +Final test from `3060` through the route: + +- observed outbound IP: `95.179.252.111` +- 10 MiB OVH download: 1.76-1.91 s +- measured speed: approximately 5.5-6.0 MiB/s + +## Final client parameters + +- Address: `5.172.178.213` +- Port: `443` +- UUID: existing UUID used by the Vultr Xray inbound +- TLS SNI: `check.kube5s.ru` +- WebSocket path: `/ws` +- WebSocket Host: `vipien.kube5s.ru` + +The final direct-IP test used Xray 26.3.27. The client-side `allowInsecure` option was not used because this Xray version reports that the option was removed. + +## Secondary Vultr IP + +Before removal, the Nginx upstream on VM 213 was switched from `104.238.177.67` to `95.179.252.111`. A post-switch end-to-end test succeeded, with outbound IP `95.179.252.111` and approximately 6.0 MiB/s. + +No Vultr IP deletion was performed in this work. The secondary address was only confirmed as no longer referenced by the transit configuration. + +## Scope audit + +- No repository source/configuration files were edited before this record. +- `git status` was clean before this documentation file was created. +- This documentation file is the only workspace file created by the current documentation action. +- Server-side files were changed on VM 213 and earlier on Vultr; temporary test files were also created on VM 3060. +- No commit was created for this record. + +## Important limitations + +The measurements prove the route worked at test time. They do not guarantee permanent availability: NUBES, Vultr, upstream providers, or network filtering policy can change independently. diff --git a/TMP/backup_2026-09-28/FPipeGmail/edge.tf b/TMP/backup_2026-09-28/FPipeGmail/edge.tf new file mode 100644 index 0000000..ec8f962 --- /dev/null +++ b/TMP/backup_2026-09-28/FPipeGmail/edge.tf @@ -0,0 +1,28 @@ +resource "nubes_vc_nsxt" "edge" { + resource_name = var.nsxt_resource_name + + # Тип родительской услуги: "vdc" (нужен vdc_uid) или "vdcGroup" (нужен vdc_group_uid) + vdc_type = var.nsxt_vdc_type + + # refSvc-поле: принимает UUID или имя. Здесь берём UID созданного VDC, + # чтобы Edge гарантированно создавался после vDC. + vdc_uid = nubes_vc_vdc.vdc.id + + need_enable_avi = var.nsxt_need_enable_avi + virtual_services_count = var.nsxt_virtual_services_count + + # routed-сеть, которую разворачивает Edge (SingleNestedAttribute -> объект) + routed_net_configuration = { + ip_addr_pool = var.nsxt_ip_addr_pool + main_dns = var.nsxt_main_dns + second_dns = var.nsxt_second_dns + } + + # «Заморозка»: destroy НЕ удаляет эдж (у платформы для эджа нет операции suspend), + # а только убирает его из состояния. Для полного удаления — keep_on_destroy = false. + keep_on_destroy = true + + # Повторный apply усыновляет уже работающий эдж, а не падает с + # «РЕСУРС С ТАКИМ ИМЕНЕМ УЖЕ СУЩЕСТВУЕТ (RUNNING)». + adopt_existing_on_create = true +} diff --git a/TMP/backup_2026-09-28/FPipeGmail/modifiers.tf b/TMP/backup_2026-09-28/FPipeGmail/modifiers.tf new file mode 100644 index 0000000..a148873 --- /dev/null +++ b/TMP/backup_2026-09-28/FPipeGmail/modifiers.tf @@ -0,0 +1,60 @@ +# ============================================================================= +# Ресурсы-модификаторы (операции modify, которых нет в create-схеме ресурсов) +# +# Порядок строго такой: +# орга (создана вручную в ЛК) +# -> nubes_vc_vdc.vdc +# -> nubes_vc_nsxt.edge +# -> nubes_vc_org_ip_allocation (выделение внешних IP на орге) +# -> nubes_vc_nsxt_snat (SNAT на эдже этим ipSpace) +# +# Почему аллокация ПОСЛЕ эджа: платформа строит список ipSpace из состояния +# `job.vcd.networkProvider` / `job.vcd.providerGateway`, то есть требует уже +# созданный vDC и Edge. Иначе modify на орге падает +# («Can't cast Complex Object Type Struct to String»). +# ============================================================================= + +# 1. Внешние IP на организации (modify: vIPConfigure, массив перезаписывается целиком) +resource "nubes_vc_org_ip_allocation" "org_ip" { + organization = var.organization + + vip_configure = jsonencode([ + { + name = var.ip_space_name + count = var.ip_count + } + ]) + + # true = «заморозка»: destroy не трогает квоту внешних IP (кластер Штурвала держит + # адреса, опустить count ниже занятых платформа не даёт). Для полного удаления — false + # (и только после удаления кластера). + keep_on_destroy = true + + depends_on = [nubes_vc_nsxt.edge] +} + +# 2. SNAT на эдже (modify: ipSpaceName) +resource "nubes_vc_nsxt_snat" "snat" { + nsxt_uid = nubes_vc_nsxt.edge.id + ip_space_name = var.ip_space_name + + # true = «заморозка»: destroy не выключает SNAT на эдже. Для полного удаления — false. + keep_on_destroy = true + + # ipSpace должен быть уже выделен на организации + depends_on = [nubes_vc_org_ip_allocation.org_ip] +} + +output "allocated_org_ip" { + description = "Выделено внешних IP на организации" + value = { + organization = var.organization + ip_space_name = var.ip_space_name + ip_count = var.ip_count + } +} + +output "snat_ip_space" { + description = "ipSpace, включённый как SNAT на эдже" + value = nubes_vc_nsxt_snat.snat.ip_space_name +} diff --git a/TMP/backup_2026-09-28/FPipeGmail/outputs.tf b/TMP/backup_2026-09-28/FPipeGmail/outputs.tf new file mode 100644 index 0000000..13ee3e0 --- /dev/null +++ b/TMP/backup_2026-09-28/FPipeGmail/outputs.tf @@ -0,0 +1,29 @@ +output "vdc_id" { + description = "UID созданного VDC" + value = nubes_vc_vdc.vdc.id +} + +output "vdc_name" { + description = "Имя VDC" + value = nubes_vc_vdc.vdc.resource_name +} + +output "vdc_state_params" { + description = "Параметры состояния VDC из API" + value = nubes_vc_vdc.vdc.state_params +} + +output "nsxt_id" { + description = "UID созданного Edge (vc_nsxt)" + value = nubes_vc_nsxt.edge.id +} + +output "nsxt_name" { + description = "Имя Edge (vc_nsxt)" + value = nubes_vc_nsxt.edge.resource_name +} + +output "nsxt_state_params" { + description = "Параметры состояния Edge (vc_nsxt) из API" + value = nubes_vc_nsxt.edge.state_params +} diff --git a/TMP/backup_2026-09-28/FPipeGmail/provider.tf b/TMP/backup_2026-09-28/FPipeGmail/provider.tf new file mode 100644 index 0000000..9d91910 --- /dev/null +++ b/TMP/backup_2026-09-28/FPipeGmail/provider.tf @@ -0,0 +1,4 @@ +provider "nubes" { + api_token = var.api_token + api_endpoint = var.api_endpoint +} diff --git a/TMP/backup_2026-09-28/FPipeGmail/shturval.tf b/TMP/backup_2026-09-28/FPipeGmail/shturval.tf new file mode 100644 index 0000000..76e2472 --- /dev/null +++ b/TMP/backup_2026-09-28/FPipeGmail/shturval.tf @@ -0,0 +1,161 @@ +# ============================================================================= +# Kubernetes кластер Штурвал — сервис 150, ресурс nubes_k8s_sthutrval_cluster +# (НЕ 148 «Менеджмент Kubernetes кластер Штурвал» — это другой сервис) +# +# Всё, что относится к Штурвалу, лежит ТОЛЬКО в этом файле: переменные, их +# значения по умолчанию и сам ресурс. Чтобы выключить Штурвал — удалить файл +# или закомментировать ресурс. +# +# Порядок (чек-лист из инструкции на услугу в ЛК): +# 1) Организация в Cloud Director — создана вручную в ЛК +# 2) nubes_vc_vdc.vdc — есть +# 3) nubes_vc_nsxt.edge — есть, обязательно ALB + AVI VS >= 3 +# 4) внешние адреса в организации — суммарно >= 3 (nubes_vc_org_ip_allocation) +# 5) SNAT на Edge — nubes_vc_nsxt_snat +# 6) Kubernetes кластер Штурвал — этот ресурс +# +# Минимальные требования к кластеру: мастер-нод >= 1, воркер-нод >= 1, +# 4 vCPU / 8 GB RAM / 50 GB диска на ноду. +# ============================================================================= + +# --- Переменные Штурвала --- + +variable "shturval_resource_name" { + type = string + default = "shturval-dev1" + description = "Имя услуги «Kubernetes кластер Штурвал» в ЛК" +} + +variable "shturval_cluster_name" { + type = string + default = "shturval-dev-01" + description = "Имя кластера внутри Штурвала" +} + +variable "shturval_app_version" { + type = string + default = "2.14.0" + description = "Версия Штурвала (значение по умолчанию платформы — 2.14.0)" +} + +variable "shturval_cp_sizing_policy" { + type = string + default = "TKG 4CPU 8RAM" + description = "Политика размера control plane: 4 vCPU / 8 GB (минимум по инструкции). Должна существовать в ресурсной платформе vDC — список политик берётся из услуги «Виртуальный датацентр»" +} + +variable "shturval_cp_sizing_disk" { + type = number + default = 50 + description = "Диск control plane, ГБ (минимум 50)" +} + +variable "shturval_cp_count" { + type = number + default = 1 + description = "Количество мастер-нод: 1, 3 или 5" +} + +variable "shturval_worker_group_name" { + type = string + default = "workers-shturval-dev" + description = "Имя группы воркеров (уникальное в кластере; допустимы строчные латинские буквы, цифры и дефис)" +} + +variable "shturval_worker_sizing_policy" { + type = string + default = "TKG 4CPU 8RAM" + description = "Политика размера воркеров: 4 vCPU / 8 GB (минимум по инструкции)" +} + +variable "shturval_worker_sizing_disk" { + type = number + default = 50 + description = "Диск воркеров, ГБ (минимум 50)" +} + +variable "shturval_worker_count" { + type = number + default = 1 + description = "Количество воркер-нод (минимум 1)" +} + +# --- Значения, которые собираются из переменных --- + +locals { + # Группы воркеров передаются JSON-строкой ВНУТРЬ услуги как есть, поэтому ключи + # должны быть ровно такими, как в манифесте услуги 150: groupName, sizingPolicy, + # sizingDisk, count, autoscale, labelDeck. + # ВНИМАНИЕ: в сгенерированном примере провайдера (docs → Example) ключи показаны + # в snake_case — это ошибка генератора, платформа на них падает с + # «Cannot invoke method split() on null object» (не находит groupName → null). + shturval_worker_config = jsonencode([ + { + groupName = var.shturval_worker_group_name + sizingPolicy = var.shturval_worker_sizing_policy + sizingDisk = var.shturval_worker_sizing_disk + count = var.shturval_worker_count + autoscale = false # автоскейл выключен + labelDeck = true # разрешить разворачивать услуги из ЛК на этих нодах + } + ]) +} + +# --- Ресурс Штурвала --- + +resource "nubes_k8s_sthutrval_cluster" "shturval" { + resource_name = var.shturval_resource_name + + # Кластер Штурвала уже существует (инстанс «shturval-dev») и в проде не + # удаляется неделями, поэтому ресурс должен УСЫНОВИТЬ существующий инстанс, + # а не падать с «РЕСУРС С ТАКИМ ИМЕНЕМ УЖЕ СУЩЕСТВУЕТ (SUSPEND)». + # Проверка/adopt выполняются в Create на apply (в plan будет «will be created»). + adopt_existing_on_create = true + + # «Заморозка»: destroy приостанавливает кластер (suspend), а не удаляет. + # Следующий apply усыновит его и разморозит (resume). + suspend_on_destroy = true + + # Штурвал создаётся долго (десятки минут) — поднимаем таймаут ожидания, + # иначе провайдер сдаётся на дефолтных 600 с. + operation_timeout = "60m" + + startup_configuration = { + # vDC и Edge из этого же конфига (обязательные поля) + vdc_uid = nubes_vc_vdc.vdc.id + nsxt_uid = nubes_vc_nsxt.edge.id + + cluster_name = var.shturval_cluster_name + + # Дополнительные возможности кластера (в ЛК — галочки при создании) + ex_logging = true # логи в Loki (без него логи услуг не видны в ЛК) + ex_monitoring = true # метрики в VictoriaMetrics (без него метрик в ЛК нет) + ex_local_csi = true + ex_vip = true + ex_update = true + ex_ingress = true + ex_named_csi = true + } + + cluster_configuration = { + app_version = var.shturval_app_version + } + + control_plane_configuration = { + sizing_policy = var.shturval_cp_sizing_policy + sizing_disk = var.shturval_cp_sizing_disk + count = var.shturval_cp_count + } + + worker_configuration = local.shturval_worker_config + + access_configuration = { + need_external_address_api = true # внешний адрес для Kubernetes API (false недопустим) + access_ip_list_api = jsonencode([]) # пусто = доступ всем + need_external_address_ingress = true # внешний адрес для Ingress + access_ip_list_ingress = jsonencode([]) # пусто = доступ всем + } + + # Кластер поднимается только после готовой сети: vDC -> Edge -> внешние IP -> SNAT + depends_on = [nubes_vc_nsxt_snat.snat] +} diff --git a/TMP/backup_2026-09-28/FPipeGmail/variables.tf b/TMP/backup_2026-09-28/FPipeGmail/variables.tf new file mode 100644 index 0000000..7e934d3 --- /dev/null +++ b/TMP/backup_2026-09-28/FPipeGmail/variables.tf @@ -0,0 +1,116 @@ +variable "api_token" { + type = string + sensitive = true + description = "API-токен Nubes" +} + +variable "api_endpoint" { + type = string + default = "https://lk-api-gateway-dev.ngcloud.ru/api/v1/svc" + description = "API Gateway URL" +} + +# Имя (display_name, напр. "kontora") ИЛИ UUID организации из ЛК +variable "organization" { + type = string + description = "Имя или UUID организации (vc_org)" +} + +# --- Модификаторы (IP на орге + SNAT на эдже) --- + +variable "ip_space_name" { + type = string + description = "Имя ipSpace, доступное организации (смотреть в ЛК, напр. internet-ipv4-v1)" +} + +variable "ip_count" { + type = string + default = "3" + description = "Сколько внешних IP выделить на организации (count — строка)" +} + +variable "vdc_resource_name" { + type = string + default = "fullpipe-vdc" + description = "Имя VDC" +} + +variable "vdc_network_provider" { + type = string + default = null + description = "Сетевой провайдер. Заполнить значением из текущей страницы ЛК" +} + +variable "vdc_provider_vdc" { + type = string + default = null + description = "Provider VDC. Заполнить значением из текущей страницы ЛК" +} + +variable "vdc_cpu_allocated" { + type = number + default = 8 + description = "vCPU (шт.)" +} + +variable "vdc_cpu_guaranteed" { + type = number + default = 0 + description = "Резервирование vCPU (%, допустимо: 0, 50, 80)" +} + +variable "vdc_mem_allocated" { + type = number + default = 32 + description = "RAM (GB)" +} + +variable "vdc_storage_config" { + type = string + default = "[{\"name\":\"SATA\",\"size\":\"200\"}]" + description = "Дисковое хранилище (JSON-массив, size в GB). Имя политики должно существовать в ресурсном пуле (например, SATA, SSD)" +} + +# --- vc_nsxt (Сетевой шлюз периметра / Edge) --- + +variable "nsxt_resource_name" { + type = string + default = "fullpipe-edge" + description = "Имя Edge (vc_nsxt)" +} + +variable "nsxt_vdc_type" { + type = string + default = "vdc" + description = "Тип родительской услуги: vdc или vdcGroup" +} + +variable "nsxt_need_enable_avi" { + type = bool + default = true + description = "Включить AVI Load Balancer (ALB)" +} + +variable "nsxt_virtual_services_count" { + type = number + default = 3 + description = "Кол-во виртуальных сервисов на AVI (1..4; Штурвал: ≥ 3)" +} + +variable "nsxt_ip_addr_pool" { + type = string + default = "10.10.102.0/24" + description = "Адресный пул routed-сети (маска /24 обязательна)" +} + +variable "nsxt_main_dns" { + type = string + default = "81.22.46.22" + description = "Основной DNS" +} + +variable "nsxt_second_dns" { + type = string + default = "185.247.187.77" + description = "Второй DNS" +} diff --git a/TMP/backup_2026-09-28/FPipeGmail/vdc.tf b/TMP/backup_2026-09-28/FPipeGmail/vdc.tf new file mode 100644 index 0000000..f69039e --- /dev/null +++ b/TMP/backup_2026-09-28/FPipeGmail/vdc.tf @@ -0,0 +1,20 @@ +resource "nubes_vc_vdc" "vdc" { + resource_name = var.vdc_resource_name + + # Организация: имя из ЛК ("kontora") или точный UUID + organization_uid = var.organization + + network_provider = var.vdc_network_provider + provider_vdc = var.vdc_provider_vdc + + cpu_allocated = var.vdc_cpu_allocated + cpu_guaranteed = var.vdc_cpu_guaranteed + mem_allocated = var.vdc_mem_allocated + + # JSON-массив дисковых политик (size в GB) + storage_config = var.vdc_storage_config + + suspend_on_destroy = true + + adopt_existing_on_create = true +} diff --git a/TMP/backup_2026-09-28/FPipeGmail/versions.tf b/TMP/backup_2026-09-28/FPipeGmail/versions.tf new file mode 100644 index 0000000..eca54b9 --- /dev/null +++ b/TMP/backup_2026-09-28/FPipeGmail/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.5.0" + + required_providers { + nubes = { + source = "tf-registry.containerk8s.services.ngcloud.ru/nubes-dev/nubes" + version = "2.0.23" + } + } +}