Мы живем в мире, где зависимость от того или иного вендора может привести к катастрофе. Если базировать свой бизнес на Google Cloud, AWS или Azure, или даже каком-то отечественном аналоге, то в любой момент тебе могут сказать, что твоё облако больше не твоё. А ты уже привык или привыкла (я очень надеюсь тут на женский взгляд на проблему) к их гитлабу, нексусу и прочим плюшкам, которые доступны из коробки и заинтегрированы между собой и твоим проектом. Как быть?

Решение тут одно. Нам не нужно одно гигантское облако, как нам не нужны моносервисы. Ими сложно управлять. Нужны микросервисы и микрооблака. Представьте, что вы хотите создать своё приложение для знакомств. Разве вам для его работы нужны ксеоны и прочие суперпроцессоры? Вряд ли. Нужен ли вам гигантский дата-центр? Ответ тот же. Скорее всего, вам достаточно 2-4 виртуалок обычного типа, на которых нужно запустить ваше приложение и небольшую инфраструктурную обвязку вроде api-gateway и sso, а также гитлабов и нексусов, если речь идет о контуре разработки. И обойдется это вам гораздо дешевле. Проблема только в том, что хостингов виртуалок на рынке полно, а вот удобно инструмента превращения этих виртуалок в своё небольшое облако - нет. А теперь представьте, что такой инструмент есть. Тогда если хостинг скажет вам "прости-прощай и собирай манатки", то мы просто берем и переезжаем со словами "а ну и пожалуйста, не очень то и хотелось".

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

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

Точкой запуска такого скрипта является докер-компоуз, который получит на вход параметры сети. Пока это виртуалки в моем VirtualBox, но со временем я расширю его параметрами реальных виртуальных машин, когда подберу себе хостера. Исходим из того, что у меня есть 5 виртуальных машин и запущенный докер-контейнер, который настраивает виртуалки и себя для объединения всех 6 машин в виртуальную сеть. Таким образом, по итогу отработки скрипта в моем распоряжении будет сеть из 6 машин. Но нужно помнить, что докер-контейнер - это не совсем обычная виртуальная машина. Она ограничена в правах, поэтому при старте через докер-компоуз придется применить несколько расширяющих возможности контейнера настроек. Чтобы понимать, что происходит, важно читать комментарии по коду вставок

version: "3.9"

services:
  # kubos - это просто название данного микрооблачного проекта
  kubos:
    # devices и cap_add дают право контейнеру задействовать возможности
    # хост-машины по настройке виртуальных сетевых устройств
    devices:      
      - /dev/net/tun
    cap_add:
      - NET_ADMIN
    # докер файл, который поместит в образ все необходимые shell- 
    # и ansible-скрипты
    build:
      context: docker
      dockerfile: Dockerfile
    image: kubos
    environment:
      # адрес машины, выбранной в качестве сервера OpenVPN
      MASTER_IP: 10.0.2.2
      # ssh-порты всех машин сети 
      # в данном случае, они все запущены на локальной машине, поэтому
      # указываются только порты, но в будущем добавим и IP
      MASTER_PORT: 10022
      WORKER1_PORT: 10122
      WORKER2_PORT: 10322
      WORKER3_PORT: 10522
      WORKER4_PORT: 10722
      # параметры создаваемой виртуальной сети
      VIRTUAL_NETWORK_CIDR: 10.10.0.0/16
      # логин и пароль пользователя с sudo-правами, от имени которого
      # будут производиться все манипуляции на виртуалках в ansible-скриптах
      USER_NAME: admin
      USER_PASSWORD: 3

Коротко рассмотрим Dockerfile. В нем нет ничего сверхестественного - просто копирование 4-х ansible-скриптов и 1-го bash-скрипта оркестратора этих ансиблов.

FROM debian:bullseye

# копируем папку с ансибл скриптами
COPY ansible /root/ansible

# делаем эту папку текущей папкой
WORKDIR /root/ansible

# копируем скрипт-оркестратор ансиблов
COPY entrypoint.sh /root/entrypoint.sh

ENTRYPOINT ~/entrypoint.sh

Давайте теперь пройдемся по скрипту оркестратору entrypoint.sh и рассмотрим его составные блоки. В самом начале идет установка всех необходимых пакетов

apt update;
# dnsmasq - локальный DNS-сервер, котоый нужен, чтобы после подключения к
# виртуальной сети, докер-контейнер смог не потерять связь с интернетом
# ipcalc - инструмент превращения CIDR адресов сетей в отдельные адрес сети и
# сетевую маску
# fping - инструмент для произведения широковещательного ping-а
apt install -y ansible openvpn dnsmasq ipcalc fping;

Далее при помощи ансибл скрипта common-dependencies.yml идёт установка всех основных зависимостей на все тачки сети. Для запуска этой установки сначала в скрипте оркестраторе entrypoint.sh создается файл инвентаризации всех машин. Единственное, что там может вызывать вопросы - это имя хоста host.docker.internal, которое используется в докере, запускаемом из Windows, где я и работаю, для обращения к портам хост-машины. Именно этот адрес в будущем будет переделан на параметр докер компоуза. Все остальные настройки файла инвентаризации уже параметризованы.

# подготовка файла инвентаризации хостов для установки общих зависимостей
{
  echo "[all]";
  echo "master ansible_host=host.docker.internal ansible_port=${MASTER_PORT} ansible_user=${USER_NAME} ansible_password=${USER_PASSWORD} ansible_sudo_pass=${USER_PASSWORD}";
  echo "worker1 ansible_host=host.docker.internal ansible_port=${WORKER1_PORT} ansible_user=${USER_NAME} ansible_password=${USER_PASSWORD} ansible_sudo_pass=${USER_PASSWORD}";
  echo "worker2 ansible_host=host.docker.internal ansible_port=${WORKER2_PORT} ansible_user=${USER_NAME} ansible_password=${USER_PASSWORD} ansible_sudo_pass=${USER_PASSWORD}";
  echo "worker3 ansible_host=host.docker.internal ansible_port=${WORKER3_PORT} ansible_user=${USER_NAME} ansible_password=${USER_PASSWORD} ansible_sudo_pass=${USER_PASSWORD}";
  echo "worker4 ansible_host=host.docker.internal ansible_port=${WORKER4_PORT} ansible_user=${USER_NAME} ansible_password=${USER_PASSWORD} ansible_sudo_pass=${USER_PASSWORD}";           
} > hosts;

# установка общих зависимостей
ansible-playbook -i hosts common-dependencies.yml;

Пора глянуть сам ансибл скрипт common-dependencies.yml

- hosts: all
  become: true
  gather_facts: no
  tasks:
    # проверяем установлен ли питон на машине
    # без него можно использовать только raw-модуль ансибла
    # и не получится задействовать всю ансибл-мощь
    - name: Check python3 installed
      raw: python3 --version
      ignore_errors: true
      register: python3_installed

    # если питон не установлен, то устанавливаем
    - name: Install python3
      raw: |
        # подправляем файл репозиториев, чтобы не морочиться
        # с валидацией сертификатов устанавливаемых пакетов
        sed -i 's/https/http/g' /etc/apt/sources.list;
        apt update;
        apt install -y python3 python3-apt;
      when: python3_installed.failed

    # устанавливаем dbus для работы timedatectl
    # это нужно, чтобы перед началом работы синхронизовать системное время
    # если этого не сделать, то время будет непредсказуемо, что
    # может привести к ошибкам при проверке сертификатов
    # а именно, дат их выпуска и дат их окончания
    - name: Install dbus and starting systemd-logind
      shell: >
        apt install -y dbus;
        service systemd-logind start;
        echo "" > dbus.working;
      args:
        chdir: $HOME
        creates: dbus.working

    # принудительно проводим синхронизацию системного времени с NTP
    - name: Update time and date from ntp
      shell: |
        timedatectl set-ntp false;
        timedatectl set-ntp true;
        
        # создаем фиктивный файл завершения ансибл-таски
        # он позволит не повторять данную таску при повторном запуске скрипта
        # это моя костыльная реализация идемпотентности :)
        echo "" > timedate.corrected;
      args:
        chdir: $HOME
        creates: timedate.corrected

Когда питон установлен, а системное время синхронизировано, у нас все готово, чтобы запустить основной ансибл-скрипт создания виртуальной openvpn-сети. Для этого в скрипте оркестраторе entrypoint.sh создается новый файл инвентаризации серверов и запускается скрипт раскатки виртуальной сети openvpn/playbook.yml

# подготовка файла инвентаризации хостов для настройки vpn
{
  echo "[servers]";
  echo "master ansible_host=host.docker.internal ansible_port=${MASTER_PORT} ansible_user=${USER_NAME} ansible_password=${USER_PASSWORD} ansible_sudo_pass=${USER_PASSWORD}";
  echo "";
  echo "[clients]";
  echo "worker1 ansible_host=host.docker.internal ansible_port=${WORKER1_PORT} ansible_user=${USER_NAME} ansible_password=${USER_PASSWORD} ansible_sudo_pass=${USER_PASSWORD}";
  echo "worker2 ansible_host=host.docker.internal ansible_port=${WORKER2_PORT} ansible_user=${USER_NAME} ansible_password=${USER_PASSWORD} ansible_sudo_pass=${USER_PASSWORD}";
  echo "worker3 ansible_host=host.docker.internal ansible_port=${WORKER3_PORT} ansible_user=${USER_NAME} ansible_password=${USER_PASSWORD} ansible_sudo_pass=${USER_PASSWORD}";
  echo "worker4 ansible_host=host.docker.internal ansible_port=${WORKER4_PORT} ansible_user=${USER_NAME} ansible_password=${USER_PASSWORD} ansible_sudo_pass=${USER_PASSWORD}";     
} > openvpn/hosts;

# установка openvpn
VIRTUAL_NETWORK_ADDRESS=$(ipcalc --nobinary "$VIRTUAL_NETWORK_CIDR" | grep Address: | sed 's/^Address:\s\+\([0-9.]\+\)\s*$/\1/');
VIRTUAL_NETWORK_NETMASK=$(ipcalc --nobinary "$VIRTUAL_NETWORK_CIDR" | grep Netmask: | sed 's/^Netmask:\s\+\([0-9.]\+\) = .*/\1/');
VIRTUAL_NETWORK_GATEWAY=$(ipcalc --nobinary "$VIRTUAL_NETWORK_CIDR" | grep HostMin: | sed 's/^HostMin:\s\+\([0-9.]\+\)\s*$/\1/');
ansible-playbook -i openvpn/hosts openvpn/playbook.yml --extra-vars \
  "master_ip=$MASTER_IP virtual_network_cidr=$VIRTUAL_NETWORK_CIDR virtual_network_address=$VIRTUAL_NETWORK_ADDRESS virtual_network_netmask=$VIRTUAL_NETWORK_NETMASK";

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

Теперь обратимся к ансибл скрипту openvpn/playbook.yml

---
- hosts: master
  become: true
  become_user: root
  become_method: sudo
  roles:
    - openvpn-server

- hosts: all,!master
  become: true
  become_user: root
  become_method: sudo
  roles:
    - openvpn-client

В нем по сути просто прописано соответствие машин из файла инвентаризации с ролями ансибла. В нашем случае master-машина будет использоваться как сервер openvpn, а остальные машины как клиенты openvpn. Все достаточно просто. Основной код данного ансибла скрипта скрыт в файлах ролей. Начнем с рассмотрения файла настройка сервера openvpn openvpn/roles/openvpn-server/tasks/main.yml.

# Установка инструментов подготовки сертификтов, openvnp и таблиц маршрутизации
- name: Install easy-rsa, openvpn, iptables
  package:
    name: "{{ item }}"
    state: present
  with_items:
    - easy-rsa
    - openvpn
    - iptables

# Создается рабочая папка, в которой будут генериться все сертификаты сети
- name: Create CA directory
  become: true
  command: make-cadir openvpn-ca
  args:
    chdir: $HOME
    creates: openvpn-ca

# Установим переменные, отвечающие за сроки выпуска сертификатов
# Очень не хочется перевыпускать сертификаты каждый 2 года,
# поэтому сразу установим срок в тысячелетие и закроем это вопрос
# для нас и наших ближайших потомков :)
- name: Customize CA variable configuration
  lineinfile:
    dest: $HOME/openvpn-ca/vars
    regexp: "^{{ item.regex | regex_escape() }}"
    line: "{{ item.value }}"
  with_items:
    - { regex: '#set_var EASYRSA_CA_EXPIRE', value: 'set_var EASYRSA_CA_EXPIRE 365000' }
    - { regex: '#set_var EASYRSA_CERT_EXPIRE', value: 'set_var EASYRSA_CERT_EXPIRE 365000' }

# Генерим сертификат и ключ, которым будем подписывать все остальные сертификаты
- name: Build the certificate authority
  become: true
  shell: >
    echo "yes" | ./easyrsa --vars=./vars init-pki;
    printf "\n" | ./easyrsa --vars=./vars build-ca nopass;
    echo "" > $HOME/ca.built;
  args:
    chdir: $HOME/openvpn-ca
    creates: $HOME/ca.built

# Генерим служебные ключи
- name: Build Diffie-Hellman parameters and TLS key generation
  become: true
  shell: >
    openssl dhparam -out pki/dh2048.pem 2048;
    openvpn --genkey --secret pki/ta.key;
    echo "" > $HOME/dh_and_ta.built;
  args:
    chdir: $HOME/openvpn-ca
    creates: $HOME/dh_and_ta.built

# Генерим ключ и сертификат сервера
- name: Build server certificate and key
  become: true
  shell: |
    # команде "./easyrsa gen-req" передается имя CN для поля Subject
    # в выпускаемом сертификате
    echo "vpn-server" | ./easyrsa --vars=./vars gen-req vpn-server nopass;
    echo "yes" | ./easyrsa --vars=./vars sign-req server vpn-server;
    echo "" > $HOME/vpn_server_crt_and_key.built;
  args:
    chdir: $HOME/openvpn-ca
    creates: $HOME/vpn_server_crt_and_key.built

# Генерим ключ и сертификат клиентов
- name: Build clients certificates and keys
  become: true
  shell: |
    # команде "./easyrsa gen-req" передается имя CN для поля Subject
    # в выпускаемом сертификате
    echo "{{ item }}" | ./easyrsa --vars=./vars gen-req {{ item }} nopass;
    echo "yes" | ./easyrsa --vars=./vars sign-req client {{ item }};
    echo "" > $HOME/{{ item }}_crt_and_key.built;
  args:
    chdir: $HOME/openvpn-ca
    creates: $HOME/{{ item }}_crt_and_key.built
  with_items: "{{ groups['clients'] }}"

# Генерим ключ и сертификат для докер контейнера,
# который тоже станет клиентом сети
- name: Build docker certificate and key
  become: true
  shell: |
    # команде "./easyrsa gen-req" передается имя CN для поля Subject
    # в выпускаемом сертификате
    echo "docker" | ./easyrsa --vars=./vars gen-req docker nopass;
    echo "yes" | ./easyrsa --vars=./vars sign-req client docker;
    echo "" > $HOME/docker_crt_and_key.built;
  args:
    chdir: $HOME/openvpn-ca
    creates: $HOME/docker_crt_and_key.built

# Копируем созданные серверные ключи и сертификаты туда,
# где им и место на сервере openvpn
- name: Copy key and certificates to /etc/openvpn/server/
  become: true
  copy:
    remote_src: yes
    src: "$HOME/openvpn-ca/pki/{{ item }}"
    dest: /etc/openvpn/server/
  with_items:
    - "ca.crt"
    - "issued/vpn-server.crt"
    - "private/vpn-server.key"
    - "ta.key"
    - "dh2048.pem"

# Создаем файл конфигурации openvpn-сервера
- name: Generate server configuration from sample config
  become: true
  copy:
    remote_src: yes
    src: /usr/share/doc/openvpn/examples/sample-config-files/server.conf
    dest: /etc/openvpn/server/vpn-server.conf
    force: no

# Коррекция параметров openvpn-сервера
- name: Adjust OpenVPN server configuration
  lineinfile:
    dest: /etc/openvpn/server/vpn-server.conf
    regexp: "^{{ item.regex | regex_escape() }}"
    line: "{{ item.value }}"
  with_items:
    # сократить количество привелегий у openvpn
    - { regex: ';user nobody', value: 'user nobody' }
    - { regex: ';group nogroup', value: 'group nogroup' }
    # настройка сертификатов
    - { regex: 'cert ', value: 'cert vpn-server.crt' }
    - { regex: 'key ', value: 'key vpn-server.key' }
    # включение логирования
    - { regex: ';log ', value: 'log /var/log/openvpn/openvpn.log' }
    # нужно раскомментить, чтобы клиенты могли достучаться друг до друга
    # (иначе говоря, чтобы ping заработал)
    - { regex: ';client-to-client', value: 'client-to-client' }
    # без этого на клиентах не будут создаваться виртуальные сетевые устройства
    # для openvpn
    - { regex: ';push "redirect-gateway def1 bypass-dhcp"', value: 'push "redirect-gateway def1 bypass-dhcp"' }               
    # протокол ставим tcp, с udp настроить не удалось
    - { regex: 'proto ', value: 'proto tcp-server' }
    - { regex: 'explicit-exit-notify 1', value: ';explicit-exit-notify 1' }
    # FIXME переделать на 10.8.0.1 после установки на нем сервера BIND
    - { regex: ';push "dhcp-option DNS ', value: 'push "dhcp-option DNS 8.8.8.8"' }
    - { regex: ';push "dhcp-option DNS ', value: 'push "dhcp-option DNS 8.8.4.4"' }
    # устанавливаем параметры создаваемой виртуальной сети
    - { regex: 'server ', value: 'server {{ virtual_network_address }} {{ virtual_network_netmask }}' }

# Включаем vpn-сервер как шлюз в интернет для всех vpn-клиентов (№ 1)
- name: Configuration IP forwarding
  become: true
  sysctl:
    name: net.ipv4.ip_forward
    value: '1'
    state: present

# Включаем vpn-сервер как шлюз в интернет для всех vpn-клиентов (№ 2)
- name: Configuration IP routing
  become: true
  shell: |
    iptables -t nat -A POSTROUTING -s {{ virtual_network_cidr }} -o enp0s3 -j MASQUERADE;
    echo "" > internet.switched_on;
  args:
    chdir: $HOME
    creates: internet.switched_on

# Запускаем openvpn сервер
- name: Start openvpn systemd service
  become: true
  systemd:
    name: openvpn-server@vpn-server
    state: started
    daemon_reload: yes
    enabled: yes

# Следующие команды забирают в докер-контейнер нужные ключи и сертификаты
# Они нужны, чтобы раздать их всем клиентам виртуальной сети
# в том числе, и самомму докер-контейнеру

- name: Fetch server CA cert and TLS key
  fetch:
    src: "/etc/openvpn/server/{{ item }}"
    dest: "{{ playbook_dir }}/share/server/"
    flat: yes
  with_items:
    - "ca.crt"
    - "ta.key"

- name: Fetch clients certs
  fetch:
    src: "$HOME/openvpn-ca/pki/issued/{{ item }}.crt"
    dest: "{{ playbook_dir }}/share/clients/"
    flat: yes
  with_items: "{{ groups['clients'] }}"

- name: Fetch clients keys
  fetch:
    src: "$HOME/openvpn-ca/pki/private/{{ item }}.key"
    dest: "{{ playbook_dir }}/share/clients/"
    flat: yes
  with_items: "{{ groups['clients'] }}"

- name: Fetch docker cert and key
  fetch:
    src: "$HOME/openvpn-ca/pki/{{ item }}"
    dest: "{{ playbook_dir }}/share/clients/"
    flat: yes
  with_items:
    - private/docker.key
    - issued/docker.crt

Теперь, когда мы настроили сервер openvpn, осталось настроить всех клиентов при помощи ансибл скрипта openvpn/roles/openvpn-client/tasks/main.yml.

# Устанавливаем openvpn и resolvcof, который используется,
# чтобы настроить на openvpn клиенте те DNS-сервера, которые передаст сервер
- name: Install openvpn, resolvconf
  package:
    name: "{{ item }}"
    state: present
  with_items:
    - openvpn
    # должен быть последним в этом списке,
    # потому что его установка временно ломает работу dns
    # и последующие тулзы поставить не получится
    - resolvconf 

# Копируем из докер-контейнера схороненные там
# ключи и сертификаты общего назначения
- name: Copy common certs and keys from server
  become: true
  copy:
    src: "{{ item }}"
    dest: "/etc/openvpn/client/"
  with_fileglob:
    - "{{ playbook_dir }}/share/server/*"

# Копируем из докер-контейнера схороненные там
# ключи и сертификаты для текущего клиента
- name: Copy client certs and keys from server
  become: true
  copy:
    src: "{{ playbook_dir }}/share/clients/{{ item }}"
    dest: "/etc/openvpn/client/"
  with_items:
    - "{{inventory_hostname}}.crt"
    - "{{inventory_hostname}}.key"

# Создаем конфигурацию openvpn клиента
- name: Generate client configuration from sample config
  become: true
  copy:
    remote_src: yes
    src: /usr/share/doc/openvpn/examples/sample-config-files/client.conf
    dest: "/etc/openvpn/client/{{inventory_hostname}}.conf"
    force: no

# Поднастраиваем параметры конфигурации openvpn клиента
- name: Adjust OpenVPN client configuration
  lineinfile:
    dest: "/etc/openvpn/client/{{inventory_hostname}}.conf"
    regexp: "^{{ item.regex | regex_escape() }}"
    line: "{{ item.value }}"
  with_items:
    # адрес сервера openvpn
    - { regex: 'remote my-server-1 1194', value: 'remote {{ master_ip }} 1194' }
    # сократить количество привелегий у openvpn
    - { regex: ';user nobody', value: 'user nobody' }
    - { regex: ';group nogroup', value: 'group nogroup' }
    # настройка сертификатов
    - { regex: 'cert ', value: 'cert {{inventory_hostname}}.crt' }
    - { regex: 'key ', value: 'key {{inventory_hostname}}.key' }
    # включение логирования
    - { regex: 'log ', value: 'log /var/log/openvpn/openvpn.log' }
    # протокол ставим tcp, с udp настроить не удалось
    - { regex: 'proto ', value: 'proto tcp4' }
    # для того чтобы работали директивы ниже, выше был установлен resolvconf
    - { regex: 'script-security ', value: 'script-security 2' }
    - { regex: 'up ', value: 'up /etc/openvpn/update-resolv-conf' }
    - { regex: 'down ', value: 'down /etc/openvpn/update-resolv-conf' }

# Запуск демона openvpn клиента
- name: Start openvpn systemd service
  become: true
  systemd:
    name: openvpn-client@{{inventory_hostname}}
    state: started
    daemon_reload: yes
    enabled: yes

И вот мы пришли к тому, что у нас все машины объединились в виртуальную сеть. Осталось подключить в эту сеть и сам докер-контейнер. Для этого снова взглянем на скрипт оркестратор entrypoint.sh

# подключение докера в созданную openvpn сеть
## Copy common certs and keys from server
cp openvpn/share/server/* /etc/openvpn/client/;
## Copy docker cert and key from server
cp openvpn/share/clients/docker.key /etc/openvpn/client/;
cp openvpn/share/clients/docker.crt /etc/openvpn/client/;
## Generate client configuration from sample config
cd /etc/openvpn/client/;
cp /usr/share/doc/openvpn/examples/sample-config-files/client.conf docker.conf;
## Adjust OpenVPN client configuration
sed -i "s/^remote my-server-1 1194$/remote host.docker.internal 1194/g" docker.conf;       
sed -i "s/^;user nobody$/user nobody/g" docker.conf;
sed -i "s/^;group nogroup$/group nogroup/g" docker.conf;
sed -i "s/^cert .*$/cert docker.crt/g" docker.conf;
sed -i "s/^key .*$/key docker.key/g" docker.conf;
sed -i "s/^proto .*$/proto tcp4/g" docker.conf;
echo "" >> docker.conf;
echo "log /var/log/openvpn/openvpn.log" >> docker.conf;

Тут идет стандартная настройка openvpn-клиента, которую мы видели в ансибл скрипте выше, за исключением момента, связанного с доменными именами. К сожалению, докер-контейнер наглухо блокирует файл /etc/resolv.conf и не позволит обновить в нем DNS-сервера, которые прислал openvpn сервер. Поэтому делаем финт ушами, а именно, поднимаем локальный DNS-сервер (dnsmasq) и прописываем все что там хотим видеть руками.

echo "nameserver 127.0.0.1" > /etc/resolv.conf;

{
  echo "no-resolv";
  # 127.0.0.11 - внутренний докерный DNS
  echo "server=/host.docker.internal/127.0.0.11";
  echo "rev-server=$VIRTUAL_NETWORK_CIDR,$VIRTUAL_NETWORK_GATEWAY";
  echo "server=8.8.8.8";
  echo "server=8.8.4.4"
} > /etc/dnsmasq.conf;

dnsmasq;

Теперь у нас все готово, для запуска openvpn клиента в докер-контейнере.

openvpn --config docker.conf &

Профит)

Заключение

Медленно, но верно, я продвигаюсь в направлении создания микрооблаков. Многие со мной не согласятся, но именно так я вижу будущее развития интернета и появление так называемого WEB 3.0. Но даже если я не прав, то у меня на примете уже есть несколько команд разработки, которым данная технология будет полезна.

Так или иначе, будущее нас рассудит.

Желающие ознакомиться со скриптом настройки сети могут взять его тут.

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

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

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

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

Всем удачи! И до связи...

Комментарии (7)


  1. dmitryvolochaev
    14.04.2022 22:57

    Третьим шагом, видимо, должен быть перенос пользовательского контента со старого хостинга: надо как-то одной командой сделать для каждого микросервиса дамп базы и восстановить его на новом месте


    1. peaceful-coder Автор
      16.04.2022 12:12

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


  1. gavk
    15.04.2022 03:49

    Хорошо, питон ты поставил. Дальше нужно пользоваться модулями. Для apt и openssl они есть.


    1. peaceful-coder Автор
      16.04.2022 12:09

      Ансибл для меня штука новая, не все грабли еще прошёл. Про openssl не знал, спасибо.


  1. casuss
    15.04.2022 09:15
    +1

    Рассмотрите, как вариант, вместо OpenVPN - WireGuard. Для интенсивного сетевого обмена - у WireGuard выше скорость. Несколько проще настройка.


    1. peaceful-coder Автор
      16.04.2022 12:07

      Спасибо. Посмотрю в эту сторону. Уже несколько человек эту штуку рекомендуют. Вероятно, есть смысл


  1. Kiber-Shinobi
    16.04.2022 12:15

    Хорошая статья, а так же твое видение на WEB 3.0 и предоставление интересной концепции которую ты в скором времени сможешь воплотить в продакшн версию. Интересно будет понаблюдать.