<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Stijn De Vos | Sitecore blog</title>
    <link>https://stijndevos.net/</link>
    <description>Lead expert consultant @ delaware. Working with Sitecore, Azure, Kubernetes.</description>
    <pubDate>Fri, 10 Apr 2026 23:59:00 +0000</pubDate>
    <item>
      <title>&#34;SSC API key is required&#34; on Sitecore K8s</title>
      <link>https://stijndevos.net/ssc-api-key-is-required-on-sitecore-k8s?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[When trying to connect to Sitecore (hosted in AKS) from my Next.JS head (hosted externally), I got following error:&#xA;&#xA;  SSC API key is required. Pass with &#39;scapikey&#39; query string or HTTP header.&#xA;&#xA;The code that caused the issue was a textbook GraphQL request:&#xA;&#xA;const graphQLClient = new GraphQLRequestClient(config.graphQLEndpoint, {&#xA;    apiKey: config.sitecoreApiKey,&#xA;});&#xA;&#xA;Turns out nginx (that connects incoming AKS traffic to the actual Sitecore containers) does not allow underscores in header keys by default:&#xA;&#xA;http://nginx.org/en/docs/http/ngxhttpcoremodule.html#underscoresinheaders&#xA;&#xA;This case (different use case though) is documented in SitecoreXP1020ProductionDeploymentWithKubernetes-en.pdf, see&#xA;&#xA;  4.6.3. When I request SSC, problems occur if there are underscores in header names&#xA;&#xA;but since that document is not indexed by Google, finding it was not that easy.&#xA;&#xA;Luckily somebody had a similar issue when running Sitecore on Amazon: https://github.com/Sitecore/jss/issues/1060.&#xA;&#xA;The solution for k8s is straightforward. Simply add&#xA;&#xA;enable-underscores-in-headers: &#34;true&#34;&#xA;&#xA;to ingress-nginx\\configuration.yaml (the configmap used by nginx).&#xA;&#xA;Note that if you install this with kubectl -k ingress-nginx this requires nginx and Sitecore to run in the same namespace. If not, the config map is created in the Sitecore namespace, but won’t be picked up by nginx.]]&gt;</description>
      <content:encoded><![CDATA[<p>When trying to connect to Sitecore (hosted in AKS) from my Next.JS head (hosted externally), I got following error:</p>

<blockquote><p>SSC API key is required. Pass with &#39;sc_apikey&#39; query string or HTTP header.</p></blockquote>

<p>The code that caused the issue was a textbook GraphQL request:</p>

<pre><code>const graphQLClient = new GraphQLRequestClient(config.graphQLEndpoint, {
    apiKey: config.sitecoreApiKey,
});
</code></pre>

<p>Turns out nginx (that connects incoming AKS traffic to the actual Sitecore containers) does not allow underscores in header keys by default:</p>

<p><a href="http://nginx.org/en/docs/http/ngx_http_core_module.html#underscores_in_headers">http://nginx.org/en/docs/http/ngx_http_core_module.html#underscores_in_headers</a></p>

<p>This case (different use case though) is documented in <em>Sitecore</em>XP<em>10</em>2<em>0</em>Production<em>Deployment</em>With<em>Kubernetes-en.pdf</em>, see</p>

<blockquote><p>4.6.3. When I request SSC, problems occur if there are underscores in header names</p></blockquote>

<p>but since that document is not indexed by Google, finding it was not that easy.</p>

<p>Luckily somebody had a similar issue when running Sitecore on Amazon: <a href="https://github.com/Sitecore/jss/issues/1060">https://github.com/Sitecore/jss/issues/1060</a>.</p>

<p>The solution for k8s is straightforward. Simply add</p>

<pre><code>enable-underscores-in-headers: &#34;true&#34;
</code></pre>

<p>to <em>ingress-nginx\configuration.yaml</em> (the configmap used by nginx).</p>

<p><img src="https://i.snap.as/QW9uo2Sb.png" alt=""/></p>

<p>Note that if you install this with <em>kubectl -k ingress-nginx</em> this requires nginx and Sitecore to run in the same namespace. If not, the config map is created in the Sitecore namespace, but won’t be picked up by nginx.</p>
]]></content:encoded>
      <guid>https://stijndevos.net/ssc-api-key-is-required-on-sitecore-k8s</guid>
      <pubDate>Wed, 15 Jun 2022 11:05:37 +0000</pubDate>
    </item>
    <item>
      <title>Integrating Sitecore Identity with Azure AD on Kubernetes</title>
      <link>https://stijndevos.net/integrating-sitecore-identity-with-azure-ad-kubernetes-style?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[Sitecore Identity (SI), introduced in Sitecore 9.1, is the single sign-on mechanism for any Sitecore instance (XM, XP, XC, …) that requires authentication.&#xA;&#xA;As it was provided in a web deploy package (WDP), making small config changes like integrating with Azure AD was straightforward. With Sitecore moving to containers, and SI thus being provided as container image, making these same config changes becomes more complex… unless you’re willing to throw some Kubernetes features at it.&#xA;&#xA;!--more--&#xA;&#xA;Sitecore Identity is an OpenID Connect compliant security token service (STS), based on IdentityServer4. Its default setup is an OAuth wrapper around good old Sitecore Membership:&#xA;&#xA;As an unauthenticated user that tries to access Sitecore CM, you are redirected to SI.&#xA;On SI you can authenticate with your Sitecore credentials (username and password, stored in the security database).&#xA;On successful authentication, SI redirects you back to Sitecore CM with a security token.&#xA;With this security token, you can now access Sitecore CM (and potential other applications behind Sitecore security).&#xA;&#xA;Because it’s based on IdentityServer4, you can use SI as a gateway to one or more external identity providers (or subproviders, sometimes also called inner providers). For example, Azure AD:&#xA;&#xA;As an unauthenticated user that tries to access Sitecore CM, you are redirected to SI.&#xA;Instead of using Sitecore Membership, SI now offloads authentication to Azure AD: you are redirected to Azure, where you authenticate with your Azure AD account.&#xA;On successful authentication, SI redirects you back to Sitecore CM with a security token, now based on information from your Azure AD account.&#xA;With this security token, you can now access Sitecore CM (and potential other applications behind Sitecore security).&#xA;&#xA;This allows business users to log into Sitecore CM using their corporate (Azure AD) account.&#xA;&#xA;Configuring SI to integrate with Azure AD is supported out-of-the-box. You need to&#xA;&#xA;Register Sitecore as an Application in your Azure AD tenant&#xA;&#xA;Create groups in your Azure AD for the users you want to give access to the Sitecore back-end&#xA;&#xA;Configure SI to connect to that Azure AD application and add a user mapping that specifies the role (e.g. Sitecore\\admin) for a specified Azure AD group&#xA;&#xA;You find the official documentation on Use the Sitecore Identity server as a federation gateway, but I recommend Setting Up Azure Active Directory Integration with Sitecore Identity Server / Sitecore 9.1 (derekc.net) for a more detailed walkthrough of this setup.&#xA;&#xA;Deploying SI with a Web Deploy Package&#xA;&#xA;Before containers, changing the configuration was straightforward: SI is provided by Sitecore as a WDP (web deploy package — see Sitecore Downloads: Sitecore Identity 600). A structured folder containing DLLs, config XML files, … that you can deploy to IIS (virtual machine, Azure App Service, …) using Web Deploy.&#xA;&#xA;Change the config files, deploy the updated package and you’re done.&#xA;&#xA;Deploying SI with a container image&#xA;&#xA;Next to WDP, SI is also provided as a container image. For example, scr.sitecore.com/sxp/sitecore-id:10.2-ltsc2019&#xA;&#xA;Compared to changing a WDP zip file, creating a custom container image requires more skills and infrastructure:&#xA;&#xA;you need to create a DockerFile that takes an official Sitecore ID image as a base image and specify which custom files (the modified config files) you will patch over it to create your own custom Sitecore ID&#xA;you need to build the image (build server?) and push it to a container registry (acr?)&#xA;you need to change your deployment (k8s specs) to use the modified image&#xA;&#xA;None of this is rocket science, but it’s quite some work to change a simple config file.&#xA;&#xA;Configuring SI with Kubernetes ConfigMap&#xA;&#xA;With the custom container image approach, you first copy modified files over an original image. Then these modifications are baked into a new image that you deploy to Kubernetes. Kubernetes uses an abstraction to run this image on actual hardware.&#xA;&#xA;Thanks to the abstraction layer, it’s possible for Kubernetes to hijack the files an image tries to access while it’s running: in the id.yaml k8s spec file provided by Sitecore, you add a volumeMount, which tells the runtime that when image wants a file from /Identity/sitecore/Sitecore.Plugin.IdentityProvider.AzureAd/Config/ it should first be search in a volume called config. The config volume forwards this to a configMap called sitecore-id-azuread-config (an alternative would be an actual storage).&#xA;&#xA;A Configmaps is an API object in Kubernetes used to store data in:&#xA;&#xA;  A ConfigMap allows you to decouple environment-specific configuration from your container images, so that your applications are easily portable.&#xA;&#xA;In our case, we can simply put the whole custom configuration file in it:&#xA;&#xA;With this setup it’s possible to run Sitecore’s official SI image with a custom Identity/sitecore/Sitecore.Plugin.IdentityProvider.AzureAd/Config/Sitecore.Plugin.IdentityProvider.AzureAd.xml_ file, containing your project specific Azure AD configuration. No need to create a custom image for it!&#xA;&#xA;Tags: #kubernetes, #sitecoreidentity]]&gt;</description>
      <content:encoded><![CDATA[<p>Sitecore Identity (SI), introduced in Sitecore 9.1, is the single sign-on mechanism for any Sitecore instance (XM, XP, XC, …) that requires authentication.</p>

<p>As it was provided in a web deploy package (WDP), making small config changes like integrating with Azure AD was straightforward. With Sitecore moving to containers, and SI thus being provided as container image, making these same config changes becomes more complex… unless you’re willing to throw some Kubernetes features at it.</p>



<p>Sitecore Identity is an OpenID Connect compliant security token service (STS), based on <a href="https://github.com/IdentityServer/IdentityServer4">IdentityServer4</a>. Its default setup is an OAuth wrapper around good old Sitecore Membership:</p>
<ul><li>As an unauthenticated user that tries to access Sitecore CM, you are redirected to SI.</li>
<li>On SI you can authenticate with your Sitecore credentials (username and password, stored in the <em>security</em> database).</li>
<li>On successful authentication, SI redirects you back to Sitecore CM with a security token.</li>
<li>With this security token, you can now access Sitecore CM (and potential other applications behind Sitecore security).</li></ul>

<p>Because it’s based on IdentityServer4, you can use SI as a gateway to one or more external identity providers (or subproviders, sometimes also called <em>inner providers</em>). For example, Azure AD:</p>
<ul><li><em>As an unauthenticated user that tries to access Sitecore CM, you are redirected to SI.</em></li>
<li>Instead of using Sitecore Membership, SI now offloads authentication to Azure AD: you are redirected to Azure, where you authenticate with your Azure AD account.</li>
<li>On successful authentication, SI redirects you back to Sitecore CM with a security token, now based on information from your Azure AD account.</li>
<li><em>With this security token, you can now access Sitecore CM (and potential other applications behind Sitecore security).</em></li></ul>

<p>This allows business users to log into Sitecore CM using their corporate (Azure AD) account.</p>

<p>Configuring SI to integrate with Azure AD is supported out-of-the-box. You need to</p>
<ol><li><p>Register Sitecore as an Application in your Azure AD tenant</p></li>

<li><p>Create groups in your Azure AD for the users you want to give access to the Sitecore back-end</p></li>

<li><p>Configure SI to connect to that Azure AD application and add a user mapping that specifies the role (e.g. Sitecore\admin) for a specified Azure AD group</p></li></ol>

<p>You find the official documentation on <a href="https://doc.sitecore.com/xp/en/developers/102/sitecore-experience-manager/use-the-sitecore-identity-server-as-a-federation-gateway.html">Use the Sitecore Identity server as a federation gateway</a>, but I recommend <a href="https://sitecore.derekc.net/setting-up-azure-active-directory-integration-with-sitecore-identity-server-sitecore-9-1/">Setting Up Azure Active Directory Integration with Sitecore Identity Server / Sitecore 9.1 (derekc.net)</a> for a more detailed walkthrough of this setup.</p>

<h1 id="deploying-si-with-a-web-deploy-package" id="deploying-si-with-a-web-deploy-package">Deploying SI with a Web Deploy Package</h1>

<p>Before containers, changing the configuration was straightforward: SI is provided by Sitecore as a WDP (web deploy package — see <a href="https://dev.sitecore.net/Downloads/Sitecore_Identity/6x/Sitecore_Identity_600.aspx">Sitecore Downloads: Sitecore Identity 600</a>). A structured folder containing DLLs, config XML files, … that you can deploy to IIS (virtual machine, Azure App Service, …) using Web Deploy.</p>

<p>Change the config files, deploy the updated package and you’re done.</p>

<h1 id="deploying-si-with-a-container-image" id="deploying-si-with-a-container-image">Deploying SI with a container image</h1>

<p>Next to WDP, SI is also provided as a container image. For example, <em>scr.sitecore.com/sxp/sitecore-id:10.2-ltsc2019</em></p>

<p>Compared to changing a WDP zip file, creating a custom container image requires more skills and infrastructure:</p>
<ul><li>you need to create a DockerFile that takes an official Sitecore ID image as a base image and specify which custom files (the modified config files) you will patch over it to create your own custom Sitecore ID</li>
<li>you need to build the image (build server?) and push it to a container registry (acr?)</li>
<li>you need to change your deployment (k8s specs) to use the modified image</li></ul>

<p>None of this is rocket science, but it’s quite some work to change a simple config file.</p>

<h1 id="configuring-si-with-kubernetes-configmap" id="configuring-si-with-kubernetes-configmap">Configuring SI with Kubernetes ConfigMap</h1>

<p>With the custom container image approach, you first copy modified files over an original image. Then these modifications are baked into a new image that you deploy to Kubernetes. Kubernetes uses an abstraction to run this image on actual hardware.</p>

<p>Thanks to the abstraction layer, it’s possible for Kubernetes to hijack the files an image tries to access while it’s running: in the <strong>id.yaml</strong> k8s spec file provided by Sitecore, you add a <strong>volumeMount</strong>, which tells the runtime that when image wants a file from <em>/Identity/sitecore/Sitecore.Plugin.IdentityProvider.AzureAd/Config/</em> it should first be search in a volume called <strong>config</strong>. The <strong>config</strong> volume forwards this to a <strong>configMap</strong> called <em>sitecore-id-azuread-config</em> (an alternative would be an actual storage).</p>

<p><img src="https://i.snap.as/5pcfXL5E.png" alt=""/></p>

<p>A <a href="https://kubernetes.io/docs/concepts/configuration/configmap/">Configmaps</a> is an API object in Kubernetes used to store data in:</p>

<blockquote><p>A ConfigMap allows you to decouple environment-specific configuration from your container images, so that your applications are easily portable.</p></blockquote>

<p>In our case, we can simply put the whole custom configuration file in it:</p>

<p><img src="https://i.snap.as/PooulMR2.png" alt=""/></p>

<p>With this setup it’s possible to run Sitecore’s official SI image with a custom <em>Identity/sitecore/Sitecore.Plugin.IdentityProvider.AzureAd/Config/Sitecore.Plugin.IdentityProvider.AzureAd.xml</em> file, containing your project specific Azure AD configuration. No need to create a custom image for it!</p>

<p>Tags: <a href="https://stijndevos.net/tag:kubernetes" class="hashtag"><span>#</span><span class="p-category">kubernetes</span></a>, <a href="https://stijndevos.net/tag:sitecoreidentity" class="hashtag"><span>#</span><span class="p-category">sitecoreidentity</span></a></p>
]]></content:encoded>
      <guid>https://stijndevos.net/integrating-sitecore-identity-with-azure-ad-kubernetes-style</guid>
      <pubDate>Fri, 14 Jan 2022 15:38:20 +0000</pubDate>
    </item>
    <item>
      <title>Sitecore ingress gives 404 (AKS / K8s   = 1.19)</title>
      <link>https://stijndevos.net/sitecore-ingress-gives-404-on-aks-k8s-1-19?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[Recently, I was refactoring my Azure DevOps CI/CD scripts that setup and deploy Sitecore 10.1 on a newly created AKS. Deployment went smooth, until I tried to browse to the deployed environments. Nginx, configured with sitecore-ingress provided by Sitecore, returned a 404 Not Found. What gives?&#xA;&#xA;!--more--&#xA;&#xA;Where’s the error?&#xA;&#xA;The logical thing to do is start looking for errors. Unfortunately, the nginx-ingress-ingress-nginx-controller pods did not show any (they did not show anything at all). Perhaps running them in debug mode could have told me more, but my experience with nginx is limited (close to none actually :)), so I postponed figuring out how to do that.&#xA;&#xA;The only pointer I had was my browser. The HTTP response did not show anything special, but the insecure https indication was odd. I used a self-signed certificate that worked in the past without any issues. Inspecting the details shows a strange issuer: Kubernetes Ingress Controller Fake Certificate. That can’t be right.&#xA;&#xA;After a quick google, I landed on a Microsoft documentation site (Use your TLS certificates for ingress - Azure Kubernetes Service | Microsoft Docs), telling me the certificate and configured ingress route are not compatible in a way.&#xA;&#xA;But, as said, I was simply using the ingress configuration provided by Sitecore.&#xA;&#xA;What changed in my scripts?&#xA;&#xA;The scripts I used had been working before, so it had to be in the details. One thing I explicitly changed in my script was setting AKS to a more recent version of Kubernetes.&#xA;&#xA;Sitecore’s Installation Guide for Production Environment with Kubernetes (Sitecore 10.1) does not specify an upper bound of the version, so this is an acceptable change:&#xA;&#xA;  An AKS cluster configured with the latest stable release of Kubernetes – version 1.16.x or later.&#xA;    For startup probes to check whether the Sitecore software container has started successfully, Kubernetes version 1.18.x or later is required.&#xA;&#xA;The most popular GitHub examples, which I based myself on for previous -working- setups, use an older version:&#xA;&#xA;Sitecore-Symposium-2020-Containers-AKS/2.CreateAKS.ps1 at main · bplasmeijer/Sitecore-Symposium-2020-Containers-AKS (github.com)&#xA;paas-to-aks/create-aks.ps1 at main · robhabraken/paas-to-aks (github.com)&#xA;&#xA;One thing that always bothered during previous setups was the warning / usage of ingress beta:&#xA;&#xA;  Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress&#xA;&#xA;According to the mentioned versions, it should still work. But with experienced 404 / certificate issues, I thought I try to upgrade it and see what happens.&#xA;&#xA;Upgrading ingress to solve the issue (sort of)&#xA;&#xA;Upgrading is rather straightforward (see end of post for full spec).&#xA;&#xA;First you change the apiVersion from extensions/v1beta1 to  networking.k8s.io/v1.&#xA;&#xA;And make the specs a bit more structured / verbose.&#xA;&#xA;spec:&#xA;  rules:&#xA;  - host: cd.globalhost&#xA;    http:&#xA;      paths:&#xA;      - path: /&#xA;        backend:&#xA;          serviceName: cd&#xA;          servicePort: 80&#xA;&#xA;becomes&#xA;&#xA;spec:&#xA;  rules:&#xA;  - host: cd.globalhost&#xA;    http:&#xA;      paths:&#xA;      - path: /&#xA;        pathType: Prefix&#xA;        backend:&#xA;          service:&#xA;            name: cd&#xA;            port: &#xA;              number: 80&#xA;&#xA;And, the most crucial part of all, you need to add ingressClassName: nginx to the spec (e.g. right below rules).&#xA;&#xA;See Basic usage - NGINX Ingress Controller (kubernetes.github.io) for full details.&#xA;&#xA;After making the changes, Sitecore URLs resolve as expected.&#xA;&#xA;Conclusion&#xA;&#xA;The ingressClassName: nginx change turned out to be the root cause of my problem. According to its documentation, it’s required for K8s   = 1.19, which is exactly the change I made.&#xA;&#xA;Note that adding this to Sitecore’s ingress spec (apiVersion extensions/v1beta1) fixes the issue as well. So the warning unavailable in v1.22+ is correct, but there are extra versioning dependencies to take into account.&#xA;&#xA;Tags: #aks, #kubernetes, #ngnix.&#xA;&#xA;Full spec&#xA;&#xA;For those interested, this is updated ingress-nginx/ingress.yaml&#xA;&#xA;apiVersion: networking.k8s.io/v1&#xA;kind: Ingress&#xA;metadata:&#xA;  name: sitecore-ingress&#xA;  annotations:&#xA;    nginx.ingress.kubernetes.io/proxy-buffer-size: &#34;32k&#34;&#xA;    nginx.ingress.kubernetes.io/affinity: &#34;cookie&#34;&#xA;    nginx.ingress.kubernetes.io/rewrite-target: /&#xA;    nginx.ingress.kubernetes.io/proxy-connect-timeout: &#34;600&#34;&#xA;    nginx.ingress.kubernetes.io/proxy-read-timeout: &#34;600&#34;&#xA;    nginx.ingress.kubernetes.io/proxy-send-timeout: &#34;600&#34;&#xA;    nginx.ingress.kubernetes.io/proxy-body-size: &#34;512m&#34;&#xA;spec:&#xA;  rules:&#xA;  host: cd.globalhost&#xA;    http:&#xA;      paths:&#xA;      path: /&#xA;        pathType: Prefix&#xA;        backend:&#xA;          service:&#xA;            name: cd&#xA;            port: &#xA;              number: 80&#xA;  host: cm.globalhost&#xA;    http:&#xA;      paths:&#xA;      path: /&#xA;        pathType: Prefix&#xA;        backend:&#xA;          service:&#xA;            name: cm&#xA;            port:&#xA;              number: 80&#xA;  host: id.globalhost&#xA;    http:&#xA;      paths:&#xA;      path: /&#xA;        pathType: Prefix&#xA;        backend:&#xA;          service:&#xA;            name: id&#xA;            port:&#xA;              number: 80&#xA;  ingressClassName: nginx&#xA;  tls:&#xA;  secretName: global-cd-tls&#xA;    hosts:&#xA;    cd.globalhost&#xA;  secretName: global-cm-tls&#xA;    hosts:&#xA;    cm.globalhost&#xA;  secretName: global-id-tls&#xA;    hosts:&#xA;    id.globalhost&#xA;`]]&gt;</description>
      <content:encoded><![CDATA[<p>Recently, I was refactoring my Azure DevOps CI/CD scripts that setup and deploy Sitecore 10.1 on a newly created AKS. Deployment went smooth, until I tried to browse to the deployed environments. Nginx, configured with sitecore-ingress provided by Sitecore, returned a 404 Not Found. What gives?</p>

<p><img src="https://i.snap.as/lrLlpFTe.png" alt=""/></p>



<h1 id="where-s-the-error" id="where-s-the-error">Where’s the error?</h1>

<p>The logical thing to do is start looking for errors. Unfortunately, the <em>nginx-ingress-ingress-nginx-controller</em> pods did not show any (they did not show anything at all). Perhaps running them in debug mode could have told me more, but my experience with nginx is limited (close to none actually :)), so I postponed figuring out how to do that.</p>

<p>The only pointer I had was my browser. The HTTP response did not show anything special, but the <em>insecure https</em> indication was odd. I used a self-signed certificate that worked in the past without any issues. Inspecting the details shows a strange issuer: <strong>Kubernetes Ingress Controller Fake Certificate</strong>. That can’t be right.</p>

<p><img src="https://i.snap.as/p15Xl2TF.png" alt=""/></p>

<p>After a quick google, I landed on a Microsoft documentation site (<a href="https://docs.microsoft.com/en-us/azure/aks/ingress-own-tls">Use your TLS certificates for ingress – Azure Kubernetes Service | Microsoft Docs</a>), telling me the certificate and configured ingress route are not compatible in a way.</p>

<p><img src="https://i.snap.as/Pd4leRbo.png" alt=""/></p>

<p>But, as said, I was simply using the ingress configuration provided by Sitecore.</p>

<h1 id="what-changed-in-my-scripts" id="what-changed-in-my-scripts">What changed in my scripts?</h1>

<p>The scripts I used had been working before, so it had to be in the details. One thing I explicitly changed in my script was setting AKS to a more recent version of Kubernetes.</p>

<p>Sitecore’s <a href="https://sitecoredev.azureedge.net/~/media/AF34449136CD4AA79921EB6F7BEEFFF7.ashx?date=20210920T195203">Installation Guide for Production Environment with Kubernetes (Sitecore 10.1)</a> does not specify an upper bound of the version, so this is an acceptable change:</p>

<blockquote><p>An AKS cluster configured with the latest stable release of Kubernetes – version 1.16.x or later.</p>

<p>For startup probes to check whether the Sitecore software container has started successfully, Kubernetes version 1.18.x or later is required.</p></blockquote>

<p>The most popular GitHub examples, which I based myself on for previous -working- setups, use an older version:</p>
<ul><li><a href="https://github.com/bplasmeijer/Sitecore-Symposium-2020-Containers-AKS/blob/main/2.CreateAKS.ps1">Sitecore-Symposium-2020-Containers-AKS/2.CreateAKS.ps1 at main · bplasmeijer/Sitecore-Symposium-2020-Containers-AKS (github.com)</a></li>
<li><a href="https://github.com/robhabraken/paas-to-aks/blob/main/azure/scripts/create-aks.ps1">paas-to-aks/create-aks.ps1 at main · robhabraken/paas-to-aks (github.com)</a></li></ul>

<p>One thing that always bothered during previous setups was the warning / usage of ingress beta:</p>

<blockquote><p>Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress</p></blockquote>

<p>According to the mentioned versions, it should still work. But with experienced 404 / certificate issues, I thought I try to upgrade it and see what happens.</p>

<h1 id="upgrading-ingress-to-solve-the-issue-sort-of" id="upgrading-ingress-to-solve-the-issue-sort-of">Upgrading ingress to solve the issue (sort of)</h1>

<p>Upgrading is rather straightforward (see end of post for full spec).</p>

<p>First you change the apiVersion from <code>extensions/v1beta1</code>to  <code>networking.k8s.io/v1.</code></p>

<p>And make the specs a bit more structured / verbose.</p>

<pre><code>spec:
  rules:
  - host: cd.globalhost
    http:
      paths:
      - path: /
        backend:
          serviceName: cd
          servicePort: 80
</code></pre>

<p>becomes</p>

<pre><code>spec:
  rules:
  - host: cd.globalhost
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: cd
            port: 
              number: 80
</code></pre>

<p>And, the most crucial part of all, you need to add <strong>ingressClassName: nginx</strong> to the spec (e.g. right below <em>rules)</em>.</p>

<p>See <a href="https://kubernetes.github.io/ingress-nginx/user-guide/basic-usage/">Basic usage – NGINX Ingress Controller (kubernetes.github.io)</a> for full details.</p>

<p>After making the changes, Sitecore URLs resolve as expected.</p>

<p><img src="https://i.snap.as/J3jED8FZ.png" alt=""/></p>

<h1 id="conclusion" id="conclusion">Conclusion</h1>

<p>The <strong>ingressClassName: nginx</strong> change turned out to be the root cause of my problem. According to its <a href="https://kubernetes.github.io/ingress-nginx/user-guide/basic-usage/">documentation</a>, it’s required for K8s &gt;= 1.19, which is exactly the change I made.</p>

<p>Note that adding this to Sitecore’s ingress spec (apiVersion <code>extensions/v1beta1</code>) fixes the issue as well. So the warning <em>unavailable in v1.22+</em> is correct, but there are extra versioning dependencies to take into account.</p>

<p>Tags: <a href="https://stijndevos.net/tag:aks"><a href="https://stijndevos.net/tag:aks" class="hashtag"><span>#</span><span class="p-category">aks</span></a></a>, <a href="https://stijndevos.net/tag:kubernetes"><a href="https://stijndevos.net/tag:kubernetes" class="hashtag"><span>#</span><span class="p-category">kubernetes</span></a></a>, <a href="https://stijndevos.net/tag:ngnix"><a href="https://stijndevos.net/tag:ngnix" class="hashtag"><span>#</span><span class="p-category">ngnix</span></a></a>.</p>

<h1 id="full-spec" id="full-spec">Full spec</h1>

<p>For those interested, this is updated <em>ingress-nginx/ingress.yaml</em></p>

<pre><code>apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: sitecore-ingress
  annotations:
    nginx.ingress.kubernetes.io/proxy-buffer-size: &#34;32k&#34;
    nginx.ingress.kubernetes.io/affinity: &#34;cookie&#34;
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/proxy-connect-timeout: &#34;600&#34;
    nginx.ingress.kubernetes.io/proxy-read-timeout: &#34;600&#34;
    nginx.ingress.kubernetes.io/proxy-send-timeout: &#34;600&#34;
    nginx.ingress.kubernetes.io/proxy-body-size: &#34;512m&#34;
spec:
  rules:
  - host: cd.globalhost
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: cd
            port: 
              number: 80
  - host: cm.globalhost
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: cm
            port:
              number: 80
  - host: id.globalhost
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: id
            port:
              number: 80
  ingressClassName: nginx
  tls:
  - secretName: global-cd-tls
    hosts:
    - cd.globalhost
  - secretName: global-cm-tls
    hosts:
    - cm.globalhost
  - secretName: global-id-tls
    hosts:
    - id.globalhost
</code></pre>
]]></content:encoded>
      <guid>https://stijndevos.net/sitecore-ingress-gives-404-on-aks-k8s-1-19</guid>
      <pubDate>Mon, 27 Sep 2021 08:48:17 +0000</pubDate>
    </item>
    <item>
      <title>Docker panic after updates</title>
      <link>https://stijndevos.net/docker-panic-after-os-updates?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[UPDATE: apparently this issues keeps happening every restart :(. It’s being tracked on https://forums.docker.com/t/error-message-when-trying-to-switch-to-windows-container/113809/15. Hopefully a permanent fix is released soon.&#xA;&#xA;Docker is a great tool, except when it’s not.&#xA;&#xA;Yesterday I picked up on container development after some inactivity. Before I could start, quite some updates were pushed (in case you missed it: in a recent change, only paid plans are allowed to skip updates). Eventually everything worked as expected&#xA;&#xA;Today, after a reboot, I faced following error:&#xA;&#xA;Resetting to factory defaults did not help (it did help clean out my hard drive since resetting deletes all your downloaded images).&#xA;&#xA;Reinstalling did help a bit: docker succeeded to start up in Linux mode. However, switching to Windows mode threw the same exception.&#xA;&#xA;Since the exception is not very detailed, I had to dig a little deeper: good old Event Viewer to the rescue. Turns out something is wrong with the panic log (love the name).&#xA;&#xA;After inspecting the file it turns out it’s marked as readonly:&#xA;&#xA;Unchecking the checkbox and restarting docker solves the issue.&#xA;&#xA;Tags: #docker]]&gt;</description>
      <content:encoded><![CDATA[<p><strong>UPDATE</strong>: apparently this issues keeps happening every restart :(. It’s being tracked on <a href="https://forums.docker.com/t/error-message-when-trying-to-switch-to-windows-container/113809/15">https://forums.docker.com/t/error-message-when-trying-to-switch-to-windows-container/113809/15</a>. Hopefully a permanent fix is released soon.</p>

<p>Docker is a great tool, except when it’s not.</p>

<p>Yesterday I picked up on container development after some inactivity. Before I could start, quite some updates were pushed (in case you missed it: in a recent change, only paid plans are allowed to skip updates). Eventually everything worked as expected</p>

<p>Today, after a reboot, I faced following error:</p>

<p><img src="https://i.snap.as/bcIZ7VGv.png" alt=""/></p>

<p>Resetting to factory defaults did not help (it did help clean out my hard drive since resetting deletes all your downloaded images).</p>

<p>Reinstalling did help a bit: docker succeeded to start up in Linux mode. However, switching to Windows mode threw the same exception.</p>

<p>Since the exception is not very detailed, I had to dig a little deeper: good old <em>Event Viewer</em> to the rescue. Turns out something is wrong with the panic log (love the name).</p>

<p><img src="https://i.snap.as/4QXEhBwP.png" alt=""/></p>

<p>After inspecting the file it turns out it’s marked as <em>readonly</em>:</p>

<p><img src="https://i.snap.as/WE9Foi8q.png" alt=""/></p>

<p>Unchecking the checkbox and restarting docker solves the issue.</p>

<p>Tags: <a href="https://stijndevos.net/tag:docker" class="hashtag"><span>#</span><span class="p-category">docker</span></a></p>
]]></content:encoded>
      <guid>https://stijndevos.net/docker-panic-after-os-updates</guid>
      <pubDate>Tue, 24 Aug 2021 11:51:16 +0000</pubDate>
    </item>
    <item>
      <title>A first look at Sitecore OrderCloud</title>
      <link>https://stijndevos.net/a-first-look-at-sitecore-ordercloud?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[As you might have heard, Sitecore acquired four51.io, a company know for it’s product called OrderCloud, a next-generation, B2B-focused commerce platform based on MACH architecture. Sitecore rebranded this into Sitecore OrderCloud.&#xA;&#xA;Since it’s a SaaS tool, you can setup a free account within minutes:  simply register on https://ordercloud.io to get your sandbox environment (no credit card needed :)). The getting started guide gives you a good understanding of the basic concepts.&#xA;&#xA;Here are my first impressions on it after browsing through the playground environment and the API reference.&#xA;&#xA;!--more--&#xA;&#xA;What’s an OrderCloud?&#xA;&#xA;The Sitecore Acquisition of Boxever and Four51 FAQ page frames it as&#xA;&#xA;Microservices-based, so individual pieces of business functionality can be independently developed, deployed, and managed.&#xA;&#xA;API-first, with all functionality exposed through an API to allow simple, seamless integration.&#xA;&#xA;Cloud-native SaaS to leverage the full capabilities of the cloud, including elastic scaling and automatic upgrades.&#xA;&#xA;Headless, where the front-end user experience is completely decoupled from the back-end logic, allowing for complete design freedom to create the user interface and connect to other channels and devices.&#xA;&#xA;It comes with following core concepts:&#xA;&#xA;Sellers&#xA;Buyers&#xA;Suppliers&#xA;Product Catalogs&#xA;Orders and Fulfillment&#xA;&#xA;Instances of all of these are grouped in a marketplace. OrderCloud allows you to setup multiple marketplaces under the same account. Marketplaces are completely separated from each other (multi-tenant principle).&#xA;&#xA;Postman friendly&#xA;&#xA;OrderCloud is basically an API in the cloud that you call (GET / POST / … over HTTPS, SDKs available for multiple languages) to setup and run your store site.&#xA;&#xA;The API is exposed with OpenApi, a standard, language-agnostic interface to RESTful APIs which allows both humans and computers to discover and understand the capabilities of the service without access to source code, documentation, or through network traffic inspection maintained by swagger.&#xA;&#xA;Simply import https://api.ordercloud.io/v1/openapi/v3 into Postman to get a collection with all available routes:&#xA;&#xA;The core concepts and their relations are easily managed using the standard GET / POST / PUT / … HTTP verbs. See the api reference to get a feeling of what’s possible.&#xA;&#xA;So what about the back-end?&#xA;&#xA;In a headless scenario, the front-end is something you build yourself of course. But what about the back-end? Some products offer you a straightforward user interface where you can see and edit the core concepts (e.g. change values), but not order cloud. The only back-end you have in the cloud is developer-oriented. Basically it’s PostMan in your browser:&#xA;&#xA;Cool, but not something you want to give (most of) your business users.&#xA;&#xA;In its feature guide, Sitecore mentions HeadStart as (among other things) a tool for commerce management (basically, a back-end). Unfortunately, the only info I found on it is an example implementation on GitHub (https://github.com/ordercloud-api/headstart). It illustrates to possibilities to do your own implementation, but -in my opinion-, it’s not something you can convince a customer with as an out-of-the-box solution. The screenshots in the guide look totally different from the demo environment, so more to come on this I hope.&#xA;&#xA;It’s all about me&#xA;&#xA;An interesting concept in OrderCloud that will certainly speed up your front-end store implementation is building up context around your current shopper, simply called me. The idea is that, in OrderCloud, you&#xA;&#xA;model a buyer organization. This defines the catalog, products, prices, …. This can range from a straightforward (single buyer / seller) to complex (lots of different buyers each having their specific sellers, prices, rules, …) setups.&#xA;assign one or more buyer users to a buyer organization. Basically, this is somebody who can buy the configured catalog / product / prices. These can be known / registered users or a default anonymous user.&#xA;&#xA;On your front-end store implementation, you&#xA;&#xA;identify the current shopper using an external login or as anonymous&#xA;get an oauth/token for this shopper from OrderCloud (identified as a buyer user)&#xA;&#xA;With this token you call the /me endpoint to get back context specific information for this user:&#xA;&#xA;/me/addresses&#xA;/me/catalogs&#xA;/me/products&#xA;/me/orders&#xA;/me/promotions&#xA;…&#xA;&#xA;The endpoints support all kinds of operations: GET by ID, filter by propery, text search, …&#xA;&#xA;In my opinion, this is where the power of OrderCloud lays: the core concepts are very flexible to set up in terms of variations and dependencies.&#xA;&#xA;This allows you to setup a back-end integration process, responsible for syncing data from various back-end systems into the OrderCloud format. Enforcing the logic business requires you to.&#xA;&#xA;On the front-end, all of this complexity is abstracted away behind the authentication token and uniform endpoints. Making it very easy to offer a personalized shopping experience (in terms of products, prices, …) for your customers.&#xA;&#xA;What’s next?&#xA;&#xA;A first investigation of OrderCloud looks promising. You can clearly see this product has grown from real world experience, having all those possibilities for modelling buyer / seller relations and personalizing offerings. I definitely see a potential to start building with it for some of my customers. Hopefully I can share you some more hand’s on experience later.&#xA;&#xA;OrderCloud has been acquired quite recently by Sitecore, so it doesn’t have much Sitecore in it (yet). The claim of HeadStart in the feature guide hints we can expect something in the near future (Horizon style tool / interface maybe?). It’s definitely something business users will be happy with.&#xA;&#xA;And what about integrating with other Sitecore products? Importing products from Sitecore Product Content Management, incorporating the API with the headless SDK or streamlining it Boxever CDP with to name just a few.&#xA;&#xA;Tags: #ordercloud]]&gt;</description>
      <content:encoded><![CDATA[<p>As you might have heard, <a href="https://www.sitecore.com//company/news-events/press-releases/2021/04/sitecore-completes-acquisition-of-four51?utm_websource=products.sitecore-commerce">Sitecore acquired four51.io</a>, a company know for it’s product called OrderCloud, <em>a next-generation, B2B-focused commerce platform based on MACH architecture</em>. Sitecore rebranded this into <strong>Sitecore OrderCloud</strong>.</p>

<p><img src="https://i.snap.as/hnCaYMKs.png" alt=""/></p>

<p>Since it’s a SaaS tool, you can setup a <strong>free</strong> account within minutes:  simply register on <a href="https://ordercloud.io">https://ordercloud.io</a> to get your sandbox environment (no credit card needed :)). The <a href="https://ordercloud.io/learn/getting-started/welcome-to-ordercloud">getting started guide</a> gives you a good understanding of the basic concepts.</p>

<p>Here are my first impressions on it after browsing through the playground environment and the API reference.</p>



<h1 id="what-s-an-ordercloud" id="what-s-an-ordercloud">What’s an OrderCloud?</h1>

<p>The <a href="https://www.sitecore.com/company/news-events/press-releases/2021/03/sitecore-acquires-boxever-and-four51/faq">Sitecore Acquisition of Boxever and Four51 FAQ</a> page frames it as</p>
<ol><li><p><em>Microservices-based, so individual pieces of business functionality can be independently developed, deployed, and managed.</em></p></li>

<li><p><em>API-first, with all functionality exposed through an API to allow simple, seamless integration.</em></p></li>

<li><p><em>Cloud-native SaaS to leverage the full capabilities of the cloud, including elastic scaling and automatic upgrades.</em></p></li>

<li><p><em>Headless, where the front-end user experience is completely decoupled from the back-end logic, allowing for complete design freedom to create the user interface and connect to other channels and devices.</em></p></li></ol>

<p>It comes with following core concepts:</p>
<ul><li>Sellers</li>
<li>Buyers</li>
<li>Suppliers</li>
<li>Product Catalogs</li>
<li>Orders and Fulfillment</li></ul>

<p>Instances of all of these are grouped in a <em>marketplace</em>. OrderCloud allows you to setup multiple marketplaces under the same account. Marketplaces are completely separated from each other (multi-tenant principle).</p>

<h2 id="postman-friendly" id="postman-friendly">Postman friendly</h2>

<p>OrderCloud is basically an API in the cloud that you call (GET / POST / … over HTTPS, SDKs available for multiple languages) to setup and run your store site.</p>

<p>The API is exposed with OpenApi, a <em>standard, language-agnostic interface to RESTful APIs which allows both humans and computers to discover and understand the capabilities of the service without access to source code, documentation, or through network traffic inspection</em> maintained by <a href="https://swagger.io/specification/" title="swagger">swagger</a>.</p>

<p>Simply import <a href="https://api.ordercloud.io/v1/openapi/v3">https://api.ordercloud.io/v1/openapi/v3</a> into Postman to get a collection with all available routes:</p>

<p><img src="https://i.snap.as/CcWDYlF2.png" alt=""/></p>

<p>The core concepts and their relations are easily managed using the standard GET / POST / PUT / … HTTP verbs. See the <a href="https://ordercloud.io/api-reference">api reference</a> to get a feeling of what’s possible.</p>

<h2 id="so-what-about-the-back-end" id="so-what-about-the-back-end">So what about the back-end?</h2>

<p>In a headless scenario, the front-end is something you build yourself of course. But what about the back-end? Some products offer you a straightforward user interface where you can see and edit the core concepts (e.g. change values), but not order cloud. The only back-end you have in the cloud is developer-oriented. Basically it’s PostMan in your browser:</p>

<p><img src="https://i.snap.as/n82DQaXT.png" alt=""/></p>

<p>Cool, but not something you want to give (most of) your business users.</p>

<p>In its <a href="https://www.sitecore.com/resources/index/guide/ordercloud-feature-guide">feature guide</a>, Sitecore mentions HeadStart as (among other things) a tool for commerce management (basically, a back-end). Unfortunately, the only info I found on it is an example implementation on GitHub (<a href="https://github.com/ordercloud-api/headstart">https://github.com/ordercloud-api/headstart</a>). It illustrates to possibilities to do your own implementation, but -in my opinion-, it’s not something you can convince a customer with as an out-of-the-box solution. The screenshots in the guide look totally different from the demo environment, so more to come on this I hope.</p>

<h2 id="it-s-all-about-me" id="it-s-all-about-me">It’s all about me</h2>

<p>An interesting concept in OrderCloud that will certainly speed up your front-end store implementation is building up context around your current shopper, simply called <em>me.</em> The idea is that, in OrderCloud, you</p>
<ul><li>model a <em>buyer organization</em>. This defines the catalog, products, prices, …. This can range from a straightforward (single buyer / seller) to complex (lots of different buyers each having their specific sellers, prices, rules, …) setups.</li>
<li>assign one or more <em>buyer users</em> to a buyer organization. Basically, this is somebody who can buy the configured catalog / product / prices. These can be known / registered users or a default anonymous user.</li></ul>

<p>On your front-end store implementation, you</p>
<ul><li>identify the current shopper using an external login or as anonymous</li>
<li>get an oauth/token for this shopper from OrderCloud (identified as a <em>buyer user</em>)</li></ul>

<p>With this token you call the <em>/me</em> endpoint to get back context specific information for this user:</p>
<ul><li>/me/addresses</li>
<li>/me/catalogs</li>
<li>/me/products</li>
<li>/me/orders</li>
<li>/me/promotions</li>
<li>…</li></ul>

<p>The endpoints support all kinds of operations: GET by ID, filter by propery, text search, …</p>

<p>In my opinion, this is where the power of OrderCloud lays: the core concepts are very flexible to set up in terms of variations and dependencies.</p>

<p>This allows you to setup a back-end integration process, responsible for syncing data from various back-end systems into the OrderCloud format. Enforcing the logic business requires you to.</p>

<p>On the front-end, all of this complexity is abstracted away behind the authentication token and uniform endpoints. Making it very easy to offer a personalized shopping experience (in terms of products, prices, …) for your customers.</p>

<h1 id="what-s-next" id="what-s-next">What’s next?</h1>

<p>A first investigation of OrderCloud looks promising. You can clearly see this product has grown from real world experience, having all those possibilities for modelling buyer / seller relations and personalizing offerings. I definitely see a potential to start building with it for some of my customers. Hopefully I can share you some more hand’s on experience later.</p>

<p>OrderCloud has been acquired quite recently by Sitecore, so it doesn’t have much Sitecore in it (yet). The claim of HeadStart in the feature guide hints we can expect something in the near future (Horizon style tool / interface maybe?). It’s definitely something business users will be happy with.</p>

<p>And what about integrating with other Sitecore products? Importing products from Sitecore Product Content Management, incorporating the API with the headless SDK or streamlining it Boxever CDP with to name just a few.</p>

<p>Tags: <a href="https://stijndevos.net/tag:ordercloud" class="hashtag"><span>#</span><span class="p-category">ordercloud</span></a></p>
]]></content:encoded>
      <guid>https://stijndevos.net/a-first-look-at-sitecore-ordercloud</guid>
      <pubDate>Fri, 20 Aug 2021 10:16:18 +0000</pubDate>
    </item>
    <item>
      <title>How browser security fixes Sitecore login</title>
      <link>https://stijndevos.net/how-browser-security-fixes-sitecore-login?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[I was contacted by a client to help them roll out Sitecore Kubernetes deployment (hooray for clients willing to invest in technology updates). Of course I came in prepared: I tried out Installation Guide for Production Environment with Kubernetes (on Azure AKS with my own subscription), and had a shiny new Sitecore 10.x up and running on https://globalhost.cd in no time.&#xA;&#xA;When repeating the setup on the client’s subscription, deployment went well but we failed to log in into Sitecore:&#xA;&#xA;!--more--&#xA;&#xA;We try to access /sitecore.&#xA;Since we’re not logged in, Sitecore CM redirects us to Sitecore ID.&#xA;On Sitecore ID, we enter our credentials.&#xA;Everything seems fine and we are redirected back to Sitecore CM.&#xA;We see the browser redirecting, but the page failed to load: the browser fails with a timeout (IIRC).&#xA;&#xA;A quick look at the log files did not reveal any errors or failures.&#xA;&#xA;In my setup, everything worked as expected, so what’s different for our client? Since this is a real project setup, of course the URLs (and TLS) are going to be customer specific (so not globalhost). Typo’s are easily made, but not this time: double checking the config did not reveal any mistakes.&#xA;&#xA;With no traces in the logs we had to dig a little bit deeper. Since single-sign on is all about redirection and passing along the required information in the process, taking a closer look at the network traffic made sense.&#xA;&#xA;(see Virtual Developer Day 2020 - Getting to Know Sitecore Identity - George Chang for a good introduction on Sitecore ID)&#xA;&#xA;Security best practices for the win&#xA;&#xA;A useful tool to do this is fiddler (fiddler classic in my case): a web debugging proxy to successfully log, inspect, and alter HTTP(s) network requests and server responses.&#xA;&#xA;I was already thinking bad tokens, encryption issues, claim mismatches, … Luckily the issue was not as complex as I feared. Comparing network traffic from my (working) setup with traffic from the client setup revealed they had one HTTP header missing: Strict-Transport-Security, also known as HSTS.&#xA;&#xA;  If a website accepts a connection through HTTP and redirects to HTTPS, visitors may initially communicate with the non-encrypted version of the site before being redirected, if, for example, the visitor types http://www.foo.com/ or even just foo.com. This creates an opportunity for a man-in-the-middle attack. The redirect could be exploited to direct visitors to a malicious site instead of the secure version of the original site.&#xA;    The HTTP Strict Transport Security header informs the browser that it should never load a site using HTTP and should automatically convert all attempts to access the site using HTTP to HTTPS requests instead.&#xA;&#xA;https://developer.mozilla.org/en-US/docs/Glossary/HSTS&#xA;&#xA;This is exactly what happens in my fiddler trace: even though the server tells my browser to go to HTTP (using the Location: header), my browser ignores this and connects over HTTPS directly (line 52).&#xA;&#xA;Why would Sitecore redirect us to HTTP? No idea. As far as I can tell all our config is set correctly. I can only assume this is leftover code from a time where local development was often done on HTTP. With Sitecore on containers, development setup is a lot closer to production setup, including running on HTTPS.&#xA;&#xA;Ingress&#xA;&#xA;So why did I did not face this issue on my setup? An important part of a Kubernetes setup is managing how external traffic can access the services in your cluster (HTTP, HTTPS, TLS, …). This is done with the Ingress API object. Out of the box, Sitecore suggests you to use the NGINX Ingress controller for this.&#xA;&#xA;NGINX is a fine default, but my client has a track record in Kubernetes (not on Azure) and for their networking requirements they standardize on Istio, a service mesh. To use it, we had to switch NGINX with Istio Ingress.&#xA;&#xA;Turns out HSTS is enabled by default in NGINX (see https://kubernetes.github.io/ingress-nginx/user-guide/tls/#http-strict-transport-security), but not in Istio. Enabling it is quite easy though (in case you might be interested): https://www.wagner-dev.com/istio-configure-strict-transport-security-hsts.html&#xA;&#xA;In a development setup, network traffic is typically handled with traefik. Sitecore provides you with docker-compose files that force this behavior.&#xA;&#xA;So why didn’t we spot this right away? The browser we were using (it’s name starts with a ‘C’) doesn’t show the protocol in the URL bar, so we simply didn’t spot it. That we had to test this through a screen share session probably didn’t help neither (but that’s a different story).&#xA;&#xA;In summary&#xA;&#xA;Sitecore login depends on HSTS.&#xA;HSTS is enabled by default for NGNIX ingress, but that might not be the case for other ingress controllers (e.g. Isitio).&#xA;You can’t see what you can’t see.&#xA;&#xA;Tags: #aks, #kubernetes, #ngnix, #istio.]]&gt;</description>
      <content:encoded><![CDATA[<p>I was contacted by a client to help them roll out Sitecore Kubernetes deployment (hooray for clients willing to invest in technology updates). Of course I came in prepared: I tried out <a href="https://sitecoredev.azureedge.net/~/media/8DCE36C9C62B49A59DA398F2C9387892.ashx?date=20210714T144106">Installation Guide for Production Environment with Kubernetes</a> (on Azure AKS with my own subscription), and had a shiny new Sitecore 10.x up and running on <a href="https://globalhost.cd">https://globalhost.cd</a> in no time.</p>

<p>When repeating the setup on the client’s subscription, deployment went well but we failed to log in into Sitecore:</p>


<ul><li>We try to access <em>/sitecore</em>.</li>
<li>Since we’re not logged in, Sitecore CM redirects us to Sitecore ID.</li>
<li>On Sitecore ID, we enter our credentials.</li>
<li>Everything seems fine and we are redirected back to Sitecore CM.</li>
<li>We see the browser redirecting, but the page failed to load: the browser fails with a timeout (IIRC).</li></ul>

<p>A quick look at the log files did not reveal any errors or failures.</p>

<p>In my setup, everything worked as expected, so what’s different for our client? Since this is a real project setup, of course the URLs (and TLS) are going to be customer specific (so not <em>globalhost)</em>. Typo’s are easily made, but not this time: double checking the config did not reveal any mistakes.</p>

<p>With no traces in the logs we had to dig a little bit deeper. Since single-sign on is all about redirection and passing along the required information in the process, taking a closer look at the network traffic made sense.</p>

<p><img src="https://i.snap.as/5D6RVSjC.png" alt=""/></p>

<p><em>(see <a href="https://www.youtube.com/watch?v=J1_4v0u2aic">Virtual Developer Day 2020 – Getting to Know Sitecore Identity – George Chang</a> for a good introduction on Sitecore ID)</em></p>

<h2 id="security-best-practices-for-the-win" id="security-best-practices-for-the-win">Security best practices for the win</h2>

<p>A useful tool to do this is fiddler (<a href="https://www.telerik.com/fiddler/fiddler-classic" title="Fiddler Classic">fiddler classic</a> in my case): a web debugging proxy to <em>successfully log, inspect, and alter HTTP(s) network requests and server responses</em>.</p>

<p>I was already thinking bad tokens, encryption issues, claim mismatches, … Luckily the issue was not as complex as I feared. Comparing network traffic from my (working) setup with traffic from the client setup revealed they had one HTTP header missing: <strong>Strict-Transport-Security</strong>, also known as <strong>HSTS</strong>.</p>

<blockquote><p>If a website accepts a connection through HTTP and redirects to HTTPS, visitors may initially communicate with the non-encrypted version of the site before being redirected, if, for example, the visitor types <a href="http://www.foo.com/">http://www.foo.com/</a> or even just foo.com. This creates an opportunity for a man-in-the-middle attack. The redirect could be exploited to direct visitors to a malicious site instead of the secure version of the original site.</p>

<p>The HTTP Strict Transport Security header informs the browser that it should never load a site using HTTP and should automatically convert all attempts to access the site using HTTP to HTTPS requests instead.</p></blockquote>

<p><em><a href="https://developer.mozilla.org/en-US/docs/Glossary/HSTS">https://developer.mozilla.org/en-US/docs/Glossary/HSTS</a></em></p>

<p>This is exactly what happens in my fiddler trace: even though the server tells my browser to go to HTTP (using the <em>Location:</em> header), my browser ignores this and connects over HTTPS directly (line 52).</p>

<p><img src="https://i.snap.as/gEP8hH9P.png" alt=""/></p>

<p>Why would Sitecore redirect us to HTTP? No idea. As far as I can tell all our config is set correctly. I can only assume this is leftover code from a time where local development was often done on HTTP. With Sitecore on containers, development setup is a lot closer to production setup, including running on HTTPS.</p>

<h2 id="ingress" id="ingress">Ingress</h2>

<p>So why did I did not face this issue on my setup? An important part of a Kubernetes setup is managing how external traffic can access the services in your cluster (HTTP, HTTPS, TLS, …). This is done with the <a href="https://kubernetes.io/docs/concepts/services-networking/ingress/">Ingress API object</a>. Out of the box, Sitecore suggests you to use the <em>NGINX Ingress controller</em> for this.</p>

<p>NGINX is a fine default, but my client has a track record in Kubernetes (not on Azure) and for their networking requirements they standardize on <a href="https://istio.io/">Istio</a>, a service mesh. To use it, we had to switch NGINX with Istio Ingress.</p>

<p>Turns out HSTS is enabled by default in NGINX (see <a href="https://kubernetes.github.io/ingress-nginx/user-guide/tls/#http-strict-transport-security">https://kubernetes.github.io/ingress-nginx/user-guide/tls/#http-strict-transport-security</a>), but not in Istio. Enabling it is quite easy though (in case you might be interested): <a href="https://www.wagner-dev.com/istio-configure-strict-transport-security-hsts.html">https://www.wagner-dev.com/istio-configure-strict-transport-security-hsts.html</a></p>

<p>In a development setup, network traffic is typically handled with <a href="https://doc.traefik.io/traefik/">traefik</a>. Sitecore provides you with docker-compose files that force this behavior.</p>

<p><img src="https://i.snap.as/QIrF5Ncv.png" alt=""/></p>

<p>So why didn’t we spot this right away? The browser we were using (it’s name starts with a ‘C’) doesn’t show the protocol in the URL bar, so we simply didn’t spot it. That we had to test this through a screen share session probably didn’t help neither (but that’s a different story).</p>

<h2 id="in-summary" id="in-summary">In summary</h2>
<ul><li>Sitecore login depends on HSTS.</li>
<li>HSTS is enabled by default for NGNIX ingress, but that might not be the case for other ingress controllers (e.g. Isitio).</li>
<li>You can’t see what you can’t see.</li></ul>

<p>Tags: <a href="https://stijndevos.net/tag:aks" class="hashtag"><span>#</span><span class="p-category">aks</span></a>, <a href="https://stijndevos.net/tag:kubernetes" class="hashtag"><span>#</span><span class="p-category">kubernetes</span></a>, <a href="https://stijndevos.net/tag:ngnix" class="hashtag"><span>#</span><span class="p-category">ngnix</span></a>, <a href="https://stijndevos.net/tag:istio" class="hashtag"><span>#</span><span class="p-category">istio</span></a>.</p>
]]></content:encoded>
      <guid>https://stijndevos.net/how-browser-security-fixes-sitecore-login</guid>
      <pubDate>Mon, 26 Jul 2021 12:22:39 +0000</pubDate>
    </item>
  </channel>
</rss>