<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="cs">
  <title>🌹 ruza · writing</title>
  <subtitle>Personal hub - links, GPG key, presence across the interwebs and possibly some articles.</subtitle>
  <link href="https://ruza.eu/" rel="alternate" type="text/html"/>
  <link href="https://ruza.eu/feed.xml" rel="self" type="application/atom+xml"/>
  <id>https://ruza.eu/feed.xml</id>
  <updated>2026-05-11T00:00:00Z</updated>
  <author>
    <name>ruza</name>
    <uri>https://ruza.eu/</uri>
  </author>
  <generator uri="https://ruza.eu/">build.py</generator>
  <entry>
    <title>Windows - restart Voicemeeter po připojení Bluetooth zařízení</title>
    <link href="https://ruza.eu/articles/windows-restart-audio-on-bt-event.html" rel="alternate" type="text/html"/>
    <id>https://ruza.eu/articles/windows-restart-audio-on-bt-event.html</id>
    <published>2026-05-11T00:00:00Z</published>
    <updated>2026-05-11T00:00:00Z</updated>
    <summary>Automatický restart Voicemeeter po připojení Bluetooth zařízení</summary>
    <content type="html"><![CDATA[
<p>Tato stránka popisuje, jak na Windows 11 automaticky restartovat <strong>Voicemeeter Potato</strong> po připojení konkrétního Bluetooth zařízení.</p>
<blockquote>
<p><strong>Aktualizace (květen 2026):</strong> Původní postup z dubna 2026 přestal fungovat po Windows Update. Při ladění se ukázaly dvě příčiny — Windows Update tiše deaktivuje Bluetooth event log, a novější verze Voicemeeteru ignorují parametr <code>-r</code> způsobem, který původní skript předpokládal. Postup byl upraven tak, aby byl odolnější vůči oběma problémům. Změny jsou popsány v každém dotčeném kroku.</p>
</blockquote>
<h2 id="predpoklady">Předpoklady</h2>
<ul>
<li>Windows 11 s povoleným Bluetooth</li>
<li>Voicemeeter Potato nainstalovaný v <code>C:\Program Files (x86)\VB\Voicemeeter\</code></li>
<li>Povolený log <code>Microsoft-Windows-Bluetooth-Policy/Operational</code> v Event Vieweru</li>
</ul>
<h2 id="1-povoleni-bluetooth-event-logu">1. Povolení Bluetooth Event Logu</h2>
<p>Ve výchozím stavu je log vypnutý. Spusť <strong>PowerShell jako Administrator</strong>:</p>
<div class="code-block"><div class="code-block-header"><span class="code-block-lang">powershell</span><button type="button" class="code-block-copy" aria-label="copy code">[ copy ]</button></div><div class="codehilite"><pre><span></span><code><span class="n">wevtutil</span> <span class="nb">sl </span><span class="n">Microsoft-Windows-Bluetooth-Policy</span><span class="p">/</span><span class="n">Operational</span> <span class="p">/</span><span class="n">e</span><span class="p">:</span><span class="n">true</span>
</code></pre></div></div>

<p>Ověření:</p>
<div class="code-block"><div class="code-block-header"><span class="code-block-lang">powershell</span><button type="button" class="code-block-copy" aria-label="copy code">[ copy ]</button></div><div class="codehilite"><pre><span></span><code><span class="n">wevtutil</span> <span class="nb">gl </span><span class="s2">&quot;Microsoft-Windows-Bluetooth-Policy/Operational&quot;</span>
</code></pre></div></div>

<p>V výstupu hledej <code>enabled: true</code>.</p>
<h3 id="1a-zabraneni-opetovneho-vypnuti-logu-po-windows-update-novy-krok">1a. Zabránění opětovného vypnutí logu po Windows Update <em>(nový krok)</em></h3>
<p>Windows Update tento log po aktualizaci tiše deaktivuje — to byl hlavní důvod, proč původní postup přestal fungovat. Aby se to neopakovalo, vytvoř startup úlohu, která log při každém startu systému znovu zapne:</p>
<div class="code-block"><div class="code-block-header"><span class="code-block-lang">powershell</span><button type="button" class="code-block-copy" aria-label="copy code">[ copy ]</button></div><div class="codehilite"><pre><span></span><code><span class="nv">$action</span>    <span class="p">=</span> <span class="nb">New-ScheduledTaskAction</span> <span class="n">-Execute</span> <span class="s2">&quot;wevtutil.exe&quot;</span> <span class="n">-Argument</span> <span class="s1">&#39;sl &quot;Microsoft-Windows-Bluetooth-Policy/Operational&quot; /e:true&#39;</span>
<span class="nv">$trigger</span>   <span class="p">=</span> <span class="nb">New-ScheduledTaskTrigger</span> <span class="n">-AtStartup</span>
<span class="nv">$principal</span> <span class="p">=</span> <span class="nb">New-ScheduledTaskPrincipal</span> <span class="n">-UserId</span> <span class="s2">&quot;SYSTEM&quot;</span> <span class="n">-LogonType</span> <span class="n">ServiceAccount</span> <span class="n">-RunLevel</span> <span class="n">Highest</span>
<span class="nv">$settings</span>  <span class="p">=</span> <span class="nb">New-ScheduledTaskSettingsSet</span> <span class="n">-ExecutionTimeLimit</span> <span class="p">(</span><span class="nb">New-TimeSpan</span> <span class="n">-Minutes</span> <span class="n">1</span><span class="p">)</span>
<span class="nb">Register-ScheduledTask</span> <span class="n">-TaskName</span> <span class="s2">&quot;Enable-BT-Log&quot;</span> <span class="n">-Action</span> <span class="nv">$action</span> <span class="n">-Trigger</span> <span class="nv">$trigger</span> <span class="n">-Principal</span> <span class="nv">$principal</span> <span class="n">-Settings</span> <span class="nv">$settings</span> <span class="n">-Description</span> <span class="s2">&quot;Zapne Bluetooth event log po startu - potrebne pro automaticky restart Voicemeeteru&quot;</span>
</code></pre></div></div>

<p>Úloha se spustí jako SYSTEM s administrátorskými právy, takže nevyžaduje přihlášeného uživatele.</p>
<h2 id="2-zjisteni-adresy-bluetooth-zarizeni">2. Zjištění adresy Bluetooth zařízení</h2>
<p>Připoj cílové BT zařízení a spusť:</p>
<div class="code-block"><div class="code-block-header"><span class="code-block-lang">powershell</span><button type="button" class="code-block-copy" aria-label="copy code">[ copy ]</button></div><div class="codehilite"><pre><span></span><code><span class="nb">Get-WinEvent</span> <span class="n">-LogName</span> <span class="s2">&quot;Microsoft-Windows-Bluetooth-Policy/Operational&quot;</span> <span class="n">-MaxEvents</span> <span class="n">10</span> <span class="p">|</span>
  <span class="nb">Format-Table</span> <span class="n">TimeCreated</span><span class="p">,</span> <span class="n">Id</span><span class="p">,</span> <span class="n">Message</span> <span class="n">-Wrap</span>
</code></pre></div></div>

<p>Hledej řádek s <strong>Event ID 9</strong> (úspěšné připojení) a poznamenej si adresu zařízení (např. <code>A00CE238FE34</code>).</p>
<h2 id="3-vytvoreni-spousteciho-skriptu">3. Vytvoření spouštěcího skriptu</h2>
<p>Vytvoř soubor <code>C:\Scripts\voicemeeter-bt.bat</code>a ihned nastav správná oprávnění na složku — Task Scheduler skript spouští automaticky, takže zápis do <code>C:\Scripts\</code> by měl mít jen administrátor:</p>
<div class="code-block"><div class="code-block-header"><span class="code-block-lang">batch</span><button type="button" class="code-block-copy" aria-label="copy code">[ copy ]</button></div><div class="codehilite"><pre><span></span><code><span class="p">@</span><span class="k">echo</span> off
timeout /t 3 /nobreak <span class="p">&gt;</span>nul
taskkill /F /IM voicemeeter8x64.exe /T <span class="mi">2</span><span class="p">&gt;</span>nul
taskkill /F /IM VoicemeeterMacroButtons.exe /T <span class="mi">2</span><span class="p">&gt;</span>nul
timeout /t 1 /nobreak <span class="p">&gt;</span>nul
<span class="k">start</span> <span class="s2">&quot;&quot;</span> <span class="s2">&quot;C:\Program Files (x86)\VB\Voicemeeter\voicemeeter8x64.exe&quot;</span>
</code></pre></div></div>

<blockquote>
<p><strong>Změna oproti původnímu postupu:</strong> Původní skript spouštěl Voicemeeter s parametrem <code>-r</code>, který dříve fungoval správně — nová instance poslala existujícímu Voicemeeteru příkaz k restartu audio enginu a sama se ukončila. Někdy po dubnu 2026 toto chování přestalo fungovat (pravděpodobně aktualizací Voicemeeteru) — parametr <code>-r</code> nově otevírá druhou plnou instanci, která zůstane běžet. Skript proto nyní nejprve všechny instance ukončí (<code>taskkill</code>) a pak spustí čistou novou.</p>
</blockquote>
<p><code>timeout /t 3</code> na začátku dává BT zařízení čas se plně inicializovat před tím, než Voicemeeter nastartuje.</p>
<p>Nastav oprávnění složky (spusť jako Administrator):</p>
<div class="code-block"><div class="code-block-header"><span class="code-block-lang">powershell</span><button type="button" class="code-block-copy" aria-label="copy code">[ copy ]</button></div><div class="codehilite"><pre><span></span><code><span class="n">icacls</span> <span class="s2">&quot;C:\Scripts&quot;</span> <span class="p">/</span><span class="n">inheritance</span><span class="p">:</span><span class="nb">r </span><span class="p">/</span><span class="n">grant</span><span class="p">:</span><span class="nb">r </span><span class="s2">&quot;BUILTIN\Administrators:(OI)(CI)(F)&quot;</span> <span class="s2">&quot;NT AUTHORITY\SYSTEM:(OI)(CI)(F)&quot;</span> <span class="s2">&quot;BUILTIN\Users:(OI)(CI)(RX)&quot;</span>
</code></pre></div></div>

<h2 id="4-nastaveni-ulohy-v-task-scheduleru">4. Nastavení úlohy v Task Scheduleru</h2>
<h3 id="41-otevreni-task-scheduleru">4.1 Otevření Task Scheduleru</h3>
<ul>
<li><code>Win+R</code> → <code>taskschd.msc</code> → Enter</li>
</ul>
<h3 id="42-vytvoreni-ulohy">4.2 Vytvoření úlohy</h3>
<ul>
<li>V pravém panelu klikni na <strong>Create Task</strong> (ne &ldquo;Create Basic Task&rdquo;)</li>
</ul>
<h3 id="43-zalozka-general">4.3 Záložka General</h3>
<ul>
<li><strong>Name:</strong> <code>Voicemeeter BT Auto-Start</code></li>
<li><strong>Description:</strong> <code>Spustí Voicemeeter Potato po připojení BT zařízení</code></li>
<li><strong>Security options:</strong> zaškrtni <strong>Run only when user is logged on</strong></li>
</ul>
<h3 id="44-zalozka-triggers">4.4 Záložka Triggers</h3>
<ol>
<li>Klikni <strong>New…</strong></li>
<li><strong>Begin the task:</strong> <code>On an event</code></li>
<li>Přepni na <strong>Custom</strong></li>
<li>Klikni <strong>New Event Filter…</strong> → záložka <strong>XML</strong> → zaškrtni <strong>Edit query manually</strong></li>
<li>Vlož následující XML (uprav <code>A00CE238FE34</code> na adresu svého zařízení):</li>
</ol>
<div class="code-block"><div class="code-block-header"><span class="code-block-lang">xml</span><button type="button" class="code-block-copy" aria-label="copy code">[ copy ]</button></div><div class="codehilite"><pre><span></span><code><span class="nt">&lt;QueryList&gt;</span>
<span class="w">  </span><span class="nt">&lt;Query</span><span class="w"> </span><span class="na">Id=</span><span class="s">&quot;0&quot;</span><span class="w"> </span><span class="na">Path=</span><span class="s">&quot;Microsoft-Windows-Bluetooth-Policy/Operational&quot;</span><span class="nt">&gt;</span>
<span class="w">    </span><span class="nt">&lt;Select</span><span class="w"> </span><span class="na">Path=</span><span class="s">&quot;Microsoft-Windows-Bluetooth-Policy/Operational&quot;</span><span class="nt">&gt;</span>
<span class="w">      </span>*[System[EventID=9]<span class="w"> </span>and<span class="w"> </span>EventData[Data<span class="w"> </span>and<span class="w"> </span>contains(.,&#39;A00CE238FE34&#39;)]]
<span class="w">    </span><span class="nt">&lt;/Select&gt;</span>
<span class="w">  </span><span class="nt">&lt;/Query&gt;</span>
<span class="nt">&lt;/QueryList&gt;</span>
</code></pre></div></div>

<ol start="6">
<li>Potvrď <strong>OK</strong></li>
</ol>
<h3 id="45-zalozka-actions">4.5 Záložka Actions</h3>
<ol>
<li>Klikni <strong>New…</strong></li>
<li><strong>Action:</strong> <code>Start a program</code></li>
<li><strong>Program/script:</strong> <code>C:\Scripts\voicemeeter-bt.bat</code></li>
<li>Potvrď <strong>OK</strong></li>
</ol>
<h3 id="46-zalozka-conditions">4.6 Záložka Conditions</h3>
<ul>
<li><strong>Odškrtni</strong> &ldquo;Start the task only if the computer is on AC power&rdquo; (pokud chceš spouštět i na baterii)</li>
</ul>
<h3 id="47-zalozka-settings">4.7 Záložka Settings</h3>
<ul>
<li>Zaškrtni <strong>Allow task to be run on demand</strong> (pro ruční testování)</li>
<li>Zaškrtni <strong>If the task is already running, then do not start a new instance</strong></li>
<li>Potvrď <strong>OK</strong></li>
</ul>
<h2 id="5-testovani">5. Testování</h2>
<ol>
<li>Odpoj BT zařízení</li>
<li>Znovu ho připoj</li>
<li>Po 2 sekundách by se měl spustit Voicemeeter Potato</li>
</ol>
<p>Pro diagnostiku zkontroluj:</p>
<div class="code-block"><div class="code-block-header"><span class="code-block-lang">powershell</span><button type="button" class="code-block-copy" aria-label="copy code">[ copy ]</button></div><div class="codehilite"><pre><span></span><code><span class="nb">Get-WinEvent</span> <span class="n">-LogName</span> <span class="s2">&quot;Microsoft-Windows-Bluetooth-Policy/Operational&quot;</span> <span class="n">-MaxEvents</span> <span class="n">5</span> <span class="p">|</span>
  <span class="nb">Format-Table</span> <span class="n">TimeCreated</span><span class="p">,</span> <span class="n">Id</span><span class="p">,</span> <span class="n">Message</span> <span class="n">-Wrap</span>
</code></pre></div></div>

<p>Historii spouštění úlohy najdeš v Task Scheduleru pod <strong>Task Scheduler Library</strong> → pravý klik na úlohu → <strong>Properties</strong> → záložka <strong>History</strong>.</p>
<h2 id="reseni-problemu">Řešení problémů</h2>
<table>
<thead>
<tr>
<th>Problém</th>
<th>Řešení</th>
</tr>
</thead>
<tbody>
<tr>
<td>Postup přestal fungovat po Windows Update</td>
<td>Windows Update deaktivuje Bluetooth event log. Ověř stav: <code>wevtutil gl "Microsoft-Windows-Bluetooth-Policy/Operational"</code> — hledej <code>enabled: true</code>. Pokud je <code>false</code>, znovu zapni (krok 1) a ujisti se, že máš vytvořenou startup úlohu <code>Enable-BT-Log</code> (krok 1a).</td>
</tr>
<tr>
<td>Log je prázdný</td>
<td>Ověř, že je log povolený (krok 1)</td>
</tr>
<tr>
<td>Event ID 9 se neobjevuje</td>
<td>Některé BT adaptéry logují jinak – zkontroluj i logy <code>Bluetooth-BthMini</code> a <code>System</code></td>
</tr>
<tr>
<td>Úloha se nespouští</td>
<td>Zkontroluj, že XML query odpovídá přesné adrese zařízení; otestuj úlohu ručně přes pravý klik → Run</td>
</tr>
<tr>
<td>Voicemeeter se spustí vícekrát</td>
<td>Ujisti se, že používáš aktuální verzi skriptu z kroku 3 (s <code>taskkill</code>). V Settings úlohy nastav také &ldquo;Do not start a new instance&rdquo;.</td>
</tr>
</tbody>
</table>
<h2 id="poznamky">Poznámky</h2>
<ul>
<li>Pokud chceš reagovat na <strong>jakékoli</strong> BT zařízení (ne jen jedno), odstraň z XML podmínku <code>and EventData[...]</code> a ponechej pouze <code>*[System[EventID=9]]</code>.</li>
<li>Skript ukončuje i <code>VoicemeeterMacroButtons.exe</code> — pokud Macro Buttons nepoužíváš, řádek s ním můžeš vynechat. Pokud ho používáš, po restartu se spustí automaticky jako závislý proces Voicemeeteru.</li>
<li>Po aktualizaci Windows ověř, že je log stále aktivní (<code>wevtutil gl ...</code>) a že startup úloha <code>Enable-BT-Log</code> existuje a je ve stavu <code>Ready</code>.</li>
</ul>
    ]]></content>
    <author>
      <name>ruza</name>
    </author>
  </entry>
  <entry>
    <title>QubesOS - DisposableVM s VPN per zákazník</title>
    <link href="https://ruza.eu/articles/qubesos-named-disposables.html" rel="alternate" type="text/html"/>
    <id>https://ruza.eu/articles/qubesos-named-disposables.html</id>
    <published>2026-04-18T00:00:00Z</published>
    <updated>2026-04-18T00:00:00Z</updated>
    <summary>Návod na zprovoznění DisposableVM s VPN ke konrétním zákazníkům</summary>
    <content type="html"><![CDATA[
<h1 id="zadani">Zadání</h1>
<p>Cílem je z menu mít relativně rychle dostupné jednotné prostředí s předpřipraveným VPN přístupem k různým zákazníkům.</p>
<ul>
<li>Jedna AppVM <code>work</code>, která drží persistentní konfiguraci v /home/ adresáři.</li>
<li>pro každého zákazníka<ul>
<li><code>work-zákazníkX</code> disposable pro práci u konkrétního zákazníka</li>
<li><code>vpn-zákazníkX</code> s VPN klientem k zákazníkX</li>
</ul>
</li>
</ul>
<h2 id="architektura">Architektura</h2>
<div class="code-block"><div class="code-block-header"><span class="code-block-lang">code</span><button type="button" class="code-block-copy" aria-label="copy code">[ copy ]</button></div><div class="codehilite"><pre><span></span><code>[work-zákazník1] ──► [vpn-zákazník1] ──► [sys-firewall] ──► [sys-net]
[work-zákazník2] ──► [vpn-zákazník2] ──► [sys-firewall] ──► [sys-net]
</code></pre></div></div>

<p>Každý Named DispVM se při startu vytvoří čistý z šablony, má vlastní VPN tunel a po zavření se smaže.</p>
<hr>
<h2 id="1-priprav-base-appvm-jako-dispvm-sablonu">1. Připrav base AppVM jako DispVM šablonu</h2>
<p>Máš AppVM (řekněme <code>work</code>), kde máš nainstalované SSH klienty, nástroje atd. Tu nastavíš jako šablonu pro disposable:</p>
<div class="code-block"><div class="code-block-header"><span class="code-block-lang">bash</span><button type="button" class="code-block-copy" aria-label="copy code">[ copy ]</button></div><div class="codehilite"><pre><span></span><code><span class="c1"># v dom0</span>
qvm-prefs<span class="w"> </span>work<span class="w"> </span>template_for_dispvms<span class="w"> </span>True
</code></pre></div></div>

<p>AppVM  <code>work</code> můžeš normálně používat a pokud budeš potřebovat nové work prostředí s VPN ke konkrétnímu zákazníkovi spustíš odpovídající work disposable.</p>
<p>Networking u <code>work</code> samotné můžeš nastavit na <code>none</code>, pokud budeš používat jen disposable. Síť se řeší až na úrovni Named DispVMs:</p>
<div class="code-block"><div class="code-block-header"><span class="code-block-lang">bash</span><button type="button" class="code-block-copy" aria-label="copy code">[ copy ]</button></div><div class="codehilite"><pre><span></span><code>qvm-prefs<span class="w"> </span>work<span class="w"> </span>netvm<span class="w"> </span>none
</code></pre></div></div>

<p>Veškerý software (SSH klient, wireshark, nmap…) instaluješ do TemplateVM, ze které <code>work</code> vychází (např. <code>debian-13</code>). Samotná <code>work</code> AppVM pak slouží jako DispVM šablona — přizpůsobení (dotfiles, SSH config) dáváš do <code>/home/user/</code> v <code>work</code>.</p>
<p>Nezapomeň, že v disposable se nic persistentně na disk neukládá.</p>
<hr>
<h2 id="2-vytvor-vpn-proxyvm-pro-kazdeho-zakaznika">2. Vytvoř VPN ProxyVM pro každého zákazníka</h2>
<p>Pro každého zákazníka potřebuješ samostatnou VM, která poběží jako VPN gateway.</p>
<div class="code-block"><div class="code-block-header"><span class="code-block-lang">bash</span><button type="button" class="code-block-copy" aria-label="copy code">[ copy ]</button></div><div class="codehilite"><pre><span></span><code><span class="c1"># v dom0</span>
qvm-create<span class="w"> </span>vpn-zákazník1<span class="w"> </span>--class<span class="w"> </span>AppVM<span class="w"> </span>--template<span class="w"> </span>debian-13<span class="w"> </span>--label<span class="w"> </span>orange
qvm-prefs<span class="w"> </span>vpn-zákazník1<span class="w"> </span>netvm<span class="w"> </span>sys-firewall
qvm-prefs<span class="w"> </span>vpn-zákazník1<span class="w"> </span>provides_network<span class="w"> </span>True

qvm-create<span class="w"> </span>vpn-zákazník2<span class="w"> </span>--class<span class="w"> </span>AppVM<span class="w"> </span>--template<span class="w"> </span>debian-13<span class="w"> </span>--label<span class="w"> </span>orange
qvm-prefs<span class="w"> </span>vpn-zákazník2<span class="w"> </span>netvm<span class="w"> </span>sys-firewall
qvm-prefs<span class="w"> </span>vpn-zákazník2<span class="w"> </span>provides_network<span class="w"> </span>True
</code></pre></div></div>

<p><code>provides_network True</code> je klíčové — díky tomu se VM chová jako ProxyVM a ostatní VM ji můžou použít jako svůj <code>netvm</code>.</p>
<h3 id="konfigurace-vpn-uvnitr-kazde-proxy-vm">Konfigurace VPN uvnitř každé proxy VM</h3>
<p>Spusť <code>vpn-zákazník1</code> a nastav VPN. Příklad s WireGuard:</p>
<div class="code-block"><div class="code-block-header"><span class="code-block-lang">bash</span><button type="button" class="code-block-copy" aria-label="copy code">[ copy ]</button></div><div class="codehilite"><pre><span></span><code><span class="c1"># uvnitř vpn-zákazník1</span>
sudo<span class="w"> </span>nano<span class="w"> </span>/rw/config/vpn/wg0.conf
<span class="c1"># vlož WireGuard config zákazníka 1</span>
</code></pre></div></div>

<p>Aby se VPN spustila automaticky při startu, přidej do <code>/rw/config/rc.local</code>:</p>
<div class="code-block"><div class="code-block-header"><span class="code-block-lang">bash</span><button type="button" class="code-block-copy" aria-label="copy code">[ copy ]</button></div><div class="codehilite"><pre><span></span><code><span class="ch">#!/bin/bash</span>
cp<span class="w"> </span>/rw/config/vpn/wg0.conf<span class="w"> </span>/etc/wireguard/wg0.conf
systemctl<span class="w"> </span>start<span class="w"> </span>wg-quick@wg0

<span class="c1"># Povolit forwarding (nutné pro ProxyVM funkci)</span>
<span class="nb">echo</span><span class="w"> </span><span class="m">1</span><span class="w"> </span>&gt;<span class="w"> </span>/proc/sys/net/ipv4/ip_forward
iptables<span class="w"> </span>-t<span class="w"> </span>nat<span class="w"> </span>-A<span class="w"> </span>POSTROUTING<span class="w"> </span>-o<span class="w"> </span>wg0<span class="w"> </span>-j<span class="w"> </span>MASQUERADE
iptables<span class="w"> </span>-A<span class="w"> </span>FORWARD<span class="w"> </span>-i<span class="w"> </span>eth0<span class="w"> </span>-o<span class="w"> </span>wg0<span class="w"> </span>-j<span class="w"> </span>ACCEPT
iptables<span class="w"> </span>-A<span class="w"> </span>FORWARD<span class="w"> </span>-i<span class="w"> </span>wg0<span class="w"> </span>-o<span class="w"> </span>eth0<span class="w"> </span>-m<span class="w"> </span>state<span class="w"> </span>--state<span class="w"> </span>RELATED,ESTABLISHED<span class="w"> </span>-j<span class="w"> </span>ACCEPT

<span class="c1"># Volitelně: blokuj traffic mimo tunel (kill switch)</span>
iptables<span class="w"> </span>-A<span class="w"> </span>OUTPUT<span class="w"> </span>-o<span class="w"> </span>wg0<span class="w"> </span>-j<span class="w"> </span>ACCEPT
iptables<span class="w"> </span>-A<span class="w"> </span>OUTPUT<span class="w"> </span>-o<span class="w"> </span>lo<span class="w"> </span>-j<span class="w"> </span>ACCEPT
iptables<span class="w"> </span>-A<span class="w"> </span>OUTPUT<span class="w"> </span>-d<span class="w"> </span><span class="m">10</span>.137.0.0/16<span class="w"> </span>-j<span class="w"> </span>ACCEPT<span class="w">  </span><span class="c1"># Qubes interní síť</span>
iptables<span class="w"> </span>-A<span class="w"> </span>OUTPUT<span class="w"> </span>-p<span class="w"> </span>udp<span class="w"> </span>--dport<span class="w"> </span><span class="m">51820</span><span class="w"> </span>-j<span class="w"> </span>ACCEPT<span class="w">  </span><span class="c1"># WG endpoint</span>
iptables<span class="w"> </span>-A<span class="w"> </span>OUTPUT<span class="w"> </span>-j<span class="w"> </span>DROP
</code></pre></div></div>

<p>Pro OpenVPN je postup analogický — místo WireGuard spustíš <code>openvpn --config /rw/config/vpn/client.ovpn --daemon</code>.</p>
<p>Totéž provedeš pro <code>vpn-zákazník2</code> s konfigurací druhého zákazníka.</p>
<hr>
<h2 id="3-vytvor-named-disposables">3. Vytvoř Named Disposables</h2>
<div class="code-block"><div class="code-block-header"><span class="code-block-lang">bash</span><button type="button" class="code-block-copy" aria-label="copy code">[ copy ]</button></div><div class="codehilite"><pre><span></span><code><span class="c1"># v dom0</span>
qvm-create<span class="w"> </span>work-zákazník1<span class="w"> </span>--class<span class="w"> </span>DispVM<span class="w"> </span>--template<span class="w"> </span>work<span class="w"> </span>--label<span class="w"> </span>green
qvm-prefs<span class="w"> </span>work-zákazník1<span class="w"> </span>netvm<span class="w"> </span>vpn-zákazník1

qvm-create<span class="w"> </span>work-zákazník2<span class="w"> </span>--class<span class="w"> </span>DispVM<span class="w"> </span>--template<span class="w"> </span>work<span class="w"> </span>--label<span class="w"> </span>blue
qvm-prefs<span class="w"> </span>work-zákazník2<span class="w"> </span>netvm<span class="w"> </span>vpn-zákazník2
</code></pre></div></div>

<p>Tím vzniknou pojmenované DispVM, které se zobrazí v App menu a můžeš je spouštět opakovaně, pokaždé čisté, ale vždy s přiřazenou VPN.</p>
<h2 id="vytvoreni-skriptem">Vytvoření skriptem</h2>
<div class="code-block"><div class="code-block-header"><span class="code-block-lang">bash</span><button type="button" class="code-block-copy" aria-label="copy code">[ copy ]</button></div><div class="codehilite"><pre><span></span><code><span class="ch">#!/bin/bash                                                                                                                           </span>
<span class="nv">ZAKAZNIK</span><span class="o">=</span><span class="s2">&quot;</span><span class="si">${</span><span class="nv">1</span><span class="si">}</span><span class="s2">&quot;</span>

<span class="c1"># Kontrola, zda je promenna ZAKAZNIK nastavena                                                                                        </span>
<span class="k">if</span><span class="w"> </span><span class="o">[</span><span class="w"> </span>-z<span class="w"> </span><span class="s2">&quot;</span><span class="si">${</span><span class="nv">ZAKAZNIK</span><span class="si">}</span><span class="s2">&quot;</span><span class="w"> </span><span class="o">]</span><span class="p">;</span><span class="w"> </span><span class="k">then</span>
<span class="w">    </span><span class="c1"># Pokud neni nastavena, zkontroluj prvni parametr                                                                                 </span>
<span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="o">[</span><span class="w"> </span><span class="nv">$#</span><span class="w"> </span>-eq<span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="o">]</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="o">[</span><span class="w"> </span>-z<span class="w"> </span><span class="s2">&quot;</span><span class="nv">$1</span><span class="s2">&quot;</span><span class="w"> </span><span class="o">]</span><span class="p">;</span><span class="w"> </span><span class="k">then</span>
<span class="w">        </span><span class="nb">echo</span><span class="w"> </span><span class="s2">&quot;Chyba: Promenna ZAKAZNIK neni nastavena a nebyl poskytnut prvni parametr.&quot;</span>
<span class="w">        </span><span class="nb">echo</span><span class="w"> </span><span class="s2">&quot;Zadejte hodnotu pro ZAKAZNIK:&quot;</span>
<span class="w">        </span><span class="nb">read</span><span class="w"> </span>-r<span class="w"> </span>ZAKAZNIK
<span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="o">[</span><span class="w"> </span>-z<span class="w"> </span><span class="s2">&quot;</span><span class="si">${</span><span class="nv">ZAKAZNIK</span><span class="si">}</span><span class="s2">&quot;</span><span class="w"> </span><span class="o">]</span><span class="p">;</span><span class="w"> </span><span class="k">then</span>
<span class="w">            </span><span class="nb">echo</span><span class="w"> </span><span class="s2">&quot;Chyba: ZAKAZNIK neni zadan. Skript konci.&quot;</span>
<span class="w">            </span><span class="nb">exit</span><span class="w"> </span><span class="m">1</span>
<span class="w">        </span><span class="k">fi</span>
<span class="w">    </span><span class="k">else</span>
<span class="w">        </span><span class="nv">ZAKAZNIK</span><span class="o">=</span><span class="s2">&quot;</span><span class="nv">$1</span><span class="s2">&quot;</span>
<span class="w">    </span><span class="k">fi</span>
<span class="k">fi</span>

<span class="nb">echo</span><span class="w"> </span><span class="s2">&quot;Vytvorim: work-</span><span class="si">${</span><span class="nv">ZAKAZNIK</span><span class="si">}</span><span class="s2"> jako NamedDispVM..&quot;</span>

qvm-create<span class="w"> </span>work-<span class="si">${</span><span class="nv">ZAKAZNIK</span><span class="si">}</span><span class="w"> </span>--class<span class="w"> </span>DispVM<span class="w"> </span>--template<span class="w"> </span>work<span class="w"> </span>--label<span class="w"> </span>blue
qvm-prefs<span class="w"> </span>work-<span class="si">${</span><span class="nv">ZAKAZNIK</span><span class="si">}</span><span class="w"> </span>netvm<span class="w"> </span>vpn-<span class="si">${</span><span class="nv">ZAKAZNIK</span><span class="si">}</span>
</code></pre></div></div>

<h2 id="4-overeni">4. Ověření</h2>
<div class="code-block"><div class="code-block-header"><span class="code-block-lang">bash</span><button type="button" class="code-block-copy" aria-label="copy code">[ copy ]</button></div><div class="codehilite"><pre><span></span><code><span class="c1"># Spusť Named DispVM</span>
qvm-run<span class="w"> </span>work-zákazník1<span class="w"> </span>--auto<span class="w"> </span>gnome-terminal

<span class="c1"># Uvnitř DispVM ověř IP</span>
curl<span class="w"> </span>ifconfig.me
<span class="c1"># Měla by se zobrazit VPN IP zákazníka 1</span>
</code></pre></div></div>

<hr>
<h2 id="shrnuti-struktury">Shrnutí struktury</h2>
<table>
<thead>
<tr>
<th>VM</th>
<th>Třída</th>
<th>NetVM</th>
<th>Účel</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>work</code></td>
<td>AppVM a DispVM šablona</td>
<td>none / sys-firewall</td>
<td>Šablona s nástroji a dotfiles</td>
</tr>
<tr>
<td><code>vpn-zákazník1</code></td>
<td>AppVM (provides_network)</td>
<td>sys-firewall</td>
<td>VPN tunel zákazníka 1</td>
</tr>
<tr>
<td><code>vpn-zákazník2</code></td>
<td>AppVM (provides_network)</td>
<td>sys-firewall</td>
<td>VPN tunel zákazníka 2</td>
</tr>
<tr>
<td><code>work-zákazník1</code></td>
<td>DispVM</td>
<td>vpn-zákazník1</td>
<td>Pracovní session zákazník 1</td>
</tr>
<tr>
<td><code>work-zákazník2</code></td>
<td>DispVM</td>
<td>vpn-zákazník2</td>
<td>Pracovní session zákazník 2</td>
</tr>
</tbody>
</table>
<h2 id="tipy">Tipy</h2>
<p><strong>Přidání dalšího zákazníka</strong> je vždy jen 3 příkazy v dom0 — vytvoř VPN proxy, nakonfiguruj VPN uvnitř, vytvoř Named DispVM s příslušným netvm.</p>
<p><strong>SSH klíče</strong> — pokud používáš split-GPG/split-SSH, klíče zůstávají ve <code>vault-net</code> a DispVM si je vyžádá přes Qubes RPC. Nemusíš je kopírovat do každé disposable.</p>
<p><strong>Persistentní SSH config</strong> — <code>/home/user/.ssh/config</code> v <code>work</code> AppVM se propaguje do každého DispVM, takže aliasy, proxy jumpy atd. stačí nastavit jednou.</p>
    ]]></content>
    <author>
      <name>ruza</name>
    </author>
  </entry>
  <entry>
    <title>Nový design ruza.eu</title>
    <link href="https://ruza.eu/articles/novy-design.html" rel="alternate" type="text/html"/>
    <id>https://ruza.eu/articles/novy-design.html</id>
    <published>2026-04-17T00:00:00Z</published>
    <updated>2026-04-17T00:00:00Z</updated>
    <summary>Přepis starého osobního rozcestníku na nový terminálový design s Markdown workflow</summary>
    <content type="html"><![CDATA[
<p>Stará verze stránky byla prostá HTML tabulka — funkční, ale vizuálně z roku 2003. Po pár iteracích se zrodil nový design: černé pozadí, žluté odkazy, monospace typografie, Mastodon feed a jednotné <code>style.css</code>.</p>
<h2 id="co-se-zmenilo">Co se změnilo</h2>
<ul>
<li>dark mode s volitelným light mode přes <code>prefers-color-scheme</code></li>
<li>socialní odkazy v karetním gridu s pořádnými SVG ikonkami</li>
<li>GPG fingerprint s copy-to-clipboard funkcí</li>
<li>sekce <em>writing</em> s odkazy na články</li>
<li>tři nejnovější Mastodon posty načítané přes veřejné API</li>
<li>všechny stránky sdílí jediný <code>/style.css</code></li>
</ul>
<h2 id="markdown-pipeline">Markdown pipeline</h2>
<p>Místo psaní HTML ručně teď mám build script <code>build.py</code>, který bere Markdown soubor s YAML frontmatter a generuje finální HTML. Proces:</p>
<ol>
<li>Napíšu článek v Obsidianu jako <code>.md</code></li>
<li>Spustím <code>python3 build.py articles/src/</code></li>
<li>Hotovo — vygeneruje se jak jednotlivý článek, tak aktualizovaný seznam</li>
</ol>
<h2 id="citace">Citace</h2>
<blockquote>
<p>Dobré rozhraní je jako vtip. Když ho musíte vysvětlovat, není moc dobré.
— Martin LeBlanc</p>
</blockquote>
    ]]></content>
    <author>
      <name>ruza</name>
    </author>
  </entry>
</feed>