Установка SemanticMDM версии 25.1

Установка PostgreSQL и развёртывание базы данных Semantic MDM

Установка сервера

Установить PostgreSQL версии 15 по инструкции с официального сайта: https://www.postgresql.org/download

Настройка сервера

Выполнить тонкую настройку в postgresql.conf по рекомендациям с https://www.pgconfig.org, введя выделенные ресурсы, а так же привести значение шифрования пароля к password_encryption = scram-sha-256 или md5.

Выполнить настройку файла pg_hba.conf, указав:

<type>   <database>   <user>   <address>   <method>

где:

type - параметр, указывающий тип подключения (local, host, hostssl, hostnossl, hostgssenc, hostnogssenc) 
database - параметр, указывающий к какой БД разрешено подключаться, так же может быть all
user- параметр, указывающий каким пользователем можно подключаться, так же может быть all
address - параметр, указывающий c какого IP адреса или подсети можно подключаться, так же может быть *
method - параметр, указывающий тип шифрования, в нашем случае scram-sha-256 или md5, смотря какой тип был указан в postgresql.conf

Перезапустить службу PostgreSQL

systemctl restart postgresql-15.service

Переключиться в пользователя postgres и создать роль и необходимые базы на сервере:

sudo su - postgres

psql

CREATE ROLE m2 WITH SUPERUSER LOGIN PASSWORD 'superPassw0rd';
CREATE DATABASE m2 WITH OWNER m2;
CREATE DATABASE keycloak WITH OWNER m2;
\c m2;
CREATE SCHEMA m2;

exit

Настройка контейнеризации

docker load -i <image_name>.tar

Установка компонента Single Sign-On Keycloak и настройка реалма

mkdir -p /opt/semanticiam && cd $_
FROM nexus.sdi-solution.ru/infra/keycloak:25.0.0-core AS builder
# Configure postgres database vendor
ENV KC_DB=postgres
ENV KC_FEATURES="token-exchange,scripts,preview" 
ENV KC_FEATURES_DISABLED="organization" 

WORKDIR /opt/keycloak

COPY kc-m2-provider.json providers/kc-m2-provider.json
# COPY sdi.keytab sdi.keytab
RUN /opt/keycloak/bin/kc.sh build --health-enabled=true --metrics-enabled=true

FROM nexus.sdi-solution.ru/infra/keycloak:25.0.0-core AS final
LABEL image.version=25.0.0
COPY --from=builder /opt/keycloak/ /opt/keycloak/

# https://github.com/keycloak/keycloak/issues/19185#issuecomment-1480763024
USER root
RUN sed -i '/disabledAlgorithms/ s/ SHA1,//' /etc/crypto-policies/back-ends/java.config
USER keycloak

RUN /opt/keycloak/bin/kc.sh show-config
ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]
[INPUT]
    Name               forward
    Listen             0.0.0.0
    Port               24224
    Tag_Prefix         kcEvent.

[OUTPUT]
    Name               opensearch
    Match              kcEvent.<wf_instance_name>
    Host               <opensearch_ip>
    Port               <opensearch_port>
    HTTP_User          <user>
    HTTP_Passwd        <password>
    Index              user_journal
    Generate_ID        On
    Suppress_Type_Name On
    Logstash_Format    Off
wf_instance_name - имя. обычно соответствует названию проекта
opensearch_ip - ip адрес сервера opensearch
opensearch_port - порт сервера opensearch
user - пользователь opensearch (admin)
password - пароль пользователя opensearch (admin)
{
  "realms": {
    "<realm_name>": {
      "clientIds": [
        "<front_client>",
        "<back_client>"
      ],
      "backend": "<semantic_uri>",
      "useSpnego": <true/false>
    }
  },
  "flbHost": "<fluent_ip>",
  "flbPort": 24222
}
realm_name - название реалма в Keycloak
front_client - имя front клиента, такое же указать и в скрипте создания реалма
back_client - имя back клиента, такое же указать и в скрипте создания реалма
semantic_uri - адрес Semantic
fluent_ip - IP адрес fluent-bit
useSpnego - включает/выключает видимость кнопки войти через Windows
services:
  keycloak:
    container_name: keycloak
    build:
      context: .
    command: ['start', '--optimized', '--spi-required-action-signal-m2-auth-settings=/opt/keycloak/providers/kc-m2-provider.json', '--spi-events-listener-m2-event-listener-settings=/opt/keycloak/providers/kc-m2-provider.json', '--spi-storage-m2-db-user-federation-settings=/opt/keycloak/providers/kc-m2-provider.json']
    environment:
      JAVA_OPTS_APPEND: -Dkeycloak.profile.feature.upload_scripts=enabled -agentlib:jdwp=transport=dt_socket,address=keycloak:8787,server=y,suspend=n
      KC_DB: postgres
      KC_DB_URL: ""
      KC_DB_USERNAME:
      KC_DB_PASSWORD:
      KC_HOSTNAME:
      # KC_HOSTNAME_PORT: 8080
      KC_HOSTNAME_URL:
      # KC_HTTP_RELATIVE_PATH: /
      # KC_HOSTNAME_ADMIN_URL: 
      KC_HTTP_ENABLED: true
      KC_PROXY_HEADERS: xforwarded
      # KC_HOSTNAME_STRICT: false
      # KC_HOSTNAME_STRICT_HTTPS: false
      # KC_LOG_LEVEL: info
      # KC_LOG_LEVEL: debug
      KC_METRICS_ENABLED: true
      KC_HEALTH_ENABLED: true
      KEYCLOAK_ADMIN: 
      KEYCLOAK_ADMIN_PASSWORD: 
    ports:
      - 9080:8080
      - 9787:8787
    networks:
      - kc

  fluent-bit:
    container_name: fluent-bit
    image: fluent/fluent-bit
    ports:
      - 24222:24224
    volumes:
      - ./fluent-bit.conf:/fluent-bit/etc/fluent-bit.conf
    networks:
      - keycloak

networks:
  keycloak:
    name: keycloak
hostname: указать FQDN, который должен совпадать с KC_HOSTNAME, KC_HOSTNAME_URL
KC_HOSTNAME: указать FQDN, который должен совпадать с hostname, KC_HOSTNAME_URL
KC_DB_URL: "jdbc:postgresql://<DB_IP>:<DB_PORT>/<DB_NAME>" - параметры подключения к БД
KC_DB_USERNAME: Пользователь БД с доступом к бд keycloak
KC_DB_PASSWORD: Пароль пользователя БД с доступом к бд keycloak
KC_HOSTNAME_URL: http:// | https:// - Полный адрес ресурса, по которому будет доступен Keycloak (необходимо добавить DNS запись и конфигурацию в Nginx, чувствительная настройка)
KC_HTTP_RELATIVE_PATH: / (дефолтное значение), раскомментировать и написать нужный location, если нужно повесить на существующую запись, например https://test.sdi-solution.ru/keycloak
KEYCLOAK_ADMIN: Пользователь, который создастся в Keycloak c администраторскими правами
KEYCLOAK_ADMIN_PASSWORD: Пароль пользователя, который создастся в Keycloak c администраторскими правами
docker compose build
docker compose up -d

Инструкция по настройке реалма

touch semantic-realm.sh && chmod +x semantic-realm.sh
#!/bin/bash

keycloakURI=
semanticURI=
semanticHTTP=
adminRealm=master
semanticRealm=
adminClient=admin-cli
frontClient=sdi-web-client
backClient=sdi-core-client
backSecret=
mlClient=sdi-ml-client
mlSecret=
username=
userpass=
systemRoles="read,editData,editMetadata,sysAdministration,exportPatterns,viewHistory,manageApplications,editDataWithoutApplications,runReports,rootNodeAccess,readSecurity,manageSecurity,readUserJournal,similaritySearch,editExpirables,sudo,viceSetup"

# Get bearer token for other Web API calls
token=$(curl --silent -X POST "${keycloakURI}/realms/${adminRealm}/protocol/openid-connect/token" \
  --header "Content-Type: application/x-www-form-urlencoded" \
  --data "client_id=${adminClient}" \
  --data "username=${username}" \
  --data "password=${userpass}" \
  --data "grant_type=password" | jq -r .access_token)

# Pass token for create a predefined realm for Semantic use
curl -X POST "${keycloakURI}/admin/realms" \
  --header "Authorization: Bearer ${token}" \
  --header "Content-Type: application/json" \
  --data '{
  "id": "'"${semanticRealm}"'",
  "realm": "'"${semanticRealm}"'",
  "loginTheme": "semantic-mdm",
  "notBefore": 0,
  "defaultSignatureAlgorithm": "RS256",
  "revokeRefreshToken": false,
  "refreshTokenMaxReuse": 0,
  "accessTokenLifespan": 300,
  "accessTokenLifespanForImplicitFlow": 900,
  "ssoSessionIdleTimeout": 1800,
  "ssoSessionMaxLifespan": 36000,
  "ssoSessionIdleTimeoutRememberMe": 0,
  "ssoSessionMaxLifespanRememberMe": 0,
  "offlineSessionIdleTimeout": 2592000,
  "offlineSessionMaxLifespanEnabled": false,
  "offlineSessionMaxLifespan": 5184000,
  "accessCodeLifespan": 60,
  "accessCodeLifespanUserAction": 300,
  "accessCodeLifespanLogin": 1800,
  "actionTokenGeneratedByAdminLifespan": 43200,
  "actionTokenGeneratedByUserLifespan": 300,
  "enabled": true,
  "sslRequired": "external",
  "registrationAllowed": false,
  "registrationEmailAsUsername": false,
  "rememberMe": false,
  "verifyEmail": false,
  "loginWithEmailAllowed": false,
  "duplicateEmailsAllowed": true,
  "resetPasswordAllowed": false,
  "editUsernameAllowed": false,
  "bruteForceProtected": false,
  "permanentLockout": false,
  "maxTemporaryLockouts": 0,
  "maxFailureWaitSeconds": 900,
  "minimumQuickLoginWaitSeconds": 60,
  "waitIncrementSeconds": 60,
  "quickLoginCheckMilliSeconds": 1000,
  "maxDeltaTimeSeconds": 43200,
  "failureFactor": 30,
  "passwordPolicy": "m2-password-policy",
  "eventsListeners": [
    "m2-event-listener",
    "jboss-logging"
  ],
  "requiredActions": [
    {
      "alias": "CONFIGURE_TOTP",
      "name": "Configure OTP",
      "providerId": "CONFIGURE_TOTP",
      "enabled": true,
      "defaultAction": false,
      "priority": 10,
      "config": {}
    },
    {
      "alias": "TERMS_AND_CONDITIONS",
      "name": "Terms and Conditions",
      "providerId": "TERMS_AND_CONDITIONS",
      "enabled": false,
      "defaultAction": false,
      "priority": 20,
      "config": {}
    },
    {
      "alias": "UPDATE_PASSWORD",
      "name": "Update Password",
      "providerId": "UPDATE_PASSWORD",
      "enabled": true,
      "defaultAction": false,
      "priority": 30,
      "config": {}
    },
    {
      "alias": "UPDATE_PROFILE",
      "name": "Update Profile",
      "providerId": "UPDATE_PROFILE",
      "enabled": true,
      "defaultAction": false,
      "priority": 40,
      "config": {}
    },
    {
      "alias": "VERIFY_EMAIL",
      "name": "Verify Email",
      "providerId": "VERIFY_EMAIL",
      "enabled": false,
      "defaultAction": false,
      "priority": 50,
      "config": {}
    },
    {
      "alias": "delete_account",
      "name": "Delete Account",
      "providerId": "delete_account",
      "enabled": false,
      "defaultAction": false,
      "priority": 60,
      "config": {}
    },
    {
      "alias": "CONFIGURE_RECOVERY_AUTHN_CODES",
      "name": "Recovery Authentication Codes",
      "providerId": "CONFIGURE_RECOVERY_AUTHN_CODES",
      "enabled": true,
      "defaultAction": false,
      "priority": 70,
      "config": {}
    },
    {
      "alias": "UPDATE_EMAIL",
      "name": "Update Email",
      "providerId": "UPDATE_EMAIL",
      "enabled": true,
      "defaultAction": false,
      "priority": 70,
      "config": {}
    },
    {
      "alia