fix and update
This commit is contained in:
@@ -19,8 +19,8 @@ SSL_CERT_DIR=
|
|||||||
SSL_CERT_FULLCHAIN=
|
SSL_CERT_FULLCHAIN=
|
||||||
SSL_CERT_KEY=
|
SSL_CERT_KEY=
|
||||||
|
|
||||||
# Доп. DNS-имена в self-signed сертификат (сайты приложений в LAN через запятую)
|
# Доп. DNS-имена в self-signed сертификат (опционально, поверх авто из OAuth redirect_uri)
|
||||||
# EXTRA_SSL_SANS=myapp.lan,portal.company.lpr
|
# EXTRA_SSL_SANS=legacy-app.lan
|
||||||
|
|
||||||
DOMAIN_API=api.idpmvk.lpr
|
DOMAIN_API=api.idpmvk.lpr
|
||||||
DOMAIN_FRONTEND=sso.idpmvk.lpr
|
DOMAIN_FRONTEND=sso.idpmvk.lpr
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { AuthGrpcController } from './domain/auth-grpc.controller';
|
|||||||
import { AuthService } from './domain/auth.service';
|
import { AuthService } from './domain/auth.service';
|
||||||
import { PinService } from './domain/pin.service';
|
import { PinService } from './domain/pin.service';
|
||||||
import { RbacService } from './domain/rbac.service';
|
import { RbacService } from './domain/rbac.service';
|
||||||
|
import { OAuthSslSansService } from './domain/oauth-ssl-sans.service';
|
||||||
import { SecurityService } from './domain/security.service';
|
import { SecurityService } from './domain/security.service';
|
||||||
import { PrismaService } from './infra/prisma.service';
|
import { PrismaService } from './infra/prisma.service';
|
||||||
import { RedisService } from './infra/redis.service';
|
import { RedisService } from './infra/redis.service';
|
||||||
@@ -70,6 +71,7 @@ import { BotFatherAssistantService } from './domain/bot/bot-father-assistant.ser
|
|||||||
AdminSeedService,
|
AdminSeedService,
|
||||||
AdminService,
|
AdminService,
|
||||||
RbacService,
|
RbacService,
|
||||||
|
OAuthSslSansService,
|
||||||
SecurityService,
|
SecurityService,
|
||||||
ProfileService,
|
ProfileService,
|
||||||
DocumentsService,
|
DocumentsService,
|
||||||
|
|||||||
99
apps/sso-core/src/domain/oauth-ssl-sans.service.ts
Normal file
99
apps/sso-core/src/domain/oauth-ssl-sans.service.ts
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { mkdir, writeFile } from 'node:fs/promises';
|
||||||
|
import { dirname } from 'node:path';
|
||||||
|
import { PrismaService } from '../infra/prisma.service';
|
||||||
|
import {
|
||||||
|
collectOAuthSslHostnames,
|
||||||
|
parseHostnameFromPublicUrl,
|
||||||
|
resolveRegistrableDomain
|
||||||
|
} from '../infra/oauth-ssl-sans.util';
|
||||||
|
|
||||||
|
const OAUTH_SSL_SANS_SETTING_KEY = 'OAUTH_SSL_SANS';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class OAuthSslSansService implements OnModuleInit {
|
||||||
|
private readonly logger = new Logger(OAuthSslSansService.name);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly prisma: PrismaService,
|
||||||
|
private readonly config: ConfigService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async onModuleInit() {
|
||||||
|
try {
|
||||||
|
await this.syncFromDatabase();
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.warn(
|
||||||
|
`Не удалось синхронизировать SAN из OAuth redirect URI: ${error instanceof Error ? error.message : 'ошибка'}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async syncFromDatabase(): Promise<string[]> {
|
||||||
|
const clients = await this.prisma.oAuthClient.findMany({
|
||||||
|
where: { isActive: true },
|
||||||
|
select: { redirectUris: true }
|
||||||
|
});
|
||||||
|
const redirectUris = clients.flatMap((client) => client.redirectUris);
|
||||||
|
const hostnames = collectOAuthSslHostnames(redirectUris, this.buildExcludedHostnames());
|
||||||
|
|
||||||
|
await this.persistHostnames(hostnames);
|
||||||
|
return hostnames;
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildExcludedHostnames(): Set<string> {
|
||||||
|
const excluded = new Set<string>();
|
||||||
|
|
||||||
|
for (const envKey of ['DOMAIN_API', 'DOMAIN_FRONTEND', 'DOMAIN_DOCS', 'DOMAIN_WS', 'DOMAIN_MINIO', 'DOMAIN_MINIO_CONSOLE']) {
|
||||||
|
const value = this.config.get<string>(envKey)?.trim().toLowerCase();
|
||||||
|
if (value) excluded.add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const url of [
|
||||||
|
this.config.get<string>('PUBLIC_API_URL'),
|
||||||
|
this.config.get<string>('PUBLIC_FRONTEND_URL'),
|
||||||
|
this.config.get<string>('PUBLIC_DOCS_URL'),
|
||||||
|
this.config.get<string>('PUBLIC_WS_URL')
|
||||||
|
]) {
|
||||||
|
const host = parseHostnameFromPublicUrl(url);
|
||||||
|
if (host) excluded.add(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
const idpHosts = [...excluded];
|
||||||
|
for (const host of idpHosts) {
|
||||||
|
excluded.add(resolveRegistrableDomain(host));
|
||||||
|
const parts = host.split('.');
|
||||||
|
if (parts.length >= 3) {
|
||||||
|
excluded.add(parts.slice(-2).join('.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return excluded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async persistHostnames(hostnames: string[]) {
|
||||||
|
const serialized = hostnames.join(',');
|
||||||
|
await this.prisma.systemSetting.upsert({
|
||||||
|
where: { key: OAUTH_SSL_SANS_SETTING_KEY },
|
||||||
|
create: {
|
||||||
|
key: OAUTH_SSL_SANS_SETTING_KEY,
|
||||||
|
value: serialized,
|
||||||
|
description: 'DNS-имена из redirect_uri OAuth-приложений для SAN сертификата (авто)',
|
||||||
|
isSecret: false
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
value: serialized,
|
||||||
|
description: 'DNS-имена из redirect_uri OAuth-приложений для SAN сертификата (авто)'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const targetFile =
|
||||||
|
this.config.get<string>('OAUTH_SSL_SANS_FILE')?.trim() ||
|
||||||
|
'/data/nginx-certs/oauth-redirect-sans.txt';
|
||||||
|
const body = `${hostnames.join('\n')}\n`;
|
||||||
|
await mkdir(dirname(targetFile), { recursive: true });
|
||||||
|
await writeFile(targetFile, body, 'utf8');
|
||||||
|
this.logger.log(`OAuth SSL SAN обновлён (${hostnames.length}): ${hostnames.join(', ') || '—'}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import { OAuthClientType } from '../generated/prisma/client';
|
|||||||
import { PrismaService } from '../infra/prisma.service';
|
import { PrismaService } from '../infra/prisma.service';
|
||||||
import { AccessService, UserAccessContext } from './access.service';
|
import { AccessService, UserAccessContext } from './access.service';
|
||||||
import { NotificationsService } from './notifications.service';
|
import { NotificationsService } from './notifications.service';
|
||||||
|
import { OAuthSslSansService } from './oauth-ssl-sans.service';
|
||||||
import { DEFAULT_USER_ROLE_SLUG, BOT_ROLE_SLUG } from './rbac.constants';
|
import { DEFAULT_USER_ROLE_SLUG, BOT_ROLE_SLUG } from './rbac.constants';
|
||||||
|
|
||||||
export interface OAuthClientListItem {
|
export interface OAuthClientListItem {
|
||||||
@@ -24,7 +25,8 @@ export class RbacService {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly prisma: PrismaService,
|
private readonly prisma: PrismaService,
|
||||||
private readonly access: AccessService,
|
private readonly access: AccessService,
|
||||||
private readonly notifications: NotificationsService
|
private readonly notifications: NotificationsService,
|
||||||
|
private readonly oauthSslSans: OAuthSslSansService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async createRole(actorUserId: string, data: { slug: string; name: string; description?: string; permissionSlugs?: string[] }) {
|
async createRole(actorUserId: string, data: { slug: string; name: string; description?: string; permissionSlugs?: string[] }) {
|
||||||
@@ -198,6 +200,7 @@ export class RbacService {
|
|||||||
include: { scopes: { include: { scope: true } }, createdBy: true }
|
include: { scopes: { include: { scope: true } }, createdBy: true }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await this.oauthSslSans.syncFromDatabase().catch(() => undefined);
|
||||||
return { client, clientSecret };
|
return { client, clientSecret };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,6 +243,7 @@ export class RbacService {
|
|||||||
include: { scopes: { include: { scope: true } }, createdBy: true }
|
include: { scopes: { include: { scope: true } }, createdBy: true }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await this.oauthSslSans.syncFromDatabase().catch(() => undefined);
|
||||||
return { client: updated, clientSecret: undefined as string | undefined };
|
return { client: updated, clientSecret: undefined as string | undefined };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,6 +254,7 @@ export class RbacService {
|
|||||||
}
|
}
|
||||||
await this.assertCanManageOAuthClient(actorUserId, existing);
|
await this.assertCanManageOAuthClient(actorUserId, existing);
|
||||||
await this.prisma.oAuthClient.delete({ where: { id: existing.id } });
|
await this.prisma.oAuthClient.delete({ where: { id: existing.id } });
|
||||||
|
await this.oauthSslSans.syncFromDatabase().catch(() => undefined);
|
||||||
return { clientId };
|
return { clientId };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
68
apps/sso-core/src/infra/oauth-ssl-sans.util.ts
Normal file
68
apps/sso-core/src/infra/oauth-ssl-sans.util.ts
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
const INTERNAL_HOSTNAMES = new Set([
|
||||||
|
'api-gateway',
|
||||||
|
'sso-core',
|
||||||
|
'frontend',
|
||||||
|
'docs',
|
||||||
|
'media-ws',
|
||||||
|
'minio',
|
||||||
|
'postgres',
|
||||||
|
'redis',
|
||||||
|
'rabbitmq',
|
||||||
|
'ldap-auth'
|
||||||
|
]);
|
||||||
|
|
||||||
|
export function resolveRegistrableDomain(hostname: string): string {
|
||||||
|
const host = hostname.trim().toLowerCase();
|
||||||
|
if (!host) return host;
|
||||||
|
const labels = host.split('.').filter(Boolean);
|
||||||
|
if (labels.length <= 2) return host;
|
||||||
|
return `${labels[labels.length - 2]}.${labels[labels.length - 1]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function extractHostnameFromRedirectUri(uri: string): string | null {
|
||||||
|
try {
|
||||||
|
const parsed = new URL(uri.trim());
|
||||||
|
if (!['http:', 'https:'].includes(parsed.protocol)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const host = parsed.hostname.trim().toLowerCase();
|
||||||
|
return host || null;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isInternalOAuthSslHostname(hostname: string): boolean {
|
||||||
|
const host = hostname.trim().toLowerCase();
|
||||||
|
if (!host || INTERNAL_HOSTNAMES.has(host)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (/^\d+\.\d+\.\d+\.\d+$/.test(host)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !host.includes('.');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function collectOAuthSslHostnames(redirectUris: string[], excludedHostnames: Iterable<string>): string[] {
|
||||||
|
const excluded = new Set(Array.from(excludedHostnames, (item) => item.trim().toLowerCase()).filter(Boolean));
|
||||||
|
const result = new Set<string>();
|
||||||
|
|
||||||
|
for (const uri of redirectUris) {
|
||||||
|
const host = extractHostnameFromRedirectUri(uri);
|
||||||
|
if (!host || excluded.has(host) || isInternalOAuthSslHostname(host)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
result.add(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [...result].sort((a, b) => a.localeCompare(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseHostnameFromPublicUrl(value?: string | null): string | null {
|
||||||
|
if (!value?.trim()) return null;
|
||||||
|
try {
|
||||||
|
return new URL(value.trim()).hostname.toLowerCase();
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -95,6 +95,9 @@ services:
|
|||||||
PUBLIC_API_URL: ${PUBLIC_API_URL:-http://localhost:3002/idp-api}
|
PUBLIC_API_URL: ${PUBLIC_API_URL:-http://localhost:3002/idp-api}
|
||||||
RABBITMQ_URL: amqp://${RABBITMQ_DEFAULT_USER:-lendry}:${RABBITMQ_DEFAULT_PASS:-lendry_password}@rabbitmq:5672/
|
RABBITMQ_URL: amqp://${RABBITMQ_DEFAULT_USER:-lendry}:${RABBITMQ_DEFAULT_PASS:-lendry_password}@rabbitmq:5672/
|
||||||
LDAP_AUTH_URL: http://ldap-auth:8086
|
LDAP_AUTH_URL: http://ldap-auth:8086
|
||||||
|
OAUTH_SSL_SANS_FILE: /data/nginx-certs/oauth-redirect-sans.txt
|
||||||
|
volumes:
|
||||||
|
- ./nginx/certs:/data/nginx-certs
|
||||||
ports:
|
ports:
|
||||||
- "3001:3001"
|
- "3001:3001"
|
||||||
- "50051:50051"
|
- "50051:50051"
|
||||||
|
|||||||
75
install.sh
75
install.sh
@@ -1603,6 +1603,45 @@ registrable_domain() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
collect_oauth_redirect_sans_hosts() {
|
||||||
|
local f="${LOCAL_CERTS_DIR}/oauth-redirect-sans.txt"
|
||||||
|
[[ -f "$f" ]] || return 0
|
||||||
|
local host
|
||||||
|
while IFS= read -r host || [[ -n "$host" ]]; do
|
||||||
|
host="${host#"${host%%[![:space:]]*}"}"
|
||||||
|
host="${host%"${host##*[![:space:]]}"}"
|
||||||
|
[[ -z "$host" || "$host" == \#* ]] && continue
|
||||||
|
echo "$host"
|
||||||
|
done < "$f"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Нормализованный список SAN для сравнения сертификата с желаемым набором.
|
||||||
|
self_signed_sans_fingerprint() {
|
||||||
|
collect_self_signed_sans | tr ',' '\n' | sed 's/^DNS://' | sort -u | paste -sd, -
|
||||||
|
}
|
||||||
|
|
||||||
|
current_self_signed_sans_fingerprint() {
|
||||||
|
local cert="${LOCAL_CERTS_DIR}/shared-intranet/fullchain.pem"
|
||||||
|
[[ -f "$cert" ]] || return 1
|
||||||
|
openssl x509 -in "$cert" -noout -ext subjectAltName 2>/dev/null \
|
||||||
|
| sed 's/ *subjectAltName=//;s/DNS://g' \
|
||||||
|
| tr ',' '\n' | sed 's/^ *//' | sort -u | paste -sd, -
|
||||||
|
}
|
||||||
|
|
||||||
|
renew_self_signed_cert_if_sans_changed() {
|
||||||
|
[[ "$(env_get SSL_TYPE none)" != "selfsigned" ]] && return 0
|
||||||
|
local desired current
|
||||||
|
desired="$(self_signed_sans_fingerprint)"
|
||||||
|
current="$(current_self_signed_sans_fingerprint 2>/dev/null || true)"
|
||||||
|
if [[ -z "$current" || "$desired" != "$current" ]]; then
|
||||||
|
if [[ -n "$current" ]]; then
|
||||||
|
log "SAN сертификата изменились (OAuth redirect URI) — пересоздаём self-signed"
|
||||||
|
fi
|
||||||
|
generate_all_self_signed_certs --force
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
collect_self_signed_sans() {
|
collect_self_signed_sans() {
|
||||||
local -a sans=()
|
local -a sans=()
|
||||||
local base domain
|
local base domain
|
||||||
@@ -1626,7 +1665,14 @@ collect_self_signed_sans() {
|
|||||||
sans+=("DNS:${apex}")
|
sans+=("DNS:${apex}")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Дополнительные SAN для сторонних приложений в LAN (через запятую в .env).
|
# Авто: домены из redirect_uri OAuth-приложений (nginx/certs/oauth-redirect-sans.txt).
|
||||||
|
local oauth_host
|
||||||
|
while IFS= read -r oauth_host; do
|
||||||
|
[[ -z "$oauth_host" ]] && continue
|
||||||
|
sans+=("DNS:${oauth_host}")
|
||||||
|
done < <(collect_oauth_redirect_sans_hosts)
|
||||||
|
|
||||||
|
# Ручной override (дополнительно к OAuth), через запятую в .env.
|
||||||
local extra_sans="${EXTRA_SSL_SANS:-}"
|
local extra_sans="${EXTRA_SSL_SANS:-}"
|
||||||
if [[ -n "$extra_sans" ]]; then
|
if [[ -n "$extra_sans" ]]; then
|
||||||
local entry
|
local entry
|
||||||
@@ -1639,7 +1685,17 @@ collect_self_signed_sans() {
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
(IFS=,; echo "${sans[*]}")
|
# Уникальные SAN
|
||||||
|
local -A _seen=()
|
||||||
|
local -a unique=()
|
||||||
|
local item key
|
||||||
|
for item in "${sans[@]}"; do
|
||||||
|
key="${item#DNS:}"
|
||||||
|
[[ -n "${_seen[$key]:-}" ]] && continue
|
||||||
|
_seen[$key]=1
|
||||||
|
unique+=("$item")
|
||||||
|
done
|
||||||
|
(IFS=,; echo "${unique[*]}")
|
||||||
}
|
}
|
||||||
|
|
||||||
find_custom_cert_file() {
|
find_custom_cert_file() {
|
||||||
@@ -1820,6 +1876,7 @@ install_custom_certificates() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
generate_shared_self_signed_cert() {
|
generate_shared_self_signed_cert() {
|
||||||
|
local force="${1:-}"
|
||||||
local cert_dir="${LOCAL_CERTS_DIR}/shared-intranet"
|
local cert_dir="${LOCAL_CERTS_DIR}/shared-intranet"
|
||||||
local base sans cn
|
local base sans cn
|
||||||
|
|
||||||
@@ -1829,7 +1886,7 @@ generate_shared_self_signed_cert() {
|
|||||||
cn="*.${base}"
|
cn="*.${base}"
|
||||||
sans="$(collect_self_signed_sans)"
|
sans="$(collect_self_signed_sans)"
|
||||||
|
|
||||||
if [[ -f "${cert_dir}/fullchain.pem" && -f "${cert_dir}/privkey.pem" ]]; then
|
if [[ -f "${cert_dir}/fullchain.pem" && -f "${cert_dir}/privkey.pem" && "$force" != "--force" ]]; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -1856,7 +1913,7 @@ generate_all_self_signed_certs() {
|
|||||||
if [[ "$force" == "--force" ]]; then
|
if [[ "$force" == "--force" ]]; then
|
||||||
rm -f "${LOCAL_CERTS_DIR}/shared-intranet/fullchain.pem" "${LOCAL_CERTS_DIR}/shared-intranet/privkey.pem"
|
rm -f "${LOCAL_CERTS_DIR}/shared-intranet/fullchain.pem" "${LOCAL_CERTS_DIR}/shared-intranet/privkey.pem"
|
||||||
fi
|
fi
|
||||||
generate_shared_self_signed_cert
|
generate_shared_self_signed_cert "$force"
|
||||||
ok "Самоподписанный сертификат создан: ${LOCAL_CERTS_DIR}/shared-intranet"
|
ok "Самоподписанный сертификат создан: ${LOCAL_CERTS_DIR}/shared-intranet"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2775,13 +2832,13 @@ print_intranet_hint() {
|
|||||||
if [[ "$(env_get SSL_TYPE none)" == "selfsigned" ]]; then
|
if [[ "$(env_get SSL_TYPE none)" == "selfsigned" ]]; then
|
||||||
echo " Сертификат: ${LOCAL_CERTS_DIR}/shared-intranet (общий для всех доменов IdP)"
|
echo " Сертификат: ${LOCAL_CERTS_DIR}/shared-intranet (общий для всех доменов IdP)"
|
||||||
echo " FedCM/One Tap: браузер открывает ${apex_domain:-корневой домен}/.well-known/web-identity —"
|
echo " FedCM/One Tap: браузер открывает ${apex_domain:-корневой домен}/.well-known/web-identity —"
|
||||||
echo " примите сертификат для ${DOMAIN_FRONTEND}, ${DOMAIN_API}${extra_apex} и сайта вашего приложения."
|
echo " примите сертификат для ${DOMAIN_FRONTEND}, ${DOMAIN_API}${extra_apex} и доменов OAuth-приложений."
|
||||||
|
echo " SAN OAuth-приложений подтягиваются автоматически из redirect_uri (файл oauth-redirect-sans.txt)."
|
||||||
echo " Надёжно: импортируйте fullchain.pem в «Доверенные корневые центры» на каждом ПК (Windows/macOS)."
|
echo " Надёжно: импортируйте fullchain.pem в «Доверенные корневые центры» на каждом ПК (Windows/macOS)."
|
||||||
if [[ -n "$(env_get EXTRA_SSL_SANS "")" ]]; then
|
if [[ -n "$(env_get EXTRA_SSL_SANS "")" ]]; then
|
||||||
echo " EXTRA_SSL_SANS: $(env_get EXTRA_SSL_SANS "")"
|
echo " EXTRA_SSL_SANS (ручной доп.): $(env_get EXTRA_SSL_SANS "")"
|
||||||
else
|
|
||||||
echo " Для другого домена приложения добавьте в .env: EXTRA_SSL_SANS=myapp.lan и ./install.sh --fix-all"
|
|
||||||
fi
|
fi
|
||||||
|
echo " После нового OAuth-клиента: ./install.sh --fix-all (или обновление SSL) пересоздаст сертификат."
|
||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
if [[ "$(env_get LDAP_USE_HOST_NETWORK false)" == "true" ]]; then
|
if [[ "$(env_get LDAP_USE_HOST_NETWORK false)" == "true" ]]; then
|
||||||
@@ -3340,7 +3397,7 @@ action_fix_all_errors() {
|
|||||||
case "$ssl_type" in
|
case "$ssl_type" in
|
||||||
none) write_nginx_http_only || warn "Не удалось записать HTTP-конфиги Nginx" ;;
|
none) write_nginx_http_only || warn "Не удалось записать HTTP-конфиги Nginx" ;;
|
||||||
selfsigned)
|
selfsigned)
|
||||||
generate_all_self_signed_certs
|
renew_self_signed_cert_if_sans_changed
|
||||||
write_all_nginx_configs "selfsigned" || warn "Не удалось обновить конфиги Nginx"
|
write_all_nginx_configs "selfsigned" || warn "Не удалось обновить конфиги Nginx"
|
||||||
;;
|
;;
|
||||||
custom)
|
custom)
|
||||||
|
|||||||
Reference in New Issue
Block a user