Csp: lua script, CspSettingsHelper, portalrename, dns change, domains array

This commit is contained in:
pavelbannov 2023-07-11 16:21:48 +03:00
parent 443453d538
commit b3afd051af
9 changed files with 196 additions and 63 deletions

View File

@ -118,17 +118,7 @@ public class Tenant : IMapFrom<DbTenant>
baseHost = HostedRegion; baseHost = HostedRegion;
} }
string result; var result = $"{Alias}.{baseHost}".TrimEnd('.').ToLowerInvariant();
if (baseHost == "localhost" || Alias == "localhost")
{
//single tenant on local host
Alias = "localhost";
result = HostName;
}
else
{
result = $"{Alias}.{baseHost}".TrimEnd('.').ToLowerInvariant();
}
if (!string.IsNullOrEmpty(MappedDomain) && allowMappedDomain) if (!string.IsNullOrEmpty(MappedDomain) && allowMappedDomain)
{ {

View File

@ -90,6 +90,31 @@ server {
proxy_set_header Connection $proxy_connection; proxy_set_header Connection $proxy_connection;
proxy_set_header Proxy ""; proxy_set_header Proxy "";
set $csp "";
access_by_lua '
local key = string.format("csp:%s",ngx.var.host)
local redis = require "resty.redis"
local red = redis:new()
local redis_host = "127.0.0.1"
local redis_port = 6379
red:set_timeout(1000) -- 1 second
local ok, err = red:connect(redis_host, redis_port)
if not ok then
ngx.log(ngx.ERR, "failed to connect to redis: ", err)
end
local csp, err = red:hget(key, "data")
if csp == ngx.null then
ngx.log(ngx.ERR, "failed to get redis key: ", err)
else
ngx.header.Content_Security_Policy = csp
end
';
location ~* ^/ds-vpath/ { location ~* ^/ds-vpath/ {
rewrite /ds-vpath/(.*) /$1 break; rewrite /ds-vpath/(.*) /$1 break;

View File

@ -40,7 +40,6 @@ public class PortalController : ControllerBase
private readonly CommonLinkUtility _commonLinkUtility; private readonly CommonLinkUtility _commonLinkUtility;
private readonly UrlShortener _urlShortener; private readonly UrlShortener _urlShortener;
private readonly AuthContext _authContext; private readonly AuthContext _authContext;
private readonly WebItemSecurity _webItemSecurity;
protected readonly SecurityContext _securityContext; protected readonly SecurityContext _securityContext;
private readonly SettingsManager _settingsManager; private readonly SettingsManager _settingsManager;
private readonly IMobileAppInstallRegistrator _mobileAppInstallRegistrator; private readonly IMobileAppInstallRegistrator _mobileAppInstallRegistrator;
@ -65,6 +64,7 @@ public class PortalController : ControllerBase
private readonly IMapper _mapper; private readonly IMapper _mapper;
private readonly IHttpContextAccessor _httpContextAccessor; private readonly IHttpContextAccessor _httpContextAccessor;
private readonly QuotaHelper _quotaHelper; private readonly QuotaHelper _quotaHelper;
private readonly CspSettingsHelper _cspSettingsHelper;
public PortalController( public PortalController(
ILogger<PortalController> logger, ILogger<PortalController> logger,
@ -75,7 +75,6 @@ public class PortalController : ControllerBase
CommonLinkUtility commonLinkUtility, CommonLinkUtility commonLinkUtility,
UrlShortener urlShortener, UrlShortener urlShortener,
AuthContext authContext, AuthContext authContext,
WebItemSecurity webItemSecurity,
SecurityContext securityContext, SecurityContext securityContext,
SettingsManager settingsManager, SettingsManager settingsManager,
IMobileAppInstallRegistrator mobileAppInstallRegistrator, IMobileAppInstallRegistrator mobileAppInstallRegistrator,
@ -98,7 +97,8 @@ public class PortalController : ControllerBase
TfaAppAuthSettingsHelper tfaAppAuthSettingsHelper, TfaAppAuthSettingsHelper tfaAppAuthSettingsHelper,
IMapper mapper, IMapper mapper,
IHttpContextAccessor httpContextAccessor, IHttpContextAccessor httpContextAccessor,
QuotaHelper quotaHelper) QuotaHelper quotaHelper,
CspSettingsHelper cspSettingsHelper)
{ {
_log = logger; _log = logger;
_apiContext = apiContext; _apiContext = apiContext;
@ -108,7 +108,6 @@ public class PortalController : ControllerBase
_commonLinkUtility = commonLinkUtility; _commonLinkUtility = commonLinkUtility;
_urlShortener = urlShortener; _urlShortener = urlShortener;
_authContext = authContext; _authContext = authContext;
_webItemSecurity = webItemSecurity;
_securityContext = securityContext; _securityContext = securityContext;
_settingsManager = settingsManager; _settingsManager = settingsManager;
_mobileAppInstallRegistrator = mobileAppInstallRegistrator; _mobileAppInstallRegistrator = mobileAppInstallRegistrator;
@ -132,6 +131,7 @@ public class PortalController : ControllerBase
_mapper = mapper; _mapper = mapper;
_httpContextAccessor = httpContextAccessor; _httpContextAccessor = httpContextAccessor;
_quotaHelper = quotaHelper; _quotaHelper = quotaHelper;
_cspSettingsHelper = cspSettingsHelper;
} }
[AllowNotPayment] [AllowNotPayment]
@ -370,10 +370,13 @@ public class PortalController : ControllerBase
await _apiSystemHelper.AddTenantToCacheAsync(newAlias, user.Id); await _apiSystemHelper.AddTenantToCacheAsync(newAlias, user.Id);
} }
var oldDomain = tenant.GetTenantDomain(_coreSettings);
tenant.Alias = alias; tenant.Alias = alias;
tenant = _tenantManager.SaveTenant(tenant); tenant = _tenantManager.SaveTenant(tenant);
_tenantManager.SetCurrentTenant(tenant); _tenantManager.SetCurrentTenant(tenant);
await _cspSettingsHelper.RenameDomain(oldDomain, tenant.GetTenantDomain(_coreSettings));
if (!string.IsNullOrEmpty(_apiSystemHelper.ApiCacheUrl)) if (!string.IsNullOrEmpty(_apiSystemHelper.ApiCacheUrl))
{ {
await _apiSystemHelper.RemoveTenantFromCacheAsync(oldAlias, user.Id); await _apiSystemHelper.RemoveTenantFromCacheAsync(oldAlias, user.Id);

View File

@ -24,12 +24,6 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0 // content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
using Joonasw.AspNetCore.SecurityHeaders.Csp.Builder;
using Microsoft.Extensions.Caching.Distributed;
using ProtoBuf;
using AuditEventDto = ASC.Web.Api.ApiModel.ResponseDto.AuditEventDto; using AuditEventDto = ASC.Web.Api.ApiModel.ResponseDto.AuditEventDto;
using LoginEventDto = ASC.Web.Api.ApiModel.ResponseDto.LoginEventDto; using LoginEventDto = ASC.Web.Api.ApiModel.ResponseDto.LoginEventDto;
@ -50,9 +44,8 @@ public class SecurityController : ControllerBase
private readonly SettingsManager _settingsManager; private readonly SettingsManager _settingsManager;
private readonly AuditActionMapper _auditActionMapper; private readonly AuditActionMapper _auditActionMapper;
private readonly CoreBaseSettings _coreBaseSettings; private readonly CoreBaseSettings _coreBaseSettings;
private readonly CoreSettings _coreSettings;
private readonly ApiContext _apiContext; private readonly ApiContext _apiContext;
private readonly IDistributedCache _distributedCache; private readonly CspSettingsHelper _cspSettingsHelper;
public SecurityController( public SecurityController(
PermissionContext permissionContext, PermissionContext permissionContext,
@ -66,8 +59,7 @@ public class SecurityController : ControllerBase
AuditActionMapper auditActionMapper, AuditActionMapper auditActionMapper,
CoreBaseSettings coreBaseSettings, CoreBaseSettings coreBaseSettings,
ApiContext apiContext, ApiContext apiContext,
IDistributedCache distributedCache, CspSettingsHelper cspSettingsHelper)
CoreSettings coreSettings)
{ {
_permissionContext = permissionContext; _permissionContext = permissionContext;
_tenantManager = tenantManager; _tenantManager = tenantManager;
@ -80,8 +72,7 @@ public class SecurityController : ControllerBase
_auditActionMapper = auditActionMapper; _auditActionMapper = auditActionMapper;
_coreBaseSettings = coreBaseSettings; _coreBaseSettings = coreBaseSettings;
_apiContext = apiContext; _apiContext = apiContext;
_distributedCache = distributedCache; _cspSettingsHelper = cspSettingsHelper;
_coreSettings = coreSettings;
} }
[HttpGet("audit/login/last")] [HttpGet("audit/login/last")]
@ -280,41 +271,17 @@ public class SecurityController : ControllerBase
} }
[HttpPost("csp")] [HttpPost("csp")]
public object Csp(CspRequestsDto request) public async Task<object> Csp(CspRequestsDto request)
{ {
ArgumentNullException.ThrowIfNull(request); ArgumentNullException.ThrowIfNull(request);
ArgumentNullException.ThrowIfNull(request.Domain, nameof(request.Domain));
var domain = request.Domain; return await _cspSettingsHelper.Save(request.Domains);
var csp = new CspBuilder(); }
csp.ByDefaultAllow [HttpGet("csp")]
.FromSelf(); public CspSettings Csp()
{
csp.AllowScripts return _cspSettingsHelper.Load();
.FromSelf()
.From(domain);
csp.AllowStyles
.FromSelf()
.From(domain);
csp.AllowImages
.FromSelf()
.From(domain);
csp.AllowFraming
.From(domain);
var (_, headerValue) = csp.BuildCspOptions().ToString(null);
using var ms = new MemoryStream();
Serializer.Serialize(ms, headerValue);
_distributedCache.Set($"csp:{_tenantManager.GetCurrentTenant().GetTenantDomain(_coreSettings)}", Encoding.UTF8.GetBytes(headerValue));
return headerValue;
} }
private void DemandAuditPermission() private void DemandAuditPermission()

View File

@ -325,9 +325,9 @@ public class SettingsController : BaseSettingsController
} }
[HttpPut("dns")] [HttpPut("dns")]
public object SaveDnsSettings(DnsSettingsRequestsDto model) public async Task<object> SaveDnsSettings(DnsSettingsRequestsDto model)
{ {
return _dnsSettings.SaveDnsSettings(model.DnsName, model.Enable); return await _dnsSettings.SaveDnsSettings(model.DnsName, model.Enable);
} }
[HttpGet("recalculatequota")] [HttpGet("recalculatequota")]

View File

@ -28,5 +28,5 @@ namespace ASC.Web.Api.ApiModels.RequestsDto;
public class CspRequestsDto public class CspRequestsDto
{ {
public string Domain { get; set; } public IEnumerable<string> Domains { get; set; }
} }

View File

@ -0,0 +1,136 @@
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.Web.Api.Core;
public class CspSettings : ISettings<CspSettings>
{
[JsonIgnore]
public Guid ID => new Guid("27504162-16FF-405F-8530-1537B0F2B89D");
public IEnumerable<string> Domains { get; set; }
public CspSettings GetDefault()
{
return new CspSettings();
}
}
[Scope]
public class CspSettingsHelper
{
private readonly SettingsManager _settingsManager;
private readonly FilesLinkUtility _filesLinkUtility;
private readonly TenantManager _tenantManager;
private readonly CoreSettings _coreSettings;
private readonly IDistributedCache _distributedCache;
public CspSettingsHelper(
SettingsManager settingsManager,
FilesLinkUtility filesLinkUtility,
TenantManager tenantManager,
CoreSettings coreSettings,
IDistributedCache distributedCache)
{
_settingsManager = settingsManager;
_filesLinkUtility = filesLinkUtility;
_tenantManager = tenantManager;
_coreSettings = coreSettings;
_distributedCache = distributedCache;
}
public async Task<string> Save(IEnumerable<string> domains)
{
var headerKey = GetKey(_tenantManager.GetCurrentTenant().GetTenantDomain(_coreSettings));
var headerValue = "";
if (domains != null && domains.Any())
{
var csp = new CspBuilder();
csp.ByDefaultAllow
.FromSelf()
.From(_filesLinkUtility.DocServiceUrl);
var scriptBuilder = csp.AllowScripts
.FromSelf()
.From(_filesLinkUtility.DocServiceUrl)
.AllowUnsafeInline();
var styleBuilder = csp.AllowStyles
.FromSelf()
.AllowUnsafeInline();
var imageBuilder = csp.AllowImages
.FromSelf();
var frameBuilder = csp.AllowFraming;
foreach (var domain in domains)
{
scriptBuilder.From(domain);
styleBuilder.From(domain);
imageBuilder.From(domain);
frameBuilder.From(domain);
}
(_, headerValue) = csp.BuildCspOptions().ToString(null);
await _distributedCache.SetStringAsync(headerKey, headerValue);
}
else
{
await _distributedCache.RemoveAsync(headerKey);
}
var current = _settingsManager.Load<CspSettings>();
current.Domains = domains;
_settingsManager.Save(current);
return headerValue;
}
public CspSettings Load()
{
return _settingsManager.Load<CspSettings>();
}
public async Task RenameDomain(string oldDomain, string newDomain)
{
var oldKey = GetKey(oldDomain);
var val = await _distributedCache.GetStringAsync(oldKey);
if (!string.IsNullOrEmpty(val))
{
await _distributedCache.RemoveAsync(oldKey);
await _distributedCache.SetStringAsync(GetKey(newDomain), val);
}
}
private string GetKey(string domain)
{
return $"csp:{domain}";
}
}

View File

@ -37,6 +37,7 @@ public class DnsSettings
private readonly StudioNotifyService _studioNotifyService; private readonly StudioNotifyService _studioNotifyService;
private readonly CommonLinkUtility _commonLinkUtility; private readonly CommonLinkUtility _commonLinkUtility;
private readonly MessageService _messageService; private readonly MessageService _messageService;
private readonly CspSettingsHelper _cspSettingsHelper;
public DnsSettings( public DnsSettings(
PermissionContext permissionContext, PermissionContext permissionContext,
@ -46,7 +47,8 @@ public class DnsSettings
CoreSettings coreSettings, CoreSettings coreSettings,
StudioNotifyService studioNotifyService, StudioNotifyService studioNotifyService,
CommonLinkUtility commonLinkUtility, CommonLinkUtility commonLinkUtility,
MessageService messageService) MessageService messageService,
CspSettingsHelper cspSettingsHelper)
{ {
_permissionContext = permissionContext; _permissionContext = permissionContext;
_tenantManager = tenantManager; _tenantManager = tenantManager;
@ -56,9 +58,10 @@ public class DnsSettings
_studioNotifyService = studioNotifyService; _studioNotifyService = studioNotifyService;
_commonLinkUtility = commonLinkUtility; _commonLinkUtility = commonLinkUtility;
_messageService = messageService; _messageService = messageService;
_cspSettingsHelper = cspSettingsHelper;
} }
public string SaveDnsSettings(string dnsName, bool enableDns) public async Task<string> SaveDnsSettings(string dnsName, bool enableDns)
{ {
try try
{ {
@ -80,8 +83,12 @@ public class DnsSettings
{ {
if (_coreBaseSettings.Standalone) if (_coreBaseSettings.Standalone)
{ {
var oldDomain = tenant.GetTenantDomain(_coreSettings);
tenant.MappedDomain = dnsName; tenant.MappedDomain = dnsName;
_tenantManager.SaveTenant(tenant); _tenantManager.SaveTenant(tenant);
await _cspSettingsHelper.RenameDomain(oldDomain, tenant.GetTenantDomain(_coreSettings));
return null; return null;
} }

View File

@ -145,17 +145,22 @@ global using AutoMapper;
global using Google.Authenticator; global using Google.Authenticator;
global using Joonasw.AspNetCore.SecurityHeaders.Csp.Builder;
global using MailKit.Security; global using MailKit.Security;
global using Microsoft.AspNetCore.Authorization; global using Microsoft.AspNetCore.Authorization;
global using Microsoft.AspNetCore.Mvc; global using Microsoft.AspNetCore.Mvc;
global using Microsoft.EntityFrameworkCore; global using Microsoft.EntityFrameworkCore;
global using Microsoft.Extensions.Caching.Distributed;
global using Microsoft.Extensions.Caching.Memory; global using Microsoft.Extensions.Caching.Memory;
global using Microsoft.Extensions.Hosting.WindowsServices; global using Microsoft.Extensions.Hosting.WindowsServices;
global using Microsoft.Extensions.Primitives; global using Microsoft.Extensions.Primitives;
global using MimeKit; global using MimeKit;
global using ProtoBuf;
global using static ASC.ActiveDirectory.Base.Settings.LdapSettings; global using static ASC.ActiveDirectory.Base.Settings.LdapSettings;
global using static ASC.Security.Cryptography.EmailValidationKeyProvider; global using static ASC.Security.Cryptography.EmailValidationKeyProvider;