Доступ к домашнему серверу без публичного IP

26.06.23

Интеграция - WEB-интеграция

У меня была идея поднять дома сервер, доступный из интернета, с шифрованием трафика и, что самое главное, бесплатный!

Рецепт сервера обычной компании:

  1. Белый IP
  2. Веб-сервер (nginx, apache, IIS, winow и т.д.)
  3. Сертификаты для протокола https

Если есть много денег, то купить ssl сертификаты, белый IP и группу сисадминов для поддержки - это не проблема. Но если ты хочешь создать пет проект в вебе, или создать свой сервис, платить деньги - не вариант. А значит, некоторые ингредиенты рецепта надо заменить на бесплатные альтернативы

 

Рецепт бесплатного сервера:

  1. DynamicDNS
  2. Веб-сервер
  3. Сертификаты Let's Encrypt
  4. Docker, потому что пора бы наконец научиться пользоваться.
  5. Какой-никакой роутер с root доступом и возможностью проброса портов

Итак, начнем по порядку.

 

1. Dynamic DNS

Динамический DNS (далее по тексту DDNS) нужен для того, чтобы вы могли стучаться в свою локальную сеть. Как это работает? Вам дается адрес, чаще всего состоящий из ВашСубДомен.ДоменПоставщика.com/org или любое другое расширение. При отправке запроса на этот адрес, вызов перенаправляется в вашу локальную сеть. Для того, чтобы DDNS знал, куда пересылать запрос, вы периодически обновляете информацию о вашем текущем IP адресе на хосте DDNS. Для каждого DDNS есть своя инструкция по обновлению информации об IP адресе.

В рамках этой статьи будем использовать вот этот ресурс, потому что он бесплатный, и потому что он элементарно прост в настройках, а также потому что я для этого сервиса нашел все необходимые библиотеки.

Нам нужно перейти на главную страницу, в верхней правой части авторизоваться любым удобным способом и увидеть на личной странице свой уникальный токен

 

 

Далее, ниже создать свой домен, увидеть как он появится в списке личных доменов, проверить что там отображается ваш текущий IP адрес. Проверить свой IP адрес можно с помощью этого ресурса

Запомним токен и домен, далее эти данные нам понадобятся.

 

2. Веб-сервер

Я думаю для 1Сников не надо объяснять что такое веб-сервер. Практически все так или иначе слышали слова apache и IIS. Но на всякий случай, веб-сервер - это такая хитрая штука, которая умеет принимать http-запросы и выдавать http-ответы. Но! Это важно. В нашем случае мы не будем настраивать веб-сервер в привычном для этой фразы понимании. Дело в том, что нам нужен обратный прокси-сервер. Это такая штука, которая принимает http-запросы и пересылает их дальше по требованию. 

 

Почему именно так?

Потому что мы будем настраивать возможность обмена информацией через защищенный протокол https. Наш прокси должен получить https-запрос, дешифровать его в незащищенный http-запрос и передать его на нужный порт в нашей локальной сети. Далее, из нашей локальной сети от принимает незащищенный http-ответ, шифрует его в защищенный https-ответ и отдает клиенту. Такая "архитектура" позволит нам не заморачиваться с сертификатами в наших сервисах, а отдать все действия по шифровке/дешифровке обратному прокси-серверу.

Обратный прокси-сервер мы будем поднимать в докер контейнере, потому что он нам в локальной сети вообще никуда не упал. Это отдельный сервис, пусть живет отдельной жизнью. Поскольку это будет не единственный контейнер, мы будем использовать docker-compose.  

Да, кстати, для обратного прокси-сервера мы будем использовать nginx, потому что пора узнать что есть что-то кроме apache и IIS.

 

Пример конфигурации контейнера в docker-compose файле:

version: '3'

services:
  nginx:
    container_name: nginx
    image: nginx:stable-alpine3.17-slim 
    ports:
      - 8080:80
      - 8443:443
    volumes:
      - ./nginx:/etc/nginx
      - ./letsencrypt:/etc/letsencrypt
    command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
    extra_hosts:
      - "host.docker.internal:host-gateway"

Что означают все эти буквы?

"version: '3'" - объявляем, какую версию compose файла мы используем

"services:" - внутри мы будем описывать, какие сервисы мы будем использовать

"nginx:" - имя сервиса (придумываем сами)

"container_name: nginx" - имя контейнера (придумываем сами)

"image: nginx:stable-alpine3.17-slim" - указываем, какой docker образ мы будем использовать в сервисе. Все образы можно посмотреть в docker hub

"ports:
      - 8080:80
      - 8443:443"
- проброс портов в формате "порт локального хоста : порт контейнера". В данном случае контейнер будет слушать порт 8080 и 8443 вашего хоста и передавать на порт 80 и 433 соответственно внутренней сети контейнера. Порт контейнера нужно устанавливать такой же, который прописан в конфигурационном файле нашего прокси-сервера (доберемся и до него в порядке живой очереди). Порт хоста нужно задать такой же, как в настройках проброса портов в роутере 

Например: у меня роутер настроен так, все входящее на порт 80 он пробрасывает на порт 8080 моего компьютера, а входящие сообщения на порт 443 отправляет на порт 8443 компьютера.

"volumes:
      - ./nginx:/etc/nginx
      - ./letsencrypt:/etc/letsencrypt"
- монтируем папки в контейнер. То есть папка nginx на вашем компьютере и папка /etc/nginx в контейнере - это одна и та же папка с файлами. Это нужно для того, чтобы мы могли отдавать в контейнер нужные файлы (например конфигурацию или сертификаты) и менять эти файлы в режиме реального времени. 

"command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"" - команда, которая выполнится при старте контейнера. Это рекомендованная строка для запуска nginx взятая из гайдов в интернете. И она работает. Все. Идем дальше.

"extra_hosts:
      - "host.docker.internal:host-gateway""
- эти строки говорят о том, что мы настраиваем связь между сетью контейнера и хостом. Нужна она для того, чтобы мы могли из докера стучаться в сеть хоста. (далее по тексту будет пример, как это работает)

 

Пример конфигурации nginx:

 

events {}
http {

  server {
    listen 80;
    location / {
      return 301 https://domain.duckdns.org$request_uri;
    }
  }

  server {
    listen 443 ssl http2;

    ssl_certificate /etc/letsencrypt/live/domain.duckdns.org/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/domain.duckdns.org/privkey.pem;
    ssl_session_cache shared:le_nginx_SSL:10m;
    ssl_session_timeout 1440m;
    ssl_session_tickets off;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;
    ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

     location / {
      add_header Cache-Control "public, must-revalidate";
      add_header Front-End-Https on;
      add_header Strict-Transport-Security "max-age=2592000; includeSubdomains";
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_pass http://host.docker.internal:443;   
     }
  }
} 

Тут мы остановимся только на тех строках, которые что-то для нас значат. В остальном - это стандартная конфигурация nginx для обратного прокси-сервера.

"listen 80" - сюда пишем порт, который мы указывали в docker-compose файле, для http-запросов

"return 301 https://domain.duckdns.org$request_uri" - если мы получили незашифрованный http-запрос, мы делаем редирект на https, чтобы получить защищенный https-запрос. Вместо domain нужно указать ваш домен в duckdns, который вы придумали и создали.

"listen 443 ssl http2" - сюда пишем порт, который мы указывали в docker-compose файле, для https-запросов.

"ssl_certificate /etc/letsencrypt/live/domain.duckdns.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/domain.duckdns.org/privkey.pem;"
- путь до сертификатов, которые мы получим позже. Что делать со словом domain, думаю, догадаетесь :)

"ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;" - невероятно важный файлик, без которого наш прокси просто не запустится. Скачать можно с официального репозитория

"proxy_pass http://host.docker.internal:443;" - а вот и отправка расшифрованного сообщения на хост. "host.docker.internal" в нашей конфигурации - это IP адрес хоста (вашего ПК/ноута или с чего вы там все это запускаете). А вот с портом сложнее. Тут надо указать такой порт, который слушает ваш сервис/приложение.

Так должен выглядеть файл с названием nginx.conf.

Ну, вроде с nginx более менее разобрались, по крайней мере в контексте настройки нашего прокси.

 

3. Сертификаты

Есть такая компания Let's Encrypt. Эта компания поставила перед собой задачу - перевести весь интернет на протокол https. Как это сделать? Правильно. Дать бесплатные сертификаты. Я не работал с платными TLS сертификатами, так как моя сфера работы чуть чуть другая, поэтому вся разница для меня состоит в цене (учитывая что я поднимаю свой домашний сервер). 

Далее, для удобного получения сертификатов была написана программа/библиотека на python - Certbot. Теперь получить сертификат можно было командой в командной строке, или вызвав программный интерфейс библиотеки, если вы встроили эту библиотеку в свою программу. Но и это еще не все.

Чтобы получить сертификат, вы должны подтвердить, что защищаемый сертификатами домен принадлежит вам. Для этого вы должны разместить специальную строку в специальном месте, которую центр сертификации обязательно проверит. А поскольку мы используем DDNS, мы не можем просто так куда-то что-то там размещать и проверять. Поэтому, сервисы DDNS создали специальный API, используя который вы можете разместить TXT запись в вашем субдомене DDNS для проверки. А где появляются какие-то API, появляются и энтузиасты, пишущие свои библиотеки/программы для использования этого самого API.

В итоге, чтобы не изобретать велосипед, вы должны перелопатить интернет в поисках той самой библиотеки, у которой будет самая адекватная документация, чтобы не тратить много времени на изучения возможностей. И тут мы приходим к интересному проекту, который был написан для интеграции программы certbot c DDNS DuckDNS - certbot_dns_duckdns. Вот ее мы и будем использовать в нашем проекте.

 

Пример конфигурации контейнера в docker-compose файле:

 

version: '3'

services:
  certbot:
    image: "infinityofspace/certbot_dns_duckdns:latest"
    container_name: "certbot"
    volumes:
      - "./letsencrypt:/etc/letsencrypt"
      - "./logs:/var/log/letsencrypt"
    command: certonly
      --non-interactive
      --agree-tos
      --email {YOUR_EMAIL}
      --preferred-challenges dns
      --authenticator dns-duckdns
      --dns-duckdns-token {YOUR_TOKEN}
      --dns-duckdns-no-txt-restore
      --dns-duckdns-propagation-seconds 15
      -d "domain.duckdns.org"
      -d "*.domain.duckdns.org"

Со значениями полей мы уже знакомы. Тут нас интересует поле "command". Это та самая команда, которая при запуске запустит процесс получения сертификатов. Тут нужно исправить несколько значений. "{YOUR_EMAIL}" - тут надо указать свой почтовый адрес, для регистрации сертификата. "{YOUR_TOKEN}" - сюда нужно установить токен, который вы получили в DuckDNS. Токен нужен как раз для интеграции. Со словами "domain" делать тоже, что и всегда - менять на свой домен из DuckDNS.

Итак, с конфигурациями контейнеров мы ознакомились. Теперь нам надо их совместить:

version: '3'

services:
  nginx:
    container_name: nginx
    image: nginx:stable-alpine3.17-slim 
    ports:
      - 8080:80
      - 8443:443
    volumes:
      - ./nginx:/etc/nginx
      - ./letsencrypt:/etc/letsencrypt
    command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
    extra_hosts:
      - "host.docker.internal:host-gateway"
  
  certbot:
    image: "infinityofspace/certbot_dns_duckdns:latest"
    container_name: "certbot"
    volumes:
      - "./letsencrypt:/etc/letsencrypt"
      - "./logs:/var/log/letsencrypt"
    command: certonly
      --non-interactive
      --agree-tos
      --email {YOUR_EMAIL}
      --preferred-challenges dns
      --authenticator dns-duckdns
      --dns-duckdns-token {YOUR_TOKEN}
      --dns-duckdns-no-txt-restore
      --dns-duckdns-propagation-seconds 15
      -d "domain.duckdns.org"
      -d "*.domain.duckdns.org"

Вот так должен выглядеть файл, с необычным названием docker-compose.yml для запуска (конечно же с вашими правками, которые мы рассмотрели выше).

Структура проекта при этом будет выглядеть примерно так:

 - letsencrypt - директория

 -- ssl-dhparams.pem - файл из п.2

 - nginx - директория

 -- nginx.conf - файл конфигурации из п.2

 - docker-compose.yml - файл вместивший в себя описание контейнеров

Директории будут монтированы в контейнеры для обмена информации

 

4. Запуск

Сначала, надо установить docker и docker-compose

Открыть командную строку и перейти в корневую директорию проекта.

Выполнить команду "docker compose -f "docker-compose.yml" up -d --build"

При первом запуске контейнер с прокси-сервером выдаст ошибку, так как не сможет найти сертификаты, они еще не созданы.

Контейнер certbot предпримет попытку создания сертификатов. Возможно, по какой-то причине не пройдет проверка домена. При этом в контейнере выйдет ошибка 

Certbot failed to authenticate some domains (authenticator: dns-duckdns). The Certificate Authority reported these problems:
   Domain: domain.duckdns.org
   Type:   unauthorized
   Detail: Incorrect TXT record "{какой-то ключ}" found at _acme-challenge.domain.duckdns.org

 Надо проверить, все ли вы правильно указали, соответствует ли ваш IP адрес в 2IP и DuckDNS, и запустить контейнер еще раз.

То, что сертификаты были правильно созданы, будет сопровождаться сообщением в логе:

 Successfully received certificate.
 Certificate is saved at: /etc/letsencrypt/live/domain.duckdns.org/fullchain.pem
 Key is saved at:         /etc/letsencrypt/live/domain.duckdns.org/privkey.pem
 This certificate expires on {какая-то дата окончания}.

При получении такого сообщения, можно запускать контейнер с прокси-сервером. Все готово для работы.

При отправке HTTPS-запроса на адрес или http://domain.duckdns.org, прокси получит данные, расшифрует и отправит HTTP-запрос на ваш хост.

Запускам наш сервис, запускаем контейнер nginx, делаем запрос на адрес https://domain.duckdns.org, получаем результат

 

 

Браузер не ругается на небезопасное соединение, сервис отдает ответ, все работает как и задумывалось.

 

Итог

В статье я постарался аккумулировать результат личного изучения темы. Возможно я где-то допустил ошибку или налил много воды. В любом случае, я надеюсь, кому-то это сэкономит время при настройке домашнего сервера. Весь описанный проект вы можете скачать на гитхаб в моем репозитории, там же есть краткая инструкция по настройке.

Также, вы должны понимать, что вам понадобится еще несколько настроек. Вам надо отправлять в duckdns свой текущий IP адрес для автоматического обновления. Как это сделать описано тут, ну или вы можете отправлять https-запрос типа https://www.duckdns.org/update?domains={YOURVALUE}&token={YOURVALUE}[&ip={YOURVALUE}]

Также, было бы неплохо настроить контейнер certbot на автоматический перевыпуск сертификатов после окончания их действия (срок жизни сертификата - 90 дней). В противном случае раз в 90 дней вы должны будете руками запускать контейнер для перевыпуска сертификатов.

nginx docker docker-compose прокси-сервер certbot letsencrypt

См. также

Сайты и интернет-магазины WEB-интеграция Системный администратор Программист Пользователь Платформа 1С v8.3 Конфигурации 1cv8 1С:Управление торговлей 11 Автомобили, автосервисы Россия Управленческий учет Платные (руб)

Интеграционный модуль обмена между конфигурацией Альфа Авто 5 и Альфа Авто 6 и порталом AUTOCRM. Данный модуль универсален. Позволяет работать с несколькими обменами AUTOCRM разных брендов в одной информационной базе в ручном и автоматическом режиме.

36000 руб.

03.08.2020    17782    19    22    

16

Сайты и интернет-магазины Интеграция WEB-интеграция Платформа 1С v8.3 Конфигурации 1cv8 Управленческий учет Платные (руб)

Интеграция 1С и Битрикс 24. Разработка имеет двухстороннюю синхронизацию 1С и Bitrix24 задачами. Решение позволяет создавать пользователя в 1С из Битрикс24 и наоборот. Данная разработка технически подходит под все основные конфигурации линейки продуктов 1С:Предприятие 8.3 (платформа начиная с 8.3.23). При приобретении предоставляется 1 месяц бесплатных обновлений разработки. Доступна демо-версия продукта с подключением Вашего Битрикс24

5040 руб.

04.05.2021    19847    13    17    

16

WEB-интеграция 8.3.8 Конфигурации 1cv8 Автомобили, автосервисы Беларусь Украина Россия Казахстан Управленческий учет Платные (руб)

Расширение предназначено для конфигурации "1С:Предприятие 8. Управление Автотранспортом. ПРОФ". Функционал модуля: 1. Заполнение регистров сведений по подсистеме "Мониторинг", а именно: события по мониторингу, координаты по мониторингу, пробег и расход по мониторингу, текущее местоположение ТС по мониторингу 2. Заполнение путевого листа: пробег по мониторингу, время выезда/заезда, табличная часть ГСМ, места стоянок по геозонам. 3. Отчеты по данным загруженным в регистры сведений. 4. Предусмотрена автоматическая загрузка данных в фоновом режиме (условия работы данной загрузке читайте в описании товара) Модуль работает без включенной константы по настройкам мониторинга. Модуль формы предоставляется с открытым кодом, общий модуль защищен. Любой заинтересованный пользователь, имеет возможность скачать демо-версию расширения.

22656 руб.

25.05.2021    14422    42    8    

18

WEB-интеграция Программист Руководитель проекта Платформа 1С v8.3 Конфигурации 1cv8 1С:Франчайзи, автоматизация бизнеса Платные (руб)

Расширение значительно упрощает написание API на 1С. Веб программисты получают простой и понятный доступ к 1С. Описание API создаётся автоматически и представляется в виде удобном как для человека, так и для программной обработки.

24000 руб.

27.09.2024    1172    1    0    

3
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. SerVer1C 815 26.06.23 14:05 Сейчас в теме
Если у вас серый динамический ip-адрес (т.е. вы сидите за NATом, или, не приснись, за двойным NATом),тогда DynDNS вам не поможет. Лично у меня краснояйцевый оператор таким грешит.
Trucker; kamisov; +2 Ответить
4. nemec 22 29.06.23 16:58 Сейчас в теме
(1) Решение тем не менее есть - покупаем вместо мусора в виде роутера, любое современное устройство компании которое начинается на буку К и на букву К заканчивается (бывшее подразделение компании З. - чтобы не сочли за рекламу), которые в своей прошивке обладает "массой ненужных функций" (ц) диванные интернет эксперты. У них есть совершенно бездвоздмездно, то есть даром, сервис динамического ДНС, в том числе и для серых адресов через собственное облако. В комплекте идет автоматически обновляемый LetsEncrypt, так что вашей задачей будет только указать настройки переадресации или просто добавить уникальное доменное имя для вашего сервера и на этом всё. Т.е. прямо для своего сервера задаете в настройках роутера адрес типа "1ссервер.мойроутердома.к.......к.линк" и вся маршрутизация заворачивается на него
Aleskey_K; Krahmalov; TerveRus; +3 1 Ответить
6. user716566 14.07.23 13:36 Сейчас в теме
(1)А связаться с Вами можно?
2. siamagic 26.06.23 14:47 Сейчас в теме
Я у себя хостинг так держал - докер опять засунули туда где не ждали, школота блин ) лижбы совать и перепечатывать инструкции с инета.
3. agentz 40 29.06.23 16:26 Сейчас в теме
Сидя за NAT вы можете сидеть в серой сети и провайдер а) не даст белый ip, даже динамический б) провайдер не даст открыть порты. Решение: проще не придумаешь ngrok , вариант по сложнее мелкий сервер в облаке и ВПН к нему
5. itmind 308 03.07.23 07:30 Сейчас в теме
Я думаю, что VPS за 150-300 руб в месяц у облачных провайдеров не такая уж и большая сумма для программиста.
7. webresurs 228 26.07.24 08:47 Сейчас в теме
ZeroTier - аналог LogMeIn Hamachi...
- в бесплатной версии до 25 пользователей
- подключение за белым,серым ип адресом, главное что бы был интернет

- при желании можно поднять свой сервер на virtualbox - бесплатно, без ограничений вроде, с серым ип адресом наверное прииидеться покумекать..
Оставьте свое сообщение