Load Balanced - how to block access to the "front end" servers.
s
If I have (umb v10); admin.mydomain.com <- scheduled publisher - Umbraco back end mydomain.com <- subscriber(s) - front end only. How do I block access to the Umbraco backoffice - users are used to going via mydomain.com/umbraco I know I've seen this on an article / document somewhere - just can't find it again!
m
https://medium.com/@dev.mjdhanesh/configuring-umbraco-v8-load-balancer-543971d29f32 v8 but think the rules still apply.. I'm using this in a
web.Subscriber.config
with the environment name
Subscriber
Copy code
xml
        <!-- Restrict access to Umbraco -->
        <rule name="Restrict access" stopProcessing="true" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing">
            <match url="umbraco$|umbraco/(?!surface\/)(?!api\/)(?!webservices\/)" />
            <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
                <add input="{HTTP_HOST}" pattern="^{publisher.url}" negate="true" />
            </conditions>
            <action type="Redirect" url="{publisher.url}" appendQueryString="false" />
        </rule>
This is only IIS/IISExpress hosted solution.. won't show in Kestrel if using for Development. 🙂
web.config in asp.net core.. (did have some issues with publish no longer generating the handler when moving from .net 5 -> 6 hence the hardcoding nd also %LAUNCHER_PATH% not working) process path is your assembly name.
Copy code
xml
<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <system.webServer>
        <!-- this is injected during dotnet publish - leaving here incase issues arise.
        <handlers>
            <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
        </handlers>
        <aspNetCore processPath=".\www.exe" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess">
            <environmentVariables>
                <environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Development" />
            </environmentVariables>
        </aspNetCore>
        or:
        <aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" />
        
        <handlers>-->
        <handlers>
            <remove name="aspNetCore" />
            <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
        </handlers>
        <aspNetCore processPath=".\www.exe" arguments="" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="InProcess" />
                <httpProtocol>
                    <customHeaders>
                        <remove name="X-Powered-By" />
                    </customHeaders>
                </httpProtocol>
        <security>
            <requestFiltering removeServerHeader="true" />
        </security>
        <system.webServer>
</configuration>
j
The most efficient way to do this is to not call
UseBackOffice()
and
UseBackOfficeEndpoints()
on "frontend-only" servers, this doesn't just prevent it being served but also stops the feature from starting at all - saving resources. How you choose to selectively do that is really up to you - we have an appsetting that we check for at boot:
Copy code
csharp
var backofficeEnabled = ... //your logic here

app.UseUmbraco()
    .WithMiddleware(u =>
    {
        if (backofficeEnabled)
        {
            u.UseBackOffice();
        }
        u.UseWebsite();
    })
    .WithEndpoints(u =>
    {
        u.UseInstallerEndpoints();
        if (backofficeEnabled)
        {
            u.UseBackOfficeEndpoints();
        }
        u.UseWebsiteEndpoints();
    });
m
Would have thought that environmentnames are already set for other appsettings that should be changed depending on the serverRole? So
if (env.EnvironmentName != "{subscriberEnvironmentName}"){}
Curios as to what you are gaining by an appsetting? Also almost begs the question as to why it's not core to disable the backoffice for the IServerRoleAccessor role of
ServerRole.Subscriber
?
j
Yeah, when I say appsettings these days I really mean the config/options API regardless of what's driving it. I generally wouldn't use an already extant setting/variable though as you never know when you might need to get into the backoffice. A dedicated setting means you've got the option to turn the backoffice on if you need to without promoting it to the scheduling server. And that's partly why they're not automatically linked to the ServerRoleAccessor as the backoffice is for more than just publishing.
s
I take it the usebackoffice doesn't affect /umbraco/api calls
m
Isn't it a bit though like... it's a production server, so productiontRole is set.. so no editing of templates.. So if subscriber server then we shouldn't have the backoffice and being guided by bestpractice? And have a mechanism to override? I guess you could be wanting backoffice access to subscriber boxes for an admin to say review the logs (if not centralised 😉 ) health checks, or use godmode to check configuration? But whatever it is as its a subscriber it should be readonly access? And are we talking our way back from disable backoffice/endpoints now 😉
if those are your frontend autogenerated apicontrollers.. then I'd suggest alter the route to be just
/api/
(Forgive me if I'm wrong now.. but all that UmbracoApiController adds is that autorouting, which we don't want?)
Copy code
csharp
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Lovel.Code.Models;
using Umbraco.Cms.Web.Common;

namespace Code.Controllers
{
    [Route("api/devinfo")]
    public class DevInfoController : Controller
    {
        private readonly ILogger _logger;
        private readonly UmbracoHelper _umbracoHelper;

        public DevInfoController(ILogger<DevInfoController> logger, UmbracoHelper umbracoHelper)
        {
            _logger = logger;
            _umbracoHelper = umbracoHelper;
        }

        [HttpGet]
        [Route("getdevbox/{id:int}")]
        public IActionResult? GetDevBox(int id)
        {
            // return a partial view with the IPublishedContent
            if (_umbracoHelper.Content(id) is DevelopmentPage node)
j
Exactly, logs (centralised is great, until there's a problem with the logging platform), config check, even gasp doing an emergency publish because there's a problem with the admin instance. Having that little bit of extra flexibility has been really useful to me to me in the past... but there's nothing stopping you from having a default/override/fallback. As I said, the logic is up to you - and doesn't really matter.
m
So if didn't want the reliance on an admin to renable the backoffice via appsetting.. would the fall back be to the urlrewriting? Or would you still advocate the middleware and maybe checking for ip with the additional httpcontext injected?
14 Views