<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>tls on Digi Hunch</title><link>https://static.digihunch.com/tag/tls/</link><description>Recent content in tls on Digi Hunch</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><lastBuildDate>Mon, 12 May 2025 23:28:25 -0400</lastBuildDate><atom:link href="https://static.digihunch.com/tag/tls/index.xml" rel="self" type="application/rss+xml"/><item><title>DICOM testing over TLS</title><link>https://static.digihunch.com/2023/02/dicom-testing-with-tls/</link><pubDate>Sat, 18 Feb 2023 01:05:00 -0400</pubDate><guid>https://static.digihunch.com/2023/02/dicom-testing-with-tls/</guid><description>&lt;img src="https://static.digihunch.com/wp-content/uploads/2025/04/dicom-testing-feature.webp" alt="Featured image of post DICOM testing over TLS" /&gt;&lt;p class="wp-block-paragraph"&gt;I have two open-source projects to deploy a medical imaging application on different platforms. In both of them, I define DICOM validation scenario, and provide steps to test DICOM traffic with TLS. The steps have been working well, until a recent change in Envoy broke the testing, and led me to revisit the test scenario. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In a nutshell, I changed from &lt;code&gt;dcm4che&lt;/code&gt; to &lt;code&gt;dcmtk&lt;/code&gt; binary builds. I&amp;#8217;ve also expanded the test case from a self-signed server certificate to one involving a self-signed CA. Although this test is about TLS for DICOM traffic, the principles apply to any traffic at TCP level. If you just need instruction for DICOM validation on &lt;a href="https://github.com/digihunch/orthweb"&gt;Orthweb&lt;/a&gt; or &lt;a href="https://github.com/digihunch/korthweb"&gt;Korthweb&lt;/a&gt; projects, skip the Background section. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-background"&gt;Background&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To test DICOM traffic with command line tool, I was investigating between &lt;code&gt;dcm4che&lt;/code&gt; and &lt;code&gt;dcmtk&lt;/code&gt;. Both are open-source projects with builds for multiple platforms. My DICOM test is as simple as a C-Echo command and a C-Store command using the tool, with TLS enabled. Once they work, other DICOM commands usually work as well. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I have been primarily using &lt;code&gt;dcm4che&lt;/code&gt; as I was familiar with its previous version from my old job. For example, I can issue a C-ECHO with TLS using storescu command:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./storescu -c ORTHANC@ec2-54-243-91-148.compute-1.amazonaws.com:11112 --tls12 --tls-aes --trust-store server.truststore --trust-store-pass Password123!&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;To turn that test into a C-Store test, simply add a DCM file as input:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./storescu -c ORTHANC@ec2-54-243-91-148.compute-1.amazonaws.com:11112 --tls12 --tls-aes --trust-store server.truststore --trust-store-pass Password123! MY.DCM&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The C-Store output tracks each DIMSE command and return codes. Note that in the command, we specify &amp;#8211;tls12 as the version, and with &amp;#8211;tls-aes switch we enabled AES or 3DES encryption. We also specified a file for trust store and password to the trust store. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This is when I first frowned over &lt;code&gt;dcm4che&lt;/code&gt;. The &lt;code&gt;dcm4che&lt;/code&gt; utility is a Java-based program we have to take an extra step of turning certificate into Java trust store. &lt;a href="https://static.digihunch.com/2018/11/the-java-confusions/"&gt;Different JVM versions&lt;/a&gt; may also cause different behaviours in the test. What later prompted me to switch to &lt;code&gt;dcmtk&lt;/code&gt; is that with &lt;code&gt;dcm4che&lt;/code&gt; I came across a weird &lt;a href="https://github.com/digihunch/orthweb/issues/14#issuecomment-1320768544"&gt;error&lt;/a&gt; since Envoy proxy version 1.23, which impacted both Orthweb (Envoy proxy) and Korthweb (Istio Ingress).&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Moreover, &lt;code&gt;dcmtk&lt;/code&gt; is available as a &lt;a href="https://formulae.brew.sh/formula/dcmtk#default"&gt;HomeBrew package&lt;/a&gt;, &lt;a href="https://packages.ubuntu.com/search?keywords=dcmtk"&gt;Ubuntu package&lt;/a&gt;, and &lt;a href="https://packages.debian.org/buster/dcmtk"&gt;Debian package&lt;/a&gt;. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;The test case, data and the tool&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We can install dcmtk utility simply with &lt;code&gt;brew install dcmtk&lt;/code&gt;, and then we need its &lt;code&gt;echoscu&lt;/code&gt; and &lt;code&gt;storescu&lt;/code&gt; commands with correct TLS options. The DICOM data I used for testing is a CT exam available for download &lt;a href="https://download.digihunch.com/dicom_data/covid_study.zip"&gt;here&lt;/a&gt;. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Before mocking with &lt;code&gt;dcmtk&lt;/code&gt;&amp;#8216;s TLS options, we first need to understand what would be a good test. Previously I have used a single self-signed certificate on server. It is an over-simplified scenario that is far from a real-life certificate chain, and also does not test client certificate. If I also self-sign the client certificate, the client and server certificates are signed by entirely different parties and have no trust relationship, making it an invalid test case for client certificate.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For a full-blown testing with TLS, we should have two levels of CA as below:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="646px" viewBox="-0.5 -0.5 646 421" style="max-width:100%;max-height:421px;"&gt;&lt;defs&gt;&lt;/defs&gt;&lt;g&gt;&lt;rect x="0" y="0" width="645" height="420" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;rect x="345" y="300" width="280" height="100" rx="15" ry="15" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 278px; height: 1px; padding-top: 307px; margin-left: 346px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Server&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="485" y="319" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Server&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="505" y="350" width="110" height="30" fill="#f5f5f5" stroke="#666666" stroke-width="3" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 365px; margin-left: 506px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RSA Private Key&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="560" y="369" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RSA Private Key&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="360" y="325" width="135" height="65" fill="#fff2cc" stroke="#d6b656" stroke-width="3" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 133px; height: 1px; padding-top: 332px; margin-left: 361px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;X509 Certificate&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="428" y="344" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;X509 Certificate&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="375" y="350" width="110" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 365px; margin-left: 376px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RSA Public Key&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="430" y="369" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RSA Public Key&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="345" y="130" width="280" height="100" rx="15" ry="15" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 278px; height: 1px; padding-top: 137px; margin-left: 346px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Client&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="485" y="149" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Client&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="505" y="180" width="110" height="30" fill="#f5f5f5" stroke="#666666" stroke-width="3" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 195px; margin-left: 506px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RSA Private Key&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="560" y="199" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RSA Private Key&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="360" y="155" width="135" height="65" fill="#fff2cc" stroke="#d6b656" stroke-width="3" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 133px; height: 1px; padding-top: 162px; margin-left: 361px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;X509 Certificate&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="428" y="174" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;X509 Certificate&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="375" y="180" width="110" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 195px; margin-left: 376px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RSA Public Key&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="430" y="199" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RSA Public Key&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="25" y="180" width="240" height="130" rx="19.5" ry="19.5" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 238px; height: 1px; padding-top: 245px; margin-left: 27px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Intermediate CA&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="27" y="249" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;Intermediate CA&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="132.5" y="270" width="110" height="30" fill="#f5f5f5" stroke="#666666" stroke-width="3" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 285px; margin-left: 134px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RSA Private Key&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="188" y="289" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RSA Private Key&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="120" y="195" width="135" height="65" fill="#fff2cc" stroke="#d6b656" stroke-width="3" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 133px; height: 1px; padding-top: 202px; margin-left: 121px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;X509 Certificate&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="188" y="214" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;X509 Certificate&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="135" y="220" width="110" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 235px; margin-left: 136px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RSA Public Key&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="190" y="239" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RSA Public Key&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="25" y="20" width="240" height="130" rx="19.5" ry="19.5" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 238px; height: 1px; padding-top: 85px; margin-left: 27px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Root CA&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="27" y="89" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;Root CA&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="132.5" y="110" width="110" height="30" fill="#f5f5f5" stroke="#666666" stroke-width="3" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 125px; margin-left: 134px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RSA Private Key&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="188" y="129" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RSA Private Key&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="120" y="35" width="135" height="65" fill="#fff2cc" stroke="#d6b656" stroke-width="3" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 133px; height: 1px; padding-top: 42px; margin-left: 121px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;X509 Certificate&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="188" y="54" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;X509 Certificate&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="135" y="60" width="110" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 75px; margin-left: 136px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RSA Public Key&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="190" y="79" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RSA Public Key&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 187.5 300 L 188 358 L 353.63 357.52" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 358.88 357.5 L 351.89 361.02 L 353.63 357.52 L 351.87 354.02 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 359px; margin-left: 281px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;sign&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="281" y="363" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;sign&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 242.5 285 L 428 285 L 427.55 226.37" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 427.51 221.12 L 431.06 228.09 L 427.55 226.37 L 424.06 228.14 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 285px; margin-left: 306px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;sign&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="306" y="288" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;sign&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 187.5 140 L 187.5 188.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 187.5 193.88 L 184 186.88 L 187.5 188.63 L 191 186.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 163px; margin-left: 187px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;sign&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="187" y="166" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;sign&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 242.5 125 L 285 125 L 285 68 L 261.37 67.61" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 256.12 67.52 L 263.18 64.14 L 261.37 67.61 L 263.06 71.13 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 91px; margin-left: 296px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;self-sign&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="296" y="94" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;self-sign&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;switch&gt;&lt;g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;/g&gt;&lt;a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank" rel="noopener"&gt;&lt;text text-anchor="middle" font-size="10px" x="50%" y="100%"&gt;Text is not SVG &amp;#8211; cannot display&lt;/text&gt;&lt;/a&gt;&lt;/switch&gt;&lt;/svg&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The chart represents a typical hierarchy of three-level certificate authorities. Sometimes we need simplicity in our testing, and it is reasonable to simplify the diagram to the following, with one CA that issues certificate for both client and server:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="601px" viewBox="-0.5 -0.5 601 301" style="max-width:100%;max-height:301px;"&gt;&lt;defs&gt;&lt;/defs&gt;&lt;g&gt;&lt;rect x="0" y="0" width="600" height="300" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"&gt;&lt;/rect&gt;&lt;rect x="290" y="180" width="280" height="90" rx="13.5" ry="13.5" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-end; width: 278px; height: 1px; padding-top: 187px; margin-left: 290px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: right;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Server&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="568" y="199" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="end"&gt;Server&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="450" y="220" width="110" height="30" fill="#f5f5f5" stroke="#666666" stroke-width="3" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 235px; margin-left: 451px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RSA Private Key&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="505" y="239" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RSA Private Key&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="305" y="195" width="135" height="65" fill="#fff2cc" stroke="#d6b656" stroke-width="3" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 133px; height: 1px; padding-top: 202px; margin-left: 306px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;X509 Certificate&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="373" y="214" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;X509 Certificate&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="320" y="220" width="110" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 235px; margin-left: 321px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RSA Public Key&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="375" y="239" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RSA Public Key&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="290" y="40" width="280" height="90" rx="13.5" ry="13.5" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-end; width: 278px; height: 1px; padding-top: 47px; margin-left: 290px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: right;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Client&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="568" y="59" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="end"&gt;Client&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="450" y="80" width="110" height="30" fill="#f5f5f5" stroke="#666666" stroke-width="3" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 95px; margin-left: 451px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RSA Private Key&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="505" y="99" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RSA Private Key&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="305" y="55" width="135" height="65" fill="#fff2cc" stroke="#d6b656" stroke-width="3" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 133px; height: 1px; padding-top: 62px; margin-left: 306px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;X509 Certificate&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="373" y="74" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;X509 Certificate&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="320" y="80" width="110" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 95px; margin-left: 321px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RSA Public Key&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="375" y="99" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RSA Public Key&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="20" y="50" width="190" height="130" rx="19.5" ry="19.5" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe flex-start; width: 188px; height: 1px; padding-top: 177px; margin-left: 22px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Test CA&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="22" y="177" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px"&gt;Test CA&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="77.5" y="140" width="110" height="30" fill="#f5f5f5" stroke="#666666" stroke-width="3" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 155px; margin-left: 79px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RSA Private Key&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="133" y="159" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RSA Private Key&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="65" y="65" width="135" height="65" fill="#fff2cc" stroke="#d6b656" stroke-width="3" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 133px; height: 1px; padding-top: 72px; margin-left: 66px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;X509 Certificate&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="133" y="84" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;X509 Certificate&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="80" y="90" width="110" height="30" fill="#d5e8d4" stroke="#82b366" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 108px; height: 1px; padding-top: 105px; margin-left: 81px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;RSA Public Key&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="135" y="109" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;RSA Public Key&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 132.5 170 L 133 228 L 298.63 227.52" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 303.88 227.5 L 296.89 231.02 L 298.63 227.52 L 296.87 224.02 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 229px; margin-left: 226px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;sign&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="226" y="233" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;sign&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 190 160 L 373 160 L 372.58 126.37" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 372.51 121.12 L 376.1 128.07 L 372.58 126.37 L 369.1 128.16 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 160px; margin-left: 247px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;sign&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="247" y="163" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;sign&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 187.5 147.5 L 230 148 L 230 98 L 206.37 97.61" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 201.12 97.52 L 208.18 94.14 L 206.37 97.61 L 208.06 101.13 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 118px; margin-left: 241px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;"&gt;self-sign&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="241" y="121" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;self-sign&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;switch&gt;&lt;g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;/g&gt;&lt;a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank" rel="noopener"&gt;&lt;text text-anchor="middle" font-size="10px" x="50%" y="100%"&gt;Text is not SVG &amp;#8211; cannot display&lt;/text&gt;&lt;/a&gt;&lt;/switch&gt;&lt;/svg&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Our DICOM validation testing will be based on both approaches depending on the project and deployment option. For Orthweb project and the Helm-chart driven option in Korthweb, we have one level of CA. For the GitOps and manual option in Korthweb, we have two levels of CA. When configuring testing, it is important to have the diagram above in mind.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In addition, we should also be aware of the limitation of the testing. As a personal project I will not pay for the certificates. I have to self-sign the certificate of the CA so there is no way to derive trust on this CA from another level. As a result, we must tell the client and server to trust the CA. The steps to create the needed certificates are: &lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;Generate a key pair for Test CA. Generate the certificate for Test CA by self-signing its own public key&lt;/li&gt;&#10;&lt;li&gt;Generate a key pair for the (DICOM) server. Generate the certificate for the server by signing its public key with Test CA&amp;#8217;s private key&lt;/li&gt;&#10;&lt;li&gt;Generate a key pair for the (DICOM) client. Generate the certificate for the client by signing its public key with Test CA&amp;#8217;s private key&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Because the client now also has its certificate, we can test with and without client certificate, using the following &lt;code&gt;dcmtck&lt;/code&gt; switches:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;-d&lt;/strong&gt; (shorthand for &amp;#8211;debug): print out detailed DICOM communication log. For succinct output, use -v (shorthand for &amp;#8211;verbose) instead.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;+tla&lt;/strong&gt; (shorthand for &amp;#8211;anonymous-tls): enable anonymous TLS (without client certificate)&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;+tls&lt;/strong&gt; (shorthand for &amp;#8211;enable-tls): enable full TLS (with client certificate), followed by client key and certificate files&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;-rc&lt;/strong&gt; (shorthand for &amp;#8211;require-peer-cert): &amp;#8211;require-peer-cert, require peer (server) certificate&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;+cf&lt;/strong&gt; (shorhand for &amp;#8211;add-cert-file): &amp;#8211;add-cert-file, add server certificate so client can trust it.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For C-ECHO, the testing commands with and without client certificate look like:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ echoscu -aet TESTER -aec ORTHANC -d +tla -rc +cf ca.crt ec2-3-98-241-51.ca-central-1.compute.amazonaws.com &lt;span style="color:#ae81ff"&gt;11112&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ echoscu -aet TESTER -aec ORTHANC -d +tls client.key client.crt -rc +cf ca.crt ec2-3-98-241-51.ca-central-1.compute.amazonaws.com &lt;span style="color:#ae81ff"&gt;11112&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;For C-STORE, the testing commands with and without client certificate look like:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ storescu -aet TESTER -aec ORTHANC -d +tla -rc +cf ca.crt ec2-3-98-241-51.ca-central-1.compute.amazonaws.com &lt;span style="color:#ae81ff"&gt;11112&lt;/span&gt; DICOM_Images/COVID/56364823.dcm&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ storescu -aet TESTER -aec ORTHANC -d +tls client.key client.crt -rc +cf ca.crt ec2-3-98-241-51.ca-central-1.compute.amazonaws.com &lt;span style="color:#ae81ff"&gt;11112&lt;/span&gt; DICOM_Images/COVID/56364823.dcm&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;As to how to create the certificates, in the post &lt;em&gt;&lt;a href="https://static.digihunch.com/2021/08/creating-tls-certificate-kubernetes/"&gt;Creating X.509 TLS certificate in Kubernetes&lt;/a&gt;&lt;/em&gt;, I discussed different ways to create certificates for testing, including using openssl. In the next two sections, I will discuss them in further details.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Orthweb Test&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The orthweb project runs Docker containers on an EC2 instance. In the &lt;a href="https://github.com/digihunch/orthweb/blob/e13c590121b6d1bd22ec6092f4f6542c9680d4fc/terraform/modules/ec2/userdata1.sh"&gt;cloud-init script&lt;/a&gt; of the EC2 instance, we self-sign a test CA with openssl. Then we create key and certificate for server and client respectively:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;IssuerComName&lt;span style="color:#f92672"&gt;=&lt;/span&gt;issuer.orthweb.digihunch.com&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ClientComName&lt;span style="color:#f92672"&gt;=&lt;/span&gt;dcmclient.orthweb.digihunch.com&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ServerComName&lt;span style="color:#f92672"&gt;=&lt;/span&gt;ca-central-1.compute.amazonaws.com&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl11 req -x509 -sha256 -newkey rsa:4096 -days &lt;span style="color:#ae81ff"&gt;365&lt;/span&gt; -nodes -subj /C&lt;span style="color:#f92672"&gt;=&lt;/span&gt;CA/ST&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Ontario/L&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Waterloo/O&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Digihunch/OU&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Imaging/CN&lt;span style="color:#f92672"&gt;=&lt;/span&gt;$IssuerComName/emailAddress&lt;span style="color:#f92672"&gt;=&lt;/span&gt;info@www.digihunch.com -keyout /tmp/ca.key -out /tmp/ca.crt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl11 req -new -newkey rsa:4096 -nodes -subj /C&lt;span style="color:#f92672"&gt;=&lt;/span&gt;CA/ST&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Ontario/L&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Waterloo/O&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Digihunch/OU&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Imaging/CN&lt;span style="color:#f92672"&gt;=&lt;/span&gt;$ServerComName/emailAddress&lt;span style="color:#f92672"&gt;=&lt;/span&gt;orthweb@www.digihunch.com -addext extendedKeyUsage&lt;span style="color:#f92672"&gt;=&lt;/span&gt;serverAuth -addext subjectAltName&lt;span style="color:#f92672"&gt;=&lt;/span&gt;DNS:orthweb.digihunch.com,DNS:$IssuerComName -keyout /tmp/server.key -out /tmp/server.csr&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl11 x509 -req -sha256 -days &lt;span style="color:#ae81ff"&gt;365&lt;/span&gt; -in /tmp/server.csr -CA /tmp/ca.crt -CAkey /tmp/ca.key -set_serial &lt;span style="color:#ae81ff"&gt;01&lt;/span&gt; -out /tmp/server.crt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl11 req -new -newkey rsa:4096 -nodes -subj /C&lt;span style="color:#f92672"&gt;=&lt;/span&gt;CA/ST&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Ontario/L&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Waterloo/O&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Digihunch/OU&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Imaging/CN&lt;span style="color:#f92672"&gt;=&lt;/span&gt;$ClientComName/emailAddress&lt;span style="color:#f92672"&gt;=&lt;/span&gt;orthweb@www.digihunch.com -keyout /tmp/client.key -out /tmp/client.csr&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl11 x509 -req -sha256 -days &lt;span style="color:#ae81ff"&gt;365&lt;/span&gt; -in /tmp/client.csr -CA /tmp/ca.crt -CAkey /tmp/ca.key -set_serial &lt;span style="color:#ae81ff"&gt;01&lt;/span&gt; -out /tmp/client.crt&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;When simulating DICOM activities from the client side, we supply the three files (&lt;code&gt;ca.key&lt;/code&gt;, &lt;code&gt;client.crt&lt;/code&gt; and &lt;code&gt;client.key&lt;/code&gt;) to the &lt;code&gt;echoscu&lt;/code&gt; and &lt;code&gt;storescu&lt;/code&gt; executables, to issue DIMSE commands on top of TLS from client side, as shown in the previous section. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Korthweb Test&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Currently, the Korthweb project deploys to Kubernetes cluster in three approaches, including two types of ingress controllers: Istio CRD (manual and GitOps deployment options) and Traefik CRD (Helm Chart driven deployment options). With both approaches, it is fairly straightforward to validate the HTTPS port. We export the CA certificate and run a curl command such as:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -HHost:web.orthweb.com -k -X GET https://web.orthweb.com:443/app/explorer.html -u admin:orthanc --cacert ca.crt&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Note that by default, the &lt;code&gt;curl&lt;/code&gt; command adds SNI (server name indication) extension by default to its TLS &lt;code&gt;ClientHello&lt;/code&gt; Message (even without -HHost switch). It acts like a &lt;a href="https://www.ssllabs.com/ssltest/clients.html"&gt;modern browser&lt;/a&gt;. &lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="465" src="https://static.digihunch.com/wp-content/uploads/2023/02/dicom-wireshark.webp" alt="" class="wp-image-12895" srcset="https://static.digihunch.com/wp-content/uploads/2023/02/dicom-wireshark.webp 1024w, https://static.digihunch.com/wp-content/uploads/2023/02/dicom-wireshark-300x136.webp 300w, https://static.digihunch.com/wp-content/uploads/2023/02/dicom-wireshark-768x349.webp 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On the ingress controller side, most ingress controllers use SNI to drive request routing (because Host field in payload is encrypted). For example, Traefik Proxy has &lt;a href="https://traefik.io/blog/announcing-traefik-proxy-2-7/"&gt;HostSNI&lt;/a&gt; matching rule. With Istio, the &lt;a href="https://istio.io/latest/docs/reference/config/networking/gateway/#Port"&gt;document&lt;/a&gt; states that: TLS implies the connection will be routed based on the SNI header to the destination. With DICOM traffic the Ingress also expects the client to make use of SNI extension in the TLS ClientHello message. The ingress supports multiple sites so the SNI even has an impact of which TLS certificate the ingress serves to the client. We can use openssl to examine which certificate an ingress serves. For example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl s_client -showcerts -connect dicom.orthweb.com:11112 -servername dicom.orthweb.com &amp;lt; /dev/null&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl s_client -showcerts -connect dicom.orthweb.com:11112 &amp;lt; /dev/null&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The second command without &lt;code&gt;-servername&lt;/code&gt; switch constructs an ClientHello message without SNI. The ingress may not have a clue of what certificate to serve, depending on its own implementation of TLS protocol. We can also force TLS version with a switch such as &lt;code&gt;-tls1_2&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When it comes to open-source DICOM client, neither &lt;code&gt;dcm4che&lt;/code&gt; or &lt;code&gt;dcmtk&lt;/code&gt; puts SNI in the TLS request. This created some limitation with my testing. Luckily I do not have multiple routing destinations for now so I only need to direct all DICOM traffic to a service. When I use &lt;a href="https://github.com/digihunch/korthweb/blob/13984a4057eb8b68bc7d15626635e171fdb0f69f/manual/orthanc.yaml#L187"&gt;Istio ingress&lt;/a&gt;, I was able to set hosts to &amp;#8220;*&amp;#8221; so the Ingress does not care missing SNI extension in the client request. With Traefik proxy, I had to set &lt;a href="https://github.com/digihunch/korthweb/blob/27a1a6e2f80b811c0ac593c95f912987a888ca03/helm/orthanc/templates/ingressroute.yaml#L11"&gt;sniStrict&lt;/a&gt; to false, and also forgo client certificate check. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The workaround is different per ingress implementation. Even worse, depending on what the available workaround can achieve, the testing steps vary as well. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For example, I perform DICOM validation (Istio ingress) with the steps below:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# bhs: generate client key pair&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl req -new -newkey rsa:4096 -nodes -subj /C&lt;span style="color:#f92672"&gt;=&lt;/span&gt;CA/ST&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Ontario/L&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Waterloo/O&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Digihunch/OU&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Imaging/CN&lt;span style="color:#f92672"&gt;=&lt;/span&gt;dcmclient.bhs.orthweb.com/emailAddress&lt;span style="color:#f92672"&gt;=&lt;/span&gt;dcmclient@www.digihunch.com -keyout bhs.client.key -out bhs.client.csr&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# bhs: export intermediate CA credentials&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n bhs-orthweb get secret int-ca-secret -o jsonpath&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;{.data.tls\.key}&amp;#39;&lt;/span&gt; | base64 -d &amp;gt; bhs.int.ca.key&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n bhs-orthweb get secret int-ca-secret -o jsonpath&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;{.data.tls\.crt}&amp;#39;&lt;/span&gt; | base64 -d &amp;gt; bhs.int.ca.crt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# bhs: get intermediate CA to sign client cert &lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl x509 -req -sha256 -days &lt;span style="color:#ae81ff"&gt;365&lt;/span&gt; -in bhs.client.csr -CA bhs.int.ca.crt -CAkey bhs.int.ca.key -set_serial &lt;span style="color:#ae81ff"&gt;01&lt;/span&gt; -out bhs.client.crt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# bhs: validate web request (without client certificate)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -HHost:web.bhs.orthweb.com -k -X GET https://web.bhs.orthweb.com:443/app/explorer.html -u admin:orthanc --cacert bhs.int.ca.crt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# bhs: validate DICOM c-echo request (with client certificate)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echoscu -aet TESTER -aec ORTHANC -d +tls bhs.client.key bhs.client.crt -rc +cf bhs.int.ca.crt dicom.bhs.orthweb.com &lt;span style="color:#ae81ff"&gt;11112&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# bhs: validate DICOM c-store request (with client certificate)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;storescu -aet TESTER -aec ORTHANC -d +tls bhs.client.key bhs.client.crt -rc +cf bhs.int.ca.crt dicom.bhs.orthweb.com &lt;span style="color:#ae81ff"&gt;11112&lt;/span&gt; DICOM_CT/0001.dcm&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;On the other hand, for Traefik ingress, I have to use anonymous TLS without client certificate:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echoscu -aet TESTER -aec ORTHANC -d +tla -ic dicom.orthweb.com &lt;span style="color:#ae81ff"&gt;11112&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;storescu -aet TESTER -aec ORTHANC -d +tla -ic dicom.orthweb.com &lt;span style="color:#ae81ff"&gt;11112&lt;/span&gt; DICOM_CT/123.dcm&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The complexities with different test paths are consequences of the missing SNI capability in both DICOM toolkits. Unfortunately, the developers of the two DICOM tools are not aware of these consequences. I tried to contact &lt;code&gt;dcmtk&lt;/code&gt; about this and will see what happens.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;TLS profile&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Another setting to pay close attention to is the security profile for TLS communication. These profiles defines the behaviours of &lt;code&gt;dcmtk&lt;/code&gt; when it establishes TLS connection. The dcmtk has the following security profiles:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&amp;#8211;profile-&lt;strong&gt;bcp195-nd&lt;/strong&gt; (+py default): Non-downgrading BCP 195 TLS Profile&lt;/li&gt;&#10;&lt;li&gt;&amp;#8211;profile-&lt;strong&gt;bcp195&lt;/strong&gt; (+px): BCP 195 TLS Profile&lt;/li&gt;&#10;&lt;li&gt;&amp;#8211;profile-&lt;strong&gt;bcp195-ex&lt;/strong&gt; (+pz): Extended BCP 195 TLS Profile&lt;/li&gt;&#10;&lt;li&gt;&amp;#8211;profile-&lt;strong&gt;aes&lt;/strong&gt; (+pa): AES TLS Secure Transport Connection Profile (retired)&lt;/li&gt;&#10;&lt;li&gt;&amp;#8211;profile-&lt;strong&gt;null&lt;/strong&gt; (+pn): Authenticated unencrypted communication (retired, was used in IHE ATNA)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The two at the bottom have been &lt;a href="https://www.dicomstandard.org/news/supplements/view/tls-security-profiles"&gt;retired&lt;/a&gt;. The current profiles are all based on &lt;a href="https://datatracker.ietf.org/doc/bcp195/"&gt;BCP195&lt;/a&gt;. BCP (best current practice) are sub-series of the corresponding RFC document series. The current revision of DICOM standard discusses bcp195-nd, bcp195 and bcp195-ex profiles in DICOM standard &lt;a href="https://dicom.nema.org/MEDICAL/Dicom/2022d/output/pdf/part15.pdf"&gt;chapter PS 3.15&lt;/a&gt; (B.9-B.11). For example, bcp195-nd requires that:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Implementation shall not negotiate TLS 1.0 or 1.1&lt;/li&gt;&#10;&lt;li&gt;Client and server shall prefer strict TLS configuration (as opposed to startTLS)&lt;/li&gt;&#10;&lt;li&gt;Ciphers that should be supported.&lt;/li&gt;&#10;&lt;li&gt;Recommend port 2762&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;These BCP profiles were incorporated into DICOM standard since 2018 and are all based on &lt;a href="https://www.rfc-editor.org/bcp/bcp195.txt"&gt;BCP195&lt;/a&gt;. BCP 195 states in section 3.6&lt;/p&gt;&#10;&lt;blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;/p&gt;&#10;&lt;cite&gt;3.6. Server Name Indication&lt;br&gt;TLS implementations MUST support the Server Name Indication (SNI)&lt;br&gt;extension defined in Section 3 of [RFC6066] for those higher-level&lt;br&gt;protocols that would benefit from it, including HTTPS. However, the&lt;br&gt;actual use of SNI in particular circumstances is a matter of local&lt;br&gt;policy.&lt;br&gt;Rationale: SNI supports deployment of multiple TLS-protected virtual&lt;br&gt;servers on a single address, and therefore enables fine-grained&lt;br&gt;security for these virtual servers, by allowing each one to have its&lt;br&gt;own certificate.&lt;/cite&gt;&lt;/blockquote&gt;&#10;&lt;p class="wp-block-paragraph"&gt;So technically, missing SNI is considered incompliant.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Summary&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I find myself switching between &lt;code&gt;dcmtk&lt;/code&gt; and &lt;code&gt;dcm4che&lt;/code&gt; back and forth in the past. This time, I spent some time hoping to settle with the better tool this time. The effort is insightful but not fruitful. It is unfortunate to realize that neither supports SNI so I had to compromise the feature of my deployment. Hopefully one of those tools will catch up.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2023/01/github-action-gotchas/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;GitHub Action Gotchas&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2023/03/a-taste-of-iot-device-tracking/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;A taste of IoT device tracking&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Service Proxy – from Nginx to Envoy</title><link>https://static.digihunch.com/2022/03/from-nginx-to-envoy-proxy/</link><pubDate>Wed, 09 Mar 2022 21:36:00 -0400</pubDate><guid>https://static.digihunch.com/2022/03/from-nginx-to-envoy-proxy/</guid><description>&lt;img src="https://static.digihunch.com/wp-content/uploads/2025/04/feature-nginx2envoy.webp" alt="Featured image of post Service Proxy – from Nginx to Envoy" /&gt;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Update&lt;/strong&gt; (Nov 20, 2022): 1. Envoy&amp;#8217;s configuration schema can be hard to get used to. It is lacking examples because the documentation is mostly generated. Use its &lt;a href="https://github.com/envoyproxy/examples"&gt;examples&lt;/a&gt; directory to find real-life configuration examples. 2. the Envoy implementation in the example project has been reverted in favour of Nginx.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Envoy proxy is the underlying technology for Istio, as well as a number of other service mesh products, such as AppMesh (AWS), Consul (Hashicorp) and OpenServiceMesh (Azure). Most of the capabilities of Isito is ultimately provided by Envoy proxy. Envoy has a &lt;a href="https://www.envoyproxy.io/docs/envoy/v1.10.0/intro/comparison"&gt;page&lt;/a&gt; outlining its differences with similar technologies. I decided to take a look into Envoy by replacing Nginx with it.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-rate-limiting-and-circuit-breaker"&gt;Rate limiting and Circuit Breaker&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In most SDLC, it is application developers that create backend APIs or server applications. Most developers specializes in application features, and cannot fathom all the nuances with the TCP/IP network stack. Nginx allows them to push network concerns (non-business features) to a dedicated proxy to handle the dynamics in network connection. Nginx can be configured as both a reverse proxy (handling incoming connection on behalf of the process) and a forward proxy (handling outgoing connection on behalf of the process). This is the prototype of sidecar pattern, an important idea behind service mesh. For example, when a sudden increase in connection to the server-side application, the server process could be either unresponsive (refer to &amp;#8220;the &lt;a href="https://queue.acm.org/detail.cfm?id=1854041"&gt;queuing knee&lt;/a&gt;&amp;#8220;, and &lt;a href="https://en.wikipedia.org/wiki/Little%27s_law"&gt;Little&amp;#8217;s Law&lt;/a&gt;), or OOM killed. When such interruptions are not automatically recovered, a downtime is caused. This traditionally requires some congestion control strategy for TCP/IP queue but two features provided by a network proxy can help circumvent this situation: rate limiting, and circuit breaking. Rate limiting keeps more requests above threshold from entering the queue. Circuit breaker releases downstream pressure by cutting out existing in-queue request. Nginx added both over the years but &lt;a href="https://www.nginx.com/blog/microservices-reference-architecture-nginx-circuit-breaker-pattern/"&gt;Circuit breaker&lt;/a&gt; still remains a premium feature exclusive to Nginx Plus. Envoy on the other hand has them free when it was launched.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Envoy also supports other advanced traffic management such as traffic shaping, and mirroring. It is on top of those features that Istio introduces its own abstraction such as virtual service, destination rules to its users. In that sense, we can think of Istio as a configurator (control plane) for Envoy proxy (data plane), similar to Ansible to Nginx proxy instances.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-dynamic-configuration-via-api"&gt;Dynamic Configuration via API&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I used Nginx previously with traditional environment and loved its flexibility. As the system grows, I started to feel the pain of management overhead. With one of the production system, there were 25 + instances of Nginx each running on a VM and I managed configuration files with Ansible. Ansible pushes out configuration files and triggers a reload from each Nginx instance. In the cloud-native era where Pods are ephemeral, this kind of overhead would snowball to an unmanageable level. Envoy was designed for cloud-native applications, with all these kinds of problems in mind. Envoy has dynamic configuration. The majority of the configurations can be pulled from &lt;a href="https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/operations/dynamic_configuration"&gt;xDS API&lt;/a&gt;, or file system. Updating configuration drains connections gracefully without runtime having to reload the file. The idea of centrally managing Nginx instances with Ansible, also evolved into the concept of control plane.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-tls-origination"&gt;TLS origination&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the Orthweb project, I used Nginx to proxy TLS and HTTP traffic, and performed TLS termination on both ports. This is know as TLS offloading. The traffic between the proxy and the upstream service takes place in the clear, even though they do not travel across different network interfaces in most cases. For a true end-to-end encryption, it is helpful to also encrypt the traffic between proxy and upstream server. This requires the capability of securing TCP traffic to upstream server. With Nginx, the ability to &lt;a href="https://dzone.com/articles/nginx-rate-limiting"&gt;secure HTTP traffic to upstream&lt;/a&gt; server is offered in open-source. The ability to &lt;a href="https://docs.nginx.com/nginx/admin-guide/security-controls/securing-tcp-traffic-upstream/"&gt;secure TCP traffic&lt;/a&gt; is available in Nginx Plus, or with self-compiled binary. In Envoy, both are available using the UpstreamTlsContext configuration.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-more-pros"&gt;More pros&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In another &lt;a href="https://static.digihunch.com/2020/01/nginx-as-a-reverse-proxy-for-nifi/"&gt;post&lt;/a&gt;, I also discussed Nginx as a LDAP proxy to front services such as Kibana and Nifi. It requires a proxy service (ldap-auth in this case), to defer auth to third party. Envoy has this capability using a &lt;a href="https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/ext_authz_filter"&gt;filter&lt;/a&gt; with extension for external authorization. Istio also exposes this capability, an enabler for the configuration proposed in my previous &lt;a href="https://static.digihunch.com/2022/02/istio-external-authorization/"&gt;post&lt;/a&gt;. Envoy also uses WebAssembly for its extensibility.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Another useful feature is protocol detection. It can use filters to detect protocol (TLS or regular TCP) and route traffic to predefined destination.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Performance wise, this &lt;a href="https://www.loggly.com/blog/benchmarking-5-popular-load-balancers-nginx-haproxy-envoy-traefik-and-alb/"&gt;benchmark&lt;/a&gt; from 2018 ran a comparison among the popular options where Envoy leads by a margin.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Observability (logging, metrics and tracing) are well supported in Envoy. User can configure format of logs that takes effect immediately. There are many metrics that works with Prometheus and they are expandable using filters. On the tracing side, Envoy supports integration with jaeger, zipkin and datadog.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-basics-of-envoy"&gt;Basics of Envoy&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The configuration of Envoy is more involving. There is an Envoy course by &lt;a href="https://academy.tetrate.io/"&gt;Tetrate&lt;/a&gt;, as well as two blog entries for envoy 101: Envoy as &lt;a href="https://www.tetrate.io/blog/envoy-101-configuring-envoy-as-a-gateway/"&gt;gateway proxy&lt;/a&gt; and File-based &lt;a href="https://www.tetrate.io/blog/envoy-101-file-based-dynamic-configurations/"&gt;dynamic configuration&lt;/a&gt;. Another good way to get started is the Sandboxes &lt;a href="https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/#start-sandboxes"&gt;projects&lt;/a&gt;, which covers a number of different areas of configuration. The admin port (by default at port 9901. Istio&amp;#8217;s default is 15000) provides helpful information. If we need to turn on debug on some features, we can do so with curl:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -X POST http://localhost:9901/logging?client&lt;span style="color:#f92672"&gt;=&lt;/span&gt;debug&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Stats are exposed at the same port:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -X GET http://localhost:9901/stats&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;When packets are received at a listener, the is first processed by listener filters. Then, depending on filter match, one or more network filter chains will further process the packet, including further actions, as illustrated below:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="726" src="https://static.digihunch.com/wp-content/uploads/2024/07/envoy-listener-filters-1024x726.png" alt="" class="wp-image-11357" srcset="https://static.digihunch.com/wp-content/uploads/2024/07/envoy-listener-filters-1024x726.png 1024w, https://static.digihunch.com/wp-content/uploads/2024/07/envoy-listener-filters-300x213.png 300w, https://static.digihunch.com/wp-content/uploads/2024/07/envoy-listener-filters-768x544.png 768w, https://static.digihunch.com/wp-content/uploads/2024/07/envoy-listener-filters-1536x1089.png 1536w, https://static.digihunch.com/wp-content/uploads/2024/07/envoy-listener-filters.png 1920w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Envoy supports dynamic configuration, which uses a set of discovery services (xDS) APIs. Some of the important xDS APIs include:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;LDS (Listener Discovery Service) &amp;#8211; allows you to add listeners dynamically while Envoy is running&lt;/li&gt;&#10;&lt;li&gt;RDS (Route Discovery Service) &amp;#8211; allows you to dynamically update routes for HTTP connection managers&lt;/li&gt;&#10;&lt;li&gt;CDS (Cluster Discovery Service) &amp;#8211; allows you to update cluster definitions dynamically&lt;/li&gt;&#10;&lt;li&gt;EDS (Endpoint Discovery Service) &amp;#8211; allows you to add or remove endpoints dynamically&lt;/li&gt;&#10;&lt;li&gt;Secret DS&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The relation can be illustrated in this diagram below:&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://i0.wp.com/www.tetrate.io/wp-content/uploads/2020/11/Screen-Shot-2020-11-18-at-12.17.08-PM-1.png?resize=1044%2C638&amp;amp;ssl=1" alt="Envoy - xDS configuration API overview"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This &lt;a href="https://www.tetrate.io/blog/envoy-101-file-based-dynamic-configurations/"&gt;post&lt;/a&gt; from Tetrate has more examples.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-nginx-to-envoy"&gt;Nginx to Envoy &lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For the advantages of Envoy, I decided to migrate from Nginx to Envoy on my &lt;a href="https://github.com/digihunch/orthweb"&gt;Orthweb&lt;/a&gt; project. Using Envoy as service proxy is not where Envoy is mostly used (as sidecar), but it is how Envoy was originally used at Lyft to replace ELB in 2015.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The original Nginx configuration was referenced in this &lt;a href="https://static.digihunch.com/2020/11/medical-imaging-web-server-deployment-pipeline/"&gt;old blog post&lt;/a&gt;. The Envoy setup also covers both TCP (&lt;a href="https://static.digihunch.com/2020/11/how-imaging-devices-talk-to-each-other-tip-in-dicom/"&gt;DICOM&lt;/a&gt;) and HTTP (HTTPS) traffic. For HTTP traffic, it also encrypts the traffic to upstream. Below is what it looks like:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;admin&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;address&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;socket_address&lt;/span&gt;: { &lt;span style="color:#f92672"&gt;address&lt;/span&gt;: &lt;span style="color:#f92672"&gt;0.0.0.0, port_value&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;9901&lt;/span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;static_resources&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;listeners&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;https_listener&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;address&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;socket_address&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;address&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;0.0.0.0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;port_value&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;443&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;filter_chains&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;filters&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;envoy.filters.network.http_connection_manager&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;typed_config&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;@type&amp;#34;: &lt;/span&gt;&lt;span style="color:#ae81ff"&gt;type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;codec_type&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;AUTO&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;stat_prefix&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ingress_http&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;route_config&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;local_route&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;virtual_hosts&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;app&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;domains&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#e6db74"&gt;&amp;#34;*&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;routes&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;match&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;prefix&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;route&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;cluster&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;service-https&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;http_filters&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;envoy.filters.http.router&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;transport_socket&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;envoy.transport_sockets.tls&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;typed_config&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;@type&amp;#34;: &lt;/span&gt;&lt;span style="color:#ae81ff"&gt;type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;common_tls_context&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;tls_certificates&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;certificate_chain&lt;/span&gt;: {&lt;span style="color:#f92672"&gt;&amp;#34;filename&amp;#34;: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;/etc/ssl/certs/site.pem&amp;#34;&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;private_key&lt;/span&gt;: {&lt;span style="color:#f92672"&gt;&amp;#34;filename&amp;#34;: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;/etc/ssl/certs/site.pem&amp;#34;&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;dicomtls_listener&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;address&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;socket_address&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;address&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;0.0.0.0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;port_value&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;11112&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;filter_chains&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;filters&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;envoy.filters.network.tcp_proxy&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;typed_config&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;@type&amp;#34;: &lt;/span&gt;&lt;span style="color:#ae81ff"&gt;type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;stat_prefix&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;downstream_cx_total&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;cluster&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;service-dicomtls&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;transport_socket&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;envoy.transport_sockets.tls&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;typed_config&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;@type&amp;#34;: &lt;/span&gt;&lt;span style="color:#ae81ff"&gt;type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;common_tls_context&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;tls_certificates&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;certificate_chain&lt;/span&gt;: {&lt;span style="color:#f92672"&gt;&amp;#34;filename&amp;#34;: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;/etc/ssl/certs/site.pem&amp;#34;&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;private_key&lt;/span&gt;: {&lt;span style="color:#f92672"&gt;&amp;#34;filename&amp;#34;: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;/etc/ssl/certs/site.pem&amp;#34;&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;validation_context&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;allow_expired_certificate&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;trusted_ca&lt;/span&gt;: {&lt;span style="color:#f92672"&gt;&amp;#34;filename&amp;#34;: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;/etc/ssl/certs/site.pem&amp;#34;&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;require_client_certificate&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;clusters&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;service-https&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;type&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;STRICT_DNS&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;lb_policy&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ROUND_ROBIN&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;load_assignment&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;cluster_name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;service-https&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;endpoints&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;lb_endpoints&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;endpoint&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;address&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;socket_address&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;address&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;orthanc-backend&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;port_value&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;8042&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;transport_socket&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;envoy.transport_sockets.tls&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;typed_config&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;@type&amp;#34;: &lt;/span&gt;&lt;span style="color:#ae81ff"&gt;type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;service-dicomtls&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;type&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;STRICT_DNS&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;lb_policy&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ROUND_ROBIN&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;load_assignment&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;cluster_name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;service-dicomtls&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;endpoints&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;lb_endpoints&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;endpoint&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;address&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;socket_address&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;address&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;orthanc-backend&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;port_value&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;4242&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;layered_runtime&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;layers&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;static_layer_0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;static_layer&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;envoy&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;resource_limits&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;listener&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;https_listener&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;connection_limit&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1000&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;overload&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;global_downstream_max_connections&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;5000&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;From Nginx to Envoy, to achieve nearly the same functionalities, it takes 100 lines of configuration instead of less than 30. The configuration also appears more abstract, which is one of the cons of Envoy to consider before the migration.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Update from Jan 2025 &amp;#8211; To align with what most uses in Orthanc community, the project switched back to using Nginx. The envoy configuration is kept &lt;a href="https://gist.github.com/digihunch/e3192481f0a54e018442d9629562d40f"&gt;here&lt;/a&gt;.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2022/02/istio-external-authorization/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Istio External Authorization via OIDC&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2022/03/istio-operation-gotchas/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Istio Operation Gotchas&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>From Ingress to CRD: why my solution needs Istio Gateways on Kubernetes platforms</title><link>https://static.digihunch.com/2021/12/from-ingress-to-gateway-why-you-need-istio-gateways-on-kubernetes-platforms/</link><pubDate>Wed, 29 Dec 2021 22:50:16 -0400</pubDate><guid>https://static.digihunch.com/2021/12/from-ingress-to-gateway-why-you-need-istio-gateways-on-kubernetes-platforms/</guid><description>&lt;img src="https://static.digihunch.com/wp-content/uploads/2025/04/feature-ingress-crd.webp" alt="Featured image of post From Ingress to CRD: why my solution needs Istio Gateways on Kubernetes platforms" /&gt;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Update&lt;/strong&gt;: also read my other article &lt;a href="https://medium.com/slalom-build/managing-ingress-traffic-on-kubernetes-platforms-ebd537cdfb46"&gt;here&lt;/a&gt; on the different generations of ingress technologies.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In my &lt;a href="https://github.com/digihunch/korthweb"&gt;Korthweb&lt;/a&gt; project I was researching for the best ingress mechanism for HTTP and TCP workload, both of which need to be secured. I started with Kubernetes Ingress but eventually decided to go with Istio Gateway. This blog post is about the justification. In this essay, I will make the distinction between Ingress and Gateway and explain why a Kubernetes platform needs the latter going forward.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The word ingress can be used either to indicate ingress resource (in conjunction with ingress controller) in the context of Kubernetes cluster, or more generally to indicate the technology (e.g. provided by service mesh) to direct north-south traffic, originated from the outside into the workload running within the cluster. I will use both meanings of the word ingress throughout the article. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-background"&gt;Background&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In my previous &lt;a href="https://static.digihunch.com/2021/07/traffic-management-in-kubernetes-service-and-ingress/"&gt;post&lt;/a&gt;, I discussed Kubernetes Service object and native Ingress. In short, a Service object addresses the problem of exposing a workload (Pod or Deployment), operating at layer 3-4. A &lt;span style="text-decoration: underline;"&gt;ClusterIP&lt;/span&gt; type of service exposes workload within the cluster only, and therefore is only used by internal services that are consumed by workload in the same cluster. A &lt;span style="text-decoration: underline;"&gt;NodePort&lt;/span&gt; type of service exposes workload to outside of the cluster, using the node&amp;#8217;s IP address and port range available on the node, a great step forward but still quite inconvenient and subject to the limitation of using Node&amp;#8217;s IP and Port. A &lt;span style="text-decoration: underline;"&gt;LoadBalancer&lt;/span&gt; type of Service brings separate IP address and port for running a service. To back up this type of service, the platform has to provide a load balancer with its own IP address and port range to manage. The implementation is platform specific e.g. Metal LB for Minikube, Azure Load Balancer for AKS, NLB for AWS EKS. In this type of service, Node Ports still exists but are not exposed to the outside world. Instead, they are only exposed to the backend of Load Balancer, whose front-end port is exposed to the outside world. In both NodePort and LoadBalancer types of Service, the kube-proxy process plays a role on each node to direct traffic into NodePort to target Pods.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Kubernetes Ingress, on the other hand, targets issues above layer 4, for example, path-based routing and is therefore mostly used for HTTP and HTTPS traffic. It is implemented by an &lt;a href="https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/"&gt;Ingress Controller&lt;/a&gt; and now there has been a diverse ecosystem of those ingress controllers. In many implementations (e.g. Azure Application Gateway Ingress Controller, AGIC), the ingress themselves usually come with a load balancer managed by themselves. This eliminates the need for a separate LoadBalancer type of Service just for L4 capability. As a result, Ingress (with both L4 and L7 capabilities) is usually deployed along with ClusterIP type Service. &lt;a href="https://docs.microsoft.com/en-us/azure/aks/ingress-basic"&gt;Here&lt;/a&gt; is an example. The declaration of the Ingress in this case usually contains numerous lines of annotations in order to communicate the specification to the implementation.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-requirement"&gt;Requirement&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Using Kubernetes Ingress along with Service of ClusterIP type seems perfect to address the majority of use cases. However, it has its blind spots. My Korthweb project deals with &lt;a href="https://static.digihunch.com/2020/11/how-imaging-devices-talk-to-each-other-tip-in-dicom/"&gt;DICOM&lt;/a&gt; traffic (a protocol on top of TCP) over TLS as well as HTTP traffic, which can be broken down as:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;The ability to proxy TCP traffic over an arbitrary TCP port&lt;/li&gt;&#10;&lt;li&gt;The ability to terminate TLS/SSL encryption &lt;span style="text-decoration: underline;"&gt;for TCP traffic&lt;/span&gt; &lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Ingress &lt;a href="https://kubernetes.io/docs/concepts/services-networking/ingress/#what-is-ingress"&gt;documentation&lt;/a&gt; of Kubernetes clearly states that: an Ingress does not expose arbitrary ports or protocols. Exposing services other than HTTP and HTTPS to the internet typically uses a service of type&amp;nbsp;&lt;a href="https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport"&gt;Service.Type=NodePort&lt;/a&gt;&amp;nbsp;or&amp;nbsp;&lt;a href="https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer"&gt;Service.Type=LoadBalancer&lt;/a&gt;. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The stance of community-driven Nginx ingress controller can be found in the &lt;a href="https://kubernetes.github.io/ingress-nginx/user-guide/exposing-tcp-udp-services/"&gt;documentation&lt;/a&gt;, which suggests the use of Service object for arbitrary TCP port. There is some unofficial &lt;a href="https://github.com/kubernetes/ingress-nginx/issues/636"&gt;claims&lt;/a&gt; of workaround available but I&amp;#8217;m not confident.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I also went through a number of other Ingress providers but to my disappointment, the only product that supports it seems to be Traefik:&lt;/p&gt;&#10;&lt;figure class="wp-block-table"&gt;&lt;table class="has-very-light-gray-to-cyan-bluish-gray-gradient-background has-background"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Product&lt;/td&gt;&lt;td&gt;Requirement #1&lt;/td&gt;&lt;td&gt;Requirement #2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;F5 driven Nginx Ingress Controller&lt;/td&gt;&lt;td&gt;Supported&lt;/td&gt;&lt;td&gt;There have also been &lt;a href="https://github.com/nginxinc/kubernetes-ingress/issues/831#issuecomment-578206759"&gt;requests&lt;/a&gt; for TLS termination for TCP traffic but the request has not been closed as of yet.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;meta charset="utf-8"&gt;HA Proxy Ingress controller&lt;/td&gt;&lt;td&gt;Supported&lt;/td&gt;&lt;td&gt;There&amp;#8217;s no mention of this in &lt;a href="https://haproxy-ingress.github.io/docs/"&gt;documentation&lt;/a&gt;.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Kong&amp;#8217;s Kubernetes Controller (with TCPIngress CRD)&lt;/td&gt;&lt;td&gt;Supported&lt;/td&gt;&lt;td&gt;&lt;a href="https://docs.konghq.com/kubernetes-ingress-controller/2.1.x/guides/using-tcpingress/"&gt;Documentation&lt;/a&gt; claims support through SNI-based routing. However, the &lt;a href="https://docs.konghq.com/kubernetes-ingress-controller/2.1.x/guides/using-tcpingress/#tls-sni-based-routing"&gt;example&lt;/a&gt; demonstrates it using self-signed certificate only. BYO cert not supported according to this GitHub &lt;a href="https://github.com/Kong/kong/issues/8151"&gt;issue&lt;/a&gt;.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;meta charset="utf-8"&gt;Traefik Lab&amp;#8217;s &lt;meta charset="utf-8"&gt;&lt;a href="https://doc.traefik.io/traefik/routing/providers/kubernetes-crd/#kind-ingressroute"&gt;IngressRoute&lt;/a&gt; (with IngressRouteTCP CRD)&lt;/td&gt;&lt;td&gt;Supported&lt;/td&gt;&lt;td&gt;Documentation seems to suggest that it is supported by &lt;a href="https://doc.traefik.io/traefik/routing/providers/kubernetes-crd/"&gt;IngressRoute&lt;/a&gt;.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;My general impression is that Req #2 isn&amp;#8217;t very popular so the providers either don&amp;#8217;t support it or delaying the implementation. Even if they do, the CRD used for TCP ingress is different than HTTP&amp;#8217;s. For example with Traefik Lab, the CRD for TCP is IngressRouteTCP, and for HTTP it is IngressRoute.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-introduction-to-gateway"&gt;Introduction to Gateway&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In addition to functional shortages, according to &lt;a href="https://kubernetes.io/blog/2021/04/22/evolving-kubernetes-networking-with-the-gateway-api/"&gt;this&lt;/a&gt; blog post, there are signs of fragmentation into different but strikingly similar CRDs and overloaded annotations. In Kubecon 2019, a group of contributors discussed the evolution of Ingress into Gateway. Below are the two key slides stolen from their &lt;a href="https://kubernetes.io/blog/2021/04/22/evolving-kubernetes-networking-with-the-gateway-api/"&gt;presentation&lt;/a&gt;:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://static.digihunch.com/wp-content/uploads/2021/12/image.png" alt="" class="wp-image-3269" width="719" height="280"/&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://static.digihunch.com/wp-content/uploads/2021/12/image-1.png" alt="" class="wp-image-3270" width="716" height="336"/&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This sums up how the concept of Gateway is different from Ingress. Gateway is an instantiation of a given LB. It works along with Route to achieve the functions brought by an Ingress. Gateway as a resource type of its own makes management easier at L4-L6 by a separate team. Routing at L7 is offloaded to &amp;#8220;Route&amp;#8221; resource type.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This conceptual evolution gives rise to the &lt;a href="https://gateway-api.sigs.k8s.io/"&gt;Gateway API&lt;/a&gt; open source &lt;a href="https://github.com/kubernetes-sigs/gateway-api"&gt;project&lt;/a&gt; managed by the SIG-NETWORK community. &lt;a href="https://github.com/kubernetes-sigs/gateway-api"&gt;Gateway API&lt;/a&gt; is a collection of resources that model service networking in Kubernetes, including GatewayClass, Gateway, HTTPRoute, TCPRoute, Service etc. The aim of this initiative is to evolve Kubernetes service networking through expressive, extensive and role-oriented interfaces that are implemented by many vendors and have broad industry support.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://gateway-api.sigs.k8s.io/images/api-model.png" alt="Gateway API Model"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The diagram above stolen from Gateway API website illustrate the management model for each resource.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-kubernetes-gateway-implementations"&gt;Kubernetes Gateway Implementations&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Gateway API is still a fairly young initiative and all the reference &lt;a href="https://gateway-api.sigs.k8s.io/implementations/"&gt;implementations&lt;/a&gt; are either work-in-progress or in early stages. In the last section, we learned that in the journey from Ingress to Gateway, the standardization initiative comes a bit behind the implementation efforts. For many supporters, the natural strategy is to continue with existing proprietary ingress controller implementation, and retrofit their technology to the emerging Gateway API along the way.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Because of that, we can see many market players with multiple flavours of implementations, typically one that evolves from their original product offering, with higher adoption rate and maturity level, and one that conforms to Gateway API specification. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For example, Traefik Labs has its &lt;a href="https://doc.traefik.io/traefik/routing/providers/kubernetes-gateway/"&gt;Gateway API implementation&lt;/a&gt; in experimental stage. Its own implementation consists of (standard) Kubernetes &lt;a href="https://doc.traefik.io/traefik/routing/providers/kubernetes-ingress/"&gt;Ingress&lt;/a&gt; and Kubernetes &lt;a href="https://doc.traefik.io/traefik/routing/providers/kubernetes-crd/"&gt;IngressRoute&lt;/a&gt; (based on custom resource with support of advanced features such as TCP route). Another example is the HAProxy &lt;a href="https://www.haproxy.com/documentation/kubernetes/latest/usage/ingress/"&gt;Ingress&lt;/a&gt;, a community driven ingress controller implementation for HAProxy. Starting from its version 0.13, it partially supports the Gateway API&amp;#8217;s v1alpha1 specification. &lt;a href="https://haproxy-ingress.github.io/docs/configuration/gateway-api/#conformance"&gt;Here&lt;/a&gt; is the conformance statement. &lt;a href="https://projectcontour.io/getting-started/"&gt;Contour&lt;/a&gt; as a CNCF project for ingress controller has support for Gateway API at &lt;a href="https://projectcontour.io/guides/gateway-api/"&gt;alpha&lt;/a&gt; version. Kong&amp;#8217;s &lt;a href="https://github.com/kong/kubernetes-ingress-controller"&gt;Kubernetes Ingress Controller&lt;/a&gt; follows the same path.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Gateway functionality is also provided as part of Service Mesh product offering, and we can see some reference implementation by service mesh providers. Istio has its own Gateway implementation but tries to adapt to &lt;a href="https://istio.io/latest/docs/tasks/traffic-management/ingress/gateway-api/#differences-from-istio-apis"&gt;Kubernetes Gateway API&lt;/a&gt;. Hashicorp Consul also claims to be building support for Kubernetes Gateway API.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Until the Gateway API project matures, it is not recommended to use Gateway implementations that conforms to it, unless you intend to be their Guinea Pig. For my project, I chose Istio Gateway.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-istio-gateway-implementation"&gt;Istio Gateway implementation&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As discussed, when the Gateway standard is still in its infancy, I chose a non-standard implementation of Gateway, even though it may include some CRDs. Out of the many Gateway implementations, I choose Istio mainly for two reasons.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;First, I need a Gateway as part of service mesh because service mesh provides many other features that are needed in the platform. One of the goals of &lt;a href="https://static.digihunch.com/2021/11/from-microservice-to-service-mesh/"&gt;Service Mesh&lt;/a&gt; is to provide commonly used features (traffic management, observability, security, extensibility) in a commodity layer on top of Kubernetes. Gateway is not the only thing I need out of this commodity layer. With one install of Istio, many common platform-level problems are also addressed (e.g. mTLS, traceability, etc). I&amp;#8217;ve compared three major service mesh technologies as below:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-regular"&gt;&lt;table class="has-very-light-gray-to-cyan-bluish-gray-gradient-background has-background"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Linkderd&lt;/th&gt;&lt;th&gt;Istio&lt;/th&gt;&lt;th&gt;Consul&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&amp;#8211; lightweight&lt;br&gt;&amp;#8211; uses linkerd2-proxy&lt;br&gt;&amp;#8211; the original service mesh&lt;br&gt;&amp;#8211; no Gateway implementation (deal breaker in my use case)&lt;/td&gt;&lt;td&gt;&amp;#8211; feature rich&lt;br&gt;&amp;#8211; uses Envoy proxy&lt;br&gt;&amp;#8211; complex but getting better&lt;/td&gt;&lt;td&gt;&amp;#8211; initially a service discovery and distributed key-value store&lt;br&gt;&amp;#8211; lacks observability features but getting better&lt;br&gt;&amp;#8211; uses Envoy proxy&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;It is worth-noting that there are some initiatives to standardize service mesh (e.g. &lt;a href="https://smi-spec.io/"&gt;SMI&lt;/a&gt;) with reference implementation such as &lt;a href="https://openservicemesh.io/"&gt;OpenServiceMesh&lt;/a&gt;. The standard is too weak to be considered important for now compared with maturity and stability. Istio has strong community support. The risk with Istio to acknowledge, is that it is &lt;span style="text-decoration: underline;"&gt;not&lt;/span&gt; following an open-governance model (unlike many other &lt;a href="https://www.cncf.io/news/2020/07/28/the-new-stack-googles-management-of-istio-raises-questions-in-the-cloud-native-community/"&gt;CNCF&lt;/a&gt; projects), which could potentially causes vendor lock-in. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Second, The Istio Gateway features the separation between Gateway and Virtual Service CRDs. The former defines entry point and the latter defines routing rules. This design separates entry points from routing rules, enabling the flexibility of reusing the same Virtual Service for different gateways.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Istio uses Envoy proxy to manage traffic between Pods. Unlike the kube-proxy pattern, the Envoy proxies are side-car containers centrally managed by Istio Control Plane, which also enables other features such as traceability, as illustrated in the diagram below (stolen from &lt;a href="https://thenewstack.io/why-do-you-need-istio-when-you-already-have-kubernetes/"&gt;this&lt;/a&gt; post).&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://cdn.thenewstack.io/media/2021/03/200a2844-image4.png" alt=""/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In addition to Virtual Service, Istio &lt;a href="https://istio.io/latest/docs/reference/config/networking/gateway/"&gt;Gateway&lt;/a&gt; also has the concept of Destination Rules. &lt;a href="https://istio.io/latest/docs/reference/config/networking/virtual-service/"&gt;Virtual Service&lt;/a&gt; defines how to route traffic to different destinations. &lt;a href="https://istio.io/latest/docs/reference/config/networking/destination-rule/"&gt;Destination rule&lt;/a&gt; defines how to split traffic to different subset at the routing destination. The concepts are documented on &lt;a href="https://istio.io/latest/docs/reference/config/networking/"&gt;this&lt;/a&gt; page. For HTTP traffic, the relevant entities can be represented in the diagram below:&lt;/p&gt;&#10;&lt;p class="has-white-background-color has-background wp-block-paragraph"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="661px" viewBox="-0.5 -0.5 661 341" style="max-width:100%;max-height:341px;"&gt;&lt;defs&gt;&lt;/defs&gt;&lt;g&gt;&lt;path d="M 120 275 L 150 275 L 150 95 L 173.63 95" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 178.88 95 L 171.88 98.5 L 173.63 95 L 171.88 91.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;rect x="0" y="210" width="120" height="130" fill="#f5f5f5" stroke="#666666" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 118px; height: 1px; padding-top: 275px; margin-left: 2px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;&lt;ul&gt;&lt;li&gt;hosts&lt;/li&gt;&lt;li&gt;gateways&lt;/li&gt;&lt;li&gt;&lt;b&gt;&lt;font color="#0000cc"&gt;http&lt;/font&gt;&lt;/b&gt;&lt;/li&gt;&lt;li&gt;tls&lt;/li&gt;&lt;li&gt;tcp&lt;/li&gt;&lt;li&gt;exportTo&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="2" y="279" fill="#333333" font-family="Helvetica" font-size="12px"&gt;hostsgatewayshttptls&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 300 95 L 333.63 95" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 338.88 95 L 331.88 98.5 L 333.63 95 L 331.88 91.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 240 160 L 240 180 L 240 160 L 240 173.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 240 178.88 L 236.5 171.88 L 240 173.63 L 243.5 171.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;rect x="180" y="30" width="120" height="130" fill="#f5f5f5" stroke="#666666" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 118px; height: 1px; padding-top: 95px; margin-left: 182px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;&lt;font color="#0000cc"&gt;match&lt;/font&gt;&lt;/b&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;&lt;font color="#0000cc"&gt;route&lt;/font&gt;&lt;/b&gt;&lt;/li&gt;&lt;li&gt;retries&lt;/li&gt;&lt;li&gt;timeout&lt;/li&gt;&lt;li&gt;rewrite&lt;/li&gt;&lt;li&gt;redirect&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="182" y="99" fill="#333333" font-family="Helvetica" font-size="12px"&gt;matchrouteretriestim&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="180" y="210" width="120" height="130" fill="#f5f5f5" stroke="#666666" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 118px; height: 1px; padding-top: 275px; margin-left: 182px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;&lt;ul&gt;&lt;li&gt;uri&lt;/li&gt;&lt;li&gt;scheme&lt;/li&gt;&lt;li&gt;method&lt;/li&gt;&lt;li&gt;headers&lt;/li&gt;&lt;li&gt;port&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="182" y="279" fill="#333333" font-family="Helvetica" font-size="12px"&gt;urischememethodheade&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="0" y="180" width="120" height="30" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 195px; margin-left: 1px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;VirtualService&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="60" y="199" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;VirtualService&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="180" y="0" width="120" height="30" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 15px; margin-left: 181px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;HTTPRoute&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="240" y="19" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;HTTPRoute&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="180" y="180" width="120" height="30" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 195px; margin-left: 181px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;HTTPMatchRequest&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="240" y="199" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;HTTPMatchRequest&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 400 160 L 400 180 L 400 160 L 400 173.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="1 4" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 400 178.88 L 396.5 171.88 L 400 173.63 L 403.5 171.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;rect x="340" y="30" width="120" height="130" fill="#f5f5f5" stroke="#666666" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 118px; height: 1px; padding-top: 95px; margin-left: 342px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;&lt;font color="#0000cc"&gt;destination&lt;/font&gt;&lt;/b&gt;&lt;/li&gt;&lt;li&gt;weight&lt;/li&gt;&lt;li&gt;headers&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="342" y="99" fill="#333333" font-family="Helvetica" font-size="12px"&gt;destinationweighthea&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="340" y="0" width="120" height="30" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 15px; margin-left: 341px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;HTTPRouteDestionation&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="400" y="19" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;HTTPRouteDestionation&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 460 275 L 485 275 L 485 95 L 503.63 95" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 508.88 95 L 501.88 98.5 L 503.63 95 L 501.88 91.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;rect x="340" y="210" width="120" height="130" fill="#f5f5f5" stroke="#666666" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 118px; height: 1px; padding-top: 275px; margin-left: 342px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;&lt;ul&gt;&lt;li&gt;host&lt;/li&gt;&lt;li&gt;&lt;font color="#0000cc"&gt;&lt;b&gt;subsets&lt;/b&gt;&lt;/font&gt;&lt;/li&gt;&lt;li&gt;trafficPolicy&lt;/li&gt;&lt;li&gt;exportTo&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="342" y="279" fill="#333333" font-family="Helvetica" font-size="12px"&gt;hostsubsetstrafficPo&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="340" y="180" width="120" height="30" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 195px; margin-left: 341px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;DestionationRule&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="400" y="199" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;DestionationRule&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 585 160 L 585 180 L 585 160 L 585 173.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 585 178.88 L 581.5 171.88 L 585 173.63 L 588.5 171.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;rect x="510" y="30" width="150" height="130" fill="#f5f5f5" stroke="#666666" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 148px; height: 1px; padding-top: 95px; margin-left: 512px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;&lt;ul&gt;&lt;li&gt;name&lt;/li&gt;&lt;li&gt;labels&lt;/li&gt;&lt;li&gt;&lt;b&gt;&lt;font color="#0000cc"&gt;trafficPolicy&lt;/font&gt;&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="512" y="99" fill="#333333" font-family="Helvetica" font-size="12px"&gt;namelabelstrafficPolicy&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="525" y="0" width="120" height="30" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 15px; margin-left: 526px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Subset&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="585" y="19" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Subset&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="510" y="210" width="150" height="130" fill="#f5f5f5" stroke="#666666" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 148px; height: 1px; padding-top: 275px; margin-left: 512px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;&lt;ul&gt;&lt;li&gt;loadBalancer&lt;/li&gt;&lt;li&gt;connectionPool&lt;/li&gt;&lt;li&gt;outlierDetection&lt;/li&gt;&lt;li&gt;tls&lt;/li&gt;&lt;li&gt;portLevelSettings&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="512" y="279" fill="#333333" font-family="Helvetica" font-size="12px"&gt;loadBalancerconnectionPoo&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="525" y="180" width="120" height="30" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 195px; margin-left: 526px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;TrafficPolicy&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="585" y="199" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;TrafficPolicy&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 60 160 L 60 180 L 60 160 L 60 173.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="1 4" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 60 178.88 L 56.5 171.88 L 60 173.63 L 63.5 171.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;rect x="0" y="30" width="120" height="130" fill="#f5f5f5" stroke="#666666" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 118px; height: 1px; padding-top: 95px; margin-left: 2px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;&lt;ul&gt;&lt;li&gt;selector&lt;/li&gt;&lt;li&gt;servers&lt;/li&gt;&lt;ul&gt;&lt;li&gt;port&lt;/li&gt;&lt;li&gt;hosts&lt;/li&gt;&lt;li&gt;tls&lt;/li&gt;&lt;li&gt;name&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="2" y="99" fill="#333333" font-family="Helvetica" font-size="12px"&gt;selectorservers&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="0" y="0" width="120" height="30" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 15px; margin-left: 1px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Gateway&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="60" y="19" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Gateway&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;switch&gt;&lt;g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;/g&gt;&lt;a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank" rel="noopener"&gt;&lt;text text-anchor="middle" font-size="10px" x="50%" y="100%"&gt;Viewer does not support full SVG 1.1&lt;/text&gt;&lt;/a&gt;&lt;/switch&gt;&lt;/svg&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://github.com/digihunch/korthweb/blob/main/manual/orthanc.yaml#L106"&gt;Here&lt;/a&gt; is an example of using Gateway and Virtual Service resources (from Korthweb sample project). Also note that in the Istio literature, there is neither a CRD name called &amp;#8220;Ingress&amp;#8221;, nor a resource type named &amp;#8220;Ingress Gateway&amp;#8221;, although they may be loosely used to refer to &amp;#8220;Gateway resources configured to manage ingress traffic&amp;#8221;. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-istio-gateway-installation"&gt;Istio Gateway Installation&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are more than one ways to install Istio. In the past, Istio Operator was used to install Istio. The Operator calls IstioOperator API. Today the use of Istio Operator is not recommended anymore but the IstioOperator API is used implicitly by Istioctl installer. The two recommended approaches to install Istio today is by &lt;a href="https://istio.io/latest/docs/setup/install/istioctl/"&gt;Istioctl&lt;/a&gt; and &lt;a href="https://istio.io/latest/docs/setup/install/helm/"&gt;Helm&lt;/a&gt;. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With istioctl, you can specify an option imperatively, or using an overlay file as did in &lt;a href="https://static.digihunch.com/2021/11/istio-ingress-egress/"&gt;this&lt;/a&gt; lab. With Helm, istio has multiple charts, and requires multiple steps:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;install CRDs using the &lt;a href="https://artifacthub.io/packages/helm/istio-official/base"&gt;base&lt;/a&gt; chart&lt;/li&gt;&#10;&lt;li&gt;install istiod using the &lt;a href="https://artifacthub.io/packages/helm/istio-official/istiod"&gt;istiod&lt;/a&gt; chart. &lt;a href="https://github.com/digihunch/korthweb/blob/main/manual/istio/istiod-values.yaml"&gt;Here&lt;/a&gt; is an example of values provided to Helm installer.&lt;/li&gt;&#10;&lt;li&gt;install ingress &lt;a href="https://istio.io/latest/docs/setup/additional-setup/gateway/"&gt;gateway&lt;/a&gt; using the &lt;a href="https://artifacthub.io/packages/helm/istio-official/gateway"&gt;gateway&lt;/a&gt; chart. &lt;a href="https://github.com/digihunch/korthweb/blob/main/manual/istio/ingress-gateway-values.yaml"&gt;Here&lt;/a&gt; is an example of values provided to Helm installer for ingress gateway. Note that egress gateway also requires the same helm chart with different value definition. &lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://digihunch.github.io/korthweb/deployment/manual/"&gt;This&lt;/a&gt; instruction contains how to manually install Istio gateways using Helm, including installation of multiple Helm charts. I attempted to create a single chart to consolidate the multiple charts required for istio. It was not successful because of an &lt;a href="https://github.com/helm/helm/issues/10392"&gt;error&lt;/a&gt; when trying to reference the same gateway chart dependency for multiple times.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2021/12/aks-lessons-learned-2-of-2/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;AKS Lessons Learned 2 of 2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2022/01/kubernetes-admission-control/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Kubernetes Admission Control&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Secure web application deployment</title><link>https://static.digihunch.com/2021/05/secure-web-application-deployment/</link><pubDate>Sun, 16 May 2021 15:19:41 -0400</pubDate><guid>https://static.digihunch.com/2021/05/secure-web-application-deployment/</guid><description>&lt;p class="wp-block-paragraph"&gt;In Nov 2020, I created &lt;a href="https://github.com/digihunch/orthweb/tree/2181001e29b0da5fd55f51a6dc2a522d3f83aee6" class="rank-math-link"&gt;OrthWeb&lt;/a&gt; project, a deployment of Orthanc&amp;#8217;s server. Orthanc is a DICOM viewer and repo shipped in Docker container. In the &lt;a href="https://static.digihunch.com/2020/11/medical-imaging-web-server-deployment-pipeline/" class="rank-math-link"&gt;deployment project&lt;/a&gt;, I use Terraform to provision infrastructure, including a managed PostgreSQL instance, an EC2 instance for docker runtime, and the init script to bring up the web service. I whipped up the project for a demo, and skipped some security configurations. For example, the password was stored in clear text in Terraform configuration. The web certificate is stored in the repository. I recently had some time to fix that. My effort leads up to the conclusion that this requires a better platform (i.e. managed Kubernetes cluster). So I wanted to note down how I got there.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Secret store&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In AWS, both parameter store and secret manager can act as secret store. Secrets manager comes at higher cost but some additional features, such as built-in password generator, secret rotation, and cross-account access. We use Secret Manager but we generate password within Terraform because we need to specify password during database provisioning. Secret store requires certain special characters to be eliminated. Terraform can specify the special characters allowed. For EC2 instance to pull from secret manager, the following entities are needed:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;A secret store&lt;/li&gt;&#10;&lt;li&gt;A VPC endpoint to expose secret store to subnet via private route. &lt;/li&gt;&#10;&lt;li&gt;The VPC endpoint needs its own security group&lt;/li&gt;&#10;&lt;li&gt;The instance profile of the EC2 instance must contain an IAM role to get secret value&lt;/li&gt;&#10;&lt;li&gt;The security group of EC2 instance needs to allow traffic to secret store&lt;/li&gt;&#10;&lt;li&gt;The script from EC2 instance uses VPC endpoint&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This is a common pattern for interaction between computing object and VPC endpoint. The details are in compute.tf, network.tf, secgrp.tf and secret.tf. The secret name needs to be partially randomized to avoid naming conflict with deactivated secrets.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="623" height="323" src="https://static.digihunch.com/wp-content/uploads/2021/05/secmgr.png" alt="" class="wp-image-2250"/&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Passing Secret to container&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Here is an example CLI command to pull secret:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ aws secretsmanager get-secret-value --secret-id DatabaseCreds51c1db4172ae9c54 --query SecretString --output text --endpoint-url https://vpce-0897b168cf1c60df2-khx32o7f.secretsmanager.us-east-1.vpce.amazonaws.com | jq -r .password&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The connection is made via private network route (whether the instance is in public or private subnet). Traffic is encrypted in TLS. Once in the operating system, the secret is available as standard output and can be stored to file, or saved in environment variable. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;My first attempted approach is docker&amp;#8217;s secret store and config so that I do not have to store secret in plain text on the file system. I eventually give up this approach due to several hiccups. First, secret and config are part of Docker swarm service. So it requires initializing docker swarm before I could port in the secret, with the following command:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ docker swarm init&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ echo mdbuser123 | docker secret create db_un -&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ echo m1p@ssw0rd | docker secret create db_pw -&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ echo 10.2.32.41 | docker config create db_ep -&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The content of the secret and config are presented as files to the container file system at different locations, as can be verified this way:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ docker service create --name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;redis&amp;#34;&lt;/span&gt; --secret&lt;span style="color:#f92672"&gt;=&lt;/span&gt;db_un --secret&lt;span style="color:#f92672"&gt;=&lt;/span&gt;db_pw --config&lt;span style="color:#f92672"&gt;=&lt;/span&gt;db_ep redis:alpine&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ docker container ls&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ docker exec -it c8ed2a278ca8 sh&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# cat /db_ep&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# cat /run/secrets/db_un&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# cat /run/secrets/db_pw&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;This is a great way to pass secret and config to container applications. However, since the values are stored as content of file, the main application must be able to load file content as its own configuration value. In my specific scenario, the application expects explicit value in its &lt;a href="https://orthanc.uclouvain.be/book/users/configuration.html" class="rank-math-link"&gt;configuration file&lt;/a&gt;, or environment variable.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On the other hand, Docker &lt;a href="https://techbeacon.com/devops/how-keep-your-container-secrets-secure" class="rank-math-link"&gt;document&lt;/a&gt; states that docker secrets do not set environment variables directly. this was a conscous decision, because env var can unintentionally be leaked between containers. In other word I could present secrets as files but the application cannot use it. There is potentially a workaround &lt;a href="https://medium.com/@adrian.gheorghe.dev/using-docker-secrets-in-your-environment-variables-7a0609659aab" class="rank-math-link"&gt;here&lt;/a&gt; which is great function wise but an additional layer of complexity.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Moreover, I later discovered that this isn&amp;#8217;t even a viable approach if I use docker compose. This is because I must declare those entries from secret store or config store as &lt;a href="https://docs.docker.com/compose/compose-file/compose-file-v3/#configs" class="rank-math-link"&gt;external&lt;/a&gt;, and external secrets are not even available to containers created by docker-compose. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With reluctance, I store the config and secret keys and values to a file, and use the &lt;a href="https://docs.docker.com/compose/environment-variables/#the-env-file" class="rank-math-link"&gt;env_file&lt;/a&gt; section in docker compose to import them as environment variables. The application can pick up environment variables as configuration values.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;X509 Certificate&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We use a self-signed X509 certificate, along with the private key. The &lt;a href="https://orthanc.uclouvain.be/book/faq/https.html#securing-orthanc-using-self-signed-certificate" class="rank-math-link"&gt;creation&lt;/a&gt; is straightforward. However, when I tested on Mac, the browser does not load the page for &lt;a href="https://support.apple.com/en-us/HT210176" class="rank-math-link"&gt;this&lt;/a&gt; reason. Since macOS 10.15, the certificate requires several extensions: ExtendedKeyUsage, Subject alternative names and DNS name. The native openssl from the operating system is outdated (v 1.0.2) and I had to install openssl11 package and create it as follows:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;openssl11 req -x509 -nodes -days &lt;span style="color:#ae81ff"&gt;365&lt;/span&gt; -newkey rsa:2048 -keyout /tmp/private.key -out /tmp/certificate.crt -subj /C&lt;span style="color:#f92672"&gt;=&lt;/span&gt;CA/ST&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Ontario/L&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Waterloo/O&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Digihunch/OU&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Imaging/CN&lt;span style="color:#f92672"&gt;=&lt;/span&gt;digihunch.com/emailAddress&lt;span style="color:#f92672"&gt;=&lt;/span&gt;info@3.237.97.93 -addext extendedKeyUsage&lt;span style="color:#f92672"&gt;=&lt;/span&gt;serverAuth -addext subjectAltName&lt;span style="color:#f92672"&gt;=&lt;/span&gt;DNS:orthweb.digihunch.com,DNS:digihunch.com&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The Mac uses libreSSL backed openSSL utility and can achieve the same with slightly different command line argument.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Next Step&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The limitation with passing secret concerns me and I&amp;#8217;m looking to move to managed Kubernetes platform where &lt;a href="https://kubernetes.io/docs/concepts/configuration/secret/#using-secrets-as-environment-variables" class="rank-math-link"&gt;secrets&lt;/a&gt; can be ported to environment variable of Pods. We can also consider &lt;a href="https://docs.aws.amazon.com/AmazonECS/latest/developerguide/specifying-sensitive-data.html" class="rank-math-link"&gt;ECS&lt;/a&gt; in AWS which allows to inject sensitive data from secret manager to container. &lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2021/04/preparing-certified-kubernetes-administrator-exam/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Certified Kubernetes Administrator (CKA) Exam&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2021/05/getting-started-with-github-actions/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Getting started with GitHub Actions&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Automatic deployment of Orthanc on AWS</title><link>https://static.digihunch.com/2020/11/medical-imaging-web-server-deployment-pipeline/</link><pubDate>Sun, 08 Nov 2020 00:54:06 -0400</pubDate><guid>https://static.digihunch.com/2020/11/medical-imaging-web-server-deployment-pipeline/</guid><description>&lt;p class="wp-block-paragraph"&gt;[&lt;strong&gt;Update&lt;/strong&gt;] I changed reverse proxy from Nginx to Envoy. &lt;a href="https://static.digihunch.com/2022/03/from-nginx-to-envoy-proxy/"&gt;Here&lt;/a&gt;&amp;#8216;s the detail.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;[&lt;strong&gt;Update&lt;/strong&gt;] Some security improvement was introduced in may 2021. &lt;a class="rank-math-link" href="https://static.digihunch.com/2021/05/secure-web-application-deployment/"&gt;Here&lt;/a&gt;&amp;#8216;s detail.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;[&lt;strong&gt;Update&lt;/strong&gt;] &lt;a href="https://github.com/digihunch/orthweb"&gt;Here&amp;#8217;s&lt;/a&gt; the link to the orthweb repository.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In this project we introduce a medical imaging web service based on Orthanc, an open-source project of DICOM server, and a pipeline to deploy such server automatically and consistently. We deploy Orthanc on AWS automatically. This little project involves a number of technical deets in DevOps, to deliver a web application prototype with an automated deployment pipeline.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-a-brief-on-imaging"&gt;A brief on imaging&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In medical imaging, scanning devices are the data collectors. It consists of various categories of scanners, such as Computed Tomography (CT), and Ultrasound (US). They are collectively referred to as modality, but vary significantly in terms of image generation and hardware manufacturing. The challenges to exchange data between these heterogeneous scanning devices and centralized computers came around as early as the 1980s, which brought about ACR-NEMA standard in 1985, under the initiative between American College Radiology (ACR) and National Electrical Manufacturers Association (NEMA). The standard lately evolved into DICOM (Digital Imaging Communication in Medicine), a comprehensive set of standard in the ISO framework that governs modern imaging data storage and exchange across several disciplines (radiology, cardiology, pathology, etc) that operate around images in medicine.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In addition to defining a &lt;a href="http://dicom.nema.org/medical/dicom/current/output/chtml/part10/"&gt;file format&lt;/a&gt; to store imaging data, DICOM also includes an upper layer protocol that dictates how two compliant devices (referred as application entity, each identified by AE title) can negotiate a common syntax to transfer objects (e.g. an image, a report or a discovery). Upper layer refers to layer 5-7 in OSI model, or application layer in TCP/IP model.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-imaging-server"&gt;Imaging server&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Once scanner acquires images from patient, they stores the exams to imaging server for persistent storage. The functionalities of such server expands overtime since 1990s and hence go by different names in different eras, such as PACS (Picture Archive and Communication Systems), VNA (Vendor Neutral Archive) and EI (enterprise imaging) archive. Regardless of naming, they can be generally seen as a highly specialized variation of enterprise content management system. They are usually hosted with a centralized database to index clinical information at patient, exam and image levels. The other key component is the persistent storage devices, usually in the form of a &lt;a href="https://en.wikipedia.org/wiki/Network-attached_storage" class="rank-math-link"&gt;NAS&lt;/a&gt;. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://www.orthanc-server.com/" class="rank-math-link"&gt;Orthanc&lt;/a&gt; is an open-source initiative for such imaging servers. It provides a DICOM endpoint, allowing scanning devices to store medical images. It also provides a web viewer allowing users to see the images stored. It is released for many platforms, including Docker images.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-infrastructure-as-code"&gt;Infrastructure as code&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We use Amazon Web Service (AWS) for infrastructure as service, and &lt;a href="https://www.terraform.io/" class="rank-math-link"&gt;Terraform&lt;/a&gt; as the tool to provision resources off AWS, in a reliable and consistent mechanism, known as Infrastructure-as-Code. Terraform is an alternative to CloudFormation, AWS&amp;#8217;s proprietary infrastructure-as-code technology. Terraform is developed by Hashicorp as an open-source project, and therefore is vendor neutral. It supports multiple public cloud vendor through different &lt;a href="https://www.terraform.io/docs/providers/index.html" class="rank-math-link"&gt;providers&lt;/a&gt;. Each provider accesses the vendor specific SDK. For example, the &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs" class="rank-math-link"&gt;AWS provider&lt;/a&gt; integrates with &lt;a href="https://aws.amazon.com/tools/" class="rank-math-link"&gt;AWS SDK&lt;/a&gt;. As a result, the code used in one vendor cannot just be applied to a different vendor without a major overhaul. Terraform&amp;#8217;s current version is 0.13 as of Oct 2020, and has gone through some &lt;a href="https://www.hashicorp.com/blog/announcing-terraform-0-12" class="rank-math-link"&gt;syntax changes&lt;/a&gt; since version 0.11. Terraform also produces files for state management locally in the working directory. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When executing, Terraform combines all files in the working directory to assess variables, and create required resources. It is compatible with the most of AWS resources. For example, you can specify user data with templates when creating EC2 instances. You can also create managed service instance as long as it is supported by the &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs" class="rank-math-link"&gt;provider&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-architecture"&gt;Architecture&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Orthanc web server stores data in sqlite by default, but also has a plugin to support &lt;a href="https://wiki.postgresql.org/wiki/Main_Page" class="rank-math-link"&gt;PostgreSQL&lt;/a&gt;, an open-source relational database. AWS has managed service (&lt;a href="https://aws.amazon.com/rds/postgresql/" class="rank-math-link"&gt;RDS&lt;/a&gt;) based on PostgreSQL. In this project, we create an RDS instance that span across two availability zones for minimum high availability. Orthanc also supports storing imaging data including pixels in PostgreSQL, which obviates the need for a dedicated file storage system.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We deploy the application in Docker&amp;#8217;s containers for compatibility and portability. The Orthanc server is shipped in &lt;a class="rank-math-link" href="https://orthanc.uclouvain.be/book/users/docker.html"&gt;Docker images&lt;/a&gt;, available in &lt;a class="rank-math-link" href="https://hub.docker.com/r/jodogne/orthanc"&gt;Docker hub&lt;/a&gt; registry. The docker environment is configured as part of EC2 instance bootstrapping, including installing packages with &lt;a href="https://static.digihunch.com/2019/02/package-repository-management-in-linux/"&gt;YUM&lt;/a&gt;, initializing and customizing environment variables. The docker-compose file, and the auxiliary configuration files are provided in the repo. The bootstrapping script installs git and pulls required files from this &lt;a class="rank-math-link" href="https://github.com/digihunch/orthweb"&gt;GitHub repo&lt;/a&gt;. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This demo project does not include load balancing, DNS management, or container orchestration.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-security"&gt;Security&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Orthanc&amp;#8217;s web browser &lt;a href="https://orthanc.uclouvain.be/book/faq/https.html"&gt;natively supports HTTPS&lt;/a&gt;. However, the DICOM port does not support TLS natively, as their development has made clear in the &lt;a href="https://orthanc.uclouvain.be/book/faq/security.html" class="rank-math-link"&gt;FAQ&lt;/a&gt;. This leaves a severe security vulnerability because all patient data (protected health information in HIPPA context) would be sent across the Internet in the clear, visible to every network interface along the route. To address this issue we brought in Nginx as a reverse proxy to work at TCP layer to terminate encrypted traffic for Orthanc&amp;#8217;s DICOM end point. DICOM upper layer works on top of TCP layer. &lt;/p&gt;&#10;&lt;p class="has-pale-cyan-blue-background-color has-background wp-block-paragraph"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="621px" viewBox="-0.5 -0.5 621 207" style="max-width:100%;max-height:207px;"&gt;&lt;defs&gt;&lt;/defs&gt;&lt;g&gt;&lt;path d="M 187.5 56 C 157.5 56 150 106 174 116 C 150 138 177 186 196.5 166 C 210 206 255 206 270 166 C 300 166 300 126 281.25 106 C 300 66 270 26 243.75 46 C 225 16 195 16 187.5 56 Z" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 148px; height: 1px; padding-top: 203px; margin-left: 151px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;Internet&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="225" y="203" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Internet&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 120 98.5 L 323.64 86.38" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 328.88 86.07 L 322.1 89.98 L 323.64 86.38 L 321.69 82.99 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 95px; margin-left: 220px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; "&gt;Encrypted DICOM traffic&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="220" y="98" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;Encrypted DICOM traffic&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="540" y="56" width="80" height="120" rx="12" ry="12" fill="#ffffff" stroke="#000000" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 78px; height: 1px; padding-top: 116px; margin-left: 541px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;DICOM &lt;br&gt;Archive&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="580" y="120" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;DICOM&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="330" y="56" width="50" height="120" rx="7.5" ry="7.5" fill="#d5e8d4" stroke="#82b366" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 48px; height: 1px; padding-top: 116px; margin-left: 331px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;Nginx&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="355" y="120" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Nginx&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 380 86 L 533.63 86" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 538.88 86 L 531.88 89.5 L 533.63 86 L 531.88 82.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 87px; margin-left: 456px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; "&gt;Unencrypted DICOM traffic&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="456" y="90" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;Unencrypted DICOM traffic&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="0" y="86" width="120" height="50" rx="7.5" ry="7.5" fill="#ffffff" stroke="#000000" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 111px; margin-left: 1px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;DICOM Device supporting TLS&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="60" y="115" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;DICOM Device support&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 540 146 L 386.37 146" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 381.12 146 L 388.12 142.5 L 386.37 146 L 388.12 149.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 146px; margin-left: 459px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; "&gt;Unencrypted DICOM traffic&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="459" y="149" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;Unencrypted DICOM traffic&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 330 146 L 126.33 124.18" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 121.11 123.62 L 128.44 120.88 L 126.33 124.18 L 127.7 127.84 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 125px; margin-left: 214px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; "&gt;Encrypted DICOM traffic&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="214" y="129" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;Encrypted DICOM traffic&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 312 6 L 312 206 M 308 206 L 308 6 M 308 206" fill="none" stroke="#6c8ebf" stroke-linejoin="round" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 7px; margin-left: 321px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; "&gt;corporate firewall&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="321" y="10" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;corporate firewall&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 142 6 L 142 206 M 138 206 L 138 6 M 138 206" fill="none" stroke="#6c8ebf" stroke-linejoin="round" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 7px; margin-left: 151px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; "&gt;corporate firewall&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="151" y="10" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle"&gt;corporate firewall&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;switch&gt;&lt;g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;/g&gt;&lt;a transform="translate(0,-5)" xlink:href="https://desk.draw.io/support/solutions/articles/16000042487" target="_blank" rel="noopener noreferrer"&gt;&lt;text text-anchor="middle" font-size="10px" x="50%" y="100%"&gt;Viewer does not support full SVG 1.1&lt;/text&gt;&lt;/a&gt;&lt;/switch&gt;&lt;/svg&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In Nginx literature, this use case is referred to as &lt;a class="rank-math-link" href="https://docs.nginx.com/nginx/admin-guide/security-controls/terminating-ssl-tcp/"&gt;SSL Termination for TCP Upstream Servers&lt;/a&gt;. Note that Nginx is providing layer 4 capability in this use case so the certificate and key configuration should not be placed under http section of the configuration file. This layer 4 capability in fact enables security configurations of all protocol that operates in upper layers and can be used in a broad range of situations. It is also noteworthy that Nginx can re-encrypt the traffic on the way out to upstream, for even tighter security control measure as outlined in this &lt;a class="rank-math-link" href="https://docs.nginx.com/nginx/admin-guide/security-controls/securing-tcp-traffic-upstream/"&gt;use case&lt;/a&gt;.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-groovy" data-lang="groovy"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;user nginx&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;worker_processes &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;error_log &lt;span style="color:#e6db74"&gt;/var/&lt;/span&gt;log&lt;span style="color:#e6db74"&gt;/nginx/&lt;/span&gt;error&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt; warn&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pid &lt;span style="color:#e6db74"&gt;/var/&lt;/span&gt;run&lt;span style="color:#e6db74"&gt;/nginx.pid;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;events {&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; worker_connections 1024;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;stream {&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; upstream dicom_backend {&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; server orthanc-backend:4242;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; server {&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; listen 11112 ssl;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; proxy_pass dicom_backend;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; ssl_certificate conf.d/&lt;/span&gt;site&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;pem&lt;/span&gt;&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ssl_certificate_key conf&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;d&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;site&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;pem&lt;/span&gt;&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ssl_protocols SSLv3 TLSv1 TLSv1&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; TLSv1&lt;span style="color:#f92672"&gt;.&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ssl_ciphers HIGH:&lt;span style="color:#f92672"&gt;!&lt;/span&gt;aNULL:&lt;span style="color:#f92672"&gt;!&lt;/span&gt;MD5:ECDH&lt;span style="color:#f92672"&gt;+&lt;/span&gt;AESGCM&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ssl_session_cache shared:SSL:&lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;m&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ssl_session_timeout &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;h&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ssl_handshake_timeout &lt;span style="color:#ae81ff"&gt;30&lt;/span&gt;s&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;It is also helpful to use Nginx to terminate HTTPS traffic, using a pair of certificate and key. When testing with self-signed certificate I realized that Chrome browser has specific &lt;a href="https://support.apple.com/en-us/HT210176" class="rank-math-link"&gt;requirement&lt;/a&gt; on self-signed certificate, or it won&amp;#8217;t load the page. So the certificate has to be created as instructed &lt;a class="rank-math-link" href="https://eengstrom.github.io/musings/self-signed-tls-certs-v.-chrome-on-macos-catalina"&gt;here&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For better security, it is advisable that the RDS instance is provisioned in private subnet, with its data encrypted both in-transit and at-rest. Docker service should also manage sensitive information as &lt;a href="https://docs.docker.com/engine/swarm/secrets/" class="rank-math-link"&gt;secrets&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-summary"&gt;Summary&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The deliverable is stored in this Github &lt;a href="https://github.com/digihunch/orthweb" class="rank-math-link"&gt;repo&lt;/a&gt;. The docker part of it can be executed on MacBook with PostgreSQL. The entire hardware stack represented by terraform code, can be executed against AWS to create required resources. Checkout README for further instruction. To emulate a modality, one will need a TLS supported DICOM application entity, &lt;a href="https://horosproject.org/" class="rank-math-link"&gt;Horos&lt;/a&gt; is a great project on MacOS to serve this purpose, both as DICOM-compliant sender and a viewer. Alternatively, consider some command-line based DICOM toolkit such as &lt;a href="https://support.dcmtk.org/redmine/projects/dcmtk" class="rank-math-link"&gt;dcmtk&lt;/a&gt;, or &lt;a href="https://sourceforge.net/projects/gdcm/" class="rank-math-link"&gt;grassroot dicom&lt;/a&gt;.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2020/11/docker-storage/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Docker storage&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2020/11/how-imaging-devices-talk-to-each-other-tip-in-dicom/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;How imaging devices talk to each other (in DICOM)&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>SASL Authentication Mechanisms</title><link>https://static.digihunch.com/2020/03/authentication-mechanisms-under-simple-authentication-and-security-layer-sasl/</link><pubDate>Thu, 19 Mar 2020 22:53:00 -0400</pubDate><guid>https://static.digihunch.com/2020/03/authentication-mechanisms-under-simple-authentication-and-security-layer-sasl/</guid><description>&lt;h3 class="wp-block-heading"&gt;Introduction&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Authentication is used in many protocols (such as LDAP binding) and it usually involves sending password. Given the nature of authentication protocol, its traffic encryption is usually mandatory. Simple Authentication and Security Layer (SASL) is introduced to ensure the security during authentication. It is not a single protocol, but rather a framework for authentication and data security involving many protocols. The intent is to decouple authentication mechanisms from application protocols, thus allowing any authentication mechanism (under SASL) to be used in any application protocol (that supports SASL). Application protocols that support SASL typically can also be built on Transport Layer Security (TLS), whose latest versions (1.2 and 1.3) are considered more secure.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;None (ANONYMOUS)&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The server basically does not authenticate the client. The client connects to the server anonymously. Under SASL framework, this may also be referred to as ANONYMOUS mechanism.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Simple (PLAIN)&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In simple authentication method the password is sent to server in the clear. This is subject to eavesdropping and is not secure. It is still surprisingly widespread in legacy configurations probably due to the simplicity of configuration. This option should not be available in cloud environment. Under SASL framework, this may also be referred to as PLAIN mechanism.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;CRAM-MD5 and DIGEST-MD5&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://web.archive.org/web/20211206115031/https://ldapwiki.com/wiki/CRAM-MD5"&gt;CRAM-MD5&lt;/a&gt;: described in RFC 2195, using HMAC-MD5 algorithm. In this challenge-response scheme based mechanism, the client&amp;#8217;s password is protected during authentication, but the application session (e.g. LDAP) traffic is not encrypted. It includes random data from the server and is slightly better than Simple authentication. However, this authentication method is not recommended either.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://web.archive.org/web/20220129132628/https://ldapwiki.com/wiki/DIGEST-MD5"&gt;DIGEST-MD5&lt;/a&gt;: described in RFC 2831. This is very similar to CRAM-MD5 but is is somewhat stronger because it includes random data from both the client and server. In addition, it also provides a provision to ensure connection integrity and confidentiality (a data security layer).&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;GSSAPI&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Generic Security Service Application Program Interface (&lt;a href="https://en.wikipedia.org/wiki/Generic_Security_Services_Application_Program_Interface"&gt;GSS-API&lt;/a&gt;) is an API specification for programs to access security services. GSS-API by itself does not provide any security. Instead, security-service vendors provide GSSAPI implementations &amp;#8211; usually in the form of libraries installed with their security software. These libraries present a GSSAPI-compatible interface to application developers who can write their application to use only the vendor-independent GSSAPI. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Under SASL, the dominant GSSAPI mechanism implementation in use is Kerberos version 5. GSSAPI allows Kerberos implementations to be API compatible. In many contexts, &lt;a href="https://en.wikipedia.org/wiki/Generic_Security_Services_Application_Program_Interface"&gt;GSSAPI&lt;/a&gt; simply implies &lt;a href="https://en.wikipedia.org/wiki/Kerberos_(protocol)"&gt;Kerberos&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;NTLM&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;NT LAN Manager (NTLM) is a challenge-response based Microsoft security protocols. It is implemented in a Security Support Provider (&lt;a href="https://en.wikipedia.org/wiki/Security_Support_Provider_Interface"&gt;SSP&lt;/a&gt;), which combines the older LAN Manager authentication protocol, NTLMv1, NTLMv2 and NTLM2 Session protocols in a single package. Group policy manages whether these protocols are used or can be used. NTLM passwords are considered weak because they can be brute-forced very easily with modern hardware. It might still be enabled in server configuration as a backup mechanism to Kerberos.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;TLS (EXTERNAL)&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;CRAM-MD5, DIGEST-MD5, GSSAPI, and NTLM are more commonly referred to as SASL mechanisms (in loose terms). These mechanisms allow for a secure password exchange without requiring TLS by trying to address the authentication traffic encryption problem at application layer. Using TLS this can also be address at transport layer. TLS can be used in combination with any of the mechanisms above but usually TLS/Simple mechanism is sufficient. In many occasions the mechanisms under SASL can be replaced by simple authentication encrypted with TLS. Under the SASL framework, this may also be referred to as EXTERNAL mechanism so TLS (in strict terms) is also considered a SASL mechanism.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading"&gt;Summary&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This article outlined several authentication protocols under SASL. Since SASL is the framework that intends to govern all authentication protocols, the use case of these protocol can be widespread. For example, in &lt;a href="https://docs.oracle.com/cd/E19253-01/816-4556/ldapsecure-75/index.html"&gt;LDAP&lt;/a&gt; you can find all of them. Here is a comparison across them:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&#10;&lt;table id="tablepress-7" class="tablepress tablepress-id-7"&gt;&#10;&lt;thead&gt;&#10;&lt;tr class="row-1"&gt;&#10;&#9;&lt;td class="column-1"&gt;&lt;/td&gt;&lt;th class="column-2"&gt;Password on wire&lt;/th&gt;&lt;th class="column-3"&gt;Session&lt;/th&gt;&#10;&lt;/tr&gt;&#10;&lt;/thead&gt;&#10;&lt;tbody class="row-striping row-hover"&gt;&#10;&lt;tr class="row-2"&gt;&#10;&#9;&lt;td class="column-1"&gt;Simple&lt;/td&gt;&lt;td class="column-2"&gt;Clear&lt;/td&gt;&lt;td class="column-3"&gt;No Encryption&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-3"&gt;&#10;&#9;&lt;td class="column-1"&gt;SASL/CRAM-MD5&lt;/td&gt;&lt;td class="column-2"&gt;Encrypted&lt;/td&gt;&lt;td class="column-3"&gt;No Encryption&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-4"&gt;&#10;&#9;&lt;td class="column-1"&gt;SASL/DIGEST-MD5&lt;/td&gt;&lt;td class="column-2"&gt;Encrypted&lt;/td&gt;&lt;td class="column-3"&gt;No Encryption&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-5"&gt;&#10;&#9;&lt;td class="column-1"&gt;SASL/GSSAPI&lt;/td&gt;&lt;td class="column-2"&gt;Kerberos&lt;/td&gt;&lt;td class="column-3"&gt;Encryption&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-6"&gt;&#10;&#9;&lt;td class="column-1"&gt;TLS:SIMPLE&lt;/td&gt;&lt;td class="column-2"&gt;Encrypted&lt;/td&gt;&lt;td class="column-3"&gt;Encrypted&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;!-- #tablepress-7 from cache --&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Common SASL implementation includes Cyrus SASL and GNU SASL. There are also some API implementations that supports some of SASL mechanisms, such as SSPI.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2020/03/oauth-and-openid-connect/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;OAuth 2.0 and OIDC 1 of 2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2020/03/introduction-to-authentication-frameworks-pam-and-sspi/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Introduction to Authentication Frameworks (PAM and SSPI)&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Lightweight Directory Access Protocol (LDAP)</title><link>https://static.digihunch.com/2020/03/lightweight-directory-access-protocol-ldap/</link><pubDate>Mon, 02 Mar 2020 21:11:00 -0400</pubDate><guid>https://static.digihunch.com/2020/03/lightweight-directory-access-protocol-ldap/</guid><description>&lt;h3 class="wp-block-heading" id="h-introduction"&gt;Introduction&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Originally LDAP only refers to the connectivity protocol to the directory server. This term is being used loosely today and it also refers to the actual directory service that supports and complies with LDAP. LDAP v3 is the current version developed in RFC 2251.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A directory is information about some set of entities such as people, organization, or stones. An example of directory would be /etc/passwd file in Linux. A directory server is simply an application with the main purpose of maintaining directories. Typically, the read traffic is high whereas write traffic is low. LDAP is a general-purpose directory server. It can store information about people, or cars, or rocks. You just need to define what a person&amp;#8217;s entry looks like as well as what a rock&amp;#8217;s entry looks like. The general architecture of LDAP provides the capability nedded for managing large amount of diverse directory entries.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;An LDAP entry consists of DN (distinguished name) and attributes. An attribute may have one or more attribute names and they are defined in attribute definitions. Attribute names are not case-sensitive. An attribute may have one or more values if multiple values are allowed for that attribute. Attribute values may be case-sensitive depending on the definition.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A special attributed named objectclass attribute provides information about what type of record it is, and what attributes canbe given to the record. For example, the organization name (o) is required for any entry with an organization object class. While a record may have multiple object classes, one of these object classes must be the structural object class for the record. A structural object class determines what type of object the record is.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In addition to regular attributes, the directory server may also attach special operational attributes to an entry. Operational attributes are used by the directory server itself to store information about entries. Such attributes are not designed for use by end users, and are usually not returned during LDAP searches.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;An LDAP schema defines types of records in a directory and how those records might relate to each other. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Information in an LDAP directory is organized into one or more hierarchies where, at the top of the hierarchy, there is a base entry, and other entries are organized in tree-like structures beneath the base entry. Each node on the hierarchy is an entry, with a DN and more than one attributes. This hierarchically organized collection of entries is called a directory information tree (DIT). In DIT, LDAP directories stores data in hierarchical relationships. The root entry sits at the top and subordinate entry is beneath that, which in turn may have its own subordinate entries. Each of these records has its own DN, and its own attributes. The DN of each entry is composed of two parts: the relative DN (RDN) and the full DN of the superior entry.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;LDAP is nothing other than a special sort of database that organizes data into tree structures, like a file system hierarchy. This view is more easily seen by comparing an LDAP directory to a relational database system (RDB), where SQL is the protocol and RDBMS is the service. LDAP refers to both the protocol and the service.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-openldap"&gt;OpenLDAP&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A common LDAP implementation is openldap. OpenLDAP suite can be broken up into four components:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Servers: slapd (stand-alone LDAP Daemon) provides LDAP services.&lt;/li&gt;&lt;li&gt;Clients: ldapsearch is used to manipulate LDAP data&lt;/li&gt;&lt;li&gt;Utilities: support LDAP servers&lt;/li&gt;&lt;li&gt;Libraries: provide programming interfaces to LDAP&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Installing OpenLDAP requires libldap-2.3-0, slapd, ldap-utiles packages. It is configured in /etc/ldap/. An HDB (hierarchical database) needs to be specified in the configuration.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To test as a client, the first thing that must happen is the client must authenticate to the server (via simple bind or &lt;a href="https://static.digihunch.com/2020/03/authentication-mechanisms-under-simple-authentication-and-security-layer-sasl/"&gt;SASL&lt;/a&gt; Bind). LDAP server verifies the identity, permission as well as password provided by the client.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;LDAPTLS_REQCERT=never ldapsearch -x -o ldif-wrap=256 -H ldaps://ldap.digihunch:636/ -b &amp;#34;OU=Admin,OU=Service Department,DC=digihunch,DC=com&amp;#34; -D &amp;#34;gh\ldap-bind-user&amp;#34; -w &amp;#39;S@f35+P@55w0rd&amp;#39; &amp;#34;(objectclass=user)&amp;#34; -s sub -d 9&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The command above first sets client environment variable LDAPTLS_REQCERT to never, in case the client is being asked to provide certificate. Then the ldapsearch command performs the bind.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To search the directory, the client needs to provide the followings:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Base DN: where in the directory to start from&lt;/li&gt;&lt;li&gt;Scope: how deep in the tree to look&lt;/li&gt;&lt;li&gt;Attributes: what information to be retrieved per result&lt;/li&gt;&lt;li&gt;Filter: what to look for&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Below is an example of ldapsearch (-b for Base DN, -s for Scope, -S for attributes, stdin for filter):&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ldapsearch -x -o ldif-wrap=256 -H ldaps://ldap.digihunch:636/ -b &amp;#34;OU=Admin,OU=Service Department,DC=digihunch,DC=com&amp;#34; -D &amp;#34;gh\ldap-bind-user&amp;#34; -w &amp;#39;S@f35+P@55w0rd&amp;#39; &amp;#34;(memberof=CN=Security-Admin,OU=Admin,OU=Service Department,DC=digihunch,DC=com)&amp;#34; -s sub -S name&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Users with appropriate permissions may also other directory operations using ldapadd, ldapmodify, ldapdelete, ldapcompare, ldapmodrdn, ldappasswd, ldapwhoami, etc&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Apart from those in Openldap toolkits, there are many other tools such as Apache Directory Studio that allows you to perform similar functionality with a user interface.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-ldap-security"&gt;LDAP security&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Historically LDAP servers listens to port 389 through which traffic is sent in clear text. This is a bad security practice known as &amp;#8220;insecure bind&amp;#8221;. To secure LDAP traffic, two prevalent approaches are Secure LDAP and StartTLS.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Secure LDAP&lt;/strong&gt; was the original attempt to secure LDAP traffic as an addition to LDAP v2. It is also known as LDAPS, LDAP over TLS/SSL or LDAP channel binding (“channel binding” just refers to the establishment of encrypted channel following TLS handshake. It provides a facility to tie an authentication exchange to security services provided at a lower layer. Defined in &lt;a href="https://tools.ietf.org/html/rfc5056"&gt;RFC 5056&lt;/a&gt;). Secure LDAP operates on port 636 on the server side and TLS handshake must be established for traffic encryption. Client application usually need to import the certificate of LDAP server. As part of TLS 1.2 protocol, the server may also request client certificate during &lt;em&gt;ServerHello&lt;/em&gt; message. The presence of &lt;em&gt;CertificateRequest&lt;/em&gt; means the server either demands client certificate, or tries to get client certificate (i.e. TLSVerifyClient is set to demand or try, which is only visible on the server). If client cert is only attempted, the LDAP client may choose to ignore it. If client cert is demanded, then a two-way TLS authentication is required and thus the client must proof its identity to the server. This Secure LDAP configuration requires the server to listen to both 389 and 636 ports on the same server to support both secure and legacy applications, which is unnecessary. Secure LDAP therefore is not the preferred approach. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The standardized way of implementing SSL/TLS in LDAP v.3 is to use the &lt;strong&gt;StartTLS&lt;/strong&gt; method. This method should be implemented whenever possible. If an AD server supports StartTLS, the client can start with a STARTTLS command to the server so that the server begins the TLS encryption process. In the binding phase, TLS handshake follows a &lt;strong&gt;LDAP_START_TLS_OID&lt;/strong&gt; command through port 389.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Here’s the summary of the three LDAP configuration mode:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&#10;&lt;table id="tablepress-5" class="tablepress tablepress-id-5"&gt;&#10;&lt;thead&gt;&#10;&lt;tr class="row-1"&gt;&#10;&#9;&lt;td class="column-1"&gt;&lt;/td&gt;&lt;th class="column-2"&gt;Legacy&lt;/th&gt;&lt;th class="column-3"&gt;SecureLDAP (aka LDAPS, LDAP over TLS/SSL)&lt;/th&gt;&lt;th class="column-4"&gt;StartTLS&lt;/th&gt;&#10;&lt;/tr&gt;&#10;&lt;/thead&gt;&#10;&lt;tbody class="row-striping row-hover"&gt;&#10;&lt;tr class="row-2"&gt;&#10;&#9;&lt;td class="column-1"&gt;Listening port&lt;/td&gt;&lt;td class="column-2"&gt;389&lt;/td&gt;&lt;td class="column-3"&gt;636&lt;/td&gt;&lt;td class="column-4"&gt;389&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-3"&gt;&#10;&#9;&lt;td class="column-1"&gt;Traffic Encrypted&lt;/td&gt;&lt;td class="column-2"&gt;No&lt;/td&gt;&lt;td class="column-3"&gt;Yes&lt;/td&gt;&lt;td class="column-4"&gt;Yes&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-4"&gt;&#10;&#9;&lt;td class="column-1"&gt;Standard&lt;/td&gt;&lt;td class="column-2"&gt;Yes but this should always be avoided since it is insecure&lt;/td&gt;&lt;td class="column-3"&gt;Introduced in the time of LDAP v2, but the option is deprecated (although still supported) by RedHat&lt;/td&gt;&lt;td class="column-4"&gt;Introduced in LDAP v3. This may be left as the only valid option.&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;!-- #tablepress-5 from cache --&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Note that one of the recent changes that drives may customer away from the legacy mode is the &lt;a href="https://support.microsoft.com/en-us/help/4520412/2020-ldap-channel-binding-and-ldap-signing-requirement-for-windows"&gt;requirement for LDAP channel binding&lt;/a&gt; on Windows servers, with a target date of March 2020. Our current strategy at CS is to direct customer towards Secure LDAP as we do not support StartTLS yet and we know we do support LDAPS. Although Secure LDAP itself is somewhat legacy this would not hold long. According to &lt;a href="https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol"&gt;this&lt;/a&gt; Wikipedia page:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The use of LDAP over SSL was common in LDAP Version 2 (LDAPv2) but it was never standardized in any formal specification. This usage has been deprecated along with LDAPv2, which was officially retired in 2003. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The trade off between StartTLS and TLS/SSL exists not only in LDAP protocol, but also in many other protocols such as SMTP (port 2525, 25, 587). StartTLS is also called &lt;a href="https://en.wikipedia.org/wiki/Opportunistic_TLS"&gt;Opportunistic TLS&lt;/a&gt;. The standard is in the relevant RFC documents.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2020/02/everything-about-the-domain/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Introduction to Active Directory (AD)&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2020/03/saml-security-assertion-markup-language/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Security Assertion Markup Language (SAML)&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Networking Basics 3 of 3 – common network protocols and technologies</title><link>https://static.digihunch.com/2019/12/networking-basics-3-of-3-common-network-technologies/</link><pubDate>Fri, 20 Dec 2019 10:28:00 -0400</pubDate><guid>https://static.digihunch.com/2019/12/networking-basics-3-of-3-common-network-technologies/</guid><description>&lt;p class="wp-block-paragraph"&gt;The 5 layer TCP/IP model (or its more rigorously defined alternative OSI model) leads to a whole world of network protocols. Understanding these new protocols requires one to map it out agains the network layers (e.g. at Layer 4 whether it is TCP or UDP, etc) .&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;VPN is a whole family of technologies with many flavours of implementation. The previous posting covered some basics of the idea, as well as the two common forms (remote access VPN and site-to-site VPN). The VPN implementation protocols vary a lot. &lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;PPTP(Point-to-Point Tunnelling Protocol) is outdated and less secure&amp;nbsp;&lt;/li&gt;&lt;li&gt;IPSec (Internet Protocol Security)&lt;/li&gt;&lt;li&gt;L2TP (Layer 2 Tunnelling Protocol) replacement of PPTP, more secure, more overhead and slightly slower.&lt;/li&gt;&lt;li&gt;OpenVPN &amp;#8211; very secure, and reliable and supported by communities all over the world.&lt;/li&gt;&lt;li&gt;TLS/SSL and SSH connections may be considered VPN as well.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Phone service protocols&lt;/strong&gt;:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;&lt;strong&gt;VoIP (voice over IP, operating at network layer)&lt;/strong&gt; &amp;#8211; allows one to make and receive phone calls over the network. Communication on the IP network is perceived as less reliable in contrast to the circuit-switched public telephone network because it does not provide a network-based mechanism to ensure that data packets are not lost, and are delivered in sequential order. It is a best-effort network without fundamental Quality of Service (QoS) guarantees. Voice, and all other data, travels in packets over IP networks with fixed maximum capacity. This system may be more prone to data loss in the presence of congestion[a] than traditional circuit switched systems; a circuit switched system of insufficient capacity will refuse new connections while carrying the remainder without impairment, while the quality of real-time data such as telephone conversations on packet-switched networks degrades dramatically. Therefore, VoIP implementations may face problems with latency, packet loss, and jitter.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;SIP (session initiation protocol, operating at application layer)&lt;/strong&gt; &amp;#8211; a VOIP signaling protocol responsible for the creation and tearing down of media connections. So it supports all types of media.&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;ALG (application layer gateway, aka proxy server)&lt;/strong&gt; &amp;#8211; a software component that manages specific application protocols such as SIP and FTP. An ALG acts as an intermediary between the Internet and an application server that can understand the application protocol. ALG proxies connection to destination on behalf of client. This adds &lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&lt;figure class="aligncenter size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://static.digihunch.com/wp-content/uploads/2019/12/image-5.png" alt="" class="wp-image-561" width="505" height="213"/&gt;&lt;figcaption&gt;Application Layer Gateway&lt;/figcaption&gt;&lt;/figure&gt;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;DDNS&lt;/strong&gt; (dynamic domain name service) &amp;#8211; a router service that assigns your device a fixed domain name even though you are using dynamic IP.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;NAT&lt;/strong&gt; &amp;#8211; another family of technologies, usually implemented in the following&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Port preservation: source port chosen by a client is the same port used by the router&amp;nbsp;&lt;/li&gt;&lt;li&gt;Port forwarding: NAT application redirects a communication request from one address and port number combination to another while the packets are traversing a network gateway, such as a router or firewall. &lt;/li&gt;&lt;li&gt;Port triggering: a dynamic form of the port forwarding model. Generally, port triggering is used when the user needs to use port forwarding to reach multiple local computers. Port are close when they aren&amp;#8217;t in use (more secure) protocol used is UPnP&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;DMZ&lt;/strong&gt; &amp;#8211; a physical or logical subnet that contains external facing service to untrusted network (e.g. Internet). The purpose is to add an additional layer of security so an external network can assess what is exposed in DMZ while the rest of network remains firewalled.&amp;nbsp;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;VPN passthrough&lt;/strong&gt; &amp;#8211; a feature that allows any device connected to the router to establish outbound VPN connections. Most modern router already have this built in.&amp;nbsp;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;WAN optimization&lt;/strong&gt; &amp;#8211; a collection of techniques for increasing data transfer efficiencies across wide-area networks.&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&lt;li&gt;Deduplication&lt;/li&gt;&lt;li&gt;Compression&lt;/li&gt;&lt;li&gt;Latency optimization&lt;/li&gt;&lt;li&gt;Caching/proxy&lt;/li&gt;&lt;li&gt;Protocol spoofing&lt;/li&gt;&lt;li&gt;Traffic shaping&lt;/li&gt;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Network performance tuning&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Performance tuning in network covers a variety of skills. It is important to understand in which layer the problem occurs. Anything above layer 4 is more likely to be an application issue. For ethernet performance tuning, I found this &lt;a href="https://cromwell-intl.com/open-source/performance-tuning/ethernet.html"&gt;page&lt;/a&gt; and this &lt;a href="https://www.coverfire.com/articles/queueing-in-the-linux-network-stack/"&gt;page&lt;/a&gt; to be helpful in my practices.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2019/12/tcp-ip-basics-2-of-3-layer-4-and-common-technologies/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Networking basics 2 of 3 – Layer 4 and common network configurations&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2020/01/nginx-as-a-reverse-proxy-for-nifi/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Nginx as a reverse proxy for Nifi web UI and Kibana&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Networking basics 2 of 3 – Layer 4 and common network configurations</title><link>https://static.digihunch.com/2019/12/tcp-ip-basics-2-of-3-layer-4-and-common-technologies/</link><pubDate>Sat, 07 Dec 2019 23:19:00 -0400</pubDate><guid>https://static.digihunch.com/2019/12/tcp-ip-basics-2-of-3-layer-4-and-common-technologies/</guid><description>&lt;h3 class="wp-block-heading" id="h-transport-layer"&gt;Transport Layer&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Transport Layer handles multiplexing &amp;amp; de-multiplexing through ports. Port is more or less a virtual concept. Source port is usually ephemeral. Two dominant protocols are TCP and UDP.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;TCP relies on acknowledgement. TCP control flags are SYN, ACK, FIN, URG, PSH, RST, ECE, CWR. TCP connection is established by 3-way handshake and torn down by 4-way termination.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1223" height="502" src="https://static.digihunch.com/wp-content/uploads/2019/12/image.png" alt="" class="wp-image-515"/&gt;&lt;figcaption class="wp-element-caption"&gt;TCP handshake and termination&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Socket &amp;#8211; the instantiation of an end-point in a potential TCP connection. A socket can be in one of the following states:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;LISTEN: a TCP socket is ready and listening for incoming connections;&lt;/li&gt;&#10;&lt;li&gt;SYN_SENT: a SYNC request has been sent but connection hasn&amp;#8217;t been established yet;&lt;/li&gt;&#10;&lt;li&gt;SYN_RECEIVED: a socket previously in a LISTEN state has received a SYNC request and sent a SYN/ACK back;&lt;/li&gt;&#10;&lt;li&gt;ESTABLISHED: connection is up;&lt;/li&gt;&#10;&lt;li&gt;FIN_WAIT: FIN sent, ACK hasn&amp;#8217;t been received yet;&lt;/li&gt;&#10;&lt;li&gt;CLOSE_WAIT: connection has been closed at the TCP layer but the application that opened the socket hasn&amp;#8217;t release the hold on the socket yet;&lt;/li&gt;&#10;&lt;li&gt;CLOSED: connection fully terminated;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="403" height="306" src="https://static.digihunch.com/wp-content/uploads/2023/01/tcp-format.gif" alt="" class="wp-image-7811"/&gt;&lt;figcaption class="wp-element-caption"&gt;TCP packet format&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;TCP is a connection-oriented protocol&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Transport layer is responsible for re-sending data if data is lost&lt;/li&gt;&#10;&lt;li&gt;Sequence # is important because packet may arrive out of sync but receiver reassemble them in order&lt;/li&gt;&#10;&lt;li&gt;There is a lot of overhead (acknowledgement, establish connection first, tear down connection afterwards)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On the other hand, UDP is connectionless. A good example is video streaming, where it is okay to lose a few packet along the way, in exchange of bandwidth saving.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Firewall may operate at different layers but it is most commonly used at transport layer, to block traffic based on port.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-application-layer"&gt;Application Layer&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There is no dominant protocol at this layer. IIS, Nginx and Apache are examples of applications operating at this layer.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-basic-network-configurations"&gt;Basic network configurations&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Standard modern network configuration involves: IP address, subnet mask, gateway and DNS server. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;DNS &lt;/strong&gt;&amp;#8211; global and highly distributed network service that resolves domain name into IP address. There are &lt;a href="https://www.golinuxhub.com/2014/01/how-does-dns-query-works-when-you-type.html"&gt;many steps in DNS resolution&lt;/a&gt;. DNS service listens on port 53. Two famous free public DNS servers are 8.8.8.8 and 8.4.4.4. DNS servers have five categories:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;Caching name servers: store known domain name lookups in cache. TTL today can be a few hours, much shorter than what it used to be in early days;&lt;/li&gt;&#10;&lt;li&gt;Recursive name servers: perform full DNS resolution request;&lt;/li&gt;&#10;&lt;li&gt;Root name servers;&lt;/li&gt;&#10;&lt;li&gt;TLD name servers;&lt;/li&gt;&#10;&lt;li&gt;Authoritative name servers;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter is-resized"&gt;&lt;img decoding="async" src="https://1.bp.blogspot.com/-JqjgddtqOiw/U6ESNhWETCI/AAAAAAAADXM/JpwsJeSpCg8/s1600/dnsquery.png" alt="" style="width:433px;height:357px"/&gt;&lt;figcaption class="wp-element-caption"&gt;DNS resolution steps&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;DNS uses UDP protocol and it can generate a lot of traffic (TCP is impractical. If implemented in TCP, it would have required 44 packet for a DNS query, which is very expensive considering DNS query is just a precursor of the real traffic)&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Anycast DNS&lt;/strong&gt; &amp;#8211; any one of a number of DNS servers can respond to DNS queries, and typically the one that is geographically closest will provide the response. This reduces latency, improves uptime for the DNS resolving service and provides protection against DNS flood DDoS attacks.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;DNS record types&lt;/strong&gt;:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;A record: domain name to IP address. DNS service round robin across multiple A records&lt;/li&gt;&#10;&lt;li&gt;AAAA (quad A) record: domain name to IPv6 address&lt;/li&gt;&#10;&lt;li&gt;CNAME: redirect traffic from one domain to another (e.g. test.com to www.test.com so you can minimize IP references)&lt;/li&gt;&#10;&lt;li&gt;MX record&lt;/li&gt;&#10;&lt;li&gt;SRV record&lt;/li&gt;&#10;&lt;li&gt;TXT record: originally for human consumption, freeform text for configuration purpose.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;A FQDN (fully qualified domain name) can have up to 127 domains, but only three in most cases. (i.e. subdomain.domain.topleveldomain)&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;DNS zones&lt;/strong&gt; &amp;#8211; allow for easier control over multiple levels of a domain. DNS zones are configured in zone files. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Domains vs zones&lt;/strong&gt; &amp;#8211; Domains are broken into zones for which individual DNS servers are responsible. A domain represents the entire set of names/machines that are contained under an organizational domain name. For example, all domain names ending with &amp;#8220;.com&amp;#8221; are part of the &amp;#8220;com&amp;#8221; domain. A &amp;#8220;zone&amp;#8221; is a domain less any sub-domains delegated to other DNS servers. A DNS server could be responsible (authoritative) for all records under the &amp;#8220;xyz.com&amp;#8221; domain, but by defining NS-records for &amp;#8220;abc.xyz.com&amp;#8221;, this part of the domain is delegated to other DNS servers &amp;#8211; and possibly a different company/entity. A zone contains exactly one SOA-record describing the general properties of the zone, and any number of other DNS records. Entire zones can transferred from a primary DNS server to secondary DNS servers through Zone Transfers. A domain administrator would be responsible for creating zones, and delegating responsibility for these zones to an administrator and DNS server.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="597" height="529" src="https://static.digihunch.com/wp-content/uploads/2024/07/DNS-Zones-Illustration.jpg" alt="" class="wp-image-11546" srcset="https://static.digihunch.com/wp-content/uploads/2024/07/DNS-Zones-Illustration.jpg 597w, https://static.digihunch.com/wp-content/uploads/2024/07/DNS-Zones-Illustration-300x266.jpg 300w" sizes="auto, (max-width: 597px) 100vw, 597px" /&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Reverse DNS lookup&lt;/strong&gt; &amp;#8211; query for FQDN by IP. This is commonly used by email servers where anti-spam mechanism on the receiver needs to validate that sender&amp;#8217;s IP is associated with a domain as claimed. This is also used in logging application to convert IP into human-readable domains in the log data. Reverse DNS lookups query DNS server for a PTR (pointer reserve record). If the server does not have a PTR record, it cannot resolve a reverse lookup.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;DHCP&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;DHCP operates at application layer and helps you to configure&amp;nbsp; IP automatically with a lease, through automatic allocation, or fixed allocation based on MAC, etc. DHCP process involves address allocation, renewal, and release. Address allocation takes four steps:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;Client sends a broadcast to discover DHCP server;&lt;/li&gt;&#10;&lt;li&gt;DHCP server broadcast a DHCP offer;&lt;/li&gt;&#10;&lt;li&gt;Client requests IP address from the DHCP server;&lt;/li&gt;&#10;&lt;li&gt;Server acknowledged the DHCP request;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Here is an illustration of DHCP address allocation.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="713" height="527" src="https://static.digihunch.com/wp-content/uploads/2024/07/DHCP-address-allocation.gif" alt="" class="wp-image-11548"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;DHCP can also be used to set NTP address.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;NAT&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Network Address Translation (NAT), can be implemented in many different ways in different OS. Essentially, it is a technology that allows a gateway, usually a router or firewall, to rewrite the source IP of an outgoing IP datagram while retaining the original IP in order to rewrite it into the response.&amp;nbsp; Two categories of NAT are:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Basic NAT: provides a one-to-one translation of IP addresses, aka one-to-one NAT. Basic NATs can be used to interconnect two IP networks that have incompatible addressing.&lt;/li&gt;&#10;&lt;li&gt;One-to-many NAT: maps multiple private hosts to one publicly exposed IP address, aka IP masquerading. This can be a security measure so that no external host can establish to your computer without knowing your actual IP. Source port conflict can be managed in two ways:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;port preservation: When making outgoing connection, NAT preserves the ephemeral port number used by internal client that initiates the connection; if two clients happen to use the same ephemeral port, then NAT picks a random port to initiate outgoing TCP connection;&lt;/li&gt;&#10;&lt;li&gt;port forwarding (port mapping): Forward traffic to certain destination based on the port of incoming request that NAT receives. &lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter is-resized"&gt;&lt;img decoding="async" src="https://upload.wikimedia.org/wikipedia/commons/thumb/5/50/Internet_port_forwarding.png/800px-Internet_port_forwarding.png" alt="File:Internet port forwarding.png" style="width:521px;height:293px"/&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Proxy&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Proxy refers to a concept rather than a specific implementation. It exists on almost every layer in the network model, and act on behalf of a client in order to access other service. For example, &lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Web proxy used to be used to cache web traffic data in slow Internet but it is not necessary any more because 1) there is not much speed benefit; 2) website today is much more dynamic. &lt;/li&gt;&#10;&lt;li&gt;Reverse proxy is a popular architecture of web server, such as Nginx, to act as a front end of web servers, as well as point of decryption so web servers are free to just serve the content.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;VPN&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;VPN &amp;#8211; allows for extension of a private or local network to host that might not be on that local private network by using encrypted tunnel. There are many flavours of implementation for many purposes. It is a general concept rather than a specific protocol (just like NAT). VPN client provisions the computer with a &lt;span style="text-decoration: underline;"&gt;virtual interface&lt;/span&gt; with an IP that matches the address space of the private network, and establish a VPN tunnel to it. Most VPNs work by using the payload section of transport layer to carry an encrypted payload that actually contains an entire second set of packets: the network, the transport and the application layers of a packet intended to traverse a network. Basically, this payload is carried to the VPN&amp;#8217;s endpoint where all the other layers are stripped away and discarded. Then, the payload is unencrypted, leaving the VPN server with the top three layers of a new packet. This gets encapsulated with the proper datalink layer information and sent across the network. This process is completed in the inverse in the opposite direction. VPN usually requires strict authentication procedures and encryption. VPN can also be used to establish site-to-site connection (aka point-to-point VPN) where individual user doesn&amp;#8217;t have to establish connections on their own. Both sites needs specialized hardware to achieve this. Site-to-site VPN is a good alternative to WAN when two site don&amp;#8217;t need to transfer large amount of data for very fast speed.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;WAN&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Wide Area Network &amp;#8211; act like a single network, but span across multiple physical locations, it requires that you contract the link across the internet with ISP. ISP handles data link from one site to another.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="586" height="160" src="https://static.digihunch.com/wp-content/uploads/2019/12/image-4.png" alt="" class="wp-image-533"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Wireless Network&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Wireless protocol (802.11 family) defines how Wifi operates at physical and data link layer. Wifi networks operates on 2.4GHz and 5GHz frequency bands. Wireless frame is fairly different from Ethernet frame due to the nature of wireless transmission.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="http://static1.squarespace.com/static/55e6d56ee4b0139f372acc16/t/57597dd259827ef6e49d01cf/1465482710010/?format=1500w" alt="Image result for wireless frame&amp;quot;"/&gt;&lt;figcaption class="wp-element-caption"&gt;Wireless frame&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Wireless access point is a device that bridges the wireless and wired portions of a network. A single wired network might have many wireless access points to cover a large area.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Wireless network can be configured in a few main ways:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Ad-hoc network &amp;#8211; nodes all directly speak to each other. No supporting infrastructure is needed but not most common. It can be powerful tool during disasters.&lt;/li&gt;&#10;&lt;li&gt;Wireless LAN (WLAN) &amp;#8211; one or more access points act as abridge between wireless and wired network. This is the most common type in business world where the wired LAN provides link to the Internet.&lt;/li&gt;&#10;&lt;li&gt;Mesh networks &amp;#8211; a hybrid of the two above&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Wireless Security&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Wireless transmission is across the air so encryption is more important. The number of bit in the encryption key corresponds to how secure the encryption is.&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;WEP (encryption technology) provides low level of privacy (40-bit encryption) and it is not preferred today;&lt;/li&gt;&#10;&lt;li&gt;WPA provides 128-bit key encryption; &lt;/li&gt;&#10;&lt;li&gt;WPA2 provides 256-bit key encryption and is most common today.&lt;/li&gt;&#10;&lt;li&gt;MAC filtering also help security in wireless&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Basic networking troubleshooting&lt;/strong&gt;&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;ICMP ping to test general quality of connection;&lt;/li&gt;&#10;&lt;li&gt;traceroute discovers the path between two nodes and give you the information along the way;&lt;/li&gt;&#10;&lt;li&gt;netcat checks port and host address (telnet is retiring);&lt;/li&gt;&#10;&lt;li&gt;nslookup: very powerful in interactive mode for resolution tools;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;IPv6 &lt;/strong&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;IPv4 address running out in November 2019 is a major crisis. IPv6 becomes more critical to implement for many organizations. IPv6 address is very long with numbers. Numbers represents a natural way that computer thinks and operates, but not for human. So IPv6 comes with two rules to shorten the address:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="289" height="159" src="https://static.digihunch.com/wp-content/uploads/2019/12/image-2.png" alt="" class="wp-image-520"/&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;You may remove any leading 0;&lt;/li&gt;&#10;&lt;li&gt;Any number of consecutive groups can be replaced with two colons;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;IPv6 header looks like below:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" width="742" height="458" src="https://static.digihunch.com/wp-content/uploads/2019/12/image-3.png" alt="" class="wp-image-521" style="width:523px;height:323px"/&gt;&lt;figcaption class="wp-element-caption"&gt;IPv6 header&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Coexistence &amp;#8211; the optimal approach for existing networks is to focus not on transition but on coexistence. Coexistence may live a long period with these phases: 1) Turn on IPv6 routing in their existing IPv4 networks and start using it; 2) Contract IPv6 service with their upstream, peer, and downstream neighbours; 3) Use the IPv6 protocol in addition to IPv4 in their applications and services both on server equipment and on their clients; 4) turn off IPv4 at some point when it is no longer a business requirement.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2019/11/storage-nitty-gritty-5-of-5-replication/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Storage Nitty-Gritty 5 of 5 – Replication&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2019/12/networking-basics-3-of-3-common-network-technologies/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Networking Basics 3 of 3 – common network protocols and technologies&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Cryptography basics 2 of 2</title><link>https://static.digihunch.com/2019/09/cryptographic-concepts-for-busy-it-professionals-2-of-2/</link><pubDate>Sun, 08 Sep 2019 21:21:41 -0400</pubDate><guid>https://static.digihunch.com/2019/09/cryptographic-concepts-for-busy-it-professionals-2-of-2/</guid><description>&lt;p class="wp-block-paragraph"&gt;My previous &lt;a href="https://static.digihunch.com/2019/07/practical-cryptography-for-it-professional/"&gt;post&lt;/a&gt; outlines several core concepts around cryptography, such as asymmetric key encryption, digital certificate, the encoding formats and relevant file extensions. In this article, we continue to explore cryptography use cases, where these concepts are connected and put into application.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The most important use case is TLS handshake. I cannot stress enough how paramount this scenario is. This use case and its variation can be found in almost every situation where connection needs to be secured.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;TLS handshake&lt;/strong&gt;: the process in which client and server establish secure connection. During the handshake, two parties agree on TLS version, decide on cipher suite, authenticate the identity of each other (although client identity authentication is less common), and generate session key for symmetric encryption after the handshake. Details steps are very important. &lt;a href="https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_7.1.0/com.ibm.mq.doc/sy10660_.htm"&gt;Here&lt;/a&gt; is a fairly thorough reference, and here is a great diagram:&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img loading="lazy" decoding="async" width="1496" height="792" src="https://static.digihunch.com/wp-content/uploads/2019/10/TLS.png" alt="" class="wp-image-152"/&gt;&lt;figcaption class="wp-element-caption"&gt;TLS handshake&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Secure browser connection&lt;/strong&gt;: The most widespread use of TLS handshake is to secure browser connection with HTTPS:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;Browser initiates connection to the server;&lt;/li&gt;&#10;&lt;li&gt;Server sends browser its certificate (public key + digital signature signed by CA);&lt;/li&gt;&#10;&lt;li&gt;Browser has preloaded public key of CA and uses it to decrypt digital signature and get the digest of public key;&amp;nbsp;&lt;/li&gt;&#10;&lt;li&gt;Browser calculate digest of received public key and compares it against the digest from the previous step;&lt;/li&gt;&#10;&lt;li&gt;If the result is the same, public key is trusted, a green lock is displayed; otherwise, a warning is displayed;&lt;/li&gt;&#10;&lt;li&gt;proceed to the rest of the steps in TLS handshake.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Secure connection in Java&lt;/strong&gt;: Java applications manages keys and certificates through two classes: java.security.KeyStore and java.security.TrustStore. Suppose a Java application client initiates TLS connection to server. The server application will present its certificates from server&amp;#8217;s Key Store. The client will use certificates stored in client&amp;#8217;s Trust Store to verify the identity of the server. Once validated, the client then presents certificates stored in client&amp;#8217;s Key Store back to the server for validation.&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;A KeyStore keeps keys and certificates for your own application. Typically, you store a KeyPair in a KeyStore file.&lt;/li&gt;&#10;&lt;li&gt;A TrustStore keeps the certificates of external systems that your application trusts.&amp;nbsp;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Below is a great diagram:&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img loading="lazy" decoding="async" width="1362" height="956" src="https://static.digihunch.com/wp-content/uploads/2019/10/JavaJKS.png" alt="" class="wp-image-153"/&gt;&lt;figcaption class="wp-element-caption"&gt;Secure Java application in TLS&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;JKS file &lt;/strong&gt;is Java&amp;#8217;s version of PKCS#12 (private key + certificate, password protected). Entries in a JKS file must have an &amp;#8220;alias&amp;#8221; that is unique. The JKS file type can be used for both Key Store and Trust Store. When it&amp;#8217;s used in Key Store, it contains a certificate and private key for the Java application. When it&amp;#8217;s used in Trust Store, it only contains certificate from external trusted applications. Note that JKS is the default keystore format until Java 8. Since Java 9 the default keystore format is PKCS12.&amp;nbsp;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Finally, we can introduce some tools:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Command line Tools&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;openssl &lt;/strong&gt;is a versatile tool for cryptography and keytool is a similar tool for Java applications.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;keytool &lt;/strong&gt;is for store keys/certificates in Java Key Store or Trust Store.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I do not include any openssl or keytool command in this article in order to remain theoretical. However, once through the conceptual hurdles, one should become fairly comfortable picking up the tools and understand why each command is needed to achieve its purpose.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2019/08/aws-certified-devops-engineer-exam-tips/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;AWS Certified DevOps Engineer Exam Tips&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2019/10/personal-vim-cheatsheet/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Personal Vim cheatsheet&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item></channel></rss>