<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="http://localhost:4000/atom.xml" rel="self" type="application/atom+xml" /><link href="http://localhost:4000/" rel="alternate" type="text/html" /><updated>2025-02-27T09:26:35+01:00</updated><id>http://localhost:4000/atom.xml</id><title type="html">Notatnik IT</title><subtitle>Blog osobisty o różnych sprawach IT</subtitle><author><name>{&quot;name&quot;=&gt;nil, &quot;picture&quot;=&gt;nil, &quot;email&quot;=&gt;nil, &quot;links&quot;=&gt;[{&quot;title&quot;=&gt;nil, &quot;url&quot;=&gt;nil, &quot;icon&quot;=&gt;nil}]}</name></author><entry><title type="html">Powtórka z Vaulta</title><link href="http://localhost:4000/hashicorp/bezpiecze%C5%84stwo/2024-08-26-powtorkazvaulta/" rel="alternate" type="text/html" title="Powtórka z Vaulta" /><published>2024-08-26T00:00:00+02:00</published><updated>2024-08-26T00:00:00+02:00</updated><id>http://localhost:4000/hashicorp/bezpiecze%C5%84stwo/powtorkazvaulta</id><content type="html" xml:base="http://localhost:4000/hashicorp/bezpiecze%C5%84stwo/2024-08-26-powtorkazvaulta/"><![CDATA[<p>Strasznie zaniedbałem ten mój blog. Nie tylko jego zresztą – mój domowy lab też obrócił się w ruinę. Porósł szuwarami niepamięci, a kilka drobnych aktualizacji wystarczyło, by cała misterna konstrukcja rozsypała się jak domek z kart. W sumie, nie ma tragedii. Żadne stworzonko nie ucierpiało, a ja mam teraz okazję powtórzyć sobie to i owo. Ubuntu 20.04 i tak już zyskało status moralnie przestarzałego, a parę rozwiązań na sznurki też swoje lata ma. No to czas zacząć od nowa. Trzy debiany 12 czekają już na VMkach, jak młode orły na pierwszy lot.</p>

<p>Pomyślałem, że opiszę tutaj krok po kroku, jak postawić trzywęzłowy klaster Hashicorpowego Vaulta, który w swojej strukturze może przypominać coś produkcyjnego. Oczywiście, można by tu pokusić się o coś bardziej “fancy &amp; jazzy” – całą rolę w Ansible’u – ale jeśli ktoś zabiera się za to po raz pierwszy, lepiej żeby zrozumiał, jak to naprawdę działa. A najlepiej przebrnąć przez tę ścieżkę zdrowia samodzielnie, palce na klawiaturze, krok za krokiem. Mój własny wpis o tym, jak stawiać całe trio – <a href="https://github.com/dominikmi/hashistuff-homelab">Vault, Consul, Nomad</a> – też już trochę się zestarzał. Ale zaglądałem tam niedawno i muszę przyznać, że wciąż można coś z tego wyciągnąć.</p>

<h3 id="czego-nam-potrzeba">Czego nam potrzeba?</h3>
<p>Nic wielkiego, wystarczy jakis Linux z libvirtd i trzema gołymi VMkami, może być Ubuntu, może być Debian.</p>

<h3 id="architektura">Architektura</h3>
<ol>
  <li>Klaster HA, jeden <em>leader</em> + dwóch <em>voters</em>. Klaster jest demokratyczny i wybiera sobie lidera przez głosowanie. Aczkolwiek tego pierwszego trzeba liderem zrobić, by pozostałe miało do kogo radośnie dołączyć, formując klaster.</li>
  <li>Do tego, by było łatwiej mamy osobnego vaulta przydatnego jako <em>transit-unseal</em>. Będzie udawał AWSowego KMSa. Przydatność polega na tym, że po każdym restarcie nie trzeba uparcie wklepywać 2-3 z pięciu kluczy by “odpieczętować” instancję. Bowiem każda instancja ma specjalny token, którym uwierzytelni się u tego osobnego vaulta, a tamten je automagicznie “odpieczętuje”.</li>
</ol>]]></content><author><name>Dominik Miklaszewski</name></author><category term="Hashicorp" /><category term="bezpieczeństwo" /><category term="bezpieczeństwo" /><category term="metryki" /><category term="ryzyko" /><category term="analizadanych" /><category term="python" /><summary type="html"><![CDATA[Strasznie zaniedbałem ten mój blog. Nie tylko jego zresztą – mój domowy lab też obrócił się w ruinę. Porósł szuwarami niepamięci, a kilka drobnych aktualizacji wystarczyło, by cała misterna konstrukcja rozsypała się jak domek z kart. W sumie, nie ma tragedii. Żadne stworzonko nie ucierpiało, a ja mam teraz okazję powtórzyć sobie to i owo. Ubuntu 20.04 i tak już zyskało status moralnie przestarzałego, a parę rozwiązań na sznurki też swoje lata ma. No to czas zacząć od nowa. Trzy debiany 12 czekają już na VMkach, jak młode orły na pierwszy lot.]]></summary></entry><entry><title type="html">Metryki bezpieczeństwa</title><link href="http://localhost:4000/ryzyko/python/2023-04-01-metrykibezpieczenstwa/" rel="alternate" type="text/html" title="Metryki bezpieczeństwa" /><published>2023-04-01T00:00:00+02:00</published><updated>2023-04-01T00:00:00+02:00</updated><id>http://localhost:4000/ryzyko/python/metrykibezpieczenstwa</id><content type="html" xml:base="http://localhost:4000/ryzyko/python/2023-04-01-metrykibezpieczenstwa/"><![CDATA[<p>Są takie książki do których trzeba się mentalnie i merytorycznie przygotować. A kiedy już się po nie sięgnie ich zawartość jest starannie czytana, ..i czytana ..i czytana, treść “obracana” w głowie, aż do pełnego zrozumienia. Ale też nie, że <em>“Achaaa.. to tak działa!”</em> i siup, następny rozdział. Trzeba to przerobić praktycznie.</p>

<p><img src="/assets/images/tmm1.png" alt="The Metrics Manifesto" class="align-center" /></p>

<p>Jeśli rozdział zawiera rozwiązanie jakiegoś problemu w sposób programistyczny, to wypada również podejść do takiego problemu samodzielnie i znaleźć rozwiązanie w ten czy inny sposób. Więc czasem robi się z tego wielomiesięczne studium. Tak było w przypadku <a href="https://www.wiley.com/en-us/How+to+Measure+Anything+in+Cybersecurity+Risk-p-9781119085294">“How to measure anything in cybersecurity”</a>, tak jest też w przypadku <a href="https://themetricsmanifesto.com/">“The Metrics Manifesto”</a>. Tę drugą właśnie studiuję, wciąż wracając do tej pierwszej.</p>

<p><em>“We confuse <strong>what’s easy to measure</strong>, like bullet holes in returned planes, with what is <strong>important to measure</strong>, like bullet holes in downed planes. We deceive ourselves by measuring only what is obvious, forgetting the goal of our measurement”</em>
[R. Seiersen, “The Metrics Manifesto”]</p>

<p>Metryki bezpieczeństwa powinny odpowiadać na całkiem zasadnicze pytania, które zadaje sobie (chyba prawie) każdy odpowiedzialny kierownik czy ekspert dziedzinowy, a także stanowić dane wejściowe do szacowania ryzyka w danym obszarze cyberbezpieczeństwa.</p>

<p>Zamiast liczyć ileż to podatności zostało załatanych miesiąc do miesiąca, czy rok do roku - bo to są tylko metryki logistyczne na zasadzie - tyle a tyle cementu dowieziono - można zadać sobie pytanie: ile średnio, każdego dnia w roku mam niepołatanych podatności o istotności krytycznej w obszarze kluczowych aplikacji. Albo zamiast liczb ile rozwiązano incydentów, jaki jest trend współczynnika incydentów rozwiązanych do nierozwiązanych w podziale na aplikacje czy obszary (np. on-prem vs cloud). Czy też, na podstawie danych z przeprowadzonych testów, oszacować ryzyko phishingu - czyli jaką procentową szanse możemy założyć, że na 1000 wysłanych emaili ileś osób jednak kliknie i obserwować ten trend miesiąc do miesiąca. To można robić i to w całkiem zautomatyzowany sposób. Wystarczą dane, python (albo R) i trochę umiejętności grzebania w danych.</p>

<p>Okazuje się, że naprawdę nie trzeba petabajtów danych, aby przygotować ciekawe metryki. Wystarczy prosty raport z wykrytych i usuniętych podatności w jakimś okresie.</p>

<h2 id="instalacja-jupytera">Instalacja Jupytera</h2>
<p>Dopóki nie spróbowałem, nie miałem pojęcia jakie to przydatne narzędzie w analityce danych. Środowisko Pythona mam już skonfigurowane z <code class="language-plaintext highlighter-rouge">pipenv</code> (łączy w sobie wygodę <code class="language-plaintext highlighter-rouge">pip</code> i <code class="language-plaintext highlighter-rouge">venv</code>). W danym katalogu projektowym odpalamy <code class="language-plaintext highlighter-rouge">pipenv shell</code> i wykonujemy:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>pipenv <span class="nb">install </span>jupyter jupyterlab ipykernel
<span class="nv">$ </span>pipenv <span class="nb">install </span>numpy scipy pandas seaborn statsmodel scikit
<span class="nv">$ </span>python <span class="nt">-m</span> ipykernel <span class="nb">install</span> <span class="nt">--user</span> <span class="nt">--name</span><span class="o">=</span>metrics
<span class="nv">$ </span>pipenv run jupyter lab
</code></pre></div></div>
<p>W nowej zakładce przeglądarki uruchomi się UI Jupyter lab, gdzie należy wybrać kernel - metrics z uprzednio zainstalowanymi bibliotekami. Jeśli jednak jakiejś zabraknie to w linii kodu wystarczy wykonać <code class="language-plaintext highlighter-rouge">!pipenv install &lt;brakująca_biblioteka&gt;</code>.</p>

<h2 id="nieco-teorii---tabele-przeżycia">Nieco teorii - tabele przeżycia</h2>

<p>Zwane również tabelami trwania życia. Naturalnym skojarzeniem jest od razu ZUS. Nam chodzi o coś innego. Zbadanie rozkładu okresów jakie upływają między danymi zdarzeniami. Na przykład, między pojawieniem się podatności a jej usunięciem. Więc zdarzeniem terminalnym jest tu usunięcie podatności. Celem tabeli jest pomiar szans na przeżycie podatności w danym okresie albo inaczej - wyliczenie prawdopodobieństwa wystąpienia zdarzenia usunięcia podatności w danym okresie. W tym kontekście upraszczając: podatność jest procesem, który wystąpił (data wykrycia) i który mógł zostać przerwany (terminacja, śmierć) przez usunięcie (załatanie podatności, podbicie wersji biblioteki itd.).</p>

<p>Kiedy już wyznaczymy sobie interwał czasowy, w którym chcemy złapać wszystkie zdarzenia wystąpienia podatności i jej usunięcia, musimy wykonać jeszcze dwie czynności - odfiltrować zdarzenia które:</p>
<ul>
  <li>zaczęły się ale nie skończyły w przyjętym interwale czasowym - ocenzurowane,</li>
  <li>zaczęły się w dniu prawej granicy interwału albo po tym dniu - odrzucone.</li>
</ul>

<p>Przykład poniżej, przypuśćmy że badamy sobie raport z pierwszej połowy roku, gdzie mamy 8 podatności. V1, V2 i V7 cenzurujemy a V8 odrzucamy. 
<img src="/assets/images/podatnosci.png" alt="Podatności" class="align-center" /></p>

<h2 id="python-i-estymator">Python i estymator</h2>
<p>Ładujemy plik CSV z raportem podatności do <code class="language-plaintext highlighter-rouge">DataFrame</code>:</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="code"><pre> 
<span class="err">!</span><span class="n">wget</span> <span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">raw</span><span class="p">.</span><span class="n">githubusercontent</span><span class="p">.</span><span class="n">com</span><span class="o">/</span><span class="n">ribsy</span><span class="o">/</span><span class="n">mdata</span><span class="o">/</span><span class="n">main</span><span class="o">/</span><span class="n">tmm_start</span><span class="p">.</span><span class="n">csv</span>
 
</pre></td></tr></tbody></table></code></pre></figure>

<p>Nagłówki w pliku CSV stają się nazwami kolumn w DataFrame, w naszym przypadku musimy lekko skorygować te nazwy zamieniając kropki na znak podkreślenia.</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="code"><pre> 
<span class="kn">import</span> <span class="n">pandas</span> <span class="k">as</span> <span class="n">pd</span>
<span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="nf">read_csv</span><span class="p">(</span><span class="sh">'</span><span class="s">tmm_start.csv</span><span class="sh">'</span><span class="p">)</span>
<span class="n">df</span><span class="p">.</span><span class="n">rename</span><span class="p">(</span><span class="n">columns</span> <span class="o">=</span> <span class="p">{</span><span class="sh">'</span><span class="s">first.seen</span><span class="sh">'</span><span class="p">:</span><span class="sh">'</span><span class="s">first_seen</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">last.seen</span><span class="sh">'</span><span class="p">:</span><span class="sh">'</span><span class="s">last_seen</span><span class="sh">'</span><span class="p">,</span> <span class="p">},</span> <span class="n">inplace</span> <span class="o">=</span> <span class="bp">True</span>
 
</pre></td></tr></tbody></table></code></pre></figure>

<p>Nasza tabela nie zawiera jeszcze dwóch kolumn istotnych dla naszej analizy. Jedna to okres występowania podatności (czyli last_seen - first_seen) i status podatności - Ok, czy ocenzurowana. Pierwsza linijka kodu wylicza nam deltę dla każdej wartości kolumn last_seen i first_seen, druga linijka zmienia nam tę deltę na liczbę dni i pakuje do nowej kolumny o nazwie <code class="language-plaintext highlighter-rouge">delta</code>. A trzecia linijka sprawdza czy daną podatność trzeba ocenzurować i w zależności od tego w kolumnie <code class="language-plaintext highlighter-rouge">status</code> pakując tam <code class="language-plaintext highlighter-rouge">0</code> lub <code class="language-plaintext highlighter-rouge">1</code>. W ostatniej linii filtrujemy sobie dane i pakujemy je do nowej tabeli <code class="language-plaintext highlighter-rouge">DataFrame</code>.</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="code"><pre> 
<span class="kn">from</span> <span class="n">datetime</span> <span class="kn">import</span> <span class="n">datetime</span> <span class="k">as</span> <span class="n">dt</span>
<span class="kn">from</span> <span class="n">datetime</span> <span class="kn">import</span> <span class="n">timedelta</span>

<span class="n">delta</span> <span class="o">=</span> <span class="p">[(</span><span class="n">dt</span><span class="p">.</span><span class="nf">strptime</span><span class="p">(</span><span class="n">stop</span><span class="p">,</span><span class="sh">"</span><span class="s">%Y-%m-%d</span><span class="sh">"</span><span class="p">)</span> <span class="o">-</span> <span class="n">dt</span><span class="p">.</span><span class="nf">strptime</span><span class="p">(</span><span class="n">start</span><span class="p">,</span><span class="sh">"</span><span class="s">%Y-%m-%d</span><span class="sh">"</span><span class="p">))</span><span class="o">+</span><span class="nf">timedelta</span><span class="p">(</span><span class="n">days</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span> <span class="k">for</span> <span class="n">start</span><span class="p">,</span> <span class="n">stop</span> <span class="ow">in</span> <span class="nf">zip</span><span class="p">(</span><span class="n">df</span><span class="p">[</span><span class="sh">'</span><span class="s">first_seen</span><span class="sh">'</span><span class="p">],</span><span class="n">df</span><span class="p">[</span><span class="sh">'</span><span class="s">last_seen</span><span class="sh">'</span><span class="p">])]</span>
<span class="n">df</span><span class="p">[</span><span class="sh">'</span><span class="s">delta</span><span class="sh">'</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="n">x</span><span class="p">.</span><span class="n">days</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">delta</span><span class="p">]</span>
<span class="n">df</span><span class="p">[</span><span class="sh">'</span><span class="s">status</span><span class="sh">'</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span> <span class="nf">if </span><span class="p">(</span><span class="n">dt</span><span class="p">.</span><span class="nf">strptime</span><span class="p">(</span><span class="n">stop</span><span class="p">,</span><span class="sh">"</span><span class="s">%Y-%m-%d</span><span class="sh">"</span><span class="p">)</span> <span class="o">&lt;</span> <span class="n">dt</span><span class="p">.</span><span class="nf">strptime</span><span class="p">(</span><span class="sh">'</span><span class="s">2020-12-31</span><span class="sh">'</span><span class="p">,</span><span class="sh">"</span><span class="s">%Y-%m-%d</span><span class="sh">"</span><span class="p">))</span> <span class="k">else</span> <span class="mi">0</span> <span class="k">for</span> <span class="n">stop</span> <span class="ow">in</span> <span class="n">df</span><span class="p">[</span><span class="sh">'</span><span class="s">last_seen</span><span class="sh">'</span><span class="p">]]</span>
<span class="n">df_flt</span> <span class="o">=</span> <span class="n">df</span><span class="p">[(</span><span class="n">df</span><span class="p">.</span><span class="n">idTeam</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="o">&amp;</span> <span class="p">(</span><span class="n">df</span><span class="p">.</span><span class="n">group</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="o">&amp;</span> <span class="p">(</span><span class="n">pd</span><span class="p">.</span><span class="nf">to_datetime</span><span class="p">(</span><span class="n">df</span><span class="p">.</span><span class="n">first_seen</span><span class="p">)</span> <span class="o">&lt;=</span> <span class="n">pd</span><span class="p">.</span><span class="nf">to_datetime</span><span class="p">(</span><span class="sh">'</span><span class="s">2020-10-01</span><span class="sh">'</span><span class="p">))]</span>
 
</pre></td></tr></tbody></table></code></pre></figure>

<p>I to właściwie tyle. Dane mamy przygotowane. Teraz wykorzystamy je do (wyliczenia i przedstawienia graficznego) odpowiedzi na pytanie: jaka liczba podatności zostaje usunięta po X dniach z wyliczonym prawdopodobieństwem. Do tego celu użyty został estymator Kaplana Meiera. Estymator ten prognozuje funkcję przeżycia danej frakcji (podzbioru) podatności.</p>

<p>Na początku, zdefiniowałem dwa kryteria (dwie frakcje - podatności krytyczne i <code class="language-plaintext highlighter-rouge">high</code>), a potem już poszło sprawnie. Wywołanie objektu <code class="language-plaintext highlighter-rouge">KaplanMeierFitter()</code> i zastosowanie metody <code class="language-plaintext highlighter-rouge">fit</code> gdzie podałem wyliczoną deltę i status dla każdego wiersza naszej tabeli <code class="language-plaintext highlighter-rouge">df_flt</code>.</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
</pre></td><td class="code"><pre> 
<span class="kn">import</span> <span class="n">datetime</span>
<span class="kn">from</span> <span class="n">lifelines</span> <span class="kn">import</span> <span class="n">KaplanMeierFitter</span>
<span class="kn">import</span> <span class="n">matplotlib.pyplot</span> <span class="k">as</span> <span class="n">plt</span>

<span class="n">critical</span> <span class="o">=</span>  <span class="p">((</span><span class="n">df_flt</span><span class="p">.</span><span class="n">severity</span> <span class="o">==</span> <span class="sh">'</span><span class="s">extreme</span><span class="sh">'</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="n">df_flt</span><span class="p">.</span><span class="n">severity</span> <span class="o">==</span> <span class="sh">'</span><span class="s">critical</span><span class="sh">'</span><span class="p">))</span>
<span class="n">high</span> <span class="o">=</span> <span class="p">(</span><span class="n">df_flt</span><span class="p">.</span><span class="n">severity</span> <span class="o">==</span> <span class="sh">'</span><span class="s">high</span><span class="sh">'</span><span class="p">)</span>
<span class="n">ax</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="nf">subplot</span><span class="p">()</span>
<span class="n">kmf</span> <span class="o">=</span> <span class="nc">KaplanMeierFitter</span><span class="p">()</span>
<span class="n">kmf</span><span class="p">.</span><span class="nf">fit</span><span class="p">(</span><span class="n">durations</span><span class="o">=</span><span class="n">df_flt</span><span class="p">[</span><span class="n">critical</span><span class="p">].</span><span class="n">delta</span><span class="p">,</span> <span class="n">event_observed</span><span class="o">=</span><span class="n">df_flt</span><span class="p">[</span><span class="n">critical</span><span class="p">].</span><span class="n">status</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="sh">'</span><span class="s">Critical vulns</span><span class="sh">'</span><span class="p">)</span>
<span class="n">kmf</span><span class="p">.</span><span class="nf">plot_survival_function</span><span class="p">(</span><span class="n">ax</span><span class="o">=</span><span class="n">ax</span><span class="p">)</span>
<span class="n">kmf</span><span class="p">.</span><span class="nf">fit</span><span class="p">(</span><span class="n">durations</span><span class="o">=</span><span class="n">df_flt</span><span class="p">[</span><span class="n">high</span><span class="p">].</span><span class="n">delta</span><span class="p">,</span> <span class="n">event_observed</span><span class="o">=</span><span class="n">df_flt</span><span class="p">[</span><span class="n">high</span><span class="p">].</span><span class="n">status</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="sh">'</span><span class="s">High vulns</span><span class="sh">'</span><span class="p">)</span>
<span class="n">kmf</span><span class="p">.</span><span class="nf">plot_survival_function</span><span class="p">(</span><span class="n">ax</span><span class="o">=</span><span class="n">ax</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="nf">title</span><span class="p">(</span><span class="sh">'</span><span class="s">Vulns survivability based on their relevancy</span><span class="sh">'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="nf">show</span><span class="p">()</span>
 
</pre></td></tr></tbody></table></code></pre></figure>

<p><img src="/assets/images/kmf1.png" alt="Podatności" class="align-center" /></p>

<p>Jaką informację mamy zawartą na powyższym wykresie (oś x - liczba dni, oś y - % liczby zdarzeń)? A taką że - w zadanym okresie:</p>
<ul>
  <li>Podatności krytyczne są szybciej usuwane niż te o statusie <code class="language-plaintext highlighter-rouge">High</code> (to dobrze, ale nie zawsze jest oczywiste),</li>
  <li>Srednio 50% usuwanych podatności krytycznych jest usuwana po ok. 23-29 dniach a <code class="language-plaintext highlighter-rouge">High</code> po 58-62 dniach,</li>
</ul>

<p>Mając różne atrybuty podatności w raporcie, w rodzaju: która aplikacja, który zespół, itd. możemy te podatności badać w rozbiciu na różne podzbiory (frakcje). Poniżej, przykład podobny do pierwszego, ale niosący dodatkową informację jak sobie różne zespoły radzą z usuwaniem podatności:</p>

<p><img src="/assets/images/kmf2.png" alt="Podatności" class="align-center" /></p>

<p>Warto zauważyć, że im więcej danych, tym przedziały wiarygodności, będa węższe a więc nasze prognozy - trafniejsze.
I to tyle na razie. Poza tym można podobną analizę zastosować do zidentyfikowanych zagrożeń, obsługi incydentów bezpieczeństwa, z badaniem czy SLA są dotrzymane itd. <a href="https://github.com/dominikmi/python-exercises/blob/main/cybersecbayes/teams_perf_vulns_rate.ipynb">Całość kodu w Jupyter notebook</a></p>

<p>Bibliografia:</p>
<ul>
  <li><a href="https://themetricsmanifesto.com/">“The Metrics Manifesto”</a></li>
  <li><a href="https://trenton3983.github.io/files/projects/2019-02-04_manipulating_dataframes_with_pandas/2019-02-04_manipulating_dataframes_with_pandas.html">“Manipulating DataFrames with pandas”</a></li>
  <li><a href="https://nextjournal.com/intelrefinery/kaplan-meier-survival-function">“Plotting Kaplan-Meier survival curves”</a></li>
</ul>]]></content><author><name>Dominik Miklaszewski</name></author><category term="Ryzyko" /><category term="Python" /><category term="bezpieczeństwo" /><category term="metryki" /><category term="ryzyko" /><category term="analizadanych" /><category term="python" /><summary type="html"><![CDATA[Są takie książki do których trzeba się mentalnie i merytorycznie przygotować. A kiedy już się po nie sięgnie ich zawartość jest starannie czytana, ..i czytana ..i czytana, treść “obracana” w głowie, aż do pełnego zrozumienia. Ale też nie, że “Achaaa.. to tak działa!” i siup, następny rozdział. Trzeba to przerobić praktycznie.]]></summary></entry><entry><title type="html">Przykład ilościowej analizy ryzyka</title><link href="http://localhost:4000/ryzyko/2023-03-01-iloscanienijakosc/" rel="alternate" type="text/html" title="Przykład ilościowej analizy ryzyka" /><published>2023-03-01T00:00:00+01:00</published><updated>2023-03-01T00:00:00+01:00</updated><id>http://localhost:4000/ryzyko/iloscanienijakosc</id><content type="html" xml:base="http://localhost:4000/ryzyko/2023-03-01-iloscanienijakosc/"><![CDATA[<p>Uff, narobiłem się. Ale po kolei. Wszystko zaczęło się dość dawno już, od wpisu z <a href="https://it.fotodev.org/ryzyko/2021-05-17-fortepiantygrysyicia/">fortepianami Fermiego, tygrysami i CIA</a>. W zeszłym roku kontynuowałem moją rozprawę z kiepskim zarządzaniem ryzykiem cyberbezpieczeństwa mikro-serią: <a href="https://it.fotodev.org/ryzyko/2022-05-15-doczegojesttarura/">A do czego jest ta rura? cz.1</a> i <a href="https://it.fotodev.org/ryzyko/2022-05-16-doczegojesttarura2/">druga</a>. Na deser był <a href="https://it.fotodev.org/ryzyko/2022-07-24-zmianapodejscia/">Bayes</a>.</p>

<p>Jestem zadowolony z treści i formy tamtych wpisów, ale to jeszcze nie było to. To “to” właśnie się zmaterializowało. A wszystko zaczęło się od <a href="https://www.coursera.org/specializations/python-3-programming">intensywnego kursu</a> przypominania sobie i rozwijania Pythona świetnie opracowanego i prowadzonego przez wykładowców uniwersytetu stanowego w Michigan. Python jest mi potrzebny do paru innych rzeczy, więc połowę tego kursu połknąłem w tydzień. Zacząłem sobie wymyślać jakieś przydatne i praktyczne <em>juzkejsy</em>. I nagle.. “hello! ..I am here!” Zawołała do mnie z półki moja ulubiona zielona książka, machając przy tym swoimi małymi zielonymi łapkami. “I am your goddamn usecase!” wrzasnęła..</p>

<p><img src="/assets/images/green_book.jpg" alt="The Green Book" class="align-center" /></p>

<p>Czym innym jest opowiadać na lewo i prawo jak świetną książkę napisali panowie Hubbard i Seiersen, a czym innym zupełnie zademonstrować jak działa ilościowa metoda szacowania ryzyka w cyberbezpieczeństwie (ale nie tylko). Przed kontynuacją czytania tego wpisu, polecam przypomnieć sobie poprzednie wpisy, od tygrysów, przez rury po Bayesa. Sam tak właśnie zrobiłem. Ważny jest kontekst. Przedstawiona tutaj ilościowa metoda analizy ryzyka opiera się na rachunku prawdopodobieństwa, zwykłej prostej algebrze z odrobiną statystyki. Od tego jest właśnie rachunek prawdopodobieństwa i statystyka aby nasze estymacje dotyczące oceny szans czy możliwości materializacji ryzyka (czyli oddziaływania jakichś zidentyfikowanych i opisanych zagrożeń) w naszych niedoskonałych i podatnych na owe zagrożenia (<em>nie na ryzyka!!!</em>) systemach IT.</p>

<p>Właściwa estymacja powinna opierać się na danych, tych które mamy w firmie, bądź takich do których możemy dotrzeć w internecie (chociażby <a href="https://www.sans.org/blog/2022-verizon-dbir-what-does-it-mean/">coroczny raport Verizona</a>). Nasze zdarzenia zagrożeń, mogą być też bezpośrednim wynikiem zastosowania metodyki <a href="https://www.mitre.org/sites/default/files/2021-11/getting-started-with-attack-october-2019.pdf">MITRE ATT&amp;CK</a> lub chociażby <a href="https://owasp.org/www-community/Threat_Modeling_Process">STRIDE</a> (dla jakiegoś projektu rozwiązania). Do tego odpowiednio dobrane grono ekspertów powinno samodzielnie i osobno popracować nad estymacją listy takich zdarzeń. Dlaczego osobno? Otóż dlatego, że jako stworki stadne, leniwe i sprytne, mamy wpływ na innych w takim sam sposób jak inni mają wpływ na nas. Na przykład dla świętego spokoju czy też z niemania-i-niechcenia-mania-pojęcia po prostu zgodzimy się z innymi na takiej nasiadówie, byleby tylko ją szybko skończyć w poczucie dobrze odwalonej pracy. Ale ok, żarty na bok. Chodzi o to by łatwiej zidentyfikować i uzasadnić ewentualne rozbieżności przy wynikach estymacji dla konkretnych osób. Pamietamy, że nasze mózgi mają swoje ograniczenia gatunkowe. Szybkie myślenie, problemy z prawdopodobieństwem jako takim, intuicja, zakotwiczenie, zbytnia pewność siebie, itd. Czy już mówiłem? polecam tę zieloną książkę.. i parę innych (wymienionych we wpisach na tym blogu.</p>

<p>Mamy więc listę dziesięciu zdarzeń, których P wyestymowaliśmy, biorąc pod uwagę rozmaite dane własne, raporty i statystyki dostępne z internetu, następnie wykonany rachunek Bayesa, wyniki porównane z innymi. Dotyczy to również przedziału strat. Konkretnie parametryzujemy sobie takie zdarzenie, przydzielając mu określone P w ciągu roku (12 miesięcy), oraz przedział możliwych strat z 90% poziomem ufności.</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="code"><pre> 
<span class="n">events</span> <span class="o">=</span> <span class="p">{</span>
    <span class="sh">"</span><span class="s">event1</span><span class="sh">"</span><span class="p">:</span> <span class="p">(</span><span class="mf">0.21</span><span class="p">,</span> <span class="mi">100000</span><span class="p">,</span> <span class="mi">550000</span><span class="p">,</span> <span class="sh">"</span><span class="s">Successful DDoS on the cloud base core app</span><span class="sh">"</span><span class="p">),</span>
    <span class="sh">"</span><span class="s">event2</span><span class="sh">"</span><span class="p">:</span> <span class="p">(</span><span class="mf">0.08</span><span class="p">,</span> <span class="mi">500000</span><span class="p">,</span> <span class="mi">1500000</span><span class="p">,</span> <span class="sh">"</span><span class="s">Sensitive data exfiltration</span><span class="sh">"</span><span class="p">),</span>
    <span class="sh">"</span><span class="s">event3</span><span class="sh">"</span><span class="p">:</span> <span class="p">(</span><span class="mf">0.15</span><span class="p">,</span> <span class="mi">200000</span><span class="p">,</span> <span class="mi">1000000</span><span class="p">,</span> <span class="sh">"</span><span class="s">Malicious code injected into production pipeline</span><span class="sh">"</span><span class="p">),</span>
    <span class="sh">"</span><span class="s">event4</span><span class="sh">"</span><span class="p">:</span> <span class="p">(</span><span class="mf">0.38</span><span class="p">,</span> <span class="mi">10000</span><span class="p">,</span> <span class="mi">100000</span><span class="p">,</span> <span class="sh">"</span><span class="s">Laptop with sensitive data lost/stolen</span><span class="sh">"</span><span class="p">),</span>
    <span class="sh">"</span><span class="s">event5</span><span class="sh">"</span><span class="p">:</span> <span class="p">(</span><span class="mf">0.17</span><span class="p">,</span> <span class="mi">15000</span><span class="p">,</span> <span class="mi">50000</span><span class="p">,</span> <span class="sh">"</span><span class="s">Malicious code in 3rd party dependencies</span><span class="sh">"</span><span class="p">),</span>
    <span class="sh">"</span><span class="s">event6</span><span class="sh">"</span><span class="p">:</span> <span class="p">(</span><span class="mf">0.15</span><span class="p">,</span> <span class="mi">1000</span><span class="p">,</span> <span class="mi">150000</span><span class="p">,</span> <span class="sh">"</span><span class="s">Broken backups</span><span class="sh">"</span><span class="p">),</span>
    <span class="sh">"</span><span class="s">event7</span><span class="sh">"</span><span class="p">:</span> <span class="p">(</span><span class="mf">0.04</span><span class="p">,</span> <span class="mi">1000000</span><span class="p">,</span> <span class="mi">12000000</span><span class="p">,</span> <span class="sh">"</span><span class="s">Malware attack, knocked out internal network</span><span class="sh">"</span><span class="p">),</span>
    <span class="sh">"</span><span class="s">event8</span><span class="sh">"</span><span class="p">:</span> <span class="p">(</span><span class="mf">0.05</span><span class="p">,</span> <span class="mi">10000</span><span class="p">,</span> <span class="mi">500000</span><span class="p">,</span> <span class="sh">"</span><span class="s">Insider</span><span class="sh">'</span><span class="s">s threat</span><span class="sh">"</span><span class="p">),</span>
    <span class="sh">"</span><span class="s">event9</span><span class="sh">"</span><span class="p">:</span> <span class="p">(</span><span class="mf">0.03</span><span class="p">,</span> <span class="mi">1000000</span><span class="p">,</span> <span class="mi">11500000</span><span class="p">,</span> <span class="sh">"</span><span class="s">Terrorist attack</span><span class="sh">"</span><span class="p">),</span>
    <span class="sh">"</span><span class="s">event10</span><span class="sh">"</span><span class="p">:</span> <span class="p">(</span><span class="mf">0.25</span><span class="p">,</span> <span class="mi">5000</span><span class="p">,</span> <span class="mi">50000</span><span class="p">,</span> <span class="sh">"</span><span class="s">Broken app release</span><span class="sh">"</span><span class="p">)</span>
<span class="p">}</span>
 
</pre></td></tr></tbody></table></code></pre></figure>

<p>Mamy również oszacowany mityczny “apetyt na ryzyko” czy też “tolerancję na ryzyko” po dłuższej dyskusji z naszymi “finansowymi”. Rozkład ewentualnych strat z oszacowanym prawdopodobieństwem wystąpienia w przeciągu 12 miesięcy. Wiadomo, małe straty to wyższa akceptacja dla ich wystąpienia, wysokie straty to niższa i bardzo niska. Czyli nieakceptowalna. Ten krok jest nie mniej ważny od poprzedniego.</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="code"><pre> 
<span class="n">risk_tolerance</span> <span class="o">=</span> <span class="p">[</span>
    <span class="p">(</span><span class="mi">100000</span><span class="p">,</span> <span class="mi">90</span><span class="p">),</span> 
    <span class="p">(</span><span class="mi">250000</span><span class="p">,</span> <span class="mi">50</span><span class="p">),</span> 
    <span class="p">(</span><span class="mi">500000</span><span class="p">,</span> <span class="mi">30</span><span class="p">),</span> 
    <span class="p">(</span><span class="mi">750000</span><span class="p">,</span> <span class="mi">15</span><span class="p">),</span> 
    <span class="p">(</span><span class="mi">1000000</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> 
    <span class="p">(</span><span class="mi">10000000</span><span class="p">,</span> <span class="mf">0.2</span><span class="p">)</span>
<span class="p">]</span>
 
</pre></td></tr></tbody></table></code></pre></figure>

<p>Do kompletu naszych danych wejściowych potrzebujemy jeszcze listę ewentualnych wdrożeń z dziedziny bezpieczeństwa i cyberbezpieczeństwa. Mogą to być działania planowane, nie-planowane. Idzie o to aby pokazać jak nakłady na tę dziedzinę ponoszone przez firmę wpływają na szanse wystąpienia różnych ryzyk. Albowiem właśnie po to są wdrożenia w cyberbezpieczeństwie. Warto więc sprawdzić czy coś dają. Tutaj pod ten konkretny przykład utworzyłem listę 1-do-1 ze zdarzeniami zagrożeń. Możemy to jednak przedstawiać w różnej formie, a potem w kodzie odpowiednio warunkując. Np. Wdrożenia ABC dotyczy zdarzeń: 1,3,4,7 dla każdego estymujemy że wpływ na wystąpienie spadnie o dany % (to ta druga liczba w krotce poniżej, pierwszą jest symulowana wartość inwestycji w cyberbezpieczeństwo).</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="code"><pre> 
<span class="n">sec_controls</span> <span class="o">=</span> <span class="p">{</span>
    <span class="sh">"</span><span class="s">event1</span><span class="sh">"</span><span class="p">:</span> <span class="p">(</span><span class="mi">70000</span><span class="p">,</span> <span class="mf">0.65</span><span class="p">),</span>
    <span class="sh">"</span><span class="s">event2</span><span class="sh">"</span><span class="p">:</span> <span class="p">(</span><span class="mi">200000</span><span class="p">,</span> <span class="mf">0.55</span><span class="p">),</span>
    <span class="sh">"</span><span class="s">event3</span><span class="sh">"</span><span class="p">:</span> <span class="p">(</span><span class="mi">77000</span><span class="p">,</span> <span class="mf">0.7</span><span class="p">),</span>
    <span class="sh">"</span><span class="s">event4</span><span class="sh">"</span><span class="p">:</span> <span class="p">(</span><span class="mi">30000</span><span class="p">,</span> <span class="mf">0.37</span><span class="p">),</span>
    <span class="sh">"</span><span class="s">event5</span><span class="sh">"</span><span class="p">:</span> <span class="p">(</span><span class="mi">25000</span><span class="p">,</span> <span class="mf">0.42</span><span class="p">),</span>
    <span class="sh">"</span><span class="s">event6</span><span class="sh">"</span><span class="p">:</span> <span class="p">(</span><span class="mi">100000</span><span class="p">,</span> <span class="mf">0.62</span><span class="p">),</span>
    <span class="sh">"</span><span class="s">event7</span><span class="sh">"</span><span class="p">:</span> <span class="p">(</span><span class="mi">230000</span><span class="p">,</span> <span class="mf">0.85</span><span class="p">),</span>
    <span class="sh">"</span><span class="s">event8</span><span class="sh">"</span><span class="p">:</span> <span class="p">(</span><span class="mi">121000</span><span class="p">,</span> <span class="mf">0.47</span><span class="p">),</span>
    <span class="sh">"</span><span class="s">event9</span><span class="sh">"</span><span class="p">:</span> <span class="p">(</span><span class="mi">130000</span><span class="p">,</span> <span class="mf">0.6</span><span class="p">),</span>
    <span class="sh">"</span><span class="s">event10</span><span class="sh">"</span><span class="p">:</span> <span class="p">(</span><span class="mi">25000</span><span class="p">,</span> <span class="mf">0.45</span><span class="p">)</span>
<span class="p">}</span>
 
</pre></td></tr></tbody></table></code></pre></figure>

<p>Nasz wehikuł metodyczny nabiera prędkości. Przed Państwem - <strong>Monte Carlo</strong>! Metoda Monte Carlo jest szeroko stosowana w zarządzaniu ryzykiem w wielu obszarach - finansowym, wydobywczym, wszędzie tam gdzie zamiast “best educated guess” panuje logika, matematyka i dbałość o własne pieniądze. Skąd się wzięła? Wymyślił ją wielki polski matematyk <a href="https://www.polityka.pl/tygodnikpolityka/nauka/2140620,1,giganci-nauki--stanislaw-marcin-ulam.read">Stanisław Ulam</a>. Był chory i stawiał z nudów jakiegoś trudnego pasjansa. Wtedy zdał sobie sprawę, że bardzo trudno byłoby obliczyć, jakie było prawdopodobieństwo powodzenia tego pasjansa. Natomiast w miarę łatwo, jak sądził, można by było zamodelować to z pomocą komputera. Taki komputer który mógłby przeprowadzić bardzo wiele prób, dzięki czemu można by zaobserwować rozkład prawdopodobieństwa powodzenia pasjansa. Ponoć nazwa wzięła się ze skojarzenia z wujem profesora Ulama, który zwykł pożyczać pieniądze od rodziny, po czym radośnie przepuszczać je właśnie w Monte Carlo.</p>

<p>Metoda ta posłuży tutaj do zamodelowania rozkładu prawdopodobieństwa wystąpienia zdarzenia i związanej z nim straty. Posługując się generatorem losowym Pythona i metodą Monte Carlo sprawdzimy 10 tysięcy razy dla tych 10 zdarzeń (każdy), czy zdarzenie wystąpi, a jeśli tak to z jaką losowo wygenerowaną stratą z przedziału. Jeśli zdarzenie nie wystąpi, strata wynosi 0. Mamy już jeden rozkład, który stanowi dla nas niejako <em>benchmark</em> dla porównania z naszymi wynikami - nasza “tolerancja na ryzyko”. Zakres strat przypisany do każdego zdarzenia nie będzie zamodelowany rozkładem normalnym - czyli takim, gdzie najprawdopodobniej strata losowa wypadnie nam gdzieś po środku przedziału. Będzie zamodelowana rozkładem logarytmicznie normalnym (<a href="https://en.wikipedia.org/wiki/Log-normal_distribution">log-normalnym</a>). A to taki rozkład, gdzie najbardziej prawdopodobne liczby z naszego przedziału wyskakują bardziej przy jego początku i są mniej prawdopodobne przy końcu takiego przedziału. Dlaczego tak? Weźmy pod uwagę naturę incydentów czy zdarzeń w obszarze cyberbezpieczeństwa. Jest to statystycznie pewne - że występuje daleko więcej zdarzeń o małej bądź nawet znikomej wartości strat, od zdarzeń których konsekwencje są znaczne albo nawet katastrofalne. Stąd asymetria kształtu względem rozkładu normalnego. Ponad to, wartości występujące w tym rozkładzie mogą być tylko równe lub większe od zera.</p>

<p>Jeśli zdarzenie zaistniało w naszej symulacji to wyliczamy medianę i odchylenie standardowe i zwracamy wartość straty wygenerowanej losowo dla tak sparametryzowanego rozkładu.</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="code"><pre> 
<span class="k">def</span> <span class="nf">threat_event_loss</span><span class="p">(</span><span class="n">lower_bnd</span><span class="p">,</span> <span class="n">upper_bnd</span><span class="p">):</span>
    <span class="sh">"""</span><span class="s">If threat event occured, let</span><span class="sh">'</span><span class="s">s get its loss it inflicts,
    given lower and upper bounds of 90% CI</span><span class="sh">"""</span>
    <span class="n">mean</span> <span class="o">=</span> <span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="n">lower_bnd</span><span class="p">)</span> <span class="o">+</span> <span class="n">np</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="n">upper_bnd</span><span class="p">))</span><span class="o">/</span><span class="mi">2</span>
    <span class="n">std_deviation</span> <span class="o">=</span> <span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="n">upper_bnd</span><span class="p">)</span> <span class="o">-</span> <span class="n">np</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="n">lower_bnd</span><span class="p">))</span><span class="o">/</span><span class="mf">3.29</span>
    <span class="k">return</span> <span class="nf">round</span><span class="p">(</span><span class="nf">float</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">random</span><span class="p">.</span><span class="nf">lognormal</span><span class="p">(</span><span class="n">mean</span><span class="p">,</span> <span class="n">std_deviation</span><span class="p">)),</span><span class="mi">2</span><span class="p">)</span>
 
</pre></td></tr></tbody></table></code></pre></figure>

<p>I tak postępujemy 10 tysięcy razy dla całej listy naszych zdarzeń. Zwrotnie dostaniemy dystrybucję takich strat na wykresie:</p>

<p><img src="/assets/images/fig-1.png" alt="Strata zdarzenia cyberbezp." class="align-center" /></p>

<p>Tak samo postępujemy z wariantem, w którym scenariusze naszych przypadków kompensowane są naszymi inwestycjami w cyberbezpieczeństwo z oszacowanym wpływem na wystąpienie zdarzenia.</p>

<p><img src="/assets/images/fig-2.png" alt="Strata zdarzenia cyberbezp." class="align-center" />
Już na pierwszy rzut oka widać, że dystrybucja i gęstość zdarzeń ze stratami jest mniejsza niż bez środków kompensujących.</p>

<p>Jedna rzecz, którą warto mieć na uwadze wprowadzając estymaty zakresu strat dla zdarzeń. Jeśli przyjmiemy zakres gdzie jego dolna wartość będzie o rzędy wielkości mniejsza od górnej wartości to możemy dostać dość nierealne wygenerowane wartości. Trzymajmy się zasady że maksymalna różnica to jeden rząd wielkości. Np. 10 tys. do 100 tys.</p>

<p>Funkcje <code class="language-plaintext highlighter-rouge">loss_distribution</code> i <code class="language-plaintext highlighter-rouge">loss_distribution_reduced</code> zapewniły nam dystrybucje strat i wygenerowały dane wejściowe (w postaci listy list kolejnych prób) do wygenerowania krzywej strat w funkcji prawdopodobieństwa.</p>

<p>To ten kawałek kodu w sekcji <code class="language-plaintext highlighter-rouge">#run</code>:</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="code"><pre> 
<span class="n">losses_no_ctrls</span> <span class="o">=</span> <span class="nf">loss_distribution</span><span class="p">(</span><span class="n">events</span><span class="p">,</span> <span class="n">NO_SIMULATIONS</span><span class="p">)</span>
<span class="n">losses_with_ctrls</span> <span class="o">=</span> <span class="nf">loss_distribution_reduced</span><span class="p">(</span><span class="n">events</span><span class="p">,</span> <span class="n">sec_controls</span><span class="p">,</span> <span class="n">NO_SIMULATIONS</span><span class="p">)</span>
 
</pre></td></tr></tbody></table></code></pre></figure>

<p>Te zmienne stają się argumentem dla wywołania funkcji generującej odpowiednio: wykres dla strat bez kompensacji i z kompensacją:</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="code"><pre> 
<span class="nf">plot_loss_exceedance</span><span class="p">(</span><span class="nf">loss_exceedance_curve</span><span class="p">(</span><span class="n">losses_no_ctrls</span><span class="p">,</span> <span class="n">STEP</span><span class="p">),</span> <span class="n">risk_tolerance</span><span class="p">,</span> <span class="sh">"</span><span class="s">Fig-3.png</span><span class="sh">"</span><span class="p">)</span>
<span class="nf">plot_loss_exceedance</span><span class="p">(</span><span class="nf">loss_exceedance_curve</span><span class="p">(</span><span class="n">losses_with_ctrls</span><span class="p">,</span> <span class="n">STEP</span><span class="p">),</span> <span class="n">risk_tolerance</span><span class="p">,</span> <span class="sh">"</span><span class="s">Fig-4.png</span><span class="sh">"</span><span class="p">)</span>
 
</pre></td></tr></tbody></table></code></pre></figure>

<p><img src="/assets/images/fig-3.png" alt="LEC bez kompensacji" class="align-center" />
<img src="/assets/images/fig-4.png" alt="LEC z komensacją" class="align-center" /></p>

<p>Dodatkowo w wyniku otrzymujemy wartości średniej oczekiwanej straty dla obu wariantów i nakłady finansowe na program komensacji tych ryzyk:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span>python-exercises<span class="o">)</span> 18:51:25:[python-exercises/distros] <span class="o">&gt;&gt;</span> python3 cyberrisk_sim_multi.py
Average loss: 558,023.05 EUR with no controls <span class="k">in </span>place
Average loss: 175,437.74 EUR with controls <span class="k">in </span>place
Total security investments: 1,008,000.00 EUR
</code></pre></div></div>
<p>Na podstawie takich wyników można wysnuć całkiem konkretne wnioski. Na przykład:</p>
<ol>
  <li>W wyniku nakładów w wysokości ~1mln EUR średnia oczekiwana strata spada nam mniej więcej 2.5 krotnie w ciągu roku. Przy czym prawdopodobieństwo jej wystąpienia spada dwukrotnie (z ok. 40% do ok. 20% - patrząc na wykresy 3 i 4)</li>
  <li>W wyniku tychże nakładów nasze ryzyka mieszczą się w założonej tolerancji na ryzyko, gdzie bez tych nakładów, straty powyżej 1 mln EUR są powyżej założonej tolerancji (wykres 3).</li>
  <li>Widać, że po wdrożonym programie kompensacji ryzyk, nawet te straty o niskiej wartości są mniej prawdopodobne o ok. połowę.</li>
  <li>Widać wyraźnie, że powinniśmy się skupić na zagrożeniach i podatnościach w naszym IT, które moga przynieść bardzo wysokie straty, pomimo niskiej szansy ich wystąpienia.</li>
  <li>Widać wreszcie, jak można zamodelować koszty w bezpieczeństwie i bez ich wydawania, po prostu wspomóc efektywniejsze planowanie.</li>
</ol>

<p>Podsumowując, analiza ilościowa bazująca na faktach, przy możliwie poprawnej estymacji, biorąc pod uwagę fakty i przetwarzając to logicznie i matematycznie, da nam wiele równie konkretnych i pomocnych odpowiedzi, których nie da nam żadna najbardziej kolorowa <em>heatmapa</em>. Kluczową sprawą jest tutaj jakość estymacji.</p>

<p><a href="https://github.com/dominikmi/python-exercises/blob/main/cyberrisk/cyberrisk_sim_multi.py">Kod</a> nie jest ani finezyjny ani optymalny, ale działa. Będę jeszcze nad nim pracował, rozwijał. Pewnie jak się douczę modułu <code class="language-plaintext highlighter-rouge">pandas</code> będę mógł z tym zrobić o wiele więcej i pewnie szybciej. Przedstawioną metodę, można też rozwinąć dodając rachunek Bayesa i zdarzenia zależne - jeśli <em>event1</em> zaistniał to <em>event3</em> jest bardziej prawdopodobny, bo otwiera możliwość ataku z wielu kierunków. Ogólnie rzecz biorąc, temat nie jest łatwy i trzeba mu poświecić sporo uwagi i czasu. Aczkolwiek jeśli pracownikom pytającym o bonus roczny, nie odpowiadamy, że dostaną między MEDIUM a MEDIUM-LOW ..to może tez warto przestać odpowiadać tak kierownictwu w kwestii ryzyka i strat? :)</p>]]></content><author><name>Dominik Miklaszewski</name></author><category term="Ryzyko" /><category term="bezpieczeństwo" /><category term="ryzyko" /><summary type="html"><![CDATA[Uff, narobiłem się. Ale po kolei. Wszystko zaczęło się dość dawno już, od wpisu z fortepianami Fermiego, tygrysami i CIA. W zeszłym roku kontynuowałem moją rozprawę z kiepskim zarządzaniem ryzykiem cyberbezpieczeństwa mikro-serią: A do czego jest ta rura? cz.1 i druga. Na deser był Bayes.]]></summary></entry><entry><title type="html">Szybki sposób zabezpieczenia kredek w Terraformie</title><link href="http://localhost:4000/devsecops/2023-02-06-terraformkredki/" rel="alternate" type="text/html" title="Szybki sposób zabezpieczenia kredek w Terraformie" /><published>2023-02-06T00:00:00+01:00</published><updated>2023-02-06T00:00:00+01:00</updated><id>http://localhost:4000/devsecops/terraformkredki</id><content type="html" xml:base="http://localhost:4000/devsecops/2023-02-06-terraformkredki/"><![CDATA[<p>Siedzę sobie nad projektem w AWSie, terraformując zawzięcie. Niczego wrażliwego nie publikuję na GitHubie, aczkolwiek pałętają mi się po dysku pliki stanu terraforma (<code class="language-plaintext highlighter-rouge">terraform.tfstate</code>). Gdybym miał w domu korporację, w której dbano by o security, cybersecurity, compliance, ochronę danych osobowych, danych wrażliwych, tajemnicę bankową, i wszelkie inne bardzo tajne i super-wrażliwe tajemnice, to musiałbym pewnie użyć (i zapłacić!) Terraform Cloud-a jako “provider-a” dla terraforma. Albo co najmniej szyfrowanego <code class="language-plaintext highlighter-rouge">S3</code> z włączonym wersjonowaniem i własnym kluczem szyfrującym.</p>

<h3 id="terraform-i-vault">Terraform i Vault</h3>

<p>W Vaulcie trzymam kredki dla AWSa (<code class="language-plaintext highlighter-rouge">iam_user</code>). Backup (snapshot) magazynu danych Vaulta leci na szyfrowany dysk. Załatwia to Consul, który serwuje Vaultowi rozproszony storage - RAFTem po HTTPSie (co prawda to rozproszenie jest trochę symulowane - bo jak szlag trafi NUC-a to i moją wirtualną sieć i VMki). Ale zostanie mi dysk :)</p>

<p>Sprawa jest prosta - w Vaulcie wrzucam kredki do kv/aws i kv/terraform (o czym za chwilę). A potem każę terraformowi, je sobie pobrać wykorzystując terraformowe źródło danych <code class="language-plaintext highlighter-rouge">vault_generic_secret</code> a potem kojarząc z dostawcą.</p>

<p><code class="language-plaintext highlighter-rouge">providers.tf</code>:</p>

<figure class="highlight"><pre><code class="language-hcl" data-lang="hcl"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
</pre></td><td class="code"><pre> 
<span class="nx">data</span> <span class="s2">"vault_generic_secret"</span> <span class="s2">"tf_token"</span> <span class="p">{</span>
  <span class="nx">path</span> <span class="o">=</span> <span class="s2">"kv/terraform"</span>
<span class="p">}</span>

<span class="nx">data</span> <span class="s2">"vault_generic_secret"</span> <span class="s2">"aws_creds"</span> <span class="p">{</span>
  <span class="nx">path</span> <span class="o">=</span> <span class="s2">"kv/aws"</span>
<span class="p">}</span>

<span class="nx">provider</span> <span class="s2">"aws"</span> <span class="p">{</span>
  <span class="nx">region</span>  <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">vault_generic_secret</span><span class="p">.</span><span class="nx">aws_creds</span><span class="p">.</span><span class="nx">data</span><span class="p">[</span><span class="s2">"DEFAULT_REGION"</span><span class="p">]</span>
  <span class="nx">access_key</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">vault_generic_secret</span><span class="p">.</span><span class="nx">aws_creds</span><span class="p">.</span><span class="nx">data</span><span class="p">[</span><span class="s2">"AWS_ACCESS_KEY_ID"</span><span class="p">]</span>
  <span class="nx">secret_key</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">vault_generic_secret</span><span class="p">.</span><span class="nx">aws_creds</span><span class="p">.</span><span class="nx">data</span><span class="p">[</span><span class="s2">"AWS_SECRET_ACCESS_KEY"</span><span class="p">]</span>
<span class="p">}</span>

<span class="nx">provider</span> <span class="s2">"consul"</span> <span class="p">{</span>
  <span class="nx">token</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">vault_generic_secret</span><span class="p">.</span><span class="nx">tf_token</span><span class="p">.</span><span class="nx">data</span><span class="p">[</span><span class="s2">"access_token"</span><span class="p">]</span>
<span class="p">}</span> 
 
</pre></td></tr></tbody></table></code></pre></figure>

<h3 id="terraform-i-consul">Terraform i Consul</h3>

<p>Pliki stanu terraforma pakuję do Consula. Tenże robi za <em>backend</em> dla terraforma. A konkretnie to za backendowy key-value store. Ale taki pojemniejszy niż Vault. Za to bez szyfrowania <em>at-rest</em>. Co prawda padła propozycja, by Vault stał się takim właściwym backendem, póki co jednak Hashicorp <a href="https://github.com/hashicorp/terraform/issues/31539">nie rozważa takiej ścieżki rozwoju</a>.</p>

<p>Tutaj też, konfiguracja jest dość prosta i szybka. Po skonfigurowaniu <code class="language-plaintext highlighter-rouge">backend.tf</code> i wykonaniu komendy <code class="language-plaintext highlighter-rouge">terraform init</code>, terraform zapyta o migrację i zamelduje wykonanie zadania. Poniżej kawałek “meldunku”:</p>

<pre><code class="language-txt">$ terraform init

Initializing the backend...
Do you want to copy existing state to the new backend?
  Pre-existing state was found while migrating the previous "local" backend to the
  newly configured "consul" backend. No existing state was found in the newly
  configured "consul" backend. Do you want to copy this state to the new "consul"
  backend? Enter "yes" to copy and "no" to start with an empty state.

  Enter a value: yes


Successfully configured the backend "consul"! Terraform will automatically
use this backend unless the backend configuration changes.

Initializing provider plugins...
- Reusing previous version of hashicorp/vault from the dependency lock file
[..]
</code></pre>

<p>a brakującym kawałkiem układanki jest ten oto <code class="language-plaintext highlighter-rouge">backend.tf</code>:</p>

<figure class="highlight"><pre><code class="language-hcl" data-lang="hcl"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="code"><pre> 
<span class="nx">terraform</span> <span class="p">{</span>
  <span class="nx">backend</span> <span class="s2">"consul"</span> <span class="p">{</span>
    <span class="nx">address</span>      <span class="o">=</span> <span class="s2">"powernukeint.nukelab.home:8501"</span>
    <span class="nx">scheme</span>       <span class="o">=</span> <span class="s2">"https"</span>
    <span class="nx">path</span>         <span class="o">=</span> <span class="s2">"terraform/aws/project1/terraform.tfstate"</span>
    <span class="nx">lock</span>         <span class="o">=</span> <span class="kc">true</span>
    <span class="nx">gzip</span>         <span class="o">=</span> <span class="kc">false</span>
  <span class="p">}</span>
<span class="p">}</span>
 
</pre></td></tr></tbody></table></code></pre></figure>

<p>Brakuje tu jednej linijki z tokenem. Ta jest jednak niepotrzebna, bo temat załatwia zdefiniowane źródło danych w Vaulcie. No i sobie to tak działa. Tym sposobem unikamy nawet przypadkowej publikacji plików stanu w repozytorium i namnażania tych plików w lokalnym systemie plików. Consul i Vault mają kontrolę dostępu. No i jeden i drugi jest dostępny przez odpowiedni token. Vault - źródło danych uwierzytelniających, Consul - w miarę bezpieczna przechowalnia plików stanu.</p>

<p><img src="/assets/images/terraconsul.png" alt="TFState w Consulu" class="align-center" /></p>]]></content><author><name>Dominik Miklaszewski</name></author><category term="DevSecOps" /><category term="terraform" /><category term="consul" /><category term="vault" /><category term="aws" /><summary type="html"><![CDATA[Siedzę sobie nad projektem w AWSie, terraformując zawzięcie. Niczego wrażliwego nie publikuję na GitHubie, aczkolwiek pałętają mi się po dysku pliki stanu terraforma (terraform.tfstate). Gdybym miał w domu korporację, w której dbano by o security, cybersecurity, compliance, ochronę danych osobowych, danych wrażliwych, tajemnicę bankową, i wszelkie inne bardzo tajne i super-wrażliwe tajemnice, to musiałbym pewnie użyć (i zapłacić!) Terraform Cloud-a jako “provider-a” dla terraforma. Albo co najmniej szyfrowanego S3 z włączonym wersjonowaniem i własnym kluczem szyfrującym.]]></summary></entry><entry><title type="html">O bezpieczeństwie domowego IoT cz. 2</title><link href="http://localhost:4000/devsecops/iot/2022-12-17-fairlysecurehomeiot2/" rel="alternate" type="text/html" title="O bezpieczeństwie domowego IoT cz. 2" /><published>2022-12-17T00:00:00+01:00</published><updated>2022-12-17T00:00:00+01:00</updated><id>http://localhost:4000/devsecops/iot/fairlysecurehomeiot2</id><content type="html" xml:base="http://localhost:4000/devsecops/iot/2022-12-17-fairlysecurehomeiot2/"><![CDATA[<p>Poprzedni odcinek mojej osobistej rozprawy z bezpieczeństwem IoT zakończyłem może nieco przedwcześnie. Ba! na pewno! Jeżeli jesteśmy jednak chociaż trochę zainteresowani metodyką MITRE ATT&amp;CK to warto poświęcić jej cały kolejny wpis. Będzie to bowiem nakreślenie całkiem fajnej, logicznej, elastycznej  i szybkiej metody podejścia do projektowania zabezpieczeń w świecie IT. Wyjście z profilu zagrożeń (scenariuszy) do nakreślenia architektury bezpieczeństwa rozwiązania i (mam nadzieję) zgrabne przejście do samych technikaliów w części trzeciej (kolejnym, ostatnim już wpisie o IoT). No tak, znowu miało być technicznie, a wyjdzie analitycznie. Bardzo jednak chcę wykonać to ćwiczenie z ATT&amp;CK-iem, by nawet samemu sobie zademonstrować przydatność tej metodyki.</p>

<p>Na obrazku widzimy prosty algorytm postępowania z <a href="https://attack.mitre.org/resources/training/">MITRE ATT&amp;CK</a> aby odpowiedzieć sobie na pytanie, co zrobić by być bezpiecznym przed konkretnym profilem zagrożeń. Nie jest to jakiś złoty środek na wszystko. Przez przypadek, domowe IoT stało się po prostu wdzięcznym przykładem dla przedstawienia tej metodyki, w miarę zwięzły sposób. Przy poważniejszych projektach, należy zabrać się jeszcze za szacowanie pracochłonności, kosztów, zasobów. Przemyśleć i oszacować wpływ na operacje i utrzymanie. Najważniejsze jednak, że mamy logiczną i spójną odpowiedź - DLACZEGO chcemy takiego wdrożenia. Bowiem jest to metodyka, którą można (i chyba nawet powinno się) stosować przy projektach cyberbezpieczeństwa. Tak by nie strzelać na ślepo, nie próbować interpretować kolorowych nonsensownych HEATMAP albo też wierzyć tylko “sejlsom”, po iluś spotkaniach (“masowania klienta”, jak to oni mówią), że teraz to już będzie dobrze.</p>

<p><img src="/assets/images/mitreattackiot.png" alt="MITRE ATT&amp;CK for IoT " class="align-center" /></p>

<h3 id="analityka">Analityka</h3>

<p>Przeglądając zaznaczone w poprzednim wpisie techniki w mapowaniu MITRE, uznałem, że nieco się rozpędziłem. Zawsze przyda się drugie spojrzenie i weryfikacja. Zatem według tego “drugiego spojrzenia” nasi potencjalni złoczyńcy (wg scenariuszy nakreślonych w <a href="https://it.fotodev.org/devsecops/iot/2022-12-15-fairlysecurehomeiot/">poprzednim wpisie</a>) mogliby posłużyć się takimi oto technikami:</p>

<p><img src="/assets/images/homeiotmitrelayer.png" alt="Techniques for IoT " class="align-center" /></p>

<p>A skąd akurat te, a nie inne? Pamiętajmy, że rozpatrujemy konkretne scenariusze zagrożeń, których celem jest nasza domowa instalacja IoT. Wobec tego tzw. powierzchnia ataku jest już znacznie bardziej ograniczona niż jakieś złożone systemy automatyki przemysłowej w przemyśle motoryzacyjnym czy przetwórstwa żywności (i napojów..). Przy rozpatrywaniu takich czy innych technik, nalezy też mieć na uwadze nie tylko szanse powodzenia takich technik, ale i szanse ich wystąpienia. Czyli szanse, że taki złoczyńca się pojawi, a jego zamiary będą mu się opłacać. Tym bardziej, że <a href="https://www.shodan.io/search?query=Tasmota">Shodan.io</a> jest dość bezlitosny dla beztroskich entuzjastów IoT i Tasmoty.</p>

<p>I z tego punktu widzenia, taktyka <strong>“<a href="https://attack.mitre.org/tactics/TA0108/">Initial Access</a>“</strong> czyli możliwość znalezienia punktu zaczepienia do dalszego postępowania w ataku praktycznie, przy naszych scenariuszach ograniczyć może się do max. trzech możliwości:</p>
<ul>
  <li>“<a href="https://attack.mitre.org/techniques/T0822/">External Remote Services</a>” - opis tej techniki może sugerować np. taką możliwość, iż istnieje jakiś portal, który poprzez SEO przyciąga uwagę amatorów Tasmoty, tam czai się już skrypt w zwykłym PHP, który wykona się na laptopie takiego amatora i sobie ładnie wypróbkuje sieć na obecność urządzeń z dostępem bez ustawionego hasła, bądź “admin/admin”. Jak włamywacz będzie miał więcej szczęścia, a entuzjasta bardziej niefrasobliwy, to od razu tamten podgra temu swój firmware. Przypadek opisany w <a href="https://github.com/arendst/Tasmota/issues/6767">issues na Githubie dla Tasmoty</a>;</li>
  <li>“<a href="https://attack.mitre.org/techniques/T0862">Supply Chain Attack</a>” - ten przypadek już pokrótce opisałem w poprzednim wpisie, kupujemy sobie gniazdko z jakiegoś “tańszego” źródła, gdzie cena jest po prostu haczykiem lub wykonujemy aktualizację nie bacząc na źródło pochodzenia binarki;</li>
  <li>“<a href="https://attack.mitre.org/techniques/T0860">Wireless Compromise</a>” - i ten przypadek jest najbardziej prawdopodobny. I nieszczególnie też trudny do realizacji. Sposoby przechwytywania i łamania haszy przy wymianie komunikacji uwierzytelniającej klienta z Access Pointem są opisane na <a href="https://brezular.com/2021/03/01/cracking-wpa-wp2-pre-shared-key/">950 tysięcy sposobów w Internecie</a>, krok po kroku z komendami i przykładami. Wystarczy mieć laptopa z WiFi, resztę zrobi <strong>aircrack-ng</strong> nawet z tutoriali.</li>
</ul>

<p>W taktyce <strong>“<a href="https://attack.mitre.org/tactics/TA0104/">Execution</a>“</strong> - nie, to jeszcze nie egzekucja - mamy techniki, które pozwolą cyberzbójcerzom zacząć mościć sobie wygodne gniazdko w gniazdkach naszej sieci. O ile, oczywiście nie jest to atak jednorazowy bez specjalnie wielkich planów na przyszłość. Tasmota charakteryzuje się tym, że jak na swoje 630kB ma i WebUI z funkcjonalnością konsoli, wystawia API do którego również możemy wysłać zdalnie komendy dla urządzenia. Wisienką jest możliwość sterowania urządzeniem przez tematy MQTT (ang. <em>topics</em>), na przykład wysyłając urządzeniu na jego właściwy temat <a href="https://github.com/arendst/Tasmota/issues/5337">komendę resetu hasła do “admin/admin”</a>:</p>
<ul>
  <li>“<a href="https://attack.mitre.org/techniques/T0807/">Command-Line Interface</a>”;</li>
  <li>“<a href="https://attack.mitre.org/techniques/T0834">Native API</a>”.</li>
</ul>

<p>Zastosowanie technik z taktyki <strong>“<a href="https://attack.mitre.org/tactics/TA0110/">Persistence</a>“</strong> pozwoli natomiast cyberzbójcerzom albo na dłuższy niedostrzeżony pobyt w naszym IoT albo też na bezpieczne (dla nich) powroty w chwili gdy uznają, że mają w naszym IoT jeszcze coś do załatwienia. Tutaj wybrałem dwie techniki, możliwe do wykorzystania, skoro już udało się na samym początku, a więc:</p>
<ul>
  <li>“<a href="https://attack.mitre.org/techniques/T0891/">Hardcoded Credentials</a>” - Tasmota ma fabrycznie ustawionego użytkownika “admin”, zostaje albo odgadnąć hasło albo je zmienić na “admin”. O ile mnie własne testy nie mylą, to Tasmota pozostaje niewzruszona na <em>brute-force</em>-ową zgadywankę. Przy czym, może się okazać, że hasło jest takie samo dla wszystkich urządzeń. Ja mam cztery takie gniazdka, ale ktoś może miec ich 140..</li>
  <li>“<a href="https://attack.mitre.org/techniques/T0857/">System Firmware</a>” - tutaj, w kontekście naszych scenariuszy i przedmiotu ataku, mamy tę samą praktycznie technikę, jak przy “<a href="https://attack.mitre.org/techniques/T0862">Supply Chain Attack</a>”. Co nie musi być regułą w innych przypadkach.</li>
</ul>

<p>Taktyka <strong>“<a href="https://attack.mitre.org/tactics/TA0102/">Discovery</a>“</strong> posłuży atakującym na bliższe poznanie się z naszą zaatakowaną siecią, jej strukturą i innymi domownikami. O ile powiodły się poprzednie techniki, nasz złoczyńca ma do dyspozycji konsole, wszelkie ustawienia WiFi, IP, DNS, NTP, MQTT, itd. Jeśli nie mamy ustawionej polityki <em>Network Isolation</em> czy też wyłączonego <em>forwarding chain</em> na ruterze/WAP, to bardzo ułatwiamy robotę nieproszonym odkrywcom. Dlatego też, poniższe dwie techniki będa raczej pasować:</p>
<ul>
  <li>“<a href="https://attack.mitre.org/techniques/T0840">Network Connection Enumeration</a>” - popatrzy w logi, popatrzy w konfigurację</li>
  <li>“<a href="https://attack.mitre.org/techniques/T0888">Remote System Information Discovery</a>” - ..i już będzie wiedział, gdzie co jest i gdzie warto sięgnąc, aby być może twórczo rozwinąć atak na inne potencjalnie ciekawe miejsca w naszej sieci.</li>
</ul>

<p>Techniki z <strong>“<a href="https://attack.mitre.org/tactics/TA0109/">Lateral Movement</a>“</strong> i <strong>“<a href="https://attack.mitre.org/tactics/TA0107/">Inhibit Response Function</a>“</strong> praktycznie wykorzystują (w naszym przypadku) już zdobyte dostępy i uprawnienia. Chodzi o rozprzestrzenienie się po sieci, np. po odgadnięciu IP/nazw i tematów innych urządzeń, schemat dla jednego zapewne zadziała dla innego tylko np. trzeba zmienić numerek IP na kolejny i w nazwie ostatnią cyfrę. To samo dotyczy maskowania swojej obecności przez modyfikację parametrów odpowiedzi na jakieś zdarzenia rejestrowane przez urządzenia IoT.</p>

<p>Ostatnia z taktyk to nic innego jak próba doprowadzenia do opłacalnej monetyzacji tego ataku, więc otwarcie bramy, odwrócenie kamery, zamknięcie/otwarcia zaworu itd. A więc sprowadzenie na instalację, dom, domowników zagrożenia całkiem bezpośredniego. Uszkodzenie jakiejś instalacji przez zakłocenie działania funkcjonalności dbającej o bezpieczeństwo fizyczne instalacji sterowanej i dozorowanej przez IoT. Ostatecznie sięgnięcie po inne zasoby informatyczne domowników, na ile konfiguracja sieci na to pozwalała, a co zostało rozpoznane w fazie <strong>“Discovery”</strong>.</p>

<p>A co z pozostałymi fazami, które nie zostały tutaj ujęte na obrazku? Navigator ma fajną funkcjonalność ukrycia rzeczy, które nas nie interesują. Dla przejrzystości rozpatrywanego obrazka. Jak już wspomniałem na początku trzymamy się konkretnego kontekstu, pamiętając, iż ICS to znaaacznie, znaaacznie szersze pojęcie, niż najszersze IoT.</p>

<h3 id="obrona">Obrona</h3>

<p>Również pamiętamy, że każda rozpatrzona tutaj technika ma w metodyce ATT&amp;CK zamapowane mechanizmy przeciwdziałania. Aczkolwiek, jesli je zacznę tutaj jedna po drugiej mapować po kolei, to wszyscy tu zaraz usną, wraz z autorem (późno już jest.). Ale tutaj nic szczególnie skomplikowanego się nie dzieje. Co robimy?</p>

<ol>
  <li>Mapujemy sobie nasze taktyki na mechanizmy przeciwdziałania (“Mitigation”) i detekcji (“Detection”).</li>
  <li>Następnie oceniamy - wciąż trzymając się kontekstu - co jest w całości możliwe do zastosowania, w częsci, co można zastąpić innym mechanizmem, a co trzeba odpuscić zupełnie. Ocena taka powinna brać pod uwagę nie tylko to co umiemy, mamy pod ręką, jesteśmy się w stanie nauczyć, czy tez kupić. Należy przy tym antycypować utrzymanie takiego systemu, który i nam może się mocno rozrosnąć w swojej złożoności, kiedy zaczniemy dodawać do niego kolejne klocki. Np. zadając sobie pytanie - czy będziemy okresowo poświęcać dzień w miesiącu, by kompilować i testować nowy firmware, wraz z pojawieniem się nowej wersji bazowej, bądź kiedy pojawi się jakaś dziura (a taką informację też musimy monitorować subskrybując jakieś źródło informacji o podatnościach. A te będziemy sumiennie przeglądać.. i tak dalej). A może sobie tę kompilację firmware-u zautomatyzować? Ale! ale to też kosztuje..i tak dalej. O ile nie jesteśmy szurnięci na punkcie bezpieczeństwa i jednak skłonni nie budować fortecy zdolnej przeżyć jednoczesny atak kilkunastu APT i nalot B-52 naraz, tylko dlatego, że umiemy albo chcemy :)</li>
  <li>A na koniec proponujemy sobie rozsądne i konkretne rozwiązania nie tylko nie zamieniającego naszego codziennego życie w utrzymanie naszych IoT, ale skutecznie utrudnią realizację niecnych planów, np. czyniąc takie realizacje nieopłacalnymi z punktu widzenia intencji włamywacza - np. szybki i duży zysk, minimalne szanse wykrycia - a komplikatory mają bezpośredni wpływ i na szanse wykrycia i na szybkość i wysokość zysku. Pewne sugestie co do zabezpieczeń w tekście niniejszego wpisu już padły.</li>
</ol>

<p>Poniżej widać tabelkę z tak zmapowanymi propozycjami przeciwdziałania, dla każdej z technik. Jest to tylko próbka. Cała tabelka dostępna będzie <a href="/assets/images/HomeIoTSecAssessment-v0.1.pdf">tutaj</a>.</p>

<table>
  
    
    <tr>
      
        <th>Technika</th>
      
        <th>ID</th>
      
        <th>Przeciwdziałanie</th>
      
        <th>Przydatność</th>
      
        <th>Rozwiązanie</th>
      
    </tr>
    
    <tr class="row1">
<td class="col1">
      Supply Chain Compromise
    </td><td class="col2">
      M0945
    </td><td class="col3">
      Code Signing
    </td><td class="col4">
      Częściowa
    </td><td class="col5">
      Własne repozytorium kodu Tasmoty. Zródło: repozytorium Tasmoty (bez podpisów). 
    </td></tr>

  
    
    <tr class="row1">
<td class="col1">
      
    </td><td class="col2">
      M0817
    </td><td class="col3">
      Supply Chain Management
    </td><td class="col4">
      Nie dotyczy
    </td><td class="col5">
      Rozwiązanie dla przedsiębiorstw z w miarę przytomnym działem IT.
    </td></tr>

  
    
    <tr class="row1">
<td class="col1">
      
    </td><td class="col2">
      M0951
    </td><td class="col3">
      Update Software
    </td><td class="col4">
      Częsciowa, ręcznie
    </td><td class="col5">
      Sledzenie zmian w repozytorium źródłowym, Github: issues.
    </td></tr>

  
    
    <tr class="row1">
<td class="col1">
      
    </td><td class="col2">
      M0916
    </td><td class="col3">
      Vulnerability Scanning
    </td><td class="col4">
      Częściowa, ręcznie
    </td><td class="col5">
      J/w. Wykorzystanie platformy GitPod lub platformio.org do automatyzacji linterów i testowania kodu.
    </td></tr>

  
    
    <tr class="row1">
<td class="col1">
      Wireless Compromise
    </td><td class="col2">
      M0802
    </td><td class="col3">
      Communication Authenticity
    </td><td class="col4">
      Częsciowa
    </td><td class="col5">
      WPA2-PSK-AES, długie mocne hasło (ze znakami specjalnymi, małe i wielkie litery i cyfry). Uwierzytelnianie przez RADIUSa.Osobne WLANy dla IoT i reszty komputerów, czy urządzeń domowych (TV, pralka?)
    </td></tr>

  
    
    <tr class="row1">
<td class="col1">
      
    </td><td class="col2">
      M0808
    </td><td class="col3">
      Encrypt Network Traffic
    </td><td class="col4">
      Częściowa
    </td><td class="col5">
      J/w.
    </td></tr>

  
    
    <tr class="row1">
<td class="col1">
      
    </td><td class="col2">
      M0806
    </td><td class="col3">
      Minimize Wireless Signal Propagation
    </td><td class="col4">
      Pełna
    </td><td class="col5">
      Ograniczenie zasięgu emisji radiowej, na WAP i na urządzenia IoT.
    </td></tr>

  
    
    <tr class="row1">
<td class="col1">
      
    </td><td class="col2">
      M0813
    </td><td class="col3">
      Software Process and Device Authentication
    </td><td class="col4">
      Częściowa
    </td><td class="col5">
      Jak w przypadku M808 + można jeszcze dodać dość słabe zabezpieczenia, jak filtrowanie po MAC adresach. Certyfikaty, jeśli urządzenia wspierają.
    </td></tr>

  
    
    <tr class="row1">
<td class="col1">
      Execution through API
    </td><td class="col2">
      M0801
    </td><td class="col3">
      Access Management
    </td><td class="col4">
      Częściowa
    </td><td class="col5">
      Ustawienie unikalnych loginów I haseł na urządzeniach. Dla każdej z usług osobno. HTTP (API), MQTT.
    </td></tr>

  
    
    <tr class="row1">
<td class="col1">
      
    </td><td class="col2">
      M0800
    </td><td class="col3">
      Authorization Enforcement
    </td><td class="col4">
      Częściowa
    </td><td class="col5">
      Wszystkie ustawienia wykonane przed kompilacją firmware, a użytkownik na urządzenia bez uprawnień admina.
    </td></tr>

  
    
    <tr class="row1">
<td class="col1">
      
    </td><td class="col2">
      M0938
    </td><td class="col3">
      Execution Prevention
    </td><td class="col4">
      Pełna
    </td><td class="col5">
      Dostęp do API urządzenia, jak również do MQTT powinien być ograniczony na WAPie regułą firewallową. A wszystkie urządzenia objęte polityką "Network Isolation".
    </td></tr>

  
    
    <tr class="row1">
<td class="col1">
      
    </td><td class="col2">
      M0804
    </td><td class="col3">
      Human User Authentication
    </td><td class="col4">
      Pełna
    </td><td class="col5">
      j/w
    </td></tr>

  
</table>

<h3 id="detekcja">Detekcja</h3>

<p>Jeśli chodzi o możliwości wykrywania podejrzanych działań, w przypadku urządzeń z Tasmotą, można po skonfigurować logowanie zdarzeń i wysyłanie ich do zdalnego sysloga. Jeżeli mamy MQTT i brokera Mosquitto, można też skonfigurować logowanie zdarzeń na tym komponencie. Logowanie zdarzeń z urządzenia z Tasmotą obejmie nam zdarzenia prób logowania się do urządzenia, aktualizację firmware, włączenia, wyłączenia zmiany parametrów. Wiele więcej nie zrobimy. Tym bardziej, że samo wysyłanie logów nam niczego nie załatwi. Musielibyśmy “uzbroić” naszego konsumenta syslogowego w jakąś warstwę pozwalającą na reakcje na określone zdarzenia przez powiadamianie, alerty w dashboardzie itd.</p>

<h3 id="architektura">Architektura</h3>

<p>Po takiej analizie, można zabrać się za rozrysowanie sobie docelowej architektury, czy też topologii z naniesioną koncepcją obrony.</p>

<p><img src="/assets/images/homeiot-net.png" alt="Techniques for IoT " class="align-center" /></p>

<p>Do naszej (zakładam) istniejącej sieci z ruterem internetowym, który zapewnia nam w domu tez WiFi i jest odpowiednio już zabezpieczony dokładamy kolejny WAP (Wireless Access Point).Tenże WAP może być podłączony z naszym ruterem albo <em>ethernetem</em> albo też po WiFi. I na tym WAPie ustawiamy osobny WLAN, a komunikację zapewniamy odpowiednimi regułami firewalla i rutingiem. Odcinamy dostęp do internetu z tego WLANu. Podczas przeglądu możliwości Tasmoty natknąłem się na funkcjonalność wystawiania metryk dla Prometheusa. Tym samym można zredukować złożoność rozwiązania rejestracji pomiarów. Zamiast:</p>

<p><img src="/assets/images/iotworkflow.png" alt="IoT Workflow" class="align-center" /></p>

<p>można zrobić to tak:</p>

<p><img src="/assets/images/homeiot-metrics.png" alt="IoT Workflow" class="align-center" /></p>

<p>Tym samym wyłączamy kompletnie MQTT i możliwości z tym związane - czyli sterowanie urządzeniem poprzez komendy wysyłane do tematu MQTT (np. <code class="language-plaintext highlighter-rouge">Publish &lt;device_topic&gt;/cmnd/WebPassword 0</code>). Pozbawiamy się tym samym możliwości np. resetu hasła na urządzeniu, czy zdalnie wysłania aktualizacji firmware-u. Aczkolwiek, również potencjalny atakujący zostaje pozbawiony tych możliwości. Kluczowe przy obu rozwiązaniach monitoringu danych z urządzeń - są bakupy konfiguracji i przechowywanie haseł w bezpiecznym miejscu. Albowiem, jeśli hasło zostanie “zapomniane” to czeka nas twardy update firmware-u przez <a href="https://pielatowski.pl/gosund-sp111-tasmota/">USB-UART</a> i <a href="https://tasmota.github.io/install/">WebInstaller</a>, bo przecież nie zalogujemy się do urządzenia by wykonać zmianę hasła, którego nie pamiętamy. Prócz tego, wciąż potrzebujemy udostępnienia trzech usług z naszej sieci - nazwanej umownie “Labem”: Syslog, DNS i NTP.</p>

<p>Ponad to, autorzy Tasmoty w dokumentacji publikują konkretne <a href="https://tasmota.github.io/docs/Securing-your-IoT-from-hacking/">zalecenia i przykłady</a> możliwości zabezpieczeń urządzeń przed atakami.</p>

<h3 id="nasz-firmware">Nasz Firmware</h3>

<p>Możliwość łatwej “kastomizacji” firmware-u jest zapewniona, poprzez przegląd <a href="https://github.com/arendst/Tasmota/blob/development/tasmota/my_user_config.h"><code class="language-plaintext highlighter-rouge">my_user_config.h</code></a> i edycję pliku <code class="language-plaintext highlighter-rouge">user_config_override.h</code> gdzie wpisujemy te definicje z <code class="language-plaintext highlighter-rouge">my_user_config.h</code> które zapewnią wymaganą konfigurację firmware. Konfiguracja <code class="language-plaintext highlighter-rouge">user_config_override.h</code> nadpisuje ustawienia z <code class="language-plaintext highlighter-rouge">my_user_config.h</code> Zalecenia i podpowiedzi w kwestii kompilacji również są <a href="https://tasmota.github.io/docs/Compile-your-build/#customize-your-build">opublikowane</a> w dokumentacji. Dobrze mieć wolne urządzenie do testów rezultatów naszych kompilacji by mieć pewność, że założone cele zostały zrealizowane. No to ściągamy <a href="https://github.com/arendst/Tasmota/archive/master.zip">kod źródłowy</a>. Należy też mieć świadomość różnic między ESP8266/8285 a ESP32. Główna jest taka, że ten pierwszy ma 1MB RAM, a ten drugi 4MB. Dlatego to co jest dobre dla ESP32 może nie być możliwe dla ESP8266. Moje Gosund-y SP111 to ESP8285 właśnie. Przy przeglądaniu kodu Tasmoty, warto wyłączyć plugin VSC “Intellisense C/C++” będzie pyszczył o konwersję plików <code class="language-plaintext highlighter-rouge">.ino</code> do <code class="language-plaintext highlighter-rouge">.cpp</code>. Biorąc pod uwagę przyjętą strategię, że użytkownik urządzenia NIE będzie miał uprawnień administratora, możemy tam zdefiniować wszystkie niezbędne parametry do połączenia z Wifi, ustawienia NTP, MQTT, MQTT z TLSem, serwera Web, a nawe IPv6 czy TLS dla komunikacji z chmurą AWS lub Azure. Jest tam mnóstwo innych ustawień, pod konkretne sensory. Więc, jeśli czymś nie jesteśmy zainteresowani, bo nasze urządzenie po prostu tego nie robi, można spróbować wyłaczyć i dzieki temu uzyskać mniejszą objętość firmware-u.</p>

<p>Rozwiązanie techniczne z konfiguracją brokera, Telegrafa i InfluxDB przedstawię w ostatnim odcinku. Temat z metrykami dla Prometheusa jest wciąż testowany.</p>]]></content><author><name>Dominik Miklaszewski</name></author><category term="DevSecOps" /><category term="IoT" /><category term="MITRE" /><category term="iot" /><category term="mqtt" /><category term="tasmota" /><category term="gosund" /><summary type="html"><![CDATA[Poprzedni odcinek mojej osobistej rozprawy z bezpieczeństwem IoT zakończyłem może nieco przedwcześnie. Ba! na pewno! Jeżeli jesteśmy jednak chociaż trochę zainteresowani metodyką MITRE ATT&amp;CK to warto poświęcić jej cały kolejny wpis. Będzie to bowiem nakreślenie całkiem fajnej, logicznej, elastycznej i szybkiej metody podejścia do projektowania zabezpieczeń w świecie IT. Wyjście z profilu zagrożeń (scenariuszy) do nakreślenia architektury bezpieczeństwa rozwiązania i (mam nadzieję) zgrabne przejście do samych technikaliów w części trzeciej (kolejnym, ostatnim już wpisie o IoT). No tak, znowu miało być technicznie, a wyjdzie analitycznie. Bardzo jednak chcę wykonać to ćwiczenie z ATT&amp;CK-iem, by nawet samemu sobie zademonstrować przydatność tej metodyki.]]></summary></entry><entry><title type="html">O bezpieczeństwie domowego IoT</title><link href="http://localhost:4000/devsecops/iot/2022-12-15-fairlysecurehomeiot/" rel="alternate" type="text/html" title="O bezpieczeństwie domowego IoT" /><published>2022-12-15T00:00:00+01:00</published><updated>2022-12-15T00:00:00+01:00</updated><id>http://localhost:4000/devsecops/iot/fairlysecurehomeiot</id><content type="html" xml:base="http://localhost:4000/devsecops/iot/2022-12-15-fairlysecurehomeiot/"><![CDATA[<p>Na sam tytuł, moja żona wymownie przewraca oczami.. Kiedy mówię, że każde gniazdko ma swój login i hasło, puka się w głowę. Po prostu wie, że żyje z lekko stukniętym typem pod jednym dachem. Ale, jak to pisze mój kolega-filozof, janieotym.</p>

<p>Rosnące ceny energii i ogólny rozwój “inteligencji” naszych domów i mieszkań, przy <em>boomie</em> na fotowoltaikę przyciągnął na dłużej moją uwagę. A jakieś ogólne rozważania o bezpieczeństwie IoT (ale też i wysokości rachunków za prąd) pchnęły mnie do działania. Na pierwszy ogień poszły gniazdka Gosund SP111 z Tasmotą na pokładzie. Fajna rzecz, wytrzymymuje obciążenie do 3,45kW i jeszcze mierzy prąd, napięcie, obiążenie i <em>cosinus fi</em> - czyli też przesunięcie fazowe!. A jak weterani różnych elektrycznych techników pamietają, ongi <em>cosinus fi</em> przynosiło się w wiaderkach. ;-) A tu proszę.. mierzy samo i z wiadrami żadnymi latać nie trzeba.</p>

<p>No więc do <em>ad remu</em> przechodząc, po głowie chodził mi temat pomiaru poboru mocy przez różne urzadzenia pożerające prąd (..i <em>cosinus fi</em>!), ale w jakiś uporządkowany i bezpieczny sposób. W swojej wrodzonej i nabytej nieufności do rzeczy, w których “samo się” dzieje, gdzie przy rzeczach “inteligentnych” zapala się czerwona lampka, chciałem zbudować działającą koncepcję tak realizowanych pomiarów. Najlepiej spełniającą kardynalny postulat “to co dzieje się w domu, pozostaje w domu”. Dlatego też, odpadły wszelkie chmury, historie typu “zanim wepniesz gniazdko.. załóż konto..” i “wygodne appki na telefon”. Postanowiłem zrobić <em>PoCa</em> (ang. <em>Proof of Concept</em>).</p>

<h3 id="elementy-układanki">Elementy układanki</h3>

<p>Poniższa lista przedstawia wszystkie elementy układanki, które postanowiłem złożyć w jedno rozwiązanie, działające i bezpiecznie i wygodnie i sprawnie. Dużo tego nie ma, ale myślę, że takie na pozór łatwe ćwiczenie dało mi niezłą dozę wiedzy, z jakimi przeciwnościami losu trzeba się mierzyć by zadbać o bezpieczeństwo takich instalacji.</p>

<ul>
  <li><a href="https://templates.blakadder.com/gosund_SP111_v1_1.html">Gosund SP111</a> - inteligentne gniazdko (układ ESP8285),</li>
  <li><a href="https://tasmota.github.io/docs/">Tasmota 12.3.1</a> - firmware Open Source do gniazdka,</li>
  <li><a href="https://mosquitto.org/">Mosquitto</a> - Open source-owy broker MQTT,</li>
  <li><a href="https://github.com/influxdata/telegraf">Telegraf</a> - konsument danych z MQTT, translacja danych do TSDB,</li>
  <li><a href="https://github.com/influxdata/influxdb">InfluxDB</a> TSDB.</li>
</ul>

<p><img src="/assets/images/iotworkflow.png" alt="IoT Workflow" class="align-center" /></p>

<p>Tu opcjonalnie, na sam koniec można dodać jeszcze Grafanę, w której można opracować ładniejsze i bardziej niż w samym InfluxDB, funkcjonalne dashboardy, alerty i powiadomienia. Na PoC wystarczy to co jest.</p>

<h3 id="analiza-zagrożeń">Analiza zagrożeń</h3>

<p>Z życia wiemy, że zagrożenia czyhają na nas wszędzie i nie chodzi tylko o własną, niewymuszoną niefrasobliwość, bądź głupotę. Postronnym sluchaczom może się to wydawać jakąś fantasmagorią - <em>ej, no weź.. kto Ci się będzie włamywał do gniazdek?!</em>. I tak dalej. Nie, nie będziemy tutaj znowu uprawiać Bayesa, chociaż można by spróbować  zebrawszy uprzednio jakieś wiarygodne statystyki dotyczące włamań do domowych IoT (nie przemysłowych - to inna odmiana tego samego zagrożenia, bo intencje są inne) i szkód przezeń spowodowanych. No dobrze, ale kontynuując - takie domowe gniazdka skoro mają nam służyć swoją nietuzinkową inteligencją, muszą zostać podłączone do jakiejś sieci TCP/IP (przez Wifi) i obsługiwać komunikację do i z gniazdka.</p>

<h4 id="zagrożenia">Zagrożenia</h4>

<p>Zagrożenie właściwie jest jedno - włamanie do domowej sieci. Natomiast jedną z miar zagrożeń (przy rozpatrywaniu istotności takiego zagrożenia) mogą być intencje, które warunkują cele takich włamań. Spróbujmy je wymienić:</p>
<ul>
  <li><strong>Scenariusz 1</strong>: Włamanie do sieci celem przejęcia kontroli nad “inteligecją” domową - z intencją późniejszego włamania do samego domu. A zatem przejęcie kontroli nad “inteligencją” posłuży do tego, aby np. w nocy brama została otwarta, a czujniki ruchu wyłączone, a przez kamerę przy wejściu prześledzić można zwyczaje domowników, kto kiedy wychodzi i wraca..itd.</li>
  <li><strong>Scenariusz 2</strong>: Włamanie do sieci celem dojścia do ważnych zasobów informacji na komputerach domowych z intencją późniejszego wykorzystania danych - wykorzystanie danych osobowych do wzięcia stu pożyczek, kompromitacja ofiar w internecie wykorzystując wrażliwe dane prywatne, itd.</li>
  <li><strong>Scenariusz 3</strong>: Włamanie się do sieci celem wykorzystania zasobów domowych z intencją użycia zasobów domowych jako trampoliny do dalszych niecnych działań w internecie - np. przez instalację botnetu i realizacja kampanii phishingowych (tu mam żywy przykład u pewnej rodziny sprzed 2 tygodni, przyszła do nich policja że z IP - rutera domowego, a miał statyczny, wysyłany był malware, przez który ludzie z innych stron Polski potracili pieniądze z kont bankowych - przyczyną nie musiało być IoT, ale skądinąd akurat mogło być..)</li>
  <li><strong>Scenariusz 4</strong>: Włamanie celem wyrządzenia szkód - jeśli na IoT mamy jakąś zaawansowaną automatykę sterującą pompą ciepła, piecem, regulacją fotowoltaiki itd. to człowiek z takimi intencjami i zaawansowanymi umiejętnościami, może np. popsuć urządzenia warte kilkanaście - kilkadziesiąt tys. złotych.</li>
  <li><strong>Scenariusz 5</strong>: Włamanie do sieci, celem zdobycia informacji pozwalającej na wyrządzenie szkód konkurencji, bo np. ofiara jest prezesem jakieś ważnej spółki i byłoby super dostać się do jej laptopa, a jak wiemy nie tylko korporacje z zabezpieczonymi po zęby laptopami biorą udział w ważnych i dużo wartych przetargach. Wiele osób ma np. tylko darmowego Avasta, jeśli ma cokolwiek..</li>
</ul>

<p>Jeśli w ten sposób spojrzymy na instalacje IoT i pomyślimy jako szczęśliwy posiadacz i użytkownik instalacji IoT - to którykolwiek z w/w scenariuszy zagrożenia z pewnością będzie mógł przypisać do swojej sytuacji. Ale jak!? No to prześledźmy:</p>

<p>Profile potencjalnych ofiar włamów do/przez IoT:</p>

<ol>
  <li>Nie mam domu, nie mam fotowoltaiki, nie jestem prezesem i nie prowadzę w ogóle żadnej firmy, nie mam nawet konta w banku:
    <ul>
      <li><strong>Scenariusz 2</strong> lub/i</li>
      <li><strong>Scenariusz 3</strong>,</li>
    </ul>
  </li>
  <li>Mam dom, nie mam fotowoltaiki, nie jestem prezesem i nie prowadzę żadnej firmy:
    <ul>
      <li><strong>Scenariusz 1</strong>,</li>
      <li><strong>Scenariusz 2</strong>,</li>
      <li><strong>Scenariusz 3</strong>,</li>
      <li>może <strong>Scenariusz 4</strong></li>
    </ul>
  </li>
  <li>Mam dom, fotowoltaikę, prowadze firmę, która startuje w różnych przetargach:
    <ul>
      <li>tu właściwie <strong>wszystkie scenariusze</strong> nam się “uaktywniają” jako możliwe, a nawet ich kombinacje.</li>
    </ul>
  </li>
</ol>

<p>Podsumujmy ten alarmistyczny wywód w kontekście natury takich małych a inteligentnych urzadzonek jak żarówki, gniazdka, czujniki czy kamery. Są to sprzety małe, oparte o układy przemysłowe, wytwarzane na przemysłową skalę, których specyfikacje i oprogramowanie jest szeroko dostępne (jak np. Tasmota). Ze swojej natury borykają się z licznymi ograniczeniami - i nie są to tylko zaniechania producentów - jest to na przykład ograniczona pojemność RAM w którą można upakować nie tylko niezbędną funkcjonalność, ale i nie-funkcjonalność w postaci mechanizmów bezpieczeństwa. Takich jak: możliwość zmiany fabrycznie ustawionej nazwy konta admina, możliwość konfiguracji TLS 1.2 do komunikacji, bezpieczne (silnie zaszyfrowane) przechowywanie haseł czy tokenów uwierzytelniających, zaawansowane metody uwierzytelniania, obsługa WPA3 czy choćby WPA2-PSK ale z szyfrowaniem AES, zmiana MAC adresu, czy choćby nazwy urządzenia. Takie gniazdko Gosund SP111 ma 1MB RAM, z czego firmware z tym co oferuje zajmuje 630kB.</p>

<p>Z drugiej strony tak jak w całym świecie IT i na całym świecie mamy zaniechania, lenistwo i nieświadomość człowieczą, przy instalacji i eksploatacji. Poza tym nie każdy instalator IoT musi się znać na wszelkich typach ruterów bezprzewodowych - na jednym “<em>network isolation</em>” ustawia się jednym klikiem, na innych wyłącza po prostu <em>forwarding</em> ruchu sieciowego, ale trzeba dorzucić reguły do firewalla. No i te aspekty dodane do siebie, wyczerpują nam w zupełności powagę istotności opisanych scenariuszy zagrożeń.</p>

<h3 id="co-robić-jak-żyć">Co robić, jak żyć?</h3>

<p>Znowu (po raz któryś! dziwne..) pojawia się zasadnicze leninowskie pytanie “co robić?!”. Spróbować wybrać tak, i zaprojektować tak, i poskładać tak tę instalację aby się przed takimi zagrożeniami jednak obronić. To znaczy tak utrudnić potencjalnym amatorom silnych wrażeń w IoT zadanie, żeby im się nie opłacało w ogóle nawet chcieć. W ocenie zagrożeń i doborze technik przeciwdziałania może nam pomóc matryca <a href="https://attack.mitre.org/matrices/ics/">MITRE</a> dla ICS (ang. <em>Industrial Control Systems</em>).</p>

<h4 id="mitre">MITRE</h4>

<p>Towarzyszom niedoli z mojej branży nazwa MITRE kojarzyć się może z <a href="https://cve.mitre.org/">bazą podatności CVE</a>. Ale nie tylko. MITRE jako organizacja non-profit sponsorowana przez rząd USA zajmuje się też rozwojem metodyki analizy zagrożeń w cyberprzestrzeni (IoT jest jej częścią) zwanej <a href="https://attack.mitre.org/">ATT&amp;CK</a>. Rzecz polega na bieżącej analizie i profilowaniu zagrożeń w cyberprzestrzeni i mapowaniu zaobserwowanych działań w podziale na poszczególne fazy ataków - taktyki oraz techniki i pod-techniki służące osiąganiu celów pośrednich by przejść do kolejnych faz i ostatecznie osiągnąć sukces (a przynieść straty ofiarom). I takie mapowanie właśnie wykonano też dla ICS, u nas znane bardziej jako systemy automatyki przemysłowej. Bo z tego właśnie nasze domowe IoT wyewoluowało wraz z postępującą miniaturyzacją, standaryzacją protokołów komunikacji, niskimi kosztami wytwórczymi i upowszechnieniem internetu. I jak się okazuje - całkiem nieźle pasuje nawet do tak niewyszukanych instalacji IoT jak moja.</p>

<p>No to nieźle. Z głupiego (sorry.. inteligentnego!) domowego gniazdka wypłynęliśmy na szerokie wody cyberprzestrzeni i sponsoring USA! A to by się Lenin zdziwił, taką odpowiedzią. ;) No niestety, jeśli chodzi o bezpieczeństwo w cyberprzestrzeni (też!) to nikt nie dzwoni do Szwecji. Bierzemy się do pracy. Pokażę, jak te groźne scenariusze zagrożeń opisane powyżej, przekładają się na metodykę ATT&amp;CK. A także jakie zabezpieczenia adekwatne do skali i możliwości naszego rozwiązania mogą  przeciwdziałać tym zagrożeniom.</p>

<h4 id="attckujemy-iot">ATT&amp;CKujemy IoT</h4>

<p>Sprawczość pomocnicza w ochronie naszego IoT, objawia się w możliwości odpowiedzi na następujące pytania:</p>
<ol>
  <li>Na jakie scenariusze zagrożeń jesteśmy narażeni posiadając taki-a-taki system IoT, który ma zaimplementowane takie-a-takie mechanizmy bezpieczeństwa i przeciwdziałania włamaniom?</li>
  <li>Jakie mechanizmy bezpieczeństwa i przeciwdziałania włamaniom powinniśmy zaimplementować, aby udaremnić realizację danych scenariuszy zagrożeń?</li>
  <li>Czy zaimplementowane mechanizmy bezpieczeństwa i przeciwdziałania włamaniom uchronią nas przed danymi zagrożeniami?</li>
</ol>

<p>I jak myślicie - na które pytanie zechcemy sobie odpowiedzieć? No tak, robimy PoC-a, jakieś pojęcie mamy, ale chcemy uzyskać konkretną listę, konkretnych tematów do implementacji w naszym PoC-u, aby nie martwić się o scenariusze, którymi sam siebie wystraszyłem tam nieco powyżej w tekście. A więc będzie to pytanie nr 2.</p>

<p>Co przeanalizujemy? Potencjalny atak, jego wektor i cele atakującego zakładamy w treści scenariusza nr 2 i 3. Celem ataku będzie zwyczajna, powszechna sieć domowa WiFi, czyli ruter WiFi podpięty do infrastruktury dostawcy z jakąś konfiguracją zapewniającą szybki internet dla domu, kilka urzadzeń domowych - komputerów/laptopów, może serwer jakiś, może jakiś TV i nasze gniazdka.</p>

<p>Otwórzmy sobie w przeglądarce narzędzie analityczne <a href="https://mitre-attack.github.io/attack-navigator/">ATT&amp;CK Navigatora</a>. Wybierzmy “Create New Layer” i poniżej kliknijmy w “ICS”. Pojawi nam się matryca z ponazywanymi kolumnami, a w każdej z kolumn techniki realizacji ataku. Coś takiego:</p>

<p><img src="/assets/images/attackiot.png" alt="ATT&amp;CK for ICS/IoT" class="align-center" /></p>

<p>Jeśli wrócimy do domowej strony <a href="https://attack.mitre.org/matrices/ics/">matrycy dla systemów automatyki przemysłowej</a> i klikniemy np. w pierwszą na samym dole technikę “<a href="https://attack.mitre.org/techniques/T0860">Wireless Compromise</a>”, to w taktyce “Initial Access” zobaczymy zamapowane scenariusze przeciwdziałania (ang. Mitigations) dla podstawowej taktyki ataku naszego systemu IoT. Bowiem, moim zdaniem, domowe IoT nie będą w dużej mierze celem samym w sobie, a słabością, którą wykorzysta atakujący by zrobić nam krzywdę (albo nawet nie nam) gdzie indziej - o czym mowią opisane wyżej scenariusze. Więc ruszajmy dalej. Pierwszym zadaniem na liście “Mitigation” jest “<a href="https://attack.mitre.org/mitigations/M0802/">M0802 	Communication Authenticity</a> “ czyli zapewnienie autentyczności komunikacji. Klikając w tę pozycję dostajemy zwrotnie listę technik atakującego, które zostaną uniemożliwione lub utrudnione przez to przeciwdziałanie. W treści tego przeciwdziałania jest link do CISA (kolejne agencji bezpieczeństwa) gdzie w prosty sposób wyszczególnione są konkretne działania do realizacji. Pasuje do naszych scenariuszy? Pasuje. Dalej pod “Mitigation” mamy mechanizmy detekcji - “Detections” z listą co powinniśmy mieć skonfigurowane, by realizację tej techniki przez atakującego wykryć! W Navigatorze zaznaczamy tę technikę na czerwono. Drugą techniką w taktyce “Initial Access”, która może realna dla naszego przypadku to moim zdaniem “<a href="https://attack.mitre.org/techniques/T0862/">Supply Chain Compromise</a>”, czyli naruszenie łańcucha dostaw. Przekładając to na realia - możemy mieć już w nierozpakowanym pudełku gniazdko z firmwarem, które zawiera nieprzyjemną niespodziankę - wgrany firmware ma dwie malutkie zmiany, praktycznie powodujący może minimalne zmiany w wielkości pliku firmware-u - podmieniony został URL do aktualizacji i uaktywniona opcja automatycznej aktualizacji. Albo też, jeszcze nieobeznani, nieostrożni, ale zapaleni do działania, sami ściągamy upgrade firmware-u z jakiejś nieznanej nam bliżej witryny w internecie. A i w <a href="http://ota.tasmota.com/tasmota/release/">repozytorium</a> Tasmoty - open source-owego firmware-u dla mojego gniazdka, sygnatur nie stosuje się.</p>

<p>I w ten sposób przeglądamy resztę taktyk zaznaczając te, które nam możliwie pasują do rozpatrywanych scenariuszy mając na uwadze zaproponowany (i już nieco rozpoznany) stos technologiczny naszego PoC-a. Tym sposobem, przeglądając resztę technik oraz ich “Mitigation” i “Detection” dochodzimy do końca matrycy. Na czerwono w matrycy w Navigatorze zaznaczamy te, które uznamy za właściwe, możliwe i zgodne z celem i intencją rozpatrywanego przez nas scenariusza zagrożenia. Ostatecznie dostajemy coś w rodzaju takiego obrazka:</p>

<p><img src="/assets/images/attackiotthreat.png" alt="ATT&amp;CK for ICS/IoT" class="align-center" /></p>

<h4 id="odpowiedź-na-pytanie">Odpowiedź na pytanie</h4>

<p>Jakie mechanizmy bezpieczeństwa i przeciwdziałania włamaniom powinniśmy zaimplementować, aby udaremnić realizację danych scenariuszy zagrożeń? Poniżej jest lista, opracowana przeze mnie na podstawie analizy poszczególnych technik. Należy naturalnie wziąć pewną poprawkę na to, że propozycje implementacji tych mechanizmów dotyczą ICS a nie IoT i nie w domu a w przedsiębiorstwie. Wobec tego zalecenia dotyczące np. audytu tego czy owego wypada “przełożyć” na rozwiązanie domowe, np. lepsze ustawienie alertów, albo okresowy jakiś prosty przegląd logów, ostatecznie skreślić z listy. No bo skąd niby w domu miałby się wziąć jakiś audyt? :) Tak samo “Supply chain management” czy “Vulnerability management”. Będą więc propozycje, które:</p>
<ul>
  <li>Da się zrealizować,</li>
  <li>Częsciowo da się zrealizować,</li>
  <li>Da się zastąpić inną metodą,</li>
  <li>Nie da się zrealizować.</li>
</ul>

<p>Przy tworzeniu tej listy robimy od razu coś w rodzaju “oceny przydatności”. Z całą tą listą z zawartą tam oceną przydatności i uzasadnieniem rozprawimy się w kolejnym wpisie. Ten wydaje się już i tak dość długi.</p>

<p><img src="/assets/images/attacklist.png" alt="Lista płaczu" class="align-center" /></p>

<p>Nie mniej jednak, dzięki tej liście, będziemy mogli odpowiedzieć na kolejne pytanie - czy dobór komponentów do PoC zapewni nam oczekiwany poziom bezpieczeństwa (np. uchroni nas przed paskudnymi skutkami scenariuszy zagrożeń). Odpowie nam też na pytanie jaką pracochłonność powinniśmy zaplanować na wdrożenie, ile rzeczy trzeba się nauczyć, ile przetestować, które elementy w naszym PoCu podmienić. No i wreszcie ile to wszystko będzie kosztować.</p>

<h3 id="podsumowanie">Podsumowanie</h3>

<p>Ten wpis miał być o technicznym wdrożeniu prostego systemu pomiaru poboru mocy przez urządzenia domowe przy pomocy gniazdka Gosund SP111 z Tasmotą na pokładzie. A jednak (surprise, surprise!) przerodził się w rozważania analityczne o bezpieczeństwie takiego rozwiązania. Nic nie szkodzi, w kolejnym wpisie przedstawię techniczną implementację tego PoC wg nakreślonych na samym początku założeń. Przykład, hmm.. może nie być zbyt adekwatny do popularnych potrzeb (mało kto w domu ma serwery z Nomadem, Consulem i Vaultem gdzie w bezpieczny sposób może składować i wymieniać dane uwierzytelniające poszczególnych komponentów rozwiązania). No ale tez chodzi o to, aby pokazać, że nie jesteśmy bez szans, ani nie jesteśmy skazani na umowne “bezpieczeństwo” danych w jakichś tam chmurach producenckich.</p>]]></content><author><name>Dominik Miklaszewski</name></author><category term="DevSecOps" /><category term="IoT" /><category term="devsecops" /><category term="docker" /><category term="iot" /><category term="mqtt" /><category term="tasmota" /><category term="influxdb" /><category term="gosund" /><summary type="html"><![CDATA[Na sam tytuł, moja żona wymownie przewraca oczami.. Kiedy mówię, że każde gniazdko ma swój login i hasło, puka się w głowę. Po prostu wie, że żyje z lekko stukniętym typem pod jednym dachem. Ale, jak to pisze mój kolega-filozof, janieotym.]]></summary></entry><entry><title type="html">Reducing att&amp;amp;ck surface</title><link href="http://localhost:4000/devsecops/docker/2022-10-13-reducingattacksurface/" rel="alternate" type="text/html" title="Reducing att&amp;amp;ck surface" /><published>2022-10-13T00:00:00+02:00</published><updated>2022-10-13T00:00:00+02:00</updated><id>http://localhost:4000/devsecops/docker/reducingattacksurface</id><content type="html" xml:base="http://localhost:4000/devsecops/docker/2022-10-13-reducingattacksurface/"><![CDATA[<p>Lately, I have been exploring great <a href="https://attack.mitre.org/">MITRE ATT&amp;CK framework</a>.  Part of their portfolio is <a href="https://www.academy.attackiq.com/learning-path/from-concept-to-practice-applying-the-world-class-research-of-the-center-for-threat-informed-defense">ATTACKIQ Academy</a> where one can acquire a great doze of useful knowledge on how to handle various cyber threats in regards to process, organization, taxonomy and daily operations.</p>

<p>Among these great tools (like this absolutely fantastic <a href="https://top-attack-techniques.mitre-engenuity.org/calculator">threat calculator</a> supported by sound and logic <a href="https://top-attack-techniques.mitre-engenuity.org/methodology">methodology</a>) there’s ATT&amp;CK Workbench written and maintained by <a href="https://github.com/center-for-threat-informed-defense">CTID</a>. An open source tool to operationalize sharing a knowledge base grown amongst blue and red teams in their efforts to keep threats at bay. A very fine tool. However, as being lately into DevOps for sometine, I’ve managed to grow this kind of devops anxiety (or “compulsive neurosis”) which makes me always keep seeking for those aspects of application development associated with proper components management in respect to security and updates through an SDLC of the app.</p>

<p>So, while enjoying the chapter of <a href="https://www.academy.attackiq.com/courses/extending-attck-with-attck-workbench">“Extending ATT&amp;CK with ATT&amp;CK Workbench”</a> I cloned the repositories and fired off the <code class="language-plaintext highlighter-rouge">docker-compose up</code> on my Docker Desktop, just to explore the tool and get this chapter accomplished. First thing which popped up right away was the size of the docker containers of this app (frontend, api and collection manager). Almost 1GB each. Wow! I took some time and searched through countless micro-tutorials on how to dockerize an angular app. None that I found had followed docker best practises. Just install the angular CLI, copy over the whole app, run <code class="language-plaintext highlighter-rouge">npm install</code> and get it into the NGINX base, <em>et voila</em>!. The only prevailing pattern was this NGINX. Apparently the Workbench creators followed those tutorials. So, let’s quickly examine this tool and fix its components Dockerfiles.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>attack-workbench-frontend_collection-manager	    959MB
attack-workbench-frontend-rest-api			    1.06GB
attack-workbench-frontend_frontend			    142MB
</code></pre></div></div>

<p>Well, next step was to see what <code class="language-plaintext highlighter-rouge">trivy</code> was about to say:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>attack-workbench-frontend_frontend (debian 10.9)
================================================
Total: 336 (UNKNOWN: 3, LOW: 146, MEDIUM: 62, HIGH: 81, CRITICAL: 44)

attack-workbench-frontend_collection-manager (debian 10.13)
===========================================================
Total: 2028 (UNKNOWN: 0, LOW: 1231, MEDIUM: 463, HIGH: 301, CRITICAL: 33)

attack-workbench-frontend_rest-api:latest (debian 10.13)
========================================================
Total: 2028 (UNKNOWN: 0, LOW: 1231, MEDIUM: 463, HIGH: 301, CRITICAL: 33)
</code></pre></div></div>

<p>Looks like a friggin lot of the crits. Snyk also confirmed that the culprit was the outdated Debian based NGINX image.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[..]
Docker image:      attack-workbench-frontend_frontend:latest
Platform:          linux/amd64
Base image:        nginx:1.19
Licenses:          enabled

Tested 136 dependencies for known issues, found 225 issues.

Base Image  Vulnerabilities  Severity
nginx:1.19  225              30 critical, 34 high, 26 medium, 135 low

Recommendations for base image upgrade:

Minor upgrades
Base Image  Vulnerabilities  Severity
nginx:1.22  84               0 critical, 0 high, 0 medium, 84 low

Alternative image types
Base Image         Vulnerabilities  Severity
nginx:1.23.1-perl  84               0 critical, 0 high, 0 medium, 84 low
</code></pre></div></div>

<h3 id="the-frontend">The frontend</h3>

<p>Further poking around showed up, that the docker wrapping was never updated since the first launch. Hence, my first move was to get this suite wrapped up with my latest alpine-node16 and refresh the NGINX parts. It’s a known docker technique to reduce size of the image - multi-stage build, where the last final image gets only compiled and static web contents of the app, accessible through <code class="language-plaintext highlighter-rouge">nginx</code> web server. However, to further reduce the size I used the base alpine 3.16.2 with nginx added from the distribution.</p>

<p>Modified <code class="language-plaintext highlighter-rouge">attack-workbench-frontend/Dockerfile</code>:</p>

<figure class="highlight"><pre><code class="language-hcl" data-lang="hcl"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
</pre></td><td class="code"><pre> 
<span class="nx">FROM</span> <span class="nx">powernuke</span><span class="err">.</span><span class="nx">nukelab</span><span class="err">.</span><span class="nx">home</span><span class="o">:</span><span class="mi">5443</span><span class="o">/</span><span class="nx">base-alpine-node16</span><span class="o">:</span><span class="mi">16</span><span class="o">-</span><span class="mi">3</span> <span class="nx">as</span> <span class="nx">build</span>

<span class="nx">WORKDIR</span> <span class="o">/</span><span class="nx">home</span><span class="o">/</span><span class="nx">node</span><span class="o">/</span><span class="nx">app</span>

<span class="nx">COPY</span> <span class="err">.</span><span class="o">/</span><span class="nx">app</span><span class="o">/</span><span class="nx">package</span><span class="o">*</span><span class="err">.</span><span class="nx">json</span> <span class="err">.</span><span class="o">/</span>
  
<span class="nx">RUN</span> <span class="nx">npm</span> <span class="nx">install</span> <span class="o">-</span><span class="nx">g</span> <span class="err">@</span><span class="nx">angular</span><span class="o">/</span><span class="nx">cli</span> <span class="o">&amp;&amp;</span> <span class="err">\</span>
    <span class="nx">npm</span> <span class="nx">install</span> 
 
<span class="nx">COPY</span> <span class="err">.</span><span class="o">/</span><span class="nx">app</span> <span class="err">.</span>
 
<span class="nx">COPY</span> <span class="err">.</span><span class="o">/</span><span class="nx">docs</span> <span class="err">..</span><span class="o">/</span><span class="nx">docs</span>
 
<span class="nx">RUN</span> <span class="nx">npm</span> <span class="nx">run</span> <span class="nx">build-prod</span>
 
<span class="nx">FROM</span> <span class="nx">powernuke</span><span class="err">.</span><span class="nx">nukelab</span><span class="err">.</span><span class="nx">home</span><span class="o">:</span><span class="mi">5443</span><span class="o">/</span><span class="nx">base-alpine</span><span class="o">:</span><span class="mf">3.16</span><span class="o">-</span><span class="mi">3</span>
 
<span class="nx">RUN</span> <span class="nx">apk</span> <span class="o">--</span><span class="nx">update</span> <span class="nx">add</span> <span class="nx">nginx</span> <span class="o">&amp;&amp;</span> <span class="err">\</span>
    <span class="nx">rm</span> <span class="o">-</span><span class="nx">rf</span> <span class="o">/</span><span class="nx">var</span><span class="o">/</span><span class="nx">cache</span><span class="o">/</span><span class="nx">apk</span><span class="cm">/*

COPY ./nginx/nginx.conf /etc/nginx/nginx.conf

WORKDIR /usr/share/nginx/html 

COPY --from=build  /home/node/app/dist/app .
 
EXPOSE 80 

STOPSIGNAL SIGQUIT 

CMD ["nginx", "-g", "daemon off;"]
 </span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>Result:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>attack-workbench-frontend_frontend:latest               33.4MB
</code></pre></div></div>

<p>Trivy:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>attack-workbench-frontend_frontend:latest (alpine 3.16.2)
=========================================================
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
</code></pre></div></div>

<h3 id="the-rest-api-and-collection-manager">The rest (api and collection manager)</h3>

<p>Let’s move onward and get the other two cleaned up. Replace old and grumpy <code class="language-plaintext highlighter-rouge">node:14</code> with my freshly built <code class="language-plaintext highlighter-rouge">base-alpine-node16:16-3</code>, address a very bad practise of running an app as local <code class="language-plaintext highlighter-rouge">root</code> - with the <code class="language-plaintext highlighter-rouge">USER node</code> line and see:</p>

<p>Result:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>attack-workbench-frontend_rest-api:latest               353MB
</code></pre></div></div>

<p>Trivy - shows no issues on the OS level, however it looks like the old code has some flawed libs (npm packages):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>attack-workbench-frontend_rest-api:latest (alpine 3.16.2)
=========================================================
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)


Node.js (node-pkg)
==================
Total: 7 (UNKNOWN: 0, LOW: 0, MEDIUM: 2, HIGH: 2, CRITICAL: 3)
</code></pre></div></div>
<p>Well, let’s just run <code class="language-plaintext highlighter-rouge">npm update</code> right before the <code class="language-plaintext highlighter-rouge">npm install</code> that’s hopefully gonna fix buggy npm modules. Ok, how’s now?</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>attack-workbench-frontend_rest-api:latest (alpine 3.16.2)
=========================================================
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)

Node.js (node-pkg)
==================
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)


attack-workbench-frontend_collection-manager:latest (alpine 3.16.2)
===================================================================
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)

Node.js (node-pkg)
==================
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
</code></pre></div></div>

<p>The ultimate <code class="language-plaintext highlighter-rouge">/attack-workbench-rest-api/Dockerfile</code> (and for the collection manager as well) now is:</p>

<figure class="highlight"><pre><code class="language-hcl" data-lang="hcl"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
</pre></td><td class="code"><pre> 
<span class="nx">FROM</span> <span class="nx">powernuke</span><span class="err">.</span><span class="nx">nukelab</span><span class="err">.</span><span class="nx">home</span><span class="o">:</span><span class="mi">5443</span><span class="o">/</span><span class="nx">base-alpine-node16</span><span class="o">:</span><span class="mi">16</span><span class="o">-</span><span class="mi">3</span>

<span class="nx">WORKDIR</span> <span class="o">/</span><span class="nx">home</span><span class="o">/</span><span class="nx">node</span><span class="o">/</span><span class="nx">app</span>

<span class="nx">COPY</span> <span class="nx">package</span><span class="o">*</span><span class="err">.</span><span class="nx">json</span> <span class="err">.</span><span class="o">/</span>

<span class="nx">RUN</span> <span class="nx">npm</span> <span class="nx">update</span> <span class="o">&amp;&amp;</span> <span class="err">\</span>
    <span class="nx">npm</span> <span class="nx">ci</span> <span class="o">--</span><span class="nx">only</span><span class="o">=</span><span class="nx">production</span> <span class="o">&amp;&amp;</span> <span class="err">\</span>
    <span class="nx">npm</span> <span class="nx">cache</span> <span class="nx">clean</span> <span class="o">--</span><span class="nx">force</span>

<span class="nx">COPY</span> <span class="err">.</span> <span class="err">.</span>

<span class="nx">RUN</span> <span class="nx">chown</span> <span class="o">-</span><span class="nx">R</span> <span class="nx">node</span><span class="o">:</span><span class="nx">node</span> <span class="o">/</span><span class="nx">home</span><span class="o">/</span><span class="nx">node</span>

<span class="nx">USER</span> <span class="nx">node</span>

<span class="nx">CMD</span> <span class="p">[</span> <span class="s2">"npm"</span><span class="p">,</span> <span class="s2">"start"</span> <span class="p">]</span>
 
</pre></td></tr></tbody></table></code></pre></figure>

<p>Oh, by the way the <code class="language-plaintext highlighter-rouge">.dockerignore</code> should be set in the way so that you won’t copy all the Dockerfiles and other unnecessary stuff into the image with the <code class="language-plaintext highlighter-rouge">COPY . .</code> line.</p>

<h3 id="results">Results</h3>

<p>The size:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>attack-workbench-frontend_collection-manager     142MB
attack-workbench-frontend_rest-api               338MB
attack-workbench-frontend_frontend               33.4MB
</code></pre></div></div>
<p>..down from original:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>attack-workbench-frontend_collection-manager    959MB
attack-workbench-frontend-rest-api             1.06GB
attack-workbench-frontend_frontend              142MB
</code></pre></div></div>
<p>..and from thousands vulnerabilities (with over 100 critical) down to none.
Security of the code, secure MongoDB, authentication is another story. Nevertheless, properly maintaining the Dockerfile alone can significantly reduce headaches and ..the attack surface. :)</p>

<p>ps. If anyone wants to use these fixed Dockerfiles, you need to replace my alpine built node16 with <a href="https://hub.docker.com/_/node?tab=description&amp;amp%3Bpage=1&amp;amp%3Bname=alpine">regular alpine base node 16</a> and <a href="https://hub.docker.com/_/nginx?tab=tags&amp;name=1.21-alpine">nginx 1.23 also alpine based</a>.</p>]]></content><author><name>Dominik Miklaszewski</name></author><category term="DevSecOps" /><category term="Docker" /><category term="devsecops" /><category term="docker" /><category term="devops" /><summary type="html"><![CDATA[Lately, I have been exploring great MITRE ATT&amp;CK framework. Part of their portfolio is ATTACKIQ Academy where one can acquire a great doze of useful knowledge on how to handle various cyber threats in regards to process, organization, taxonomy and daily operations.]]></summary></entry><entry><title type="html">Bezpieczna dostępność</title><link href="http://localhost:4000/devsecops/2022-09-14-bezpdostepnosc/" rel="alternate" type="text/html" title="Bezpieczna dostępność" /><published>2022-09-14T00:00:00+02:00</published><updated>2022-09-14T00:00:00+02:00</updated><id>http://localhost:4000/devsecops/bezpdostepnosc</id><content type="html" xml:base="http://localhost:4000/devsecops/2022-09-14-bezpdostepnosc/"><![CDATA[<p>Po wstępnych dywagacjach w poprzednim wpisie, pora podkasać rękawy i zabrać się do pracy. Naszymi <em>dramatis personae</em> będą stali bywalcy: Nomad, Consul i Vaulta oraz <a href="https://traefik.io/traefik/">Traefik</a>. Plan jest taki, aby mieć jeden <em>ingress</em> point (Traefik) dla mojej mikro-chmurki prywatnej, który nie tylko automatycznie wyniucha co w serwisach piszczy, a również automatycznie je obsłuży wraz z TLS.</p>

<h3 id="czym-jest-traefik-proxy">Czym jest Traefik proxy?</h3>

<p>Jest kawałkiem oprogramowania, którego jedynym przeznaczeniem jest rola <em>reverse proxy</em>. Dlaczego właśnie to on? Dlatego, że od początku autorzy postawili na integracje i automatyzację z różnymi technologiami orkiestracji i zarządzania mikro-serwisami. Jest tam docker, kubernetes (a jakże!), Nomad, Consul, Mesos itd. Przy czym wersja <em>community</em> - a więc darmowa, posiada <a href="https://traefik.io/pricing/">dostateczne możliwości</a> i funkcjonalności do zautomatyzowania. W przeciwieństwie do NGINX. Dla Traefika pojawienie się nowego serwisu nie wymaga restartu czegokolwiek. Traefik jako <em>Nomad deployment</em> skorzysta z integracji Nomada i z Consulem i z Vaultem. W taki sposób, że Nomad zapewni bezpieczne dostarczenie <em>Consul Tokena</em> (przechowywanego w Vaulcie) konfiguracji Traefika do integracji z Consulowym katalogiem serwisów. Dzięki temu, Traefik na bieżąco będzie w stanie obsłużyć cały cykl życia usług i aplikacji.</p>

<p><img src="/assets/images/revproxy-traefik.png" alt="Logika działania Traefika w Nomadzie" class="align-center" /></p>

<h4 id="logika-traefika---jak-to-działa">Logika Traefika - jak to działa?</h4>

<p>Koncepcja przedstawiona na diagramie zamieszczonym poniżej jest następująca:</p>
<ol>
  <li>Uruchomiony Traefik jako <em>job</em> w Nomadzie poleceniem <code class="language-plaintext highlighter-rouge">nomad job run -check-index 0 traefik.nomad</code> (<em>jobspecs file</em> <a href="https://github.com/dominikmi/hashistuff-homelab/blob/master/nomad/jobs/traefik.nomad">tutaj</a>)</li>
  <li>Traefik skonfigurowany zostaje tak aby dla swej statycznej, sięgnąć do Vaulta po:
    <ul>
      <li>certyfikaty TLS dla <code class="language-plaintext highlighter-rouge">*.nukelab.home</code>(generowanie certyfikatu opisane <a href="https://github.com/dominikmi/hashistuff-homelab/tree/master/cfssl">tutaj</a>)</li>
      <li><em>Consul Token</em> celem autoryzacji dla integracji z consulowym katalogiem usług (<em>Consul Catalog</em>),</li>
      <li>Przygotowania dynamicznej konfiguracji (docelowo ta konfiguracja powinna być usunięta z pliku <em>jobspecs</em> i umieszczona w GitHubie)</li>
    </ul>
  </li>
  <li>Aplikacja <em>myapp.nukelab.home</em> zostaje uruchomiona w Nomadzie, z odpowiednimi tagami dla usługi (Traefik: <em>labels</em>), które zostaną zaprezentowane w Consulu - Nomad wszystko co się w nim uruchomi, melduje Consulowi jako usługę.</li>
  <li>Nomad jako orkiestrator - zapewnia komunikację między wirtualną siecią dokera (domyślnie ustawiona jako <em>bridge</em>) a interfejsem hosta, łącznie z mapowaniem i wystawieniem odpowiednich portów tcp/udp.</li>
  <li>DNS na potrzeby ćwiczenia, ma rekord dla <code class="language-plaintext highlighter-rouge">*.nukelab.home</code> ustawiony na IP Traefika,</li>
  <li>Traefik pobiera sobie stan katalogu usług z Consula wraz z pełną informacją o każdej usłudze,</li>
  <li>Przychodzi zapytanie o <em>myapp.nukelab.home</em> i dzięki w/w Traefik wie, przez który swój wewnętrzny ruter i do jakiego <em>endpointa</em> ma skierować ruch.</li>
</ol>

<p><img src="/assets/images/revproxy-traefik2.png" alt="Logika działania Traefika w Nomadzie" class="align-center" /></p>

<h3 id="na-plasterki">Na plasterki!</h3>

<p>No to poszatkujmy tego słonia na plasterki, aby kawałek po kawałku pokazać jak to wszystko zostało skonfigurowane i jak działa. Aspekt integracji Nomada, Consula i Vaulta jest poza zakresem tego wpisu. Ważne jest aby klient Nomada (agent) mógł sięgać zarówno do Vaulta jak i Consula, autoryzując się tokenami. Podpowiem, token do autoryzacji w Vaulcie Nomad ma w swoim pliku konfiguracyjnym agenta, a token do Consula przechowuje w.. Vaulcie. Zaczniemy zatem od konfiguracji Traefika.</p>

<h4 id="konfiguracja-traefika">Konfiguracja Traefika</h4>

<p>Konfiguracja Traefika w pliku nomadowym może być zrealizowana poprzez zmienne środowiskowe, pliki konfiguracyjne YAML albo TOML oraz parametry przy odpalaniu binarki. Wykorzystamy kombinację. <strong>Parametry</strong> - przy uruchomieniu podamy traefikowej binarce ścieżkę do plików konfiguracyjnych i <strong>pliki YAML</strong> - te, wygenerujemy poprzez <em>template</em> nomadowy. Naszym słoniem będzie <a href="https://github.com/dominikmi/hashistuff-homelab/blob/master/nomad/jobs/traefik.nomad"><em>jobspecs</em> dla Traefika</a>.</p>

<p>Zaczynamy od wiersza 19, gdzie zaczyna się <em>stanza</em> <code class="language-plaintext highlighter-rouge">group</code>:</p>

<figure class="highlight"><pre><code class="language-hcl" data-lang="hcl"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
</pre></td><td class="code"><pre> 
  <span class="nx">group</span> <span class="s2">"traefik"</span> <span class="p">{</span>
    <span class="nx">count</span> <span class="o">=</span> <span class="mi">1</span>
    <span class="nx">update</span> <span class="p">{</span>
      <span class="nx">auto_revert</span> <span class="o">=</span> <span class="kc">true</span>
    <span class="p">}</span>
    <span class="nx">network</span> <span class="p">{</span>
      <span class="nx">port</span>  <span class="s2">"http"</span> <span class="p">{</span>
        <span class="nx">to</span>     <span class="o">=</span> <span class="mi">80</span> 
        <span class="nx">static</span> <span class="o">=</span> <span class="mi">80</span>
      <span class="p">}</span>
      <span class="nx">port</span> <span class="s2">"https"</span> <span class="p">{</span>
        <span class="nx">to</span>     <span class="o">=</span> <span class="mi">443</span>
        <span class="nx">static</span> <span class="o">=</span> <span class="mi">443</span>
      <span class="p">}</span>
      <span class="nx">port</span>  <span class="s2">"api"</span> <span class="p">{</span>
        <span class="nx">to</span>     <span class="o">=</span> <span class="mi">8080</span>
        <span class="nx">static</span> <span class="o">=</span> <span class="mi">8080</span>
      <span class="p">}</span>
      <span class="nx">dns</span> <span class="p">{</span>
        <span class="nx">servers</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"192.168.120.231"</span><span class="p">]</span>
      <span class="p">}</span>
    <span class="p">}</span>

 
</pre></td></tr></tbody></table></code></pre></figure>

<p>W tym miejscu mapujemy i wystawiamy do sieci porty Traefika, w jego nomenklaturze zwane <em>entrypointami</em>. Można jeszcze wystawić metryki na port 8082/tcp, ale na nie jest to niezbędne na potrzeby naszego ćwiczenia. Dalej będzie już tylko ciekawiej.</p>

<p>Od wiersza 44:</p>

<figure class="highlight"><pre><code class="language-hcl" data-lang="hcl"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
</pre></td><td class="code"><pre> 
    <span class="nx">service</span> <span class="p">{</span>
      <span class="nx">port</span> <span class="o">=</span> <span class="s2">"https"</span>
      <span class="nx">tags</span> <span class="o">=</span> <span class="p">[</span>
          <span class="s2">"traefik"</span><span class="p">,</span>
          <span class="s2">"traefik.enable=true"</span><span class="p">,</span>
          <span class="s2">"traefik.http.routers.dashboard.rule=Host(`traefik.nukelab.home`) &amp;&amp; (PathPrefix(`/api`) || PathPrefix(`/dashboard`))"</span><span class="p">,</span>
          <span class="s2">"traefik.http.routers.dashboard.tls: true"</span><span class="p">,</span>
          <span class="s2">"traefik.http.routers.dashboard.service=api@internal"</span>
      <span class="p">]</span>
      <span class="nx">check</span> <span class="p">{</span>
       <span class="nx">type</span>     <span class="o">=</span> <span class="s2">"tcp"</span>
       <span class="nx">interval</span> <span class="o">=</span> <span class="s2">"15s"</span>
       <span class="nx">timeout</span>  <span class="o">=</span> <span class="s2">"5s"</span>
      <span class="p">}</span>
    <span class="p">}</span>

    <span class="nx">service</span> <span class="p">{</span>
      <span class="nx">tags</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"lb"</span><span class="p">,</span> <span class="s2">"api"</span><span class="p">]</span>
      <span class="nx">port</span> <span class="o">=</span> <span class="s2">"api"</span>
 
      <span class="nx">check</span> <span class="p">{</span>
        <span class="nx">type</span>     <span class="o">=</span> <span class="s2">"http"</span>
        <span class="nx">path</span>     <span class="o">=</span> <span class="s2">"/ping"</span>
        <span class="nx">interval</span> <span class="o">=</span> <span class="s2">"15s"</span>
        <span class="nx">timeout</span>  <span class="o">=</span> <span class="s2">"5s"</span>
      <span class="p">}</span>
    <span class="p">}</span>
 
</pre></td></tr></tbody></table></code></pre></figure>

<p>W ramach grupy <code class="language-plaintext highlighter-rouge">traefik</code> i wystawionych portów, definiujemy usługi, które Nomad zamelduje Consulowi. Pierwsza usługa z portem <code class="language-plaintext highlighter-rouge">https</code> to sam on - Traefik. Sam dla siebie musi wiedzieć, że jak ktoś wyśle zapytanie o <code class="language-plaintext highlighter-rouge">https://traefik.nukelab.home/api|dashboard</code> to ma odpowiedzieć własnym dashboardem.</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">"traefik"</code>, tag, który oznacza usługę, która ma być wystawiona przez Consula do Traefika</li>
  <li><code class="language-plaintext highlighter-rouge">"raefik.enable=true"</code>, tag potwierdzający gotowość do usługi do obsługi (sic!) przez Traefika</li>
  <li><code class="language-plaintext highlighter-rouge">"traefik.http.routers.dashboard.rule=Host(</code>traefik.nukelab.home<code class="language-plaintext highlighter-rouge">) &amp;&amp; (PathPrefix(</code>/api<code class="language-plaintext highlighter-rouge">) || PathPrefix(</code>/dashboard<code class="language-plaintext highlighter-rouge">))"</code> - tag definiujący ruter i reguły sterujące tym ruterem,</li>
  <li><code class="language-plaintext highlighter-rouge">"traefik.http.routers.dashboard.tls: true"</code>, tag, który mówi tyle, że wszystko co przechodzi przez ten ruter ma być obłużone przez HTTPS,</li>
  <li><code class="language-plaintext highlighter-rouge">"traefik.http.routers.dashboard.service=api@internal"</code>, serwis traefikowy który ma być obsłużony przez ten ruter</li>
</ul>

<p>Druga usługa to api. Obie usługi mają zdefiniowane <em>checki</em> - też dla Consula, aby ten wiedział czy usługi żyją i są zdrowe. A więc tu dochodzimy do ważnej obserwacji - w Nomadzie, aby dana aplikacja czy cokolwiek innego (MongoDB, czy inny mikro-serwis) był widoczny i obsłużony przez nasze rev-proxy, musi mieć odpowiednie tagi w definicji <code class="language-plaintext highlighter-rouge">service</code>.</p>

<p>Od wiersza 74:</p>

<figure class="highlight"><pre><code class="language-hcl" data-lang="hcl"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="code"><pre> 
<span class="nx">task</span> <span class="s2">"proxy"</span> <span class="p">{</span>
      <span class="nx">driver</span> <span class="o">=</span> <span class="s2">"docker"</span>
      <span class="nx">config</span> <span class="p">{</span>
<span class="c1">#        network_mode  = "host"</span>
        <span class="nx">command</span>       <span class="o">=</span> <span class="s2">"traefik"</span>
        <span class="nx">args</span>          <span class="o">=</span> <span class="p">[</span> <span class="s2">"--configFile"</span><span class="p">,</span> <span class="s2">"/local/traefik.yml"</span> <span class="p">]</span>
        <span class="nx">image</span>         <span class="o">=</span> <span class="s2">"powernuke.nukelab.home:5443/traefik:2.8.4-8"</span>
        <span class="nx">ports</span>         <span class="o">=</span> <span class="p">[</span><span class="s2">"api"</span><span class="p">,</span> <span class="s2">"http"</span><span class="p">,</span> <span class="s2">"https"</span><span class="p">]</span>
      <span class="p">}</span>
      <span class="nx">vault</span> <span class="p">{</span>
        <span class="nx">policies</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"traefik-access"</span><span class="p">]</span>
      <span class="p">}</span>
 
</pre></td></tr></tbody></table></code></pre></figure>

<p>Tutaj standard, Nomad po prostu każe dockerowi ściągnąć obraz, odpalić z zadanymi argumentami i wystawić uprzednio zdefiniowane porty. Dzięki integracji z Vaultem, na końcu pojawia się deklaracja, że w tym przypadku agent nomadowy będzie autoryzowany przez politykę Vaultową o nazwie <code class="language-plaintext highlighter-rouge">traefik-access</code> do sięgnięcia po sekrety w Vaulcie, w ścieżce przeznaczonej dla Traefika. Obraz Traefika mam swój - żadna filozofia, <code class="language-plaintext highlighter-rouge">alpine 3.16</code> z binarką Traefika + <code class="language-plaintext highlighter-rouge">entrypoint.sh</code>.</p>

<p>Dalej, od wiersza 89 do 134:</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="code"><pre> 
      <span class="s">template {</span>
       <span class="s">data        = &lt;&lt;EOH</span>
<span class="na">tls</span><span class="pi">:</span>
  <span class="na">stores</span><span class="pi">:</span>
    <span class="na">default</span><span class="pi">:</span>
      <span class="na">defaultCertificate</span><span class="pi">:</span>
        <span class="na">certFile</span><span class="pi">:</span> <span class="s">/local/traefik.crt</span>
        <span class="na">keyFile</span><span class="pi">:</span> <span class="s">/local/traefik.key</span>
<span class="s">EOH</span>
       <span class="s">destination = "/local/dynamic.yml"</span>
       <span class="s">change_mode = "restart"</span>
       <span class="s">splay       = "1m"</span>
      <span class="s">}</span>
 
</pre></td></tr></tbody></table></code></pre></figure>

<p>Budujemy tutaj konfigurację dynamiczną, którą poprzez konfigurację statyczną, Traefik sobie “w locie” sprawdza, czy nie ma jakichś zmian. Ta jest inicjalna, więc może tu być, ale np. inne certyfikaty, czy inne specyficzne sprawy dla danej usługi wypada obsługiwać przez kontrolę wersji w GitHubie i po przez GH Action zasilać ten katalog <code class="language-plaintext highlighter-rouge">local</code>.</p>

<figure class="highlight"><pre><code class="language-hcl" data-lang="hcl"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="code"><pre> 
      <span class="nx">template</span> <span class="p">{</span>
        <span class="nx">data</span>        <span class="o">=</span> <span class="o">&lt;&lt;</span><span class="nx">EOH</span>
<span class="p">{{</span> <span class="nx">with</span> <span class="nx">secret</span> <span class="s2">"kv/data/traefik/nukelab"</span> <span class="p">}}</span>
<span class="p">{{</span> <span class="p">.</span><span class="nx">Data</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">certkey</span> <span class="p">}}</span>
<span class="p">{{</span> <span class="nx">end</span> <span class="p">}}</span>
<span class="nx">EOH</span>
        <span class="nx">destination</span> <span class="o">=</span> <span class="s2">"/local/traefik.key"</span>
        <span class="nx">change_mode</span> <span class="o">=</span> <span class="s2">"restart"</span>
        <span class="nx">splay</span>       <span class="o">=</span> <span class="s2">"1m"</span>
      <span class="p">}</span>
      <span class="p">[..]</span>
 
</pre></td></tr></tbody></table></code></pre></figure>

<p>Mamy trzy instancje <code class="language-plaintext highlighter-rouge">template</code>. Po kolei - pierwsza wyciąga z Vaulta klucz do certyfikatu dla <code class="language-plaintext highlighter-rouge">*.nukelab.home</code>, druga sam certyfikat, a trzecia CA. Przy czym, znalazłem tutoriala, gdzie autor miał jakieś problemy i jemu zadziałało to tylko wtedy kiedy miał cert <em>fullchain</em>, łącznie z kluczem (czyli: klucz, cert, intermediate, ca). U mnie działa poprawnie, a nie działało jak sugerował :-) Sekrety wyciągnięte z Vaulta lądują w lokalnym katalogu kontenera z Traefikiem w <code class="language-plaintext highlighter-rouge">/local</code>. Można podejrzeć w Nomadzie (o ile ma się token dostępowy admina - rzecz jasna):</p>

<h4 id="na-koniec-konfiguracja-statyczna-traefika">Na koniec konfiguracja statyczna Traefika</h4>

<p>Od wiersza 138:</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
</pre></td><td class="code"><pre> 
      <span class="s">template {</span>
        <span class="s">data = &lt;&lt;EOH</span>
<span class="pi">{{</span> <span class="nv">with secret "kv/data/traefik/nukelab"</span> <span class="pi">}}</span>
<span class="na">serversTransport</span><span class="pi">:</span>
  <span class="na">insecureSkipVerify</span><span class="pi">:</span> <span class="kc">true</span>
<span class="na">entryPoints</span><span class="pi">:</span>
  <span class="na">web</span><span class="pi">:</span>
    <span class="na">address</span><span class="pi">:</span> <span class="s2">"</span><span class="s">:80"</span>
  <span class="na">websecure</span><span class="pi">:</span>
    <span class="na">address</span><span class="pi">:</span> <span class="s2">"</span><span class="s">:443"</span>
<span class="na">api</span><span class="pi">:</span>
  <span class="na">dashboard</span><span class="pi">:</span> <span class="kc">true</span>
  <span class="na">insecure</span><span class="pi">:</span> <span class="kc">false</span>
  <span class="na">debug</span><span class="pi">:</span> <span class="kc">true</span>
<span class="na">ping</span><span class="pi">:</span> <span class="pi">{}</span>
<span class="na">accessLog</span><span class="pi">:</span> <span class="pi">{}</span>
<span class="na">log</span><span class="pi">:</span>
  <span class="na">level</span><span class="pi">:</span> <span class="s">DEBUG</span>
<span class="na">providers</span><span class="pi">:</span>
  <span class="na">providersThrottleDuration</span><span class="pi">:</span> <span class="s">15s</span>
  <span class="na">file</span><span class="pi">:</span>
    <span class="na">watch</span><span class="pi">:</span> <span class="kc">true</span>
    <span class="na">directory</span><span class="pi">:</span> <span class="s2">"</span><span class="s">/local"</span>
  <span class="na">consulCatalog</span><span class="pi">:</span>
    <span class="na">endpoint</span><span class="pi">:</span>
      <span class="na">scheme</span><span class="pi">:</span> <span class="s">https</span>
      <span class="na">address</span><span class="pi">:</span> <span class="s2">"</span><span class="s">powernuke.nukelab.home:8501"</span>
      <span class="na">datacenter</span><span class="pi">:</span> <span class="s">nukelab</span>
      <span class="na">token</span><span class="pi">:</span> <span class="pi">{{</span> <span class="nv">.Data.data.consultoken | toJSON</span><span class="pi">}}</span>
      <span class="na">tls</span><span class="pi">:</span>
        <span class="na">ca</span><span class="pi">:</span> <span class="s">/local/ca.crt</span>
        <span class="na">cert</span><span class="pi">:</span> <span class="s">/local/traefik.crt</span>
        <span class="na">key</span><span class="pi">:</span> <span class="s">/local/traefik.key</span>
        <span class="na">insecureSkipVerify</span><span class="pi">:</span> <span class="kc">true</span> 
    <span class="na">cache</span><span class="pi">:</span> <span class="kc">false</span>
    <span class="na">prefix</span><span class="pi">:</span> <span class="s">traefik</span>
    <span class="na">connectAware</span><span class="pi">:</span> <span class="kc">true</span>
    <span class="na">exposedByDefault</span><span class="pi">:</span> <span class="kc">false</span>
    <span class="na">watch</span><span class="pi">:</span> <span class="kc">true</span>
<span class="pi">{{</span> <span class="nv">end</span> <span class="pi">}}</span>
<span class="s">EOH</span>

       <span class="s">destination = "local/traefik.yml"</span>
       <span class="s">change_mode = "noop"</span>
 
</pre></td></tr></tbody></table></code></pre></figure>

<p>Template w tym wypadku ściąga nam token Consula, dlatego występuje. Jadąc od góry:</p>
<ol>
  <li>Definicja <code class="language-plaintext highlighter-rouge">entrypointów</code> - porty dla HTTP i HTTPS,</li>
  <li>Dostępność dashboardu Traefika po HTTPS,</li>
  <li>Ustawienie poziomu logowania zdarzeń na <code class="language-plaintext highlighter-rouge">DEBUG</code>,</li>
  <li>Najważniejsza rzecz: <code class="language-plaintext highlighter-rouge">providers</code> - <code class="language-plaintext highlighter-rouge">file</code> i tutaj Traefik dynamicznie obserwuje katalog <code class="language-plaintext highlighter-rouge">/local</code> oraz..</li>
  <li><code class="language-plaintext highlighter-rouge">consulCatalog</code> - gdzie definiujemy rodzaj komunikacji HTTP/HTTPS (w naszym przypadku), URL Consula, token i ścieżki do certyfikatów TLS - Consul sprawdza poprawność certyfikatów klienta. <strong>Ważne</strong> by mieć definicję <code class="language-plaintext highlighter-rouge">providers file</code> bez tego nie zadziała nam HTTPS, po prostu Traefik nie będzie wiedział, gdzie ma pliki z certami,</li>
  <li>Na koniec <code class="language-plaintext highlighter-rouge">prefix</code> czyli tag jaki musi mieć każda usługa zdeployowana w Nomadzie, którą chcemy udostępnić przez Traefika, <code class="language-plaintext highlighter-rouge">connectAware</code> czy obsługiwać Consul Connecta (jeszcze nie do końca wiem co robi, ale nie przeszkadza), i <code class="language-plaintext highlighter-rouge">exposedByDefault</code> ustawiony na false - czyli nie chcemy aby Consul udostępniał Traefikowi wszystkiego co ma, a tylko te usługi otagowane “traefik”.</li>
</ol>

<p>Poniżej pliki konfiguracyjne deploymentu Traefika widziane przez konsolę Nomada:</p>

<p><img src="/assets/images/traefik-config.png" alt="Traefik Nomad" class="align-center" /></p>

<h4 id="zmiany-w-aplikacji">Zmiany w aplikacji</h4>

<p>W <a href="https://github.com/dominikmi/hashistuff-homelab/blob/master/nomad/jobs/app.nomad">nomadowym pliku aplikacji</a> zmieniło się tylko to, że doszły tagi w definicji usługi (<code class="language-plaintext highlighter-rouge">service</code> stanza) - wiersz 63:</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="code"><pre> 
      <span class="s">tags = [</span>
        <span class="s">"traefik",</span>
        <span class="s">"traefik.enable=true",</span>
        <span class="s">"traefik.http.routers.myapp.rule=Host(`myapp.nukelab.home`)",</span>
        <span class="s">"traefik.http.routers.myapp.tls=true",</span>
      <span class="s">]</span>
 
</pre></td></tr></tbody></table></code></pre></figure>

<p>Tak jak przy objaśnieniu konfiguracji samego Traefika, znaczą one tyle:</p>
<ol>
  <li>Usługa udostępniana Traefikowi przez Consula,</li>
  <li>Ruter <code class="language-plaintext highlighter-rouge">myapp</code> ma obsługiwać tylko zapytania dla <code class="language-plaintext highlighter-rouge">myapp.nukelab.home</code></li>
  <li>i tylko poprzez <code class="language-plaintext highlighter-rouge">entrypoint</code> HTTPS</li>
</ol>

<p>Jest też testowa usługa <a href="https://github.com/dominikmi/hashistuff-homelab/blob/master/nomad/jobs/whoami.nomad"><code class="language-plaintext highlighter-rouge">whoami</code></a></p>

<h4 id="jak-to-wygląda">Jak to wygląda?</h4>

<ul>
  <li>Dashboard:</li>
</ul>

<p><img src="/assets/images/traefik1.png" alt="Traefik Dashboard" class="align-center" /></p>

<ul>
  <li>Informacja o ruterze dla <code class="language-plaintext highlighter-rouge">myapp</code>:</li>
</ul>

<p><img src="/assets/images/traefik2.png" alt="Traefik Dashboard" class="align-center" /></p>

<ul>
  <li>Informacja o serwisie <code class="language-plaintext highlighter-rouge">myapp</code>:</li>
</ul>

<p><img src="/assets/images/traefik3.png" alt="Traefik Dashboard" class="align-center" /></p>

<ul>
  <li>Tagi aplikacji w Consulu:</li>
</ul>

<p><img src="/assets/images/myapp-tags.png" alt="Tags in Consul" class="align-center" /></p>

<ul>
  <li>Obsługa aplikacji przez HTTPS:</li>
</ul>

<p><img src="/assets/images/myapp-https.png" alt="Aplikacja" class="align-center" /></p>

<h4 id="podsumowanie">Podsumowanie</h4>

<p>Mamy rev-proxy, który zapewnia nam bezpieczeństwo serwisów uruchomionych na naszej platformie. A także, jakiś elementarny porządek. Oczywiście, w środowisku produkcyjnym, <em>wildcard cert</em> nie jest mile widziany i naprawdę nie wypada w dzisiejszych czasach nie mieć PKI czy jakiegoś Sectigo dla certów. Prócz tego, w Traefiku możemy też skonfigurować integrację z <a href="https://www.crowdsec.net/blog/crowdsec-community-report">CrowdSec</a> czy plugin <a href="https://plugins.traefik.io/plugins/6294728cffc0cd18356a97c2/souin#project-description">Souin</a> (HTTP cache z ciekawymi opcjami). Przedstawioną wyżej konfigurację można zrealizować pewnie bardziej elegancko, bądź w ogóle inaczej - sekrety wstawić do <em>GitHub Secrets</em> pliki konfiguracyjne osobno i tylko templetami wciągać do <em>jobspecs</em>, a dla pełnej dynamiki katalog <code class="language-plaintext highlighter-rouge">/local</code> po prostu podmontować jako <em>host mounted</em> catalog w tasku (też <em>jobspecs</em>).</p>

<h4 id="co-jeszcze">Co jeszcze?</h4>

<p>Warto byłoby zabezpieczyć same serwisy w Consulu, stosując <em>Consul intentions</em> i <em>Consul Connect</em> - czyli takie reguły firewallowe regulujące ruch do/z usługi i poprzez CC - poufność tego ruchu stosując side-car proxy Envoy i TLS.</p>

<p>I tym sposobem dotarliśmy do końca pod-ścieżki rozwoju i zabezpieczania naszych <em>deploymentów</em>. Kolejne wpisy powrócą do głównego zadania jakim jest automatyzacja i testy w moim domowym CI/CD wykorzystując GH Actions. I w ramach powrotu do głównego wątku, zajmę się testowaniem różnych rozwiązań SAST/SCA. No, i oczywiście automatyka samego deploymentu.</p>

<h4 id="użyteczne-sznurki">Użyteczne sznurki:</h4>
<ul>
  <li><a href="https://doc.traefik.io/traefik/providers/nomad/">Traefik service discovery in Nomad</a></li>
  <li><a href="https://www.youtube.com/watch?v=PqkFvIXFxZc">Integracja Traefika z Vaultem</a></li>
  <li><a href="https://traefik.io/blog/traefik-2-tls-101-23b4fbee81f1/?hss_channel=lcp-17887475">Traefik + TLS</a></li>
  <li><a href="https://www.youtube.com/watch?v=7VtZEZAi6qU">Network Automation with Hashicorp and Traefik - <em>use cases</em></a></li>
  <li><a href="https://github.com/traefik-tech-blog/hashicorp-webinar">Pomoce naukowe do w/w webinara</a></li>
</ul>]]></content><author><name>Dominik Miklaszewski</name></author><category term="DevSecOps" /><category term="devops" /><category term="bezpieczeństwo" /><category term="hashicorp" /><category term="nomad" /><category term="vault" /><category term="traefik" /><summary type="html"><![CDATA[Po wstępnych dywagacjach w poprzednim wpisie, pora podkasać rękawy i zabrać się do pracy. Naszymi dramatis personae będą stali bywalcy: Nomad, Consul i Vaulta oraz Traefik. Plan jest taki, aby mieć jeden ingress point (Traefik) dla mojej mikro-chmurki prywatnej, który nie tylko automatycznie wyniucha co w serwisach piszczy, a również automatycznie je obsłuży wraz z TLS.]]></summary></entry><entry><title type="html">Wstęp do wysokiej dostępności</title><link href="http://localhost:4000/devsecops/2022-08-30-wysokadostepnosc/" rel="alternate" type="text/html" title="Wstęp do wysokiej dostępności" /><published>2022-08-30T00:00:00+02:00</published><updated>2022-08-30T00:00:00+02:00</updated><id>http://localhost:4000/devsecops/wysokadostepnosc</id><content type="html" xml:base="http://localhost:4000/devsecops/2022-08-30-wysokadostepnosc/"><![CDATA[<p>Ostatnie dwa odcinki mojej domowej devopsowej epopei doprowadziły mnie do fajnie działającego deploymentu aplikacji z bazą danych. Uwzględniając zabezpieczenie wszelkich danych uwierzytelniających (kredek) - tak jak to powinno wyglądać, co do zasady. Ale to nie wszystko..</p>

<h3 id="usprawnienia-deploymentu">Usprawnienia deploymentu</h3>

<p>W międzyczasie, usprawniłem i poprawiłem <em>specs file</em> dla deploymentu aplikacji z MongoDB, tak aby temat był ogarnięty w jednym <em>jobie</em> a aplikacja została uruchomiona wtedy i tylko wtedy kiedy stan zadania deploymentu MongoDB jest <em>healthy</em>. Ta technika w świecie Nomada nazywa się <em>prestart side-car</em>. I MongoDB jest takim <em>side-carem</em>.</p>

<p><img src="/assets/images/depl-zal.png" alt="Deployment zależny aplikacji" class="align-center" /></p>

<p>Jak widzimy, <em>side-car</em> może być uruchomiony jako:</p>

<ul>
  <li>prestart - zanim uruchomimy co innego, ten musi działać, bez niego nic dalej nie pójdzie,</li>
  <li>poststart - wjeżdża po tym jak główne zadanie - aplikacja - zostanie uruchomiona,</li>
  <li>poststop - uruchomione po tym jak główne zadanie przestaje działać, np. jakieś zadanie “sprzątające”</li>
</ul>

<p>A <a href="https://github.com/dominikmi/hashistuff-homelab/blob/master/nomad/jobs/app.nomad">tu jest</a> <em>specs file</em>. Prócz skonsolidowania dwóch odrębnych deploymentów w jednej grupie jako dwa zadania; to pierwsze - dla Mongo ma dodatkowy wpis:</p>

<figure class="highlight"><pre><code class="language-html" data-lang="html"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="code"><pre> 
lifecycle {
        sidecar = true
        hook = "prestart"
      }
 
</pre></td></tr></tbody></table></code></pre></figure>

<p>Bez tej zmiany, oba zadania nadal są zależne od siebie, ale bardziej jako towarzysze niedoli - jeśli którekolwiek z zadań jest <em>unhealthy</em> i koniec końców umiera (status: <em>dead</em>), to nomadowy <em>scheduler</em> widząc to, solidarnie uśmierca też to już działające. Co oczywiście zabiera mu trochę czasu, generuje logi, komunikaty i trochę komplikuje <em>troubleshooting</em>. Stąd wynikło ulepszenie a to przy kolejnym zadaniu związanym z dostępnością ułatwi zabawę. Dlatego tak zdefiniowany deployment będzie bazą do dalszych rozważań i usprawnień. Na przykład do tego, by rozbić oba serwisy na dwie grupy, tak by można było utworzyć kilka instancji aplikacji (a niekoniecznie już MongoDB).</p>

<h3 id="co-z-tą-dostępnością">Co z tą dostępnością?</h3>

<p>Kolejnym etapem dbania o bezpieczeństwo aplikacji jest zapewnienie jej dostępności (jeden z wymiarów bezpieczeństwa). Wypadałoby postawić jakiejś reverse-proxy, na podstawie nagłówków sprawdzić czy zapytanie jest Ok, przekierować (ang. <em>upstream</em>) do właściwego serwisu, trzymać sesję itd. Generalnie są trzy scenariusze, jak można to zrobić:</p>
<ul>
  <li>albo wykorzystując jeden główny rev-proxy dla całego klastra Nomadowego z auto-skalowaniem (w ramach pojedynczego data-center, ang. <em>ingress</em>),</li>
  <li>albo wykorzystując technikę <em>side-car</em> i dać aplikacji jej własnego i osobistego rev-proxy w ramach pojedynczego deploymentu,</li>
  <li>albo ulepszony scenariusz pierwszy - tzn. na każdym serwerowym węźle lokalnego klastra Nomadowego postawić rev-proxy, ustawić round-robin w DNSie dla pojedynczej nazwy klastra i ustawić rozproszony load-balancing temu proxy, poprzez konfigurację Consula.</li>
</ul>

<p>Jak wszystko, te podejścia mają swoje wady i zalety. Przy pierwszym podejściu - “wiadomix” - pojedynczy punkt awarii, jak nam ten <em>ingress</em> siądzie to kicha z dostępem do wszystkiego co tenże proxy obsługuje. Z drugiej strony jeden proxy, jeden <em>payload</em>, jeden proces/kontener. Łatwo się kontroluje, mało zasobów zżera Nomadowi <em>et consortes</em> (+Consul i Vault). Drugie podejście ma odwrotnie - jak się popsuje, to nie działa jedna appka z wielu, ale też tych proxiaków możemy mieć poupychane w klastrze na tyle ile mamy tych aplikacji. A zatem, to rozwiązanie nadaje się na np. deployment pojedynczego autonomicznego klastra z aplikacją w AWSie. Najfajniej byłoby mieć to trzecie rozwiązanie - czyli stoją sobie proxy na każdym węźle, user robi deployment na adres Nomada, np. nomad.mojadomena.com, robi się aktualizacja konfiguracji proxy o nowy <em>upstream</em> e voila!. To ostatnie podejście jest najbardziej skomplikowaną sprawą do konfiguracji. Celowo tutaj nie wspominam jeszcze o obsłudze certyfikatów, żeby nie komplikować. Po kolei.</p>

<p>Nomad z Consulem, czyli po polsku ;) - orkiestracja z <em>mesh-em</em> (sic!) są w stanie obsłużyć wszystkie te podejścia z wykorzystaniem najpopularniejszych proxy: NGINX, HA-Proxy, traefik i envoy. Przy czym ten ostatni jest w pełni zintegrowany w taki sposób, że wystarczy “hokus-pokus” (z wykorzystaniem <em>Consul Connect</em>) w <em>specs file-u</em> nomadowym i mamy serwis obługiwany poprzez envoya w ramach pierwszego scenariusza. Natomiast ja wolałbym się zająć, już tradycyjnie komplikując sobie, tym ostatnim scenariuszem. Przypomina to taki systemowy <em>ingress</em> kubernetesowy (np. NGINX), gdzie wszystko definiuje nam <em>helm chart</em> i odpowiednie <em>annotations</em>,a my tylko robimy deployment i hyc! ..pod wskazanym URI odnajdujemy nasz serwis.</p>

<p>Poniżej diagramy przedstawiające poglądowo scenariusz, który chcę zrealizować i opisać w kolejnej części - a będzie tego sporo: grzebanie w konfiguracji Nomada i Consula, poprawnie zestawione TLSy między komponentami klastra, integracja, <em>consul-template</em>, przykładowe problemy do troubleshootingu, przykłady spraw niejasno opisanych w dokumentacji, konfiguracja proxy i na koniec certyfikaty. Fajnie byłoby mieć gotowe PKI na Vaulcie, ale to na razie w planach, jako wisienka na torcie.</p>

<p>Diagram logiki rozwiązania - Nomad odpali kontenery z proxy i z instancjami aplikacji na <em>runtime</em> dokerowym
<img src="/assets/images/revproxy-nomad.png" alt="Logika aplikacji" class="align-center" /></p>

<p>Diagram logiki sieciowej (usługi Consula) - usługi przy deploymencie zostaną zarejestrowane w Consulu przez Nomada z informacjami o IP, portach, <em>healthcheck-ach</em>, a proxy przy pomocy <em>Consul template</em> zaktualizuje swoją konfigurację. 
<img src="/assets/images/revproxy-consul.png" alt="Logika usług" class="align-center" /></p>

<h4 id="użyteczne-sznurki">Użyteczne sznurki:</h4>
<ul>
  <li><a href="https://www.hashicorp.com/blog/configuring-third-party-loadbalancers-with-consul-nginx-haproxy-f5">Integracja Consula z proxy</a></li>
  <li><a href="https://learn.hashicorp.com/tutorials/nomad/load-balancing-nginx">Tutka dla Nomad+NGINX (i inne)</a></li>
  <li><a href="https://learn.hashicorp.com/tutorials/consul/load-balancing-nginx?in=consul/load-balancing">Tutka dla Consul+NGINX (i inne)</a></li>
  <li><a href="https://learn.hashicorp.com/tutorials/consul/load-balancing-envoy?in=consul/service-mesh-traffic-management">LB w <em>meshu</em> Consula +zintegrowany Envoy</a></li>
  <li><a href="https://www.nomadproject.io/docs/job-specification/lifecycle">Deployment zależny w Nomadzie <em>lifecycle</em></a></li>
  <li><a href="https://www.nomadproject.io/docs/configuration/consul">Nomad + Consul TLS + kontrola dostępu</a></li>
  <li><a href="https://www.consul.io/docs/connect/proxies/envoy">Consul Connect + envoy</a></li>
</ul>]]></content><author><name>Dominik Miklaszewski</name></author><category term="DevSecOps" /><category term="devops" /><category term="bezpieczeństwo" /><category term="hashicorp" /><category term="nomad" /><category term="vault" /><summary type="html"><![CDATA[Ostatnie dwa odcinki mojej domowej devopsowej epopei doprowadziły mnie do fajnie działającego deploymentu aplikacji z bazą danych. Uwzględniając zabezpieczenie wszelkich danych uwierzytelniających (kredek) - tak jak to powinno wyglądać, co do zasady. Ale to nie wszystko..]]></summary></entry><entry><title type="html">Closing thoughts on DevOps</title><link href="http://localhost:4000/devops/2022-08-22-thoughtsondevops/" rel="alternate" type="text/html" title="Closing thoughts on DevOps" /><published>2022-08-22T00:00:00+02:00</published><updated>2022-08-22T00:00:00+02:00</updated><id>http://localhost:4000/devops/thoughtsondevops</id><content type="html" xml:base="http://localhost:4000/devops/2022-08-22-thoughtsondevops/"><![CDATA[<p>Throughout my short DevOps team member and manager career I used to take notes. All in English. Hence this post in English. It outlines some thoughts and experience gathered along the line confronted with hints and knowledge taken from good readings I studied. The readings are listed at the end of this post.</p>

<h1 id="thoughts-on-some-devops-improvement-tactics">Thoughts on some DevOps improvement tactics</h1>

<p>A migration is often a painful process. Especially migrating some old applications along with the delivery process around them to a new platform featuring all those modern bells and whistles (like AWS/Azure driven Kubernetes or HashiCloud Platform etc.). And it does not only convey switching the technology from A to B (or lift-shift), nor only focuses on writing a new set of procedures (playbooks) on how to do things on Platform B - either. It actually requires a thorough refinement of the SDLC process, which all teams engaged and involved in this process should follow in their daily work to benefit from the new platform and make the migration meaningful and cost controlled.</p>

<h4 id="a-bit-of-background---a-very-common-story">A bit of background - a very common story</h4>

<p>Imagine that once, the whole software delivery was a model of four teams - the architects, the development team, the Q/A team and the maintenance team. Each managed independently. Of course there were also some business analysts who were shovelling the requirements to the architects. They all followed a sequence of hand-offs to get the apps to production. However, there was no automation in place. The architects were making nice looking drawings in PPT, discussing some non-functional requirements with (totally separate, living on another planet) security dudes and that was it. The PPT diagrams were then handed over to the development team, who led by a PM and business relation analysts were taking care for functionalities to be cranked out of it. The developers were working on their own separate branches (multi-branch approach). So, once a specific functionality was announced done, it took a while merging those branches to the main one. Then Q/A was taking care of testing for weeks.. or months. When they were happy a release took place and if it worked bottles of champagne could be put to an ice bucket and celebration of a release could start. After those (rare) moments of sheer joy, the app was then handed off to the maintenance team, who were to support the app, handle issues reported by the users. Yet, they had no idea how the app was designed, developed and tested. They operated through playbooks.</p>

<p>Unfortunately, while the process looked orderly and coherent, it had certain flaws due to siloed teams and the way the communication across the SDLC process functioned. Even though, the Agile methodology was introduced, it had very limited impact on efficiency and productivity, mainly because only the development team alone followed the agile principles with all those sprints and stand-ups. While the maintenance followed through tickets in mainly a reactive mode. That all being said, resulted in perception of the reality around a given app - it was either in project-driven state (new functionalities were developed and old bugs being remediated) or in a business as usual mode where only maintenance teams were actively operating around the app, while the other teams already jumped out to another projects. Neither reality fit the Continuous Delivery model.</p>

<h4 id="what-was-wrong-in-this-picture">What was wrong in this picture?</h4>

<ol>
  <li>The architects position was so low in the process, that once their job was done and the design was handed off to the developers, they were never given any feedback and never had any say (as not being involved) in subsequent sprints onward regarding new functionalities, design flaws, enhancements etc.</li>
  <li>The only pattern the architects followed was a PPT pattern. There we no design pattern and components were largely determined by the business expectations and business analysts advocacy towards certain technology stacks.</li>
  <li>The developers considered diamonds in the crown, were essentially cranking out new features, not quite caring of the code quality, tech stacks, libraries, security nor were they using any advanced tooling beyond their IDE on laptops (Visual Studio Code).</li>
  <li>The Q/A were testing things manually, which lasted weeks and months.</li>
  <li>The maintenance team, disconnected from the design and development phases were handed off just a piece of software being told what to do (scope limited to the playbooks they were given).</li>
  <li>There was a separate ticketing system for developers and maintenance teams.</li>
  <li>No continuous delivery was actually happening, although Jenkins and Docker orchestration with some monitoring was in place.</li>
</ol>

<p>So, did the Jenkins pipelines, some basic Docker orchestration, Agile development and some monitoring establish any DevOps practise?
The answer is: No. Not at all. It’s just a good starting point.</p>

<p>Why? A common platform was indeed in place (the Docker orchestration) where the apps landed, built and pushed through Jenkins pipelines. Yes, but all profound principles of DevOps were still missing, undermining the whole effort. Some tech/Linux savvy architects had built the platform and set up the pipelines. Hence, effectively all the components were commodity operated on daily basis without any thoughts on improvements going forward.</p>

<h1 id="symptoms-gaps">Symptoms, gaps</h1>

<p>The “DevOps” is just a term, a buzzword. Can be meaningful or meaningless. Like Agile, if best practises are not followed within the entire delivery process setup, it’s miserably falling into old days Ford’s assembly line model - <em>I did my part, now you do yours, bye</em>. Regardless how agile a given organisation looks on a powerpoint presentation, these below are signs (or metrics) that Agile in the core development phase of the SDLC does not bring expected benefits:</p>

<ul>
  <li>the organisation is unable to act quickly upon critical bugs or vulnerability found,</li>
  <li>the organisation is unable to provide frequent successful deployments on daily basis,</li>
  <li>the organisation is unable to act frequently, so that delta between changes is kept low, hence less risky for any potential piled up faults,</li>
  <li>the deployment process is not transparent to the user community (downtime announcements),</li>
  <li>the organisation is unable to bring and test new feature within 24h after a standup.</li>
</ul>

<h4 id="gaps">Gaps</h4>

<p>The above listed are symptoms. And usually what it means is that there are certain (and very common) gaps at the operational and technical layers of any given DevOps to-be-driven environment:</p>

<ul>
  <li>designs of the apps handled through the DevOps driven process are either outdated, non existent, or not consulted and agreed with the relevant IT architects (proper design and changes to the design must be a prerequisite for any further proceeding across the CI/CD),</li>
  <li>no automated tests in place - unit tests, acceptance tests, integration tests, security tests, smoke tests; or at least their coverage is not satisfactory,</li>
  <li>teams do not truly works together (there’s no real devops philosophy in place of a shared responsibility),</li>
  <li>long technical debt tail, long time spans between updates and upgrades (if any were in place),</li>
  <li>outdated documentation,</li>
</ul>

<h4 id="real-cases">Real cases</h4>

<p>Let’s see what it all means in real world:</p>

<ul>
  <li><strong>Case 1</strong>: At any given moment the organisation is unable to provide a successful production deployment, due to outstanding vulnerabilities in the codebase, which makes one of the quality (security) gate blocking the release, effectively stalling the deployment.</li>
  <li><strong>Case 2</strong>: At any given moment, the organisation is unable to patch up and test a deployment, because there are no automated post-commit checks which would catch all outstanding discrepancies and overall quality posture.</li>
  <li><strong>Case 3</strong>: Faulty deployments are handled ex-post, upon users community feedback; yet without any traces back in the CI/CD steps. And it requires hours to troubleshoot efforts to find a root cause.</li>
  <li><strong>Case 4</strong>: Developers and/or Q/A manually test things with no quick feedback featured across the delivery process.</li>
</ul>

<p>Also, often teams although empowered by visibility and controls do not feel responsibility for the design, quality, testing and deployment. They do not feel it’s a loop and that any single prod deployment does not ends their mission.</p>

<h4 id="some-improvements">Some improvements</h4>

<p>To engage with the DevOps philosophy and gradually improve the awareness and fluency across the teams, a dedicated and devoted DevOps team had to start from themselves. Let’s see it as a DevOps <em>center of excellence</em> at its very infancy. The team’s main purpose was:</p>

<ul>
  <li>be <strong>a platform (or XaaS) team</strong> - helps the other teams to overcome obstacles while working towards successful (more secure, quicker, more stable) software delivery. Also, they were up to detect and identify flaws, deficiencies and missing capabilities. And to promote enhancements and mindset shifts across the table. The main way of communication in this role is : component as a service (Jenkins, Docker orchestration, Vault etc.) offered to other teams use, for all components of the platform.</li>
  <li>be <strong>the Enablers</strong> - beyond what they were up to wearing the “Platform” hat, they provided a steady and upward learning curve delivering new/improved components, techniques and products to accelerate the delivery process. Showcasing them and explaining profits to the other teams.</li>
</ul>

<p>Given these two major roles of the DevOps team, they managed to establish some specific way of communication. Which also had positive impact on outlooks for successful development of DevOps practices. There were four main ways of communication they were trying to establish (really, everyone talking to everyone is not the best idea):</p>

<ul>
  <li><strong>Collaboration</strong>, based on daily routines, pieces of sprints, and other tasks - the DevOps team collaborates on daily basis using available instant communication means like Zoom, Teams, Slack channels, virtual boards. It’s important to record these efforts through JIRA or the likes.</li>
  <li><strong>X-as-a-service</strong>, this model of communication is largely streamlined through dedicated ticketing system board to measure overall improvements and enhancements of the whole DevOps eco-systems put together encompassing a bunch of components/services.</li>
  <li><strong>Facilitators</strong>, advocating, helping, showcasing the other teams the enhancements, introduced principles (like semantic versioning, docker image optimisation techniques), good practises and methodologies (like Trunk Based Development) or changes impacting daily routines of the other teams. Zoom &amp; Teams.</li>
  <li><strong>Writers</strong>, finding a common platform to host all necessary documentation, tagged and sorted out appropriately, supported by a search engine. So, that during any talks, discussions any good pattern to follow, any how-to write-up, design or tech structure can be found, referred to and understood broadly. A platform like Wiki would be helpful. Sharepoint is awful for this kind of stuff, but doable.</li>
</ul>

<h1 id="considerations-before-taking-on-a-migration">Considerations before taking on a migration</h1>

<h4 id="the-apps">The apps</h4>

<p>Usually there are variety of flavours and designs of the running apps across the organisation. Their architecture, technology differ from one another. Nevertheless, let’s group them up and later, propose a possible approach to tackle issues and problems associated with the modernisation and migration to the “new default” given the app’s tech stack.</p>

<p><strong>Windows Realm</strong>:</p>

<ul>
  <li>Apps developed in .Net, codebase stored in TFS, built locally on a programmer’s machine &amp; IDE. Deployed straight to non-prod/prod WinTel server with IIS. Largely a monolithic design, lack of proper documentation. Some integration with back-end MS SQL or other MS oriented services. The apps often suffer from lack of proper documentation, standardised security controls, no automated testing, and no secure coding practises. Deployments take place occasionally with many teams around engaged to address issues.</li>
</ul>

<p><strong>Open Source Realm</strong>:</p>

<ul>
  <li>Apps developed using open source stacks like popular Node.JS+Angular, and the likes. Some already docker-ized run on Docker swarm or just on a docker runtime. Most of those IDE environment consist of defined Jenkins pipelines, however there’s no clear evidence on using only declarative pipelines, some of them are outdated and or not properly maintained. Often these apps are integrated with other Open Source components like MongoDB, PostgreSQL, Kafka etc. The codebase is stored at some git driven repositories (Gitlab, BitBucket, TFS). Active development is in place, hence the support is somewhat more reliable in terms of updates, testing and upgrades. None the less, automated testing is scarce, and no coherent best practises in coding or security is in place (secrets are stored in Jenkins, git repository, local programmer’s computer etc.).</li>
</ul>

<p>One of the most prevailing advantages of moving into containerisation in Cloud is leveraging and driving modern and secure design, programming (TDD, TBD, DDD)  and continuous delivery approaches. Ensuring appropriate security are in place in both CI/CD pipelines and the developed product (security by design).</p>

<h4 id="strategies">Strategies</h4>

<p>Inspired by some white papers regarding moving-to-the-cloud strategies, some standardised approaches can be proposed given current apps states. They may be considered as a general effort for the modernisation. The 6Rs. Collectively known as the “6R of migration”. Actually some of them can be applicable in most cases:</p>

<ul>
  <li><strong>Retiring</strong>, you just plan decommissioning of the app and designing a new one, from scratch. Also functionality can be replaced by some other app/component(s) being already migrated or augmented by an off-the-shelf purchase to the existing IT eco-system.</li>
  <li><strong>Retaining</strong>, you keep the thing as is for determined time span. As a given app is going to be replaced or phased out altogether (by “retiring” approach in future). Associated costs of maintenance as well as other apps or components this retained app integrates with, will also remain.</li>
  <li><strong>Re-hosting</strong>, known as “lift and shift” approach, where you move the app as is to the new environment and new platform. Apps with long tail of technical debt will likely incur costs of maintenance to cut the tail and conform to new S-SDLC (quality and security gates).</li>
  <li><strong>Refactoring</strong> - and re-architecting if the scope requires some profoundly deep changes of the applications. This approach is usually driven by a need to add features, make the app passable through the new platform quality gates, security checks, automated testing - all leveraging much quicker and transparent deployments and scalability. Refactoring often boosts agility, business continuity, shifted-left security and overall productivity through better collaboration across the teams. However, this strategy tend to be most time and resource consuming.</li>
</ul>

<h4 id="best-practises">Best Practises</h4>

<p>These best practises should be tirelessly propagated and advertised by the DevOps team. However, a successful migration can be achieved (where a given timeframe and assessed costs are met) through the IT organisation capabilities, which should be capable to ensure the following best practises:</p>

<p><strong>Architecture</strong>:</p>
<ul>
  <li>Precise design using modern methodologies (DDD),</li>
  <li>Precise and standardised design covering all functional and non-functional requirements, clear components lists, specified tech stack (compliant with approved tech stack components), concise and clear diagrams, threat model, identified risks and agreed trade-offs,</li>
  <li>Standardised building components and valid sources (delivery chain security),</li>
  <li>Statelessness of the app components - meaning that the app’s state is not retained within the app, and can be easily scaled out through orchestration platform,</li>
</ul>

<p><strong>Programming</strong>:</p>
<ul>
  <li>Trunk Based Development (or further monorepo),</li>
  <li>Test Driven Development,</li>
  <li>At least two programmers in the project, to facilitate such good practises like “code review”, “secure branching”, etc.</li>
  <li>Semantic versioning,</li>
  <li>Standardised building components and IDE integrated with modern secure development components like GitGuardian, Snyk, (hashicorp vault), Docker Desktop, other SAST/SCA/DAST scanners, Sonarqube,</li>
</ul>

<p><strong>CI/CD</strong>:</p>
<ul>
  <li>full automation of all repeatable steps,</li>
  <li>quick and comprehensive feedback from the automated components,</li>
  <li>unit tests, acceptance tests, integration tests, security tests, capacity/load/smoke tests etc.</li>
  <li>Capacity and scalability testing under various load (require business metrics the Kubernetes will act on),</li>
  <li>Frequent small, atomic changes to the codebase (to keep delta at manageable level and make the code reviewers life easier, not to let them review hundreds line of code changes across multiple files),</li>
  <li>Deployments fully transparent to the user communities.</li>
</ul>

<p><strong>Clear communication channels for the DevOps team</strong>:</p>
<ul>
  <li>Team - enablers/collaborators - working together as time allows to discover and experiment with new solutions, fixes, ideas through technologies, APIs, methodologies, etc.</li>
  <li>Team - Platform/XaaS - provide support for multiple SDLC components - like Jenkins, GitHub, SonarQube, Nomad, Vault, etc.</li>
  <li>Team - Facilitating/performing - showcasing, coaching, lending hand to other teams about changes, shifts, best practises, gradual improvements.</li>
</ul>

<p><strong>Organisational capabilities</strong>:</p>
<ul>
  <li>Less risk of cognitive overload, so the DevOps and other teams have comfort for deep working, learning and experimenting,</li>
  <li>Keeping the teams project agendas close to one another and the once formed teams are not getting disbanded too often, so that natural engagement in a project does not take long each time it starts (storming, norming, performing),</li>
  <li>Apps grouped up around business areas and business values, so the support teams is not required to spread their limited cognitive capacity across multiple exclusive (constantly evolving) tech stacks.</li>
</ul>

<p><img src="/assets/images/sdlc.jpg" alt="General SDLC" class="align-center" /></p>

<h3 id="further-good-readings">Further (good) readings</h3>

<ul>
  <li><a href="https://www.davefarley.net/">Dave Farley’s blog</a> should be read frequently, before you take a look at your morning news, get to Dave’s first,</li>
  <li><a href="https://www.goodreads.com/book/show/8686650-continuous-delivery">“Continuous Delivery” by Dave Farley and Jez Humble</a>, an absolute must read!</li>
  <li><a href="https://www.goodreads.com/book/show/35747076-accelerate">“Accelerate - the science of Lean Software and DevOps “ by Jez Humble, Nicole Forsgren and Gene Kim</a>, you should know at least two out of these three names from “the Unicorn Project”,</li>
  <li><a href="https://continuousdelivery.com/">“Continuous Delivery blog”</a>, principles and other varia by Jez Humble,</li>
  <li><a href="https://www.goodreads.com/book/show/44144499-monolith-to-microservices">“Monolith to Microservices” by Sam Newman</a>, if you want to understand and support an old app migration,</li>
</ul>]]></content><author><name>Dominik Miklaszewski</name></author><category term="DevOps" /><category term="devops" /><category term="english" /><summary type="html"><![CDATA[Throughout my short DevOps team member and manager career I used to take notes. All in English. Hence this post in English. It outlines some thoughts and experience gathered along the line confronted with hints and knowledge taken from good readings I studied. The readings are listed at the end of this post.]]></summary></entry></feed>