Подготовка окружения#

Предварительные условия#

Проверка версии утилиты Dactl в дистрибутиве#

Утилита расположена в дистрибутиве K8S-2.2.3-fstec-58-distrib.zip. Получив дистрибутив в виде zip-архива, загрузите его на машину администратора, вся дальнейшая работа будет вестись с машины администратора.

Разархивируйте дистрибутив, получив вложенные архивы и файлы:

  • k8sf-2.2.3-owned-distrib.zip;

  • k8sf-2.2.3-owned.pom.

Архив имеет большой размер - более 10 Гб, рекомендуется перед разархивацией установить переменную окружения UNZIP_DISABLE_ZIPBOMB_DETECTION=TRUE

Разархивируйте архив K8S-2.2.3-fstec-58-distrib.zip, состоящий из следующих вложенных файлов:

  • k8sf-lsc-images-2.2.3-distrib.zip;

  • k8sf-binaries-2.2.3-distrib.zip;

  • k8sf-scripts-2.2.3-distrib.zip;

  • k8sf-rpms-alt-2.2.3-distrib.zip;

  • k8sf-images-2.2.3-distrib.zip;

  • k8sf-rpms-2.2.3-distrib.zip.

Разархивируйте архив k8sf-binaries-2.2.3-distrib.zip и получите утилиту Dactl по пути ./package/bh/dactl-2.2.3-fstec.zip..

Проверьте версию Dactl:

$ ./dactl version
Platform V DropApp version: 2.2.3-fstec

Общая схема развертывания представлена на диаграмме:

Общая схема развертывания

Требования к виртуальной машине администратора кластера#

DropApp настраивается администратором на виртуальной машине с предустановленной ОС

.

Для установки требуется машина администратора кластера с которой будет запускаться утилита Dactl.

Машина администратора кластера должна быть в одном сетевом контуре с:

  • будущими машинами-узлами кластера;

  • image registry (реестром образов), куда будут загружены образы контейнеров DropApp.

Планирование пула IP-адресов#

DropApp использует балансировщик Metallb для развертывания приложений.

Для использования Metallb необходимо зарезервировать диапазон IP-адресов.

Например, 10 адресов из той же подсети, в которой развернуты серверы для установки DropApp.

DNS#

В DNS должны быть зарегистрированы записи для приложений, а также для стартовый реестр Harbor. Формат hostname: <tool>.<clusteName>.<domain>. Пример указан в таблице ниже.

Стартовый реестр Harbor:

Приложения и их IP-адреса#

Приложение

Пример записи

IP-адрес

Harbor

harbor.bootstrap.domain.ru

Адрес машины для стартового реестра

Основной реестр Harbor:

Приложения и их IP-адреса#

Приложение

Пример записи

IP-адрес

Console

console.registry.domain.ru

Первый из пула адресов Metallb

Harbor

harbor.registry.domain.ru

Первый из пула адресов Metallb

Dex

auth.registry.domain.ru

Первый из пула адресов Metallb

Основной кластер DropApp:

Приложения и их IP-адреса#

Приложение

Пример записи

IP-адрес

Console

console.fstec-cluster.domain.ru

Первый из пула адресов Metallb

Dex

auth.fstec-cluster.domain.ru

Первый из пула адресов Metallb

Grafana

grafana.fstec-cluster.domain.ru

Первый из пула адресов Metallb

Имена должны разрешаться c nodes кластера.

LDAP-каталог или IPA-сервер#

DropApp использует сертифицированный LDAP-каталог пользователей и групп. Например IPA-сервер из дистрибутива Platform V SberLinux OS Server.

В IPA-сервере должны быть настроены группы, согласно ролевой модели.

Стартовый реестр образов#

Для установки DropApp необходим реестр образов контейнеров. Далее описан процесс создания стартового реестра Harbor из поставки DropApp.

Создание реестра образов контейнеров состоит из следующих шагов:

  1. Создать стартовый реестр Harbor, он необязательно должен быть на сертифицированной ОС и служит для загрузки всех образов из дистрибутива.

  2. Создать сертифицированный кластер DropApp для установки основного реестра Harbor. При установке используется стартовый реестр Harbor. Основной реестр Harbor будет использоваться источником образов контейнеров для всех сертифицированных кластеров DropApp.

  3. Создать основной сертифицированный кластер DropApp для установок пользовательских приложений, использующий реестр Harbor из шага 2.

Подготовка к установке#

Для установки стартового реестра Harbor требуется:

  • Машина с необязательно сертифицированной версией ОС (так как это стартовый реестр):

    • у пользователя в системе должны быть административные полномочия (root или аналогичные);

    • машина должна быть в одном сетевом контуре с будущими машинами-узлами кластера;

    • на машине должны быть установлены Docker или Podman.

  • Дистрибутив K8S-2.2.3-fstec-58-distrib.zip;

  • DNS-запись вида harbor.bootstrap.<public_domain> с хостом, на котором осуществляется развертывание Harbor, см. раздел «DNS».

Примечание

Средство контейнеризации Podman версии 5.2.2-11 входит в состав ОС Plarform V SberLinux OS Server версии 9.1.0-fstec.

Для стартового реестра Harbor будет достаточно одной машины.

Минимальная конфигурация машины стартового реестра Harbor#

Значение

Размер

ЦПУ [CPU] (в ядрах)

4 (с поддержкой VT*)

ОЗУ [RAM]

8192 Мб

Размер диска [HDD/SSD]

50 Гб

Примечание

После развертывания основного реестра Harbor машину стартового реестра Harbor можно удалить.

Работа с дистрибутивом#

Получение образов#

Из состава дистрибутива K8S-2.2.3-fstec-58-distrib.zip разархивируйте архив k8sf-images-2.2.3-distrib.zip (подробнее в подразделе «Предварительные условия»):

$ unzip k8sf-images-2.2.3-distrib.zip -d ./images
Archive:  k8sf-images-2.2.3-distrib.zip
  inflating: ./images/harbor-portal-v2.9.3.tar
....

Загрузка образов Harbor в локальный cache#

Для загрузка образов Harbor в локальный cache выполните следующие шаги:

  1. Создайте каталог harbor и перейдите в него.

  2. Создайте в каталоге harbor скрипт load_harbor_images.sh:

    load_harbor_images.sh
    #!/bin/bash
    set -e
    
    CONTAINER_ENGINE="podman"
    IMAGES_PATH=""
    
    usage() {
        echo "Использование: $0 --images-path <путь> [--container-engine <docker|podman>]"
        exit 1
    }
    
    while [[ "$#" -gt 0 ]]; do
        case $1 in
            --images-path)
                if [[ -n "$2" ]]; then
                    IMAGES_PATH="$2"
                    shift 2
                else
                    echo "Ошибка: Для --images-path не указано значение."
                    usage
                fi
                ;;
            --container-engine)
                if [[ -n "$2" ]]; then
                    CONTAINER_ENGINE="$2"
                    shift 2
                else
                    echo "Ошибка: Для --container-engine не указано значение."
                    usage
                fi
                ;;
            *)
                echo "Неизвестный параметр: $1"
                usage
                ;;
        esac
    done
    
    if [[ -z "$IMAGES_PATH" ]]; then
        echo "Ошибка: Параметр --images-path является обязательным."
        usage
    fi
    
    if [[ ! -d "$IMAGES_PATH" ]]; then
        echo "Ошибка: Указанный путь к образам '$IMAGES_PATH' не существует или не является каталогом."
        exit 1
    fi
    
    if ! command -v "$CONTAINER_ENGINE" &> /dev/null; then
        echo "Ошибка: Команда '$CONTAINER_ENGINE' не найдена. Убедитесь, что она установлена."
        exit 1
    fi
    
    echo "Используется контейнерный движок: $CONTAINER_ENGINE"
    echo "Путь к образам: $IMAGES_PATH"
    
    CURRENT_DIR=$(pwd)
    
    echo "Загружаем образы Harbor ..."
    cd "$IMAGES_PATH"
    
    $CONTAINER_ENGINE load -i prepare-v2.9.3.tar
    $CONTAINER_ENGINE load -i harbor-core-v2.9.3.tar
    $CONTAINER_ENGINE load -i harbor-db-v2.9.3.tar
    $CONTAINER_ENGINE load -i harbor-exporter-v2.9.3.tar
    $CONTAINER_ENGINE load -i harbor-jobservice-v2.9.3.tar
    $CONTAINER_ENGINE load -i harbor-log-v2.9.3.tar
    $CONTAINER_ENGINE load -i harbor-portal-v2.9.3.tar
    $CONTAINER_ENGINE load -i harbor-registryctl-v2.9.3.tar
    $CONTAINER_ENGINE load -i harbor-scanner-trivy-0.30.22.tar
    $CONTAINER_ENGINE load -i nginx-fstec-v2.9.3.tar
    $CONTAINER_ENGINE load -i redis-fstec-v2.9.3.tar
    $CONTAINER_ENGINE load -i registry-fstec-v2.9.3.tar
    
    cd "$CURRENT_DIR"
    
  3. Чтобы выгрузить образы, которые требует Harbor воспользуйтесь скриптом load_harbor_images.sh:

    ./load_harbor_images.sh --images-path ../images --container-engine <docker|podman>
    

Генерация сертификатов для реестра Harbor#

Для генерации сертификатов для реестра Harbor выполните следующие шаги:

  1. Создайте в каталоге harbor скрипт harbor_certs.sh:

    harbor_certs.sh
    #!/bin/bash
    set -e
    
    # Парсинг аргументов
    while [[ "$#" -gt 0 ]]; do
        case $1 in
            --ip) IP="$2"; shift ;;
            --hostname) HOSTNAME="$2"; shift ;;
            --passphrase) PASSPHRASE="$2"; shift ;;
            --container-engine) CONTAINER_ENGINE="$2"; shift ;;
            *) echo "Unknown parameter passed: $1"; exit 1 ;;
        esac
        shift
    done
    
    # Если IP не передан, используем первый IP из вывода hostname -I
    if [ -z "$IP" ]; then
        IP=$(hostname -I | awk '{print $1}')
    fi
    
    # Если HOSTNAME не передан, используем текущий hostname
    if [ -z "$HOSTNAME" ]; then
        HOSTNAME=$(hostname)
    fi
    
    if [ -z "$CONTAINER_ENGINE" ]; then
        CONTAINER_ENGINE="podman"
    fi
    
    # Создаем директорию certs
    if [ -d "./certs" ]; then
        rm -rf ./certs
    fi
    mkdir ./certs
    
    # Генерация корневого ключа и сертификата
    openssl genrsa -out ./certs/harbor_internal_ca.key 4096
    if [ -z "$PASSPHRASE" ]; then
        openssl req -x509 -new -nodes -key ./certs/harbor_internal_ca.key -sha512 -days 3650 -out ./certs/harbor_internal_ca.crt -subj "/C=RU/L=Moscow/O=Platform V DropApp/CN=dropapp_internal_ca"
    else
        openssl req -x509 -new -key ./certs/harbor_internal_ca.key -sha512 -days 3650 -out ./certs/harbor_internal_ca.crt -subj "/C=RU/L=Moscow/O=Platform V DropApp/CN=dropapp_internal_ca" -passin pass:"$PASSPHRASE"
    fi
    
    # Генерация ключа для сертификата приложения
    openssl genrsa -out ./certs/harbor.key 4096
    
    # Создание конфигурационного файла для расширений сертификата
    cat > ./certs/v3.ext <<-EOF
    authorityKeyIdentifier=keyid,issuer
    basicConstraints=CA:FALSE
    keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
    subjectAltName = @alt_names
    
    [alt_names]
    IP.1 = $IP
    DNS.1 = $HOSTNAME
    DNS.2 = portal
    DNS.3 = core
    DNS.4 = registry
    DNS.5 = job_service
    DNS.6 = trivy_adapter
    DNS.7 = harbor_db
    DNS.8 = redis
    DNS.9 = registryctl
    DNS.8 = proxy
    EOF
    
    # Генерация CSR для сертификата приложения
    openssl req -new -key ./certs/harbor.key -out ./certs/harbor.csr -subj "/C=RU/L=Moscow/O=Platform V DropApp/CN=$HOSTNAME"
    
    # Подписание сертификата приложения корневым сертификатом
    if [ -z "$PASSPHRASE" ]; then
        openssl x509 -req -in ./certs/harbor.csr -CA ./certs/harbor_internal_ca.crt -CAkey ./certs/harbor_internal_ca.key -CAcreateserial -out ./certs/harbor.crt -days 3650 -sha512 -extfile ./certs/v3.ext
    else
        openssl x509 -req -in ./certs/harbor.csr -CA ./certs/harbor_internal_ca.crt -CAkey ./certs/harbor_internal_ca.key -CAcreateserial -out ./certs/harbor.crt -days 3650 -sha512 -extfile ./certs/v3.ext -passin pass:"$PASSPHRASE"
    fi
    
    for NAME in portal core registry job_service trivy_adapter harbor_db redis proxy registryctl; do
        cp ./certs/harbor.crt "./certs/${NAME}.crt"
        cp ./certs/harbor.key "./certs/${NAME}.key"
    done
    
    echo "Сертификаты успешно созданы в директории ./certs"
    
    echo "Копируем harbor.crt в директорию /etc/containers/certs.d/$HOSTNAME"
    sudo rm -rf /etc/containers/certs.d/$HOSTNAME
    sudo mkdir -p /etc/containers/certs.d/$HOSTNAME
    sudo cp ./certs/harbor.crt /etc/containers/certs.d/$HOSTNAME/ca.crt
    
    # Условие для container-engine
    if [[ "$CONTAINER_ENGINE" == "docker" ]]; then
        echo "Копируем harbor.crt в директорию /etc/docker/certs.d/$HOSTNAME"
        sudo rm -rf /etc/docker/certs.d/$HOSTNAME
        sudo mkdir -p /etc/docker/certs.d/$HOSTNAME
        sudo cp ./certs/harbor.crt /etc/docker/certs.d/$HOSTNAME/ca.crt
        sudo systemctl restart docker
    fi
    
    if [[ "$CONTAINER_ENGINE" == "podman" ]]; then    
        sudo systemctl restart podman
    fi
    

    Параметры скрипта harbor_certs.sh:

    • --ip - опциональный параметр, по умолчанию равен выводу hostname -I, то есть публичному IP текущей машины;

    • --hostname - обязательный параметр, сертификат будет выписан на этот hostname;

    • --passphrase - опциональный параметр для пароля сертификата;

    • --container-engine - опциональный параметр, можно передать docker или podman, по умолчанию podman.

  2. Запустите скрипт, он сгенерирует самоподписанные сертификаты для реестра и расположит их в каталоге certs.d для container runtime:

    ./harbor_certs.sh --hostname harbor.bootstrap.domain.ru --container-engine <docker|podman>
    

Установка стартового реестра Harbor#

Конфигурация Harbor#

  1. Создайте в каталоге harbor файл harbor.yml:

    harbor.yml
    # Configuration file of Harbor
    
    # The IP address or hostname to access admin UI and registry service.
    # DO NOT use localhost or 127.0.0.1, because Harbor needs to be accessed by external clients.
    hostname: harbor.bootstrap.domain.ru
    
    # http related config
    http:
      # port for http, default is 80. If https enabled, this port will redirect to https port
    #  port: 80
    
    # https related config
    https:
      # https port for harbor, default is 443
      port: 443
      # The path of cert and key files for nginx
      certificate: /home/user/harbor/certs/harbor.crt
      private_key: /home/user/harbor/certs/harbor.key
    
    # # Uncomment following will enable tls communication between all harbor components
    #internal_tls:
    #   # set enabled to true means internal tls is enabled
    #  enabled: true
    #   # put your cert and key files on dir
    #  dir: /root/harbor/certs
    #   # enable strong ssl ciphers (default: false)
    #   strong_ssl_ciphers: false
    
    # Uncomment external_url if you want to enable external proxy
    # And when it enabled the hostname will no longer used
    # external_url: https://reg.mydomain.com:8433
    
    # The initial password of Harbor admin
    # It only works in first time to install harbor
    # Remember Change the admin password from UI after launching Harbor.
    harbor_admin_password: <harbor-admin-password>
    
    # Harbor DB configuration
    database:
      # The password for the root user of Harbor DB. Change this before any production use.
      password: <root-password>
      # The maximum number of connections in the idle connection pool. If it <=0, no idle connections are retained.
      max_idle_conns: 100
      # The maximum number of open connections to the database. If it <= 0, then there is no limit on the number of open connections.
      # Note: the default number of connections is 1024 for postgres of harbor.
      max_open_conns: 900
      # The maximum amount of time a connection may be reused. Expired connections may be closed lazily before reuse. If it <= 0, connections are not closed due to a connection's age.
      # The value is a duration string. A duration string is a possibly signed sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
      conn_max_lifetime: 5m
      # The maximum amount of time a connection may be idle. Expired connections may be closed lazily before reuse. If it <= 0, connections are not closed due to a connection's idle time.
      # The value is a duration string. A duration string is a possibly signed sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
      conn_max_idle_time: 0
    
    # The default data volume
    data_volume: ./data_harbor
    
    # Harbor Storage settings by default is using /data dir on local filesystem
    # Uncomment storage_service setting If you want to using external storage
    # storage_service:
    #   # ca_bundle is the path to the custom root ca certificate, which will be injected into the truststore
    #   # of registry's containers.  This is usually needed when the user hosts a internal storage with self signed certificate.
    #   ca_bundle:
    
    #   # storage backend, default is filesystem, options include filesystem, azure, gcs, s3, swift and oss
    #   # for more info about this configuration please refer https://docs.docker.com/registry/configuration/
    #   filesystem:
    #     maxthreads: 100
    #   # set disable to true when you want to disable registry redirect
    #   redirect:
    #     disable: false
    
    # Trivy configuration
    #
    # Trivy DB contains vulnerability information from NVD, Red Hat, and many other upstream vulnerability databases.
    # It is downloaded by Trivy from the GitHub release page https://github.com/aquasecurity/trivy-db/releases and cached
    # in the local file system. In addition, the database contains the update timestamp so Trivy can detect whether it
    # should download a newer version from the Internet or use the cached one. Currently, the database is updated every
    # 12 hours and published as a new release to GitHub.
    trivy:
      # ignoreUnfixed The flag to display only fixed vulnerabilities
      ignore_unfixed: false
      # skipUpdate The flag to enable or disable Trivy DB downloads from GitHub
      #
      # You might want to enable this flag in test or CI/CD environments to avoid GitHub rate limiting issues.
      # If the flag is enabled you have to download the `trivy-offline.tar.gz` archive manually, extract `trivy.db` and
      # `metadata.json` files and mount them in the `/home/scanner/.cache/trivy/db` path.
      skip_update: false
      #
      # skipJavaDBUpdate If the flag is enabled you have to manually download the `trivy-java.db` file and mount it in the
      # `/home/scanner/.cache/trivy/java-db/trivy-java.db` path
      skip_java_db_update: false
      #
      # The offline_scan option prevents Trivy from sending API requests to identify dependencies.
      # Scanning JAR files and pom.xml may require Internet access for better detection, but this option tries to avoid it.
      # For example, the offline mode will not try to resolve transitive dependencies in pom.xml when the dependency doesn't
      # exist in the local repositories. It means a number of detected vulnerabilities might be fewer in offline mode.
      # It would work if all the dependencies are in local.
      # This option doesn't affect DB download. You need to specify "skip-update" as well as "offline-scan" in an air-gapped environment.
      offline_scan: false
      #
      # Comma-separated list of what security issues to detect. Possible values are `vuln`, `config` and `secret`. Defaults to `vuln`.
      security_check: vuln
      #
      # insecure The flag to skip verifying registry certificate
      insecure: true
      # github_token The GitHub access token to download Trivy DB
      #
      # Anonymous downloads from GitHub are subject to the limit of 60 requests per hour. Normally such rate limit is enough
      # for production operations. If, for any reason, it's not enough, you could increase the rate limit to 5000
      # requests per hour by specifying the GitHub access token. For more details on GitHub rate limiting please consult
      # https://docs.github.com/rest/overview/resources-in-the-rest-api#rate-limiting
      #
      # You can create a GitHub token by following the instructions in
      # https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line
      #
      # github_token: xxx
    
    jobservice:
      # Maximum number of job workers in job service
      max_job_workers: 10
      # The jobLoggers backend name, only support "STD_OUTPUT", "FILE" and/or "DB"
      job_loggers:
        - STD_OUTPUT
        - FILE
        # - DB
      # The jobLogger sweeper duration (ignored if `jobLogger` is `stdout`)
      logger_sweeper_duration: 1 #days
    
    notification:
      # Maximum retry count for webhook job
      webhook_job_max_retry: 3
      # HTTP client timeout for webhook job
      webhook_job_http_client_timeout: 3 #seconds
    
    # Log configurations
    log:
      # options are debug, info, warning, error, fatal
      level: info
      # configs for logs in local storage
      local:
        # Log files are rotated log_rotate_count times before being removed. If count is 0, old versions are removed rather than rotated.
        rotate_count: 50
        # Log files are rotated only if they grow bigger than log_rotate_size bytes. If size is followed by k, the size is assumed to be in kilobytes.
        # If the M is used, the size is in megabytes, and if G is used, the size is in gigabytes. So size 100, size 100k, size 100M and size 100G
        # are all valid.
        rotate_size: 200M
        # The directory on your host that store log
        location: /var/log/harbor
    
      # Uncomment following lines to enable external syslog endpoint.
      # external_endpoint:
      #   # protocol used to transmit log to external endpoint, options is tcp or udp
      #   protocol: tcp
      #   # The host of external endpoint
      #   host: localhost
      #   # Port of external endpoint
      #   port: 5140
    
    #This attribute is for migrator to detect the version of the .cfg file, DO NOT MODIFY!
    _version: 2.9.0
    
    # Uncomment external_database if using external database.
    # external_database:
    #   harbor:
    #     host: harbor_db_host
    #     port: harbor_db_port
    #     db_name: harbor_db_name
    #     username: harbor_db_username
    #     password: harbor_db_password
    #     ssl_mode: disable
    #     max_idle_conns: 2
    #     max_open_conns: 0
    
    # Uncomment redis if need to customize redis db
    # redis:
    #   # db_index 0 is for core, it's unchangeable
    #   # registry_db_index: 1
    #   # jobservice_db_index: 2
    #   # trivy_db_index: 5
    #   # it's optional, the db for harbor business misc, by default is 0, uncomment it if you want to change it.
    #   # harbor_db_index: 6
    #   # it's optional, the db for harbor cache layer, by default is 0, uncomment it if you want to change it.
    #   # cache_db_index: 7
    
    # Uncomment redis if need to customize redis db
    # redis:
    #   # db_index 0 is for core, it's unchangeable
    #   # registry_db_index: 1
    #   # jobservice_db_index: 2
    #   # trivy_db_index: 5
    #   # it's optional, the db for harbor business misc, by default is 0, uncomment it if you want to change it.
    #   # harbor_db_index: 6
    #   # it's optional, the db for harbor cache layer, by default is 0, uncomment it if you want to change it.
    #   # cache_layer_db_index: 7
    
    # Uncomment external_redis if using external Redis server
    # external_redis:
    #   # support redis, redis+sentinel
    #   # host for redis: <host_redis>:<port_redis>
    #   # host for redis+sentinel:
    #   #  <host_sentinel1>:<port_sentinel1>,<host_sentinel2>:<port_sentinel2>,<host_sentinel3>:<port_sentinel3>
    #   host: redis:6379
    #   password: 
    #   # Redis AUTH command was extended in Redis 6, it is possible to use it in the two-arguments AUTH <username> <password> form.
    #   # there's a known issue when using external redis username ref:https://github.com/goharbor/harbor/issues/18892
    #   # if you care about the image pull/push performance, please refer to this https://github.com/goharbor/harbor/wiki/Harbor-FAQs#external-redis-username-password-usage
    #   # username:
    #   # sentinel_master_set must be set to support redis+sentinel
    #   #sentinel_master_set:
    #   # db_index 0 is for core, it's unchangeable
    #   registry_db_index: 1
    #   jobservice_db_index: 2
    #   trivy_db_index: 5
    #   idle_timeout_seconds: 30
    #   # it's optional, the db for harbor business misc, by default is 0, uncomment it if you want to change it.
    #   # harbor_db_index: 6
    #   # it's optional, the db for harbor cache layer, by default is 0, uncomment it if you want to change it.
    #   # cache_layer_db_index: 7
    
    # Uncomment uaa for trusting the certificate of uaa instance that is hosted via self-signed cert.
    # uaa:
    #   ca_file: /path/to/ca
    
    # Global proxy
    # Config http proxy for components, e.g. http://my.proxy.com:3128
    # Components doesn't need to connect to each others via http proxy.
    # Remove component from `components` array if want disable proxy
    # for it. If you want use proxy for replication, MUST enable proxy
    # for core and jobservice, and set `http_proxy` and `https_proxy`.
    # Add domain to the `no_proxy` field, when you want disable proxy
    # for some special registry.
    proxy:
      http_proxy:
      https_proxy:
      no_proxy:
      components:
        - core
        - jobservice
        - trivy
    
    # metric:
    #   enabled: false
    #   port: 9090
    #   path: /metrics
    
    # Trace related config
    # only can enable one trace provider(jaeger or otel) at the same time,
    # and when using jaeger as provider, can only enable it with agent mode or collector mode.
    # if using jaeger collector mode, uncomment endpoint and uncomment username, password if needed
    # if using jaeger agetn mode uncomment agent_host and agent_port
    # trace:
    #   enabled: true
    #   # set sample_rate to 1 if you wanna sampling 100% of trace data; set 0.5 if you wanna sampling 50% of trace data, and so forth
    #   sample_rate: 1
    #   # # namespace used to differenciate different harbor services
    #   # namespace:
    #   # # attributes is a key value dict contains user defined attributes used to initialize trace provider
    #   # attributes:
    #   #   application: harbor
    #   # # jaeger should be 1.26 or newer.
    #   # jaeger:
    #   #   endpoint: http://hostname:14268/api/traces
    #   #   username:
    #   #   password:
    #   #   agent_host: hostname
    #   #   # export trace data by jaeger.thrift in compact mode
    #   #   agent_port: 6831
    #   # otel:
    #   #   endpoint: hostname:4318
    #   #   url_path: /v1/traces
    #   #   compression: false
    #   #   insecure: true
    #   #   # timeout is in seconds
    #   #   timeout: 10
    
    # Enable purge _upload directories
    upload_purging:
      enabled: true
      # remove files in _upload directories which exist for a period of time, default is one week.
      age: 168h
      # the interval of the purge operations
      interval: 24h
      dryrun: false
    
    # Cache layer configurations
    # If this feature enabled, harbor will cache the resource
    # `project/project_metadata/repository/artifact/manifest` in the redis
    # which can especially help to improve the performance of high concurrent
    # manifest pulling.
    # NOTICE
    # If you are deploying Harbor in HA mode, make sure that all the harbor
    # instances have the same behaviour, all with caching enabled or disabled,
    # otherwise it can lead to potential data inconsistency.
    cache:
      # not enabled by default
      enabled: false
      # keep cache for one day by default
      expire_hours: 24
    
    # Harbor core configurations
    # Uncomment to enable the following harbor core related configuration items.
    # core:
    #   # The provider for updating project quota(usage), there are 2 options, redis or db,
    #   # by default is implemented by db but you can switch the updation via redis which
    #   # can improve the performance of high concurrent pushing to the same project,
    #   # and reduce the database connections spike and occupies.
    #   # By redis will bring up some delay for quota usage updation for display, so only
    #   # suggest switch provider to redis if you were ran into the db connections spike aroud
    #   # the scenario of high concurrent pushing to same project, no improvment for other scenes.
    #   quota_update_provider: redis # Or db
    
  2. Отредактируйте файл harbor.yml:

Запуск Harbor#

  1. Создайте в каталоге harbor скрипт harbor_install.sh:

    harbor_install.sh
    #!/bin/bash
    set -e
    
    CONTAINER_ENGINE="podman"
    
    while [[ "$#" -gt 0 ]]; do
        case $1 in
            --container-engine)
                if [[ -n "$2" ]]; then
                    CONTAINER_ENGINE="$2"
                    shift
                else
                    echo "Ошибка: Для --container-engine не указано значение."
                    exit 1
                fi
                ;;
            *)
                echo "Неизвестный параметр: $1"
                exit 1
                ;;
        esac
        shift 
    done
    
    if ! command -v "$CONTAINER_ENGINE" &> /dev/null; then
        echo "Ошибка: Команда '$CONTAINER_ENGINE' не найдена. Убедитесь, что она установлена."
        exit 1
    fi
    
    echo "Используется контейнерный движок: $CONTAINER_ENGINE"
    
    if [[ -n "$HARBOR_BUNDLE_DIR" ]]; then
        harbor_prepare_path=$HARBOR_BUNDLE_DIR
    else
        harbor_prepare_path="$( cd "$(dirname "$0")" ; pwd -P )"
    fi
    echo "Подготовительная директория: ${harbor_prepare_path}"
    
    rm -rf ${harbor_prepare_path}/input
    
    mkdir -p ${harbor_prepare_path}/input
    input_dir=${harbor_prepare_path}/input
    
    if [[ ! "$1" =~ ^\-\- ]] && [ -f "$1" ]
    then
        cp $1 $input_dir/harbor.yml
        shift
    else
        if [ -f "${harbor_prepare_path}/harbor.yml" ];then
            cp ${harbor_prepare_path}/harbor.yml $input_dir/harbor.yml
        else
            echo "Не найден конфиг файл ${harbor_prepare_path}/harbor.yml"
            exit 1
        fi
    fi
    
    data_path=$(grep '^[^#]*data_volume:' $input_dir/harbor.yml | awk '{print $NF}')
    echo "Директория с данными: ${data_path}"
    
    mkdir -p $data_path
    
    previous_secretkey_path=/data/secretkey
    previous_defaultalias_path=/data/defaultalias
    
    if [ -f $previous_secretkey_path ]; then
        mkdir -p $data_path/secret/keys
        mv $previous_secretkey_path $data_path/secret/keys
    fi
    if [ -f $previous_defaultalias_path ]; then
        mkdir -p $data_path/secret/keys
        mv $previous_defaultalias_path $data_path/secret/keys
    fi
    
    
    secret_dir=${data_path}/secret
    config_dir=$harbor_prepare_path/common/config
    
    mkdir -p $secret_dir
    mkdir -p $config_dir
    
    $CONTAINER_ENGINE run --rm -v $input_dir:/input \
                        -v $data_path:/data \
                        -v $harbor_prepare_path:/compose_location \
                        -v $config_dir:/config \
                        -v /:/hostfs \
                        --privileged \
                        sberworks.ru/k8sf/2.2.3/dapp-harbor/prepare:v2.9.3 prepare --with-trivy
    
    echo "Удаляем input директорию"
    rm -rf ${harbor_prepare_path}/input
    
    sed -i 's|goharbor/trivy-adapter-photon:v2.9.3|sberworks.ru/k8sf/2.2.3/dapp-harbor-scanner-trivy/harbor-scanner-trivy:0.30.22|g' docker-compose.yml
    sed -i 's|goharbor|sberworks.ru/k8sf/2.2.3/dapp-harbor|g' docker-compose.yml
    sed -i 's|photon|fstec|g' docker-compose.yml
    sed -i 's|"syslog"|"journald"|g' docker-compose.yml
    sed -i '/syslog-address:/d' docker-compose.yml
    sudo chmod -R 777 ./data_harbor/database
    sudo chmod -R 777 ./data_harbor/redis
    
    mkdir ${data_path}/cert
    sudo cp certs/* ${data_path}/cert/
    sudo chown -R 10000:10000 ${data_path}/cert/
    sudo chmod -R 777 ${data_path}/cert/
    
    echo "Запускаем Harbor"
    sudo $CONTAINER_ENGINE compose up -d
    
  2. Запустите скрипт harbor_install.sh:

    ./harbor_install.sh --container-engine <docker|podman>
    

Валидация установки#

Выполните sudo <docker|podman> compose ps и убедитесь, что все контейнеры запущены, рестартов нет:

sudo podman compose ps

Пользовательский интерфейс Harbor будет доступен в браузере по адресу, который был указан в harbor.yml, например https://harbor.bootstrap.domain.ru.

Создание проекта для образов#

Зайдите в пользовательский интерфейс Harbor под учетной записью администратора и создайте проект dapp:

Создание проекта в Harbor

Загрузка образов в стартовый реестр Harbor#

Разархивируйте архив k8sf-binaries-2.2.3-distrib.zip и получите утилиту Dactl, выполнив шаги из подраздела «Предварительные условия».

Выполните вход в созданный реестр. Логин (Username) - это admin, пароль установлен в harbor.yml поле harbor_admin_password:

podman login harbor.bootstrap.domain.ru
Username: admin
Password:
Login Succeeded!

Запустите копирование образов в созданный реестр:

./dactl copy images --src ../images --dest harbor.bootstrap.domain.ru/dapp

Примечание

Поскольку этот Harbor является стартовым, он может не включать базу данных Trivy из состава поставки DropApp.

В случае необходимости добавления базы данных Trivy в созданный Harbor, воспользуйтесь утилитой Cosign из поставки DropApp.

Для добавления Trivy воспользуйтесь сценарием:

  1. Разархивируйте содержимое trivy-db.tar:

    tar xvf ./images/trivy-db.tar
    
  2. Используя утилиту Cosign, загрузите каталог в реестр, там он будет храниться в виде образа:

    ./cosign-linux-amd64 load --dir ./trivy-db harbor.bootstrap.domain.ru/dapp/2.2.3-fstec/trivy-db:2 --allow-insecure-registry=true
    

Стартовый реестр Harbor установлен.

Создание robot-аккаунта для доступа в проект dapp#

Чтобы стягивать образы из реестра создайте robot-аккаунт в Harbor для проекта dapp.

Для этого зайдите в проект dapp, перейдите на вкладку «Robot Accounts» и создайте аккаунт dapp-guest с ограниченными правами на read, pull, list и create/stop scan:

Создание Robot-аккаунта

Нажмите кнопку Add и сохраните secret - он понадобится для работы реестра кластера.

RPM репозиторий#

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

Примечание

RPM-репозиторий поставляется в составе дистрибутива отдельным архивом. Пример названия - k8sf-rpms-2.2.3-distrib.zip.

Настройки node кластера#

Следующие настройки node кластера устанавливаются автоматически при развертывании кластера с помощью Dactl:

  • Включение IPv6;

  • Отключение использования swap;

  • Переадресация IPv4;

  • Загрузка модулей br_netfilter и overlay;

  • Проверка системных переменных.

Подготовка и запуск основного реестра Harbor#

Основной реестр Harbor будет установлен в отдельный кластер DropApp.

Данный кластер будет использоваться Harbor, а не пользовательскими приложениями. С помощью Harbor будет осуществляться эксплуатация пользовательских приложений в основном кластере DropApp.

Подготовка#

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

  • в одном сетевом контуре с будущими nodes кластера;

  • в одном сетевом контуре со стартовым реестром Harbor, куда загружены образы контейнеров DropApp;

  • на архитектуре linux x86 amd64 (например, ОС Platform V SberLinux OS Server)

Примечание

Эта машина не будет node будущего кластера. На эту машину будут сохранены артефакты установки: kubeconfig реестра кластера, файл с паролем сервисного пользователя - администратора кластера для доступа в UI консоль.

Машины для реестра кластера#

Создание SSH-ключа#

Сгенерируйте пару SSH-ключей, используя команду ssh-keygen.

  • Для генерации SSH-ключей с использованием RSA используйте команду:

    ssh-keygen -t rsa -b 4096 -C "your_email@example.com" -f ~/.ssh/id_dapp
    
  • Для генерации SSH-ключей с использованием Ed25519 используйте команду:

    ssh-keygen -t ed25519 -C "your_email@example.com" -f ~/.ssh/id_dapp
    

Используемые в примерах параметры:

-t - тип ключа, например rsa или ed25519; -b - длина ключа (для RSA рекомендуется 4096 бит); -C - комментарий, обычно email; -f - путь и имя файла для сохранения ключей.

Создание машин#

Для основного реестра Harbor достаточно две машины - control plane и worker.

Минимальная конфигурация Control plane node#

Значение

Размер

ЦПУ [CPU] (в ядрах)

4 (с поддержкой VT*)

ОЗУ [RAM]

8192 Мб

Размер диска [HDD/SSD]

15 Гб

Минимальная конфигурация Worker node#

Значение

Размер

ЦПУ [CPU] (в ядрах)

4 (с поддержкой VT*)

ОЗУ [RAM]

16000 Мб

Размер диска [HDD/SSD]

50 Гб

На каждой машине:

  • создайте пользователя dapp;

  • поместите публичный ключ ~/.ssh/id_dapp.pub в домашний каталог;

  • обеспечьте пользователю беспарольный sudo;

  • обеспечьте доступ к необходимой сети и IP адрес каждой машине, доступный с машины администратора.

Остальные настройки обеспечит Dactl.

Важно!

Образы DropApp занимают примерно 25 Гб. Планируйте место в своём хранилище с учетом объема образов, а также с учетом того, что сертифицированный кластер должен брать образы из этого Harbor. Значит все образы пользовательских приложений тоже должны храниться в устанавливаемом Harbor.

Формирование конфигурационного файла DropAppConfiguration#

Перед формированием конфигурационного файла DropAppConfiguration для Dactl, убедитесь в наличии:

  • стартового реестра Harbor с образами контейнеров K8S-2.2.3-fstec-58-distrib.zip, например harbor.bootstrap.domain.ru/dapp;

  • сертификат от реестра, например в каталоге ./harbor/certs/harbor.crt на машине bootstrap Harbor registry;

  • репозиторий с RPM-пакетами K8S-2.2.3-fstec-58-distrib.zip, например http://my-rpm-repo.com/dapp/2.2.3-fstec;

  • репозиторий с baseos RPM-пакетами Platform V SberLinux OS Server, например http://my-rpm-repo.com/SLO/9.1-fstec, в этом репозитории есть зависимости для K8S-2.2.3-fstec-58-distrib.zip;

  • две машины с пользователем dapp, пример адресов 00.00.10.01 и 00.00.10.02;

  • адрес пула metallb, для секции spec.network.metallb;

  • LDAP-каталог с пользователями, настроенный по инструкции, например его адрес freeipa.fstec.domain.ru.

Создайте конфигурационный файл da-harbor.yaml:

da-harbor.yaml
apiVersion: config.dropapp.ru/v1alpha1
kind: DropAppConfiguration
metadata:
  name: "registry"
spec:
  network:
      publicDomain: domain.ru
      metallb:
        pools:
        - addresses: "00.00.00.31/32"
          name: "dapp"
          l2Advertisement: true
  artifacts:
    imageRegistry:
      host: harbor.bootstrap.domain.ru
      path: dapp
      credentials: ZEh...Q== # токен аутентификации в base64, в нашем примере это echo -n 'robot$dapp-guest:fvnk38AJmTv3behe1pLFaYqjidfJSiNe' | base64 -w0 | base64 -w0
      certificate: <cert-part>... # cat harbor.crt | base64 -w0
    rpmRepository:
      - name: "dapp-2.2.3-fstec"
        baseUrl: http://my-rpm-repo.com/dapp/2.2.3-fstec
        gpgCheck: 1
        enabled: 1
        gpgKey: "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-SberLinux"
      - name: "slo-baseos-9.1-fstec"
        baseUrl: http://my-rpm-repo.com/SLO/9.1-fstec
        gpgCheck: 1
        enabled: 1
        gpgKey: "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-SberLinux"
  compute:
    sshAccess:
      user: dapp
      privateKeyPath: "~/.ssh/id_dapp"
    userProvisioned:
      nodes:
        controlPlane:
          - ip: "00.00.10.01"
            nodePool: "control-plane"
        worker:
          - ip: "00.00.10.02"
            nodePool: "worker"
  k8s:
    podSubnetCIDR: 00.000.0.0/16 # можно настроить сеть для pods
    serviceSubnetCIDR: 00.00.0.1/16 # можно настроить сеть для сервисов
 
    # если необходимо, настраиваем ingressTLS, если не указан, то будет сгенерирован автоматически
    ingressTLS:
      cert: <ingressTLS-cert>...
      key: <ingressTLS-key>...
 
    # настраиваем аудит для kube-apiserver
    apiServer:
      extraArgs:
        audit-policy-file: "/etc/kubernetes/policies/audit-policy.yaml"
        audit-log-path: "/var/log/kubernetes/audit.log"
        audit-log-format: "json"
        audit-log-maxbackup: "5"
        audit-log-maxsize: "128"
        audit-log-maxage: "7"
        audit-log-mode: "batch"
      extraVolumes:
      - name: policies
        hostPath: /etc/kubernetes/policies
        mountPath: /etc/kubernetes/policies
      - name: kube-audit
        hostPath: /var/log/kubernetes
        mountPath: /var/log/kubernetes
 
  # подключение LDAP каталога
  components:
  - name: auth
    tools:
    - name: dex
      values:
        config:
          connectors:
          - type: ldap
            name: Ipa Server
            id: ldap
            config:
              bindDN: uid=dapp-sync-svc,cn=users,cn=accounts,dc=dapp,dc=un,dc=sbt
              bindPW: qwerty12345
              groupSearch:
                baseDN: cn=groups,cn=accounts,dc=dapp,dc=un,dc=sbt
                filter: (objectClass=groupOfNames)
                nameAttr: cn
                userMatchers:
                - groupAttr: member
                  userAttr: DN
              host: freeipa.fstec.domain.ru:636
              insecureNoSSL: false
              insecureSkipVerify: true
              userSearch:
                baseDN: cn=users,cn=accounts,dc=dapp,dc=un,dc=sbt
                emailAttr: mail
                filter: (objectClass=person)
                idAttr: DN
                nameAttr: uid
                preferredUsernameAttr: uid
                username: uid
            usernamePrompt: 'Username:'

Запуск установки кластера#

Для установки кластера выполните команду:

./dactl create cluster --config ./da-harbor.yaml

В конце установки появится сообщение:

======================================

Done! DropApp cluster with name 'registry' and version '2.2.3-fstec' is successfully installed!

Next steps are:
 - Cluster config is stored to this machine as /home/dapp/.dropapp/registry/kubeconfig
   you may need to export this path to the KUBECONFIG environment variable
   or copy it as '~/.kube/config'
   otherwise use it as --kubeconfig parameter value for kubectl or dactl commands
 - Check pods and nodes by executing `kubectl get pods -A; kubectl get nodes`
 - Check installed Platform V DropApp components by executing `kubectl get components -o wide`
 - Check installed Platform V DropApp tools by executing `kubectl get tools -o wide`
 - Visit Platform V DropApp UI: https://console.fstec-cluster.domain.ru
   login: kubeadmin
   password is stored in: /home/dapp/.dropapp/registry/kubeadmin-password

Артефакты установки#

По выполнению установки артефактами являются:

  • конфигурационный файл от кластера: /home/dapp/.dropapp/registry/kubeconfig;

  • суперадмин kubeadmin, пароль для учетной записи в файле /home/dapp/.dropapp/registry/kubeadmin-password, этот пароль можно использовать для доступа в пользовательскую консоль по адресу из примера https://console.fstec-cluster.domain.ru.

Установка основного реестра Harbor#

Создадайте манифест tool-harbor.yaml, настройте в нем пароль для доступа к базе данных trivy-db, которую пользователь расположил в стартовом реестре Harbor:

apiVersion: config.dropapp.ru/v1alpha1
kind: Tool
metadata:
  name: "harbor"

Для установки инструмента Harbor примените манифест, используя Kubectl из дистрибутива поставки:

kubectl apply -f tool-harbor.yaml

Проверка установки#

Дождитесь установки pods Harbor:

kubectl get pods -n dapp-security

Pods Harbor устанавливаются в namespace dapp-security, этот namespace создается в процессе установки кластера автоматически.

Все pods должны получить статус Running и Ready, это означает, что основной реестр Harbor, предназначенный для эксплуатации основных кластеров Platform V DropApp 2.2.3-fstec-1 установлен.

Использование постоянного хранилища#

После установки основного реестра Harbor, инструмент Harbor разворачивается с использованием томов EmptyDir. Для промышленной эксплуатации необходимо переконфигурировать их на использование постоянных томов - Persistent Volumes. В результате создания кластера с утилитой Dactl по умолчанию создается StorageClass local-storage.

Для этого необходимо:

  1. Установить и настроить StorageProvider. В составе DropApp используется Trident, при необходимости можно использовать иной адаптер.

  2. Зарегистрировать StorageClass - в результате создания кластера с утилитой Dactl по умолчанию создается StorageClass local-storage.

  3. Настроить Persistent Volume Claims для использования StorageClass Persistent Volumes.

Настройка постоянного хранилища для Harbor#
  1. Создайте файл tool-harbor-persistence.yaml и настройте persistence для Harbor:

    tool-harbor-persistence.yaml
    apiVersion: config.dropapp.ru/v1alpha1
    kind: Tool
    metadata:
      name: harbor
    spec:
      values:
        persistence:
          enabled: true
          persistentVolumeClaim:
            registry:
              storageClass: "openebs-hostpath" # openebs в качестве примера
              accessMode: ReadWriteOnce
              size: 15Gi
            jobservice:
              jobLog:
                storageClass: "openebs-hostpath"
                accessMode: ReadWriteOnce
                size: 1Gi
            database:
              storageClass: "openebs-hostpath" # openebs в качестве примера
              accessMode: ReadWriteOnce
              size: 1Gi
            redis:
              storageClass: "openebs-hostpath" # openebs в качестве примера
              accessMode: ReadWriteOnce
              size: 1Gi
            trivy:
              storageClass: "openebs-hostpath" # openebs в качестве примера
              accessMode: ReadWriteOnce
              size: 1Gi
    

    Примечание

    Просто обновить конфигурацию не получится, так как Kubernetes не позволяет обновлять разделы volume для StatefulSet, в данном случае это harbor_database, harbor_registry, harbor_jobservice, harbor_trivy, поэтому нужно удалить инструмент Harbor.

  2. Удалите инструмент Harbor:

    kubectl delete tool harbor
    
  3. Пересоздайте ресурсы конфигурационного файла с персистентным хранилищем, применив манифест:

    kubectl apply -f ./tool-harbor-persistence.yaml
    

Ресурсы инструмента Harbor будут пересозданы.

Доступ в Harbor#

Пользовательский интерфейс Harbor будет доступен по адресу: https://harbor.registry.domain.ru.

Доступ в пользовательский интерфейс Harbor происходит через LDAP-каталог, настроенный при установке кластера. По ролевой модели администратор Harbor должен быть LDAP-пользователь с ролью harbor-sysadmin.

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

kubectl get secret ingress-tls -n dapp-security -o jsonpath="{.data['tls\.crt']}"

Полученную base64-строку используйте для конфигурации секции imageRegistry для основного кластера DropApp.

Создание проекта для образов#

Зайдите в пользовательский интерфейс Harbor администратором и создайте проект dapp:

Создание проекта в Harbor

Создание robot-аккаунта для доступа в проект dapp#

Чтобы стягивать образы из реестра создайте robot-аккаунт в Harbor для проекта dapp.

Для этого зайдите в проект dapp, перейдите на вкладку «Robot Accounts» и создайте аккаунт dapp-guest с ограниченными правами на read, pull, list и create/stop scan:

Создание Robot-аккаунта

Нажмите кнопку Add и сохраните secret - он понадобится для работы реестра кластера.

Для подключения robot-аккаунта для Harbor выполните следующие шаги:

  1. Создайте файл robot-harbor.yaml с манифестом:

    robot-harbor.yaml
    apiVersion: config.dropapp.ru/v1alpha1
    kind: Tool
    metadata:
      name: harbor
    spec:
      values:
        trivy:
          trivy_db: harbor.registry.domain.ru/dapp/2.2.3-fstec/trivy-db
          trivyDBImagePullSecret:
            enabled: true
            username: robot$dapp+dapp-guest
            password: <password> # пароль secret полученный при добавлении робот аккаунта
            registry: harbor.registry.domain.ru
    
  2. Примените файл:

    kubectl apply -f robot-harbor.yaml
    

Загрузка образов в основной реестр Harbor#

Загрузка образов контейнеров#

  1. На машине администратора получите сертификат для Harbor:

    kubectl get secret ingress-tls -n dapp-security -o jsonpath="{.data['tls\.crt']}" | base64 --decode > harbor-fstec.crt
    
  2. Создадайте каталог для сертификата:

    • для container-engine Docker:

      sudo mkdir -p /etc/docker/certs.d/harbor.registry.domain.ru
      sudo cp ./harbor-fstec.crt /etc/docker/certs.d/harbor.registry.domain.ru/ca.crt
      sudo systemctl restart docker
      
    • ддля container-engine Podman:

      sudo mkdir -p /etc/containers/certs.d/harbor.registry.domain.ru
      sudo cp ./harbor-fstec.crt /etc/containers/certs.d/harbor.registry.domain.ru/ca.crt
      sudo systemctl restart podman
      
  3. Выполните вход в созданный реестр:

    podman login harbor.registry.domain.ru
    Username: admin
    Password:
    Login Succeeded!
    

    Учетной записью может быть:

    • admin, с паролем по умолчанию <harbor-admin-password>;

    • LDAP-пользователь с паролем, который он получает в пользовательском меню Harbor (не LDAP-паролем).

  4. Запустите копирование образов в созданный реестр:

    ./dactl copy images --src ../images --dest harbor.registry.domain.ru/dapp
    

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

Загрузка образа Trivy-db#

Образ Trivy-db - не классический docker-образ и имеет отличную от других образов структуру. Его нельзя стянуть, используя Docker, Podman или Crictl - Trivy стягивает его своими способами. При этом, dactl copy images не копирует его вместе с другими образами в реестр. Загрузить его нужно отдельно утилитой Cosign-linux-amd64 из состава поставки.

Разархивация trivy-db.tar#

Разархивируйте содержимое trivy-db.tar:

tar xvf ./images/trivy-db.tar

Загрузка trivy-db в реестр образов#

Используя утилиту Cosign загрузите каталог в реестр, там он будет храниться в виде образа:

./cosign-linux-amd64 load --dir ./trivy-db harbor.registry.domain.ru/dapp/2.2.3-fstec/trivy-db:2 --allow-insecure-registry=true

Сам путь до базы данных Trivy прописан по умолчанию в скрытых values инструмента Harbor, отдельно настраивать не нужно.

Подпись загруженных образов с Cosign#

Для подписи образов воспользуйтесь утилитой Cosign-linux-amd64 из состава поставки.

Генерация подписи#

На машине администратора создайте каталог cosign_prepare и создайте в ней скрипт generate_sign.sh.

generate_sign.sh
#!/bin/bash

# Функция для вывода справки
usage() {
    echo "Использования: $0 --passphrase <your_passphrase>"
    exit 1
}

# Проверка наличия аргументов
if [ "$#" -lt 2 ]; then
    usage
fi

# Парсинг аргументов командной строки
while [[ "$#" -gt 0 ]]; do
    case $1 in
        --passphrase)
            PASSPHRASE="$2"
            shift 2
            ;;
        *)
            echo "Неизвестный параметр: $1"
            usage
            ;;
    esac
done

# Проверка, что passphrase был передан
if [ -z "$PASSPHRASE" ]; then
    echo "Ошибка: Passphrase обязательный параметр."
    usage
fi


echo "Генерирую pkey через openssl"
openssl genpkey -algorithm ed25519 -out private.pem

# Импорт ключевой пары с помощью cosign
echo "Импортирую ключ в cosign"
echo "$PASSPHRASE" | ./cosign-linux-amd64 import-key-pair --key private.pem --output-key-prefix cosign

Запустите скрипт generate_sign.sh, передав в параметр --passphrase секретный пароль для подписи, например:

chmod +x ./generate_sign.sh
./generate_sign.sh --passphrase dropapp

Результатом будет пара ключей:

cosign.key
cosign.pub

Все образы, которые были загружены в harbor.registry.domain.ru, нужно подписать с ключом cosign.key. Для этого создайте скрипт sign_images.sh

generate_sign.sh
#!/bin/bash

# Функция для вывода справки
usage() {
    echo "Использование: $0 --harbor-url URL --harbor-project PROJECT --harbor-path PATH --cosign-key KEY --username USER --password PASS --passphrase PHRASE"
    exit 1
}

# Проверка наличия аргументов
if [[ $# -eq 0 ]]; then
    usage
fi

# Парсинг аргументов
while [[ $# -gt 0 ]]; do
    case "$1" in
        --harbor-url)
            HARBOR_URL=$2
            shift 2
            ;;
        --harbor-project)
            HARBOR_PROJECT=$2
            shift 2
            ;;
        --harbor-path)
            HARBOR_PATH=$2
            shift 2
            ;;
        --cosign-key)
            COSIGN_KEY=$2
            shift 2
            ;;
        --username)
            USERNAME=$2
            shift 2
            ;;
        --password)
            PASSWORD=$2
            shift 2
            ;;
        --passphrase)
            PASSPHRASE=$2
            shift 2
            ;;
        *)
            echo "Неизвестный параметр: $1"
            usage
            ;;
    esac
done

# Проверка обязательных параметров
if [[ -z "$HARBOR_URL" || -z "$HARBOR_PROJECT" || -z "$HARBOR_PATH" || -z "$COSIGN_KEY" || -z "$USERNAME" || -z "$PASSWORD" || -z "$PASSPHRASE" ]]; then
    echo "Ошибка: Не все обязательные параметры указаны."
    usage
fi

# Функция для кодирования символов в URL
urlencode() {
    local string="${1}"
    local strlen=${#string}
    local encoded=""
    for (( pos=0 ; pos<strlen ; pos++ )); do
        c=${string:$pos:1}
        case "$c" in
            [-_.~a-zA-Z0-9] ) o="${c}" ;;
            * )               printf -v o '%%%02x' "'$c" ;;
        esac
        encoded+="${o}"
    done
    echo "${encoded}"
}

# Функция для получения списка репозиториев из проекта с фильтром по пути
get_repository_list() {
    local url="https://${HARBOR_URL}/api/v2.0/projects/${HARBOR_PROJECT}/repositories?page_size=100&name=~${HARBOR_PATH}"
    curl -s -k -u "${USERNAME}:${PASSWORD}" "${url}" | jq -r '.[].name' | grep "/${HARBOR_PATH}"
}

# Функция для получения тегов репозитория
get_repository_tags() {
    local repository=$1
    # Убираем имя проекта и кодируем символ /
    local encoded_repo=$(urlencode "${repository#${HARBOR_PROJECT}/}")
    local url="https://${HARBOR_URL}/api/v2.0/projects/${HARBOR_PROJECT}/repositories/${encoded_repo}/artifacts"
    curl -s -k -u "${USERNAME}:${PASSWORD}" "${url}" | jq -r '.[].tags[].name'
}

# Функция для подписания образа с помощью cosign
sign_image_with_cosign() {
    local image_name=$1
    echo "$PASSPHRASE" | ./cosign-linux-amd64 sign --key "${COSIGN_KEY}" "${image_name}" --registry-username $USERNAME --registry-password $PASSWORD --tlog-upload=false --allow-insecure-registry
}

# Основной скрипт
echo "Получение списка репозиториев..."

# Получаем список репозиториев с фильтром по пути
REPOSITORIES=$(get_repository_list)
if [[ -z "$REPOSITORIES" ]]; then
    echo "Ошибка: Не найдено репозиториев в проекте '$HARBOR_PROJECT' с путем '$HARBOR_PATH'."
    exit 1
fi

echo "Репозитории: $REPOSITORIES"

# Для каждого репозитория получаем теги и подписываем образы
for REPO in $REPOSITORIES; do
    echo "Обрабатывается репозиторий: $REPO"
    TAGS=$(get_repository_tags "$REPO")
    if [[ -z "$TAGS" ]]; then
        echo "  Теги не найдены."
        continue
    fi

    for TAG in $TAGS; do
        echo "  Найден тег: $TAG"
        FULL_IMAGE_NAME="${HARBOR_URL}/${REPO}:${TAG}"
        echo "  Подписываю образ $FULL_IMAGE_NAME"
        sign_image_with_cosign "$FULL_IMAGE_NAME"
    done
done

echo "Подписание завершено."

Запустите скрипт, передав в параметры адрес реестра, логин и пароль, имя созданного проекта, ключ cosign и секретный пароль:

chmod +x ./sign_images.sh
./sign_images.sh \
 --harbor-registry harbor.registry.domain.ru \
 --harbor-project dapp  \
 --harbor-path 2.2.3-fstec \
 --cosign-key cosign.key \
 --username admin \
 --password <harbor-admin-password> \
 --passphrase dropapp

Результатом вывода будет:

Получение списка репозиториев...
...
Подписание завершено.

Подготовка окружения завершена.

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