Проект юного DevOps Глава 2: Настройка центра сертификации и репозитория

9c30a1f3a333a32f03de9d3d931284c5.png

В данной серии статей описан процесс создания первого pet-проекта для начинающего инженера в DevOps:

Глава 1: Введение и подготовка стенда

Глава 2: Настройка центра сертификации и репозитория

Глава 3: Настройка OpenVPN

Глава 4: Настройка мониторинга

Настройка центра сертификации (CA)

Easy-RSA — это набор скриптов для упрощения управления сертификатами в инфраструктуре открытых ключей (PKI). Он широко используется для создания и управления сертификатами в контексте VPN, шифрования и безопасности сетевых соединений. Изначально разработан для работы с OpenVPN, но может использоваться и в других сценариях.

Зайдем на vm «ca» и выполним установку Easy-RSA версии 3.8.0:

sudo apt-get update
sudo apt-get install -y easy-rsa=3.0.8-1ubuntu1

Заметка

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

Скопируем рабочую директорию Easy-RSA в домашнюю директорию нового пользователя и изменим права владения:

sudo cp -r /usr/share/easy-rsa /home/nikolay/
sudo chown -R nikolay:nikolay ~/easy-rsa/
sudo chmod -R 700 easy-rsa/

Настроим Easy-RSA, раскомментировав и изменив следующие строки в конфигурационном файле »~/easy-rsa/vars.example»:

sсt_var EASYRSA_REQ_COUNTRY     "RUS"
set_var EASYRSA_REQ_PROVINCE    "Moscow"
set_var EASYRSA_REQ_CITY        "Moscow City"
set_var EASYRSA_REQ_ORG         "Justnikobird"
set_var EASYRSA_REQ_EMAIL       "justnikobird@yandex.ru"
set_var EASYRSA_REQ_OU          "LLC"
set_var EASYRSA_ALGO            ec
set_var EASYRSA_DIGEST          "sha512"

Применим конфигурацию и создадим инфраструктуру открытых ключей (Public Key Infrastructure):

cd ~/easy-rsa
mv vars.example vars
./easyrsa init-pki

Результат выполнения команды

Note: using Easy-RSA configuration from: /home/nikolay/easy-rsa/vars

init-pki complete; you may now create a CA or requests.
Your newly created PKI dir is: /home/nikolay/easy-rsa/pki

В результате программа создаст директорию »/home/nikolay/easy-rsa/pki», в которой будут храниться все создаваемые в дальнейшем ключи.

Теперь сгенерируем ключи удостоверяющего центра:

./easyrsa build-ca

Система запросит у нас ввести пароль для приватного ключа ca и имя нового ca:

Note: using Easy-RSA configuration from: /home/nikolay/easy-rsa/vars
Using SSL: openssl OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022)

Enter New CA Key Passphrase: *******
Re-Enter New CA Key Passphrase: *******
read EC key
writing EC key
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Common Name (eg: your user, host, or server name) [Easy-RSA CA]:

CA creation complete and you may now import and sign cert requests.
Your new CA certificate file for publishing is at:
/home/nikolay/easy-rsa/pki/ca.crt

В результате программа создаст два фала:

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

Подробнее о том, что такое сертификаты и какую роль выполняет CA можно изучить в статье »Разбираем TLS по байтам. Кто такой этот HTTPS? »

Выпуск пары ключ-сертификат

Клиентский сертификат и сертификат сервера — это два различных типа сертификатов, используемых в контексте безопасности сети, особенно в протоколе SSL/TLS для шифрования данных.

Технически создание и подписание клиентского и серверного сертификатов схожи: оба включают открытый ключ, метаданные и подпись центра сертификации. Разница между ними сводится к прикрепленным метаданным и их проверке. Возможно использование клиентских сертификатов в роли серверных и наоборот, если отключены проверки подлинности и ограничений в библиотеке TLS. Сертификат сервера проверяется по DNS-имени в поле SAN, а сертификат клиента содержит проверенные атрибуты пользователя, устанавливаемые центром сертификации, и проверяется сервером на соответствие требованиям аутентификации.

Выпуск пары ключ-сертификат для сервера

Для выпуска и подписания новой пары ключ-сертификат для сервера с доменным именем «example.justnikobird.ru», необходимо выполнить следующие действия:

cd ~/easy-rsa
./easyrsa gen-req example.justnikobird.ru nopass
./easyrsa sign-req server example.justnikobird.ru

В результате мы получим два файла:

Выпуск пары ключ-сертификат для клиента

Для выпуска и подписания новой клиентской пары ключ-сертификат, необходимо выполнить следующие действия:

cd ~/easy-rsa
./easyrsa gen-req example nopass
./easyrsa sign-req client example

В результате мы получим два файла:

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

Сборка deb-пакета Easy-RSA

Весь процесс сборки я описал в статье »Работа с DEB-пакетами».

Здесь я только помечу следующие данные для сборки:

Файлы, которые буем хранить в пакете:

/home/nikolay/easy-rsa

install

easy-rsa/ usr/share

control

Source: easy-rsa-lab
Section: unknown
Priority: optional
Maintainer: Nikolay 
Build-Depends: debhelper-compat (= 13)
Standards-Version: 4.6.0
Homepage: https://github.com/OpenVPN/easy-rsa
#Vcs-Browser: https://salsa.debian.org/debian/just-easy-rsa
#Vcs-Git: https://salsa.debian.org/debian/just-easy-rsa.git
Rules-Requires-Root: no

Package: easy-rsa-lab
Architecture: all
Depends: easy-rsa
Recommends: opensc
Description: 
 

Скриптов нет.

В результате мы собрали пакет »easy-rsa-lab_0.1–1_all.deb».

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

Приступим!

Настройка собственного репозитория

Репозитории — это архивы программ. Каждый репозиторий содержит список пакетов, в нём хранящихся, с указанием версий, зависимостей и прочей необходимой информации. В контексте данного лабораторного стенда в настроенном репозитории будут храниться все пакеты, которые мы соберем.

Aptly — это инструмент управления пакетами для Debian-подобных систем, таких как Ubuntu. Он позволяет создавать, обновлять и управлять локальными репозиториями пакетов, облегчая процесс установки и обновления программного обеспечения.

Настройка Aptly

Зайдем на vm «repo» и применим bash-скрипт «vm-start.sh».

Перед работой с Aptly, выполним установку пакета «bzip2»:

sudo apt-get install -y bzip2

Скачаем архив с Aptly версии 1.5.0 с официальной страницы разработчиков на GitHub в домашнюю директорию и распакуем его:

wget -P ~/ https://github.com/aptly-dev/aptly/releases/download/v1.5.0/aptly_1.5.0_linux_amd64.tar.gz
tar xvf ~/aptly_1.5.0_linux_amd64.tar.gz

Заметка

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

Скопируем исполняемый файл программы в директорию »/usr/local/bin»:

sudo cp ~/aptly_1.5.0_linux_amd64/aptly /usr/local/bin/

Создадим конфигурационный файл »/etc/aptly.conf» от имени пользователя «root» и запишем в него следующие строки:

{
  "rootDir": "/opt/aptly",
  "downloadConcurrency": 4,
  "downloadSpeedLimit": 0,
  "architectures": ["all"],
  "dependencyFollowSuggests": false,
  "dependencyFollowRecommends": false,
  "dependencyFollowAllVariants": false,
  "dependencyFollowSource": false,
  "dependencyVerboseResolve": false,
  "gpgDisableSign": false,
  "gpgDisableVerify": false,
  "gpgProvider": "gpg",
  "downloadSourcePackages": true,
  "skipLegacyPool": true,
  "ppaDistributorID": "ubuntu",
  "ppaCodename": "",
  "FileSystemPublishEndpoints": {
    "lab": {
      "rootDir": "/var/www/aptly",
      "linkMethod": "symlink",
      "verifyMethod": "md5"
    }
  },
  "enableMetricsEndpoint": false
}

Описание конфигурационного файла

rootDir:  Определяет корневой каталог, в котором Aptly будет хранить свои данные.

downloadConcurrency:  Задает количество одновременных загрузок при скачивании пакетов.

downloadSpeedLimit:  Ограничивает скорость загрузки (в байтах в секунду). Установка в 0 отключает ограничение.

architectures:  Определяет архитектуры, которые Aptly будет использовать (в данном случае, «all»).

dependencyFollowSuggests, dependencyFollowRecommends, dependencyFollowAllVariants, dependencyFollowSource:  Управляют тем, следует ли Aptly автоматически добавлять пакеты, зависимые от других пакетов, указанных в полях Suggests, Recommends, Variants и Source.

dependencyVerboseResolve:  Активируетзапись подробных логов при разрешении зависимостей.

gpgDisableSign, gpgDisableVerify:  Опции, позволяющие отключить подпись или верификацию GPG.

gpgProvider:  Определяет провайдера GPG, который будет использоваться.

downloadSourcePackages:  Определяет, следует ли загружать и хранить исходные пакеты.

skipLegacyPool:  Пропускает использование старой файловой системы хранения для пула пакетов.

ppaDistributorID, ppaCodename:  Информация о распределении (Distributor ID) и кодовом имени (Codename) для использования при работе с PPA (Personal Package Archive).

FileSystemPublishEndpoints:  Определяет конечные точки публикации файловой системы для различных репозиториев. В нашем случае есть одна конечная точка с именем «lab», указывающая на »/var/www/aptly».

enableMetricsEndpoint:  Включает или отключает метрический конечный пункт для мониторинга.

Создадим репозиторий с названием «lab»:

sudo aptly repo create -comment="lab repo" -component="main" -distribution="focal" lab

Перенесем пакет »easy-rsa-lab_0.1–1_all.deb» в домашнюю директорию vm «repo» и добавим его в репозиторий:

sudo aptly repo add lab ~/easy-rsa-lab_0.1-1_all.deb

Перенос пакета с помощью SCP (Windows)

В терминале машины, с которой выполняется администрирование всех vm, выполним перенос пакета с vm «ca» на vm «repo».

Скачаем пакет с vm «ca»:

scp -P 1870 nikolay@ca:/home/nikolay/deb/easy-rsa-lab_0.1-1_all.deb .

Загрузим пакет в домашнюю директорию vm «repo»:

scp -P 1870 easy-rsa-lab_0.1-1_all.deb nikolay@repo:/home/nikolay/

Для получения абсолютного пути файла из рабочей директории поможет команда «realpath»:

realpath easy-rsa-lab_0.1-1_all.deb

Вывод:

/home/nikolay/deb/easy-rsa-lab_0.1-1_all.deb

В работе нам могут быть полезны следующие команды:

sudo aptly repo list
sudo aptly publish list
sudo aptly repo show -with-packages lab
sudo aptly package show easy-rsa-lab_0.1-1_all
sudo aptly repo drop lab

Снятие репозитория с публикации на примере «lab»:

sudo aptly publish drop focal filesystem:lab:lab
sudo aptly repo remove lab easy-rsa-lab_0.1-1_all

Публикация репозиториев

Для публикации нам потребуется gpg-ключ. Для начала установим набор утилит для генерации случайных чисел в ядре:

sudo apt-get install -y rng-tools

Запустим утилиту:

sudo rngd -r /dev/urandom

Создадим ключ:

sudo gpg --default-new-key-algo rsa4096 --gen-key --keyring pubring

Система запросит ввести имя и email.

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

Ключ создан, его можно увидеть с использованием следующей командой:

sudo gpg --list-keys

Теперь сделаем первую публикацию ранее созданного репозитория «lab»:

sudo aptly publish repo lab filesystem:lab:lab

Появится окно для ввода пароля закрытого gpg-ключа.

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

Обновление базы пакетов репозитория

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

sudo aptly publish update focal filesystem:lab:lab

После публикации, в каталоге »/var/www/aptly/lab» мы должны увидеть две директории:

ls /var/www/aptly/lab
dists  pool

В директории »dists» хранятся метаданные для опубликованных дистрибутивов.

В директории »pool» хранятся загруженные пакеты (в нашем случае симлинки на файлы в основном каталоге »/opt/aptly»).

Перенесем в домашнюю директорию vm «repo» сертификат открытого ключа CA.

Для удобства работы с нашим репозиторием в дальнейшем, опубликуем открытый gpg-ключ и сертификат открытого ключа CA в директории »/var/www/aptly/lab» — это необходимы для подключения к репозиторию клиентов:

sudo gpg --export --armor | sudo tee /var/www/aptly/lab/labtest.asc > /dev/null
sudo cp ~/ca.crt /var/www/aptly/lab/

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

Настройка Nginx

Теперь нам необходимо предоставить доступ к нашему репозиторию через интернет, для этого нам понадобится Nginx Web Server.

Выполним установку Nginx:

sudo apt-get update
sudo apt-get install -y nginx

Перенесем подписанные на CA ключи для vm «repo» в рабочую директорию Nginx:

sudo cp ~/repo.justnikobird.ru.crt /etc/nginx/
sudo cp ~/repo.justnikobird.ru.key /etc/nginx/

Подготовим хэш пароля для нового пользователя — скачаем необходимый пакет с инструментами и сгенерируем пароль «password» для пользователя «admin»:

sudo apt-get install -y apache2-utils
htpasswd -nbB -C 10 admin "password"

В результате выполнения команды мы получим следующую строку, которую запишем в конфигурационный файл »/etc/nginx/conf.d/.htpasswd» для аутентификации на сервере.:

admin:$2y$10$iWOlBiff2IDDovW2wy2SauStp7ahuHpOULM2W7yQ6JpNcbDKeBPYS

Выполним настройку Nginx в конфигурационном файле »/etc/nginx/sites-available/default»:

server {
        listen 1111 ssl default_server;
        server_name repo.justnikobird.ru;
        auth_basic              "Restricted Access!";
        auth_basic_user_file    /etc/nginx/conf.d/.htpasswd;
        ssl_certificate     repo.justnikobird.ru.crt;
        ssl_certificate_key repo.justnikobird.ru.key;
        ssl_protocols       TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
        ssl_ciphers         HIGH:!aNULL:!MD5;

        root /var/www/aptly;

        location / {
                autoindex on;
        }
}

# nginx prometheus exporter
server {
        listen 8080;

        location /stub_status {
                stub_status;
                allow 127.0.0.1;
                deny all;
        }
}

Заметка

Настройка Nginx на порту 8080 понадобится для подключения системы мониторинга в дальнейшем.

Перезапустим Nginx и активируем автоматический запуск:

sudo systemctl restart nginx.service
sudo systemctl enable nginx.service

Проверка исправности работы сервиса

Проверим статус сервиса Nginx:

sudo systemctl status nginx.service

Если сервис работает исправно, то он должен быть в статусе «active (running)»

● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; disabled; vendor preset: enabled)
     Active: active (running) since Tue 2024-01-30 18:37:36 MSK; 2 weeks 0 days ago
       Docs: man:nginx(8)
   Main PID: 5562 (nginx)
      Tasks: 2 (limit: 1013)
     Memory: 100.8M
        CPU: 32.199s
     CGroup: /system.slice/nginx.service
             ├─5562 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;"
             └─5567 "nginx: worker process" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""

Jan 30 18:37:36 2454768-justnikobird.twc1.net systemd[1]: nginx.service: Deactivated successfully.
Jan 30 18:37:36 2454768-justnikobird.twc1.net systemd[1]: Stopped A high performance web server and a reverse proxy server.
Jan 30 18:37:36 2454768-justnikobird.twc1.net systemd[1]: Starting A high performance web server and a reverse proxy server...
Jan 30 18:37:36 2454768-justnikobird.twc1.net systemd[1]: Started A high performance web server and a reverse proxy server.

Если сервис в статусе «failed», то необходимо поверить его логи:

sudo journalctl -u nginx.service

Также необходимо проверить какие порты прослушивает запущенный сервис с помощью инструмента «netstat», который входит в пакет «net-tools»:

sudo apt-get install -y net-tools
sudo netstat -lptun

В выводе видно, то сервис Nginx прослушивает порты »8080» и »1111»:

Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      54594/nginx: master
tcp        0      0 0.0.0.0:1111            0.0.0.0:*               LISTEN      54594/nginx: master

Теперь необходимо проверить настройки iptables на наличие правил, разрешающих доступ к сервису:

sudo iptables -L -v -n | grep 1111

...

Chain INPUT (policy DROP 531K packets, 27M bytes)
 pkts bytes target     prot opt in     out     source               destination
  969 56172 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:1111 /* repo_nginx */

...

Chain OUTPUT (policy DROP 214 packets, 76866 bytes)
 pkts bytes target     prot opt in     out     source               destination
 631K 2580M ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            state RELATED,ESTABLISHED

Правила присутствуют, доступ к сервису открыт.

Настройка iptables

sudo iptables -A INPUT -p tcp --dport 1111 -j ACCEPT -m comment --comment repo_nginx

Сохраним конфигурацию iptables с помощью инструмента iptables-persistent:

sudo apt-get install -y iptables-persistent
sudo service netfilter-persistent save

Проверка работы репозитория

Проверим исправность работы репозитория, перейдя по ссылке »https://repo.justnikobird.ru:1111»:

27f900592a47e31bd1439e214ba2ed74.pnge3a8f4ba4df4df433a8dca37ba3bdefd.png

Репозиторий успешно настроен.

Как подружить браузер с новой web-страницей?

Чтобы браузер не ругался на наш сертификат, необходимо загрузить файл сертификата с открытым ключом CA в библиотеку «Trusted Root Certificate Authorities» в настройках браузера.

Подключение к Repo

В системе, где планируем подключиться к репозиторию, создадим файл »/etc/apt/sources.list.d/own_repo.list» и запишем в него данные для подключения к repo:

echo "deb https://repo.justnikobird.ru:1111/lab focal main" | sudo tee -a /etc/apt/sources.list.d/own_repo.list > /dev/null

Настроим https аутентификацию в файле »/etc/apt/auth.conf» для пользователя «admin» с паролем «password»:

machine repo.justnikobird.ru:1111
login admin
password password

Добавим правило в iptables и сохраним конфигурацию с помощью инструмента iptables-persistent:

sudo iptables -A OUTPUT -p tcp --dport 1111 -j ACCEPT -m comment --comment repo.justnikobird.ru
sudo apt-get install -y iptables-persistent
sudo service netfilter-persistent save

Импортируем в систему ключ, который опубликовали ранее:

wget --no-check-certificate -P ~/ https://admin:password@repo.justnikobird.ru:1111/lab/labtest.asc 
sudo apt-key add ~/labtest.asc

Импортирует в систему сертификат от CA чтобы новый репозиторий считался доверенным:

wget --no-check-certificate -P ~/ https://admin:password@repo.justnikobird.ru:1111/lab/ca.crt
sudo cp ~/ca.crt /usr/local/share/ca-certificates/ca.crt
sudo update-ca-certificates

Обновим список пакетов:

sudo apt-get update

Проверим наличие пакета «easy-rsa-lab» в подключенном репозитории:

sudo apt search easy-rsa-lab

В результате мы должны получить следующий вывод:

Sorting... Done
Full Text Search... Done
easy-rsa-lab/focal 0.1-1 all
  

Репозиторий успешно подключен к системе!

Bash-скрипт для Repo

Настало время написать bush-скрипт, который сможет выполнить настройку Repo автоматически:

repo.sh

Версия на GitHub

#!/bin/bash

# проверим, запущен ли скрипт от пользователя root
if [[ "${UID}" -ne 0 ]]; then
  echo "You need to run this script as root!"
  exit 1
fi

# функция, которая проверяет наличие пакета в системе и в случае его отсутствия выполняет установку
command_check() {
  if ! command -v "$1" &>/dev/null; then
    echo -e "\n====================\n$2 could not be found!\nInstalling...\n====================\n"
    apt-get install -y "$3"
    echo -e "\nDONE\n"
  fi
}

# функция, которая запрашивает путь до файла и проверяет его валидность
path_request() {
  while true; do
    read -r -e -p $'\n'"Please input full valid path to ${1}: " path
    if [ -f "$path" ]; then
      echo "$path"
      break
    fi
  done
}
# функция, которая проверяет наличие правила в iptables и в случае отсутствия применяет его
iptables_add() {
  if ! iptables -C "$@" &>/dev/null; then
    iptables -A "$@"
  fi
}

# установим все необходимые пакеты используя функцию command_check
systemctl restart systemd-timesyncd.service
apt-get update
command_check wget "Wget" wget
command_check iptables "Iptables" iptables
command_check netfilter-persistent "Netfilter-persistent" iptables-persistent
command_check rngd "Rng-tools" rng-tools
command_check nginx "Nginx" nginx
command_check htpasswd "Htpasswd" apache2-utils
command_check basename "Basename" coreutils
command_check bzip2 "Bzip2" bzip2

# настроим aptly
echo -e "\n====================\nAptly Configuration...\n====================\n"

# проверим на наличие старых файлов aptly (полезно при переустановке)
if [ -f /tmp/aptly_1.5.0_linux_amd64.tar.gz ] || [ -d /tmp/aptly_1.5.0_linux_amd64 ]; then
  rm -rf /tmp/aptly_1.5.0_linux_amd64*
fi

# скачаем исходники aptly с распакуем их
if wget -P /tmp/ https://github.com/aptly-dev/aptly/releases/download/v1.5.0/aptly_1.5.0_linux_amd64.tar.gz; then
  tar -xvf /tmp/aptly_1.5.0_linux_amd64.tar.gz -C /tmp/
  mv -f /tmp/aptly_1.5.0_linux_amd64/aptly /usr/local/bin/
else
  exit 1
fi
rm -rf /opt/aptly
rm -rf /var/www/aptly

# создадим конфигурационный файл aptly
echo '{
  "rootDir": "/opt/aptly",
  "downloadConcurrency": 4,
  "downloadSpeedLimit": 0,
  "architectures": ["all","amd64"],
  "dependencyFollowSuggests": false,
  "dependencyFollowRecommends": false,
  "dependencyFollowAllVariants": false,
  "dependencyFollowSource": false,
  "dependencyVerboseResolve": false,
  "gpgDisableSign": false,
  "gpgDisableVerify": false,
  "gpgProvider": "gpg",
  "downloadSourcePackages": true,
  "skipLegacyPool": true,
  "ppaDistributorID": "ubuntu",
  "ppaCodename": "",
  "FileSystemPublishEndpoints": {
    "lab": {
      "rootDir": "/var/www/aptly",
      "linkMethod": "symlink",
      "verifyMethod": "md5"
    }
  },
  "enableMetricsEndpoint": false
}' >/etc/aptly.conf

# создадим репозиторий lab
if aptly repo create -comment="lab repo" -component="main" -distribution="focal" lab; then
  # запросим путь до первого deb-пакета
  package_path=$(path_request "first package (architecture is all or amd64)")
  # загрузим пакет в новый репозиторий
  if ! aptly repo add lab "$package_path"; then
    exit 1
  fi
else
  exit 1
fi
echo -e "\n====================\nLab Repo Successfully Created\n====================\n"

# сгенерируем gpg-ключи
echo -e "\n====================\nGPG Key Generating...\n====================\n"
rngd -r /dev/urandom
rngd_check=$?
if [ $rngd_check -eq 0 ] || [ $rngd_check -eq 10 ]; then
  gpg --default-new-key-algo rsa4096 --gen-key --keyring pubring
  gen_key_check=$?
  if [ $gen_key_check -eq 0 ] || [ $gen_key_check -eq 2 ]; then
    gpg --list-keys
  fi
else
  exit 1
fi
echo -e "\nDONE\n"

# опубликуем репозиторий
aptly publish repo lab filesystem:lab:lab

# экспортируем открытый gpg-ключ на web-страницу репозитория
gpg --export --armor | tee /var/www/aptly/lab/labtest.asc >/dev/null

# экспортируем открытый ключ ca на web-страницу репозитория
cp "$(path_request "ca certificate")" /var/www/aptly/lab/ca.crt

echo -e "\n====================\nLab Repo Successfully Published\n====================\n"

# настроим nginx
echo -e "\n====================\nNginx Configuration...\n====================\n"

# запросим доменное имя репозитория
read -r -p $'\n'"repo domain name (example repo.justnikobird.ru): " repo_name

# запросим путь до сертификата с помощью функции path_request и перенес файл в рабочую директорию nginx
server_crt=$(path_request certificate)
cp "$server_crt" /etc/nginx/
cert_file=$(basename "$server_crt")

# запросим путь до приватного ключа с помощью функции path_request и перенес файл в рабочую директорию nginx
server_key=$(path_request key)
cp "$server_key" /etc/nginx/
key_file=$(basename "$server_key")

# запросим логин и пароль для нового репозитория
read -r -p $'\n\n'"login for ${repo_name}: " repo_login
read -r -p "password for ${repo_name}: " -s repo_pass

# сгенерируем хэш пароля
htpasswd -nbB -C 10 "$repo_login" "$repo_pass" >>/etc/nginx/conf.d/.htpasswd

# создадим конфигурационный файл nginx
echo '
server {
        listen 1111 ssl default_server;
        server_name '"$repo_name"';
        auth_basic              "Restricted Access!";
        auth_basic_user_file    /etc/nginx/conf.d/.htpasswd;
        ssl_certificate     '"$cert_file"';
        ssl_certificate_key '"$key_file"';
        ssl_protocols       TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
        ssl_ciphers         HIGH:!aNULL:!MD5;

        root /var/www/aptly;

        location / {
                autoindex on;
        }
}

# nginx prometheus exporter
server {
        listen 8080;

        location /stub_status {
                stub_status;
                allow 127.0.0.1;
                deny all;
        }
}' >/etc/nginx/sites-available/default
echo -e "\nDONE\n"

# настроим iptables
echo -e "\n====================\nIptables configuration\n====================\n"
iptables_add INPUT -p tcp --dport 1111 -j ACCEPT -m comment --comment repo_nginx
echo -e "\n====================\nSaving iptables config...\n====================\n"
service netfilter-persistent save
echo -e "\nDONE\n"

# перезагрузим nginx-сервис
systemctl restart nginx.service
systemctl enable nginx.service

echo -e "\n====================\nRepo listening on port 1111\n====================\n"
echo -e "\nOK\n"
exit 0

Обновление bash-скрипта для vm

После успешной настройки Repo, добавим в скрипт первичной настройки vm процесс подключения к новому Repo и правило в iptables:

vm-start.sh

Версия на GitHub

#!/bin/bash

# активируем опцию, которая прерывает выполнение скрипта, если любая команда завершается с ненулевым статусом
set -e

# проверим, запущен ли скрипт от пользователя root
if [[ "${UID}" -ne 0 ]]; then
  echo -e "You need to run this script as root!\nPlease apply 'sudo su root' and add your host-key to /root/.ssh/authorized_keys before run this script!"
  exit 1
fi

# проверим, загружены ли открытые ssh-ключи у пользователя root
if [ ! -f /root/.ssh/authorized_keys ]; then
  echo -e "\n====================\nFile /root/.ssh/authorized_keys not found!\n====================\n"
  exit 1
else
  if [ ! -s /root/.ssh/authorized_keys ]; then
    echo -e "\n====================\nFile /root/.ssh/authorized_keys is empty!\n====================\n"
    exit 1
  fi
fi

# функция, которая проверяет наличие пакета в системе и в случае его отсутствия выполняет установку
command_check() {
  if ! command -v "$1" &>/dev/null; then
    echo -e "\n====================\n$2 could not be found!\nInstalling...\n====================\n"
    apt-get install -y "$3"
    echo -e "\nDONE\n"
  fi
}

# функция, которая запрашивает имя нового пользователя и проверяет его на наличие в системе
username_request() {
  while true; do
    read -r -p $'\n'"new username: " username
    if id "$username" >/dev/null 2>&1; then
      echo -e "\nUser $username exists!\n"
    else
      break
    fi
  done
}

# функция, которая проверяет наличие правила в iptables и в случае отсутствия применяет его
iptables_add() {
  if ! iptables -C "$@" &>/dev/null; then
    iptables -A "$@"
  fi
}

# функция, которая выполняет backup файла путем копирования его и модификации названия
bkp() {
  if [ -f "$1" ]; then
    cp "$1" "$1".bkp
  fi
}

# функция, которая восстанавливает файл из backup
restore_bkp() {
  if [ -f "$1".bkp ]; then
    if [ -f "$1" ]; then
      rm "$1" && mv "$1".bkp "$1"
    else
      mv "$1".bkp "$1"
    fi
  else
    echo -e "\nCan't find backup file!\n"
  fi
}

# настроим часовой пояс
echo -e "\n====================\nSetting timezone\n===================="
timedatectl set-timezone Europe/Moscow
systemctl restart systemd-timesyncd.service
timedatectl
echo -e "\nDONE\n"

# установим все необходимые пакеты используя функцию command_check
apt-get update
command_check wget "Wget" wget
command_check iptables "Iptables" iptables
command_check netfilter-persistent "Netfilter-persistent" iptables-persistent
command_check openssl "Openssl" openssl
command_check update-ca-certificates "Ca-certificates" ca-certificates
command_check tee "Tee" coreutils

# проверим наличие конфигурационного файла ssh
if [ ! -f /etc/ssh/sshd_config ]; then
  echo -e "\n====================\nFile /etc/ssh/sshd_config not found!\n====================\n"
  exit 1
fi

# проверим наличие конфигурационного файла grub
if [ ! -f /etc/default/grub ]; then
  echo -e "\n====================\nFile /etc/default/grub not found!\n====================\n"
  exit 1
fi

# создадим нового пользователя
echo -e "\n====================\nNew user config\n===================="

while true; do
  read -r -n 1 -p "Continue or Skip? (c|s) " cs
  case $cs in
  [Cc]*)
    # запросим имя пользователя используя функцию username_request (функция создаст глобальную переменную "username")
    username_request

    # запросим пароль для нового пользователя
    read -r -p "new password: " -s password

    # создадим нового пользователя и перенесем ssh-ключи
    useradd -p "$(openssl passwd -1 "$password")" "$username" -s /bin/bash -m -G sudo
    cp -r /root/.ssh/ /home/"$username"/ && chown -R "$username":"$username" /home/"$username"/.ssh/
    echo -e "\n\nDONE\n"

    # выполним корректировку prompt statement
    echo -e "\n====================\nEdit prompt statement for this user?\n===================="

    while true; do
      read -r -n 1 -p "Continue or Skip? (c|s) " cs
      case $cs in
      [Cc]*)
        # запросим имя vm
        read -r -p $'\n'"vm name: " vm_name

        # выполним корректировку prompt statement
        echo "PS1='\${debian_chroot:+(\$debian_chroot)}\\u@$vm_name:\\w\\\$ '" >>/home/"$username"/.bashrc

        echo -e "\n\nDONE\n"
        break
        ;;
      [Ss]*)
        echo -e "\n"
        break
        ;;
      *) echo -e "\nPlease answer C or S!\n" ;;
      esac
    done

    break
    ;;
  [Ss]*)
    echo -e "\n"
    break
    ;;
  *) echo -e "\nPlease answer C or S!\n" ;;
  esac
done

# настроим ssh
echo -e "\n====================\nEdit sshd_config file\n===================="

while true; do
  read -r -n 1 -p "Continue or Skip? (c|s) " cs
  case $cs in
  [Cc]*)
    sed -i 's/#\?\(Port\s*\).*$/\1 1870/' /etc/ssh/sshd_config
    sed -i 's/#\?\(PermitRootLogin\s*\).*$/\1 no/' /etc/ssh/sshd_config
    sed -i 's/#\?\(PubkeyAuthentication\s*\).*$/\1 yes/' /etc/ssh/sshd_config
    sed -i 's/#\?\(PermitEmptyPasswords\s*\).*$/\1 no/' /etc/ssh/sshd_config
    sed -i 's/#\?\(PasswordAuthentication\s*\).*$/\1 no/' /etc/ssh/sshd_config
    echo -e "\n\n"
    /etc/init.d/ssh restart
    echo -e "\nDONE\n"
    break
    ;;

  [Ss]*)
    echo -e "\n"
    break
    ;;
  *) echo -e "\nPlease answer C or S!\n" ;;
  esac
done

# выключим ipv6
echo -e "\n====================\nDisabling ipv6\n===================="

while true; do
  read -r -n 1 -p "Continue or Skip? (c|s) " cs
  case $cs in
  [Cc]*)
    echo -e "\n\n"
    sed -i 's/^GRUB_CMDLINE_LINUX_DEFAULT="/&ipv6.disable=1 /' /etc/default/grub
    sed -i 's/^GRUB_CMDLINE_LINUX="/&ipv6.disable=1 /' /etc/default/grub
    update-grub
    echo -e "\nDONE\n"
    break
    ;;

  [Ss]*)
    echo -e "\n"
    break
    ;;
  *) echo -e "\nPlease answer C or S!\n" ;;
  esac
done

# подключим репозиторий
echo -e "\n====================\nRepo config\n===================="

while true; do
  read -r -n 1 -p "Continue or Skip? (c|s) " cs
  echo -e "\n"
  case $cs in
  [Cc]*)
    # выполним backup файлов с помощью функции bkp
    bkp /etc/apt/sources.list.d/own_repo.list
    bkp /etc/apt/auth.conf

    # запросим логин и пароль для подключения к репозиторию
    read -r -p $'\n\n'"login for repo.justnikobird.ru: " repo_login
    read -r -p "password for repo.justnikobird.ru: " -s repo_pass

    # проверим файл /etc/apt/sources.list.d/own_repo.list на наличие записи о репозитории, и в случае ее отсутствия добавим
    if ! grep -Fxq "deb https://repo.justnikobird.ru:1111/lab focal main" /etc/apt/sources.list.d/own_repo.list &>/dev/null; then
      echo "deb https://repo.justnikobird.ru:1111/lab focal main" | tee -a /etc/apt/sources.list.d/own_repo.list >/dev/null
    fi

    # проверим файл /etc/apt/auth.conf на наличие записей о репозитории, и в случае их отсутствия добавим
    if ! grep -Fxq "machine repo.justnikobird.ru:1111" /etc/apt/auth.conf &>/dev/null; then
      echo -e "machine repo.justnikobird.ru:1111\nlogin $repo_login\npassword $repo_pass" | tee -a /etc/apt/auth.conf >/dev/null
    else
      # если в файле /etc/apt/auth.conf записи обнаружены, то попросим пользователя удалить их
      echo -e "\n\nrepo.justnikobird.ru has been configured in /etc/apt/auth.conf!\nPlease manually clean configuration or skip this stage."
      restore_bkp /etc/apt/sources.list.d/own_repo.list
      restore_bkp /etc/apt/auth.conf
      exit 1
    fi

    # скачаем и установим gpg-ключ от репозитория
    if ! wget --no-check-certificate -P ~/ https://"$repo_login":"$repo_pass"@repo.justnikobird.ru:1111/lab/labtest.asc; then
      restore_bkp /etc/apt/sources.list.d/own_repo.list
      restore_bkp /etc/apt/auth.conf
      exit 1
    else
      apt-key add ~/labtest.asc
    fi

    # скачаем и установим открытый ключ ca-сертификата от репозитория
    if ! wget --no-check-certificate -P /usr/local/share/ca-certificates/ https://"$repo_login":"$repo_pass"@repo.justnikobird.ru:1111/lab/ca.crt; then
      restore_bkp /etc/apt/sources.list.d/own_repo.list
      restore_bkp /etc/apt/auth.conf
      exit 1
    else
      update-ca-certificates
    fi

    # выполним синхронизацию списков пакетов в системе
    if ! apt update; then
      restore_bkp /etc/apt/sources.list.d/own_repo.list
      restore_bkp /etc/apt/auth.conf
      exit 1
    fi
    echo -e "\nDONE\n"
    break
    ;;

  [Ss]*)
    echo -e "\n"
    break
    ;;
  *) echo -e "\nPlease answer C or S!\n" ;;
  esac
done

# настроим iptables
echo -e "\n====================\nIptables config\n===================="
while true; do
  read -r -n 1 -p "Current ssh session may drop! To continue you have to relogin to this host via 1870 ssh-port and run this script again. Are you ready? (y|n) " yn
  case $yn in
  [Yy]*) #---DNS---
    iptables_add OUTPUT -p tcp --dport 53 -j ACCEPT -m comment --comment dns
    iptables_add OUTPUT -p udp --dport 53 -j ACCEPT -m comment --comment dns
    #---NTP---
    iptables_add OUTPUT -p udp --dport 123 -j ACCEPT -m comment --comment ntp
    #---REPO---
    iptables_add OUTPUT -p tcp --dport 1111 -j ACCEPT -m comment --comment repo.justnikobird.ru
    #---ICMP---
    iptables_add OUTPUT -p icmp -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
    iptables_add INPUT -p icmp -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
    #---loopback---
    iptables_add OUTPUT -o lo -j ACCEPT
    iptables_add INPUT -i lo -j ACCEPT
    #---Input-SSH---
    iptables_add INPUT -p tcp --dport 1870 -j ACCEPT -m comment --comment ssh
    #---Output-HTTP---
    iptables_add OUTPUT -p tcp -m multiport --dports 443,80 -j ACCEPT
    #---ESTABLISHED---
    iptables_add INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
    iptables_add OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
    #---INVALID---
    iptables_add OUTPUT -m state --state INVALID -j DROP
    iptables_add INPUT -m state --state INVALID -j DROP
    #---Defaul-Drop---
    iptables -P OUTPUT DROP
    iptables -P INPUT DROP
    iptables -P FORWARD DROP
    # save iptables config
    echo -e "\n====================\nSaving iptables config\n====================\n"
    service netfilter-persistent save
    echo -e "DONE\n"
    break
    ;;
  [Nn]*)
    echo -e "\n"
    exit
    ;;
  *) echo -e "\nPlease answer Y or N!\n" ;;
  esac
done

echo -e "\nOK\n"
exit 0

Bash-скрипт для Easy-RSA

Настало время написать bush-скрипт, который сможет выполнить настройку Easy-RSA автоматически:

easy-rsa.sh

Версия на GitHub

#!/bin/bash

# активируем опцию, которая прерывает выполнение скрипта, если любая команда завершается с ненулевым статусом
set -e

# проверим, запущен ли скрипт от пользователя root
if [[ "${UID}" -ne 0 ]]; then
  echo "You need to run this script as root!"
  exit 1
fi

# проверим подключен ли репозиторий
if [[ ! $(grep -rhE ^deb /etc/apt/sources.list*) == *"deb https://repo.justnikobird.ru:1111/lab focal main"* ]]; then
  echo -e "Lab repo not connected!\nPlease run vm_start.sh script!\n"
  exit 1
fi

# запросим путь будущего расположения рабочей директории easy-rsa
while true; do
  read -r -e -p $'\n'"Path for easy-rsa location (format: /home/nikolay): " dest_dir
  if [[ "$dest_dir" == */ ]]; then
    echo -e "\nWrong path format!\n"
  else
    if [ ! -d "$dest_dir" ]; then
      echo -e "\nDirectory $dest_dir doesn't exist!\n"
    else
      break
    fi
  fi
done

# проверим установлена ли программа Easy-RSA
if [ ! -d /usr/share/easy-rsa/ ]; then
  echo -e "\n====================\nEasy-rsa could not be found\nInstalling...\n====================\n"
  systemctl restart systemd-timesyncd.service
  apt-get update
  apt-get install -y easy-rsa-lab
  echo -e "\nDONE\n"
else
  while true; do
    read -r -n 1 -p $'\n'"Are you ready to reinstall easy-rsa? (y|n) " yn
    case $yn in
    [Yy]*)
      apt-get purge -y easy-rsa
      apt-get install -y easy-rsa-lab
      echo -e "\nDONE\n"
      break
      ;;
    [Nn]*) exit ;;
    *) echo -e "\nPlease answer Y or N!\n" ;;
    esac
  done
fi

# запросим username у администратора Easy-RSA и скопируем рабочую директорию Easy-RSA в рабочую директорию введенного пользователя
while true; do
  read -r -p $'\n'"Easy-rsa owner username: " username
  if id "$username" >/dev/null 2>&1; then
    cp -r /usr/share/easy-rsa "$dest_dir"/easy-rsa
    chmod -R 700 "$dest_dir"/easy-rsa/
    chown -R "$username":"$username" "$dest_dir"/easy-rsa/
    break
  else
    echo -e "\nUser $username doesn't exists!\n"
  fi
done

# создадим пару CA-ключей
while true; do
  read -r -n 1 -p $'\n'"Are you ready to create pair of CA keys? (y|n) " yn
  case $yn in
  [Yy]*)
    cd "$dest_dir"/easy-rsa
    sudo -u "$username" ./easyrsa build-ca
    echo -e "\nDONE\n"
    break
    ;;
  [Nn]*) exit ;;
  *) echo -e "\nPlease answer Y or N!\n" ;;
  esac
done

echo -e "\nOK\n"
exit 0

© Habrahabr.ru