<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://lunik.tiwabbit.fr/blog/</id>
    <title>Lunik's Blog</title>
    <updated>2026-05-16T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://lunik.tiwabbit.fr/blog/"/>
    <subtitle>LeadTech DevOps &amp; Cloud &amp; IA engineer blog</subtitle>
    <icon>https://lunik.tiwabbit.fr/blog/img/favicon.ico</icon>
    <rights>Copyright © 2026 Guillaume MARTINEZ</rights>
    <entry>
        <title type="html"><![CDATA[Vibe Coding for Nearly Free: Claude Code + OpenRouter with Free Models]]></title>
        <id>https://lunik.tiwabbit.fr/blog/vibe-coding-claude-code-openrouter</id>
        <link href="https://lunik.tiwabbit.fr/blog/vibe-coding-claude-code-openrouter"/>
        <updated>2026-05-16T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[How I'm building software for virtually zero cost using Claude Code and OpenRouter's free tier models]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2026-05-16-vibe-coding-claude-code-openrouter/cover.png" alt="cover" class="img__Ss2"></p>
<p>I've been integrating AI into my development workflow, but I quickly realized that costs can spiral out of control if not monitored carefully. Premium AI coding assistants with their subscription models and "pro" tiers can easily become expensive-especially for personal and open-source projects where budgets are tight. I found myself needing to track every API call and watch my usage like a hawk.</p>
<p>The cost barrier to AI-assisted development has finally crumbled. After months of experimenting with various setups, I've found a combination that lets me build production-quality software while spending almost nothing: <a href="https://github.com/anthropics/claude-code" target="_blank" rel="noopener noreferrer" class="">Claude Code</a> paired with <a href="https://openrouter.ai/" target="_blank" rel="noopener noreferrer" class="">OpenRouter</a>'s free tier models.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-evolution-of-my-ai-coding-setup">The Evolution of My AI Coding Setup<a href="https://lunik.tiwabbit.fr/blog/vibe-coding-claude-code-openrouter#the-evolution-of-my-ai-coding-setup" class="hash-link" aria-label="Direct link to The Evolution of My AI Coding Setup" title="Direct link to The Evolution of My AI Coding Setup" translate="no">​</a></h2>
<p>When I first started using AI coding assistants, I was quickly confronted with the reality that quality comes at a price. Premium models from Anthropic, OpenAI, and others can easily run up bills of $50-100+ per month for regular usage. As an indie developer, that's not sustainable.</p>
<p>My journey went something like this:</p>
<ol>
<li class=""><strong>Early experiments</strong>: Direct API calls to premium models (expensive) - <a href="https://platform.claude.com/docs/en/about-claude/models/overview" target="_blank" rel="noopener noreferrer" class="">Claude Opus/Sonnet</a> were excellent but prohibitively expensive</li>
<li class=""><strong>Intermediate phase</strong>: Mix of premium and open-source models (moderate cost) - Tried cheaper Mistral models on paid APIs but quality was significantly worse</li>
<li class=""><strong>Local experiments</strong>: Attempted running models locally with Ollama - MacBook Air M2 (16GB) lacks VRAM for large models, small models (Gemma) have poor quality, and even with swap, medium models (quantized Qwen) have terrible performance</li>
<li class=""><strong>Current setup</strong>: <a href="https://github.com/anthropics/claude-code" target="_blank" rel="noopener noreferrer" class="">Claude Code</a> + <a href="https://openrouter.ai/" target="_blank" rel="noopener noreferrer" class="">OpenRouter</a> free tier (near-zero cost)</li>
</ol>
<p>The breakthrough came when I realized that for many coding tasks, I don't need the most expensive, cutting-edge models. I need models that are:</p>
<ul>
<li class="">Good enough to understand code context</li>
<li class="">Capable of generating functional code</li>
<li class="">Fast enough to not break my flow</li>
<li class="">Free or extremely cheap</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="why-claude-code">Why Claude Code?<a href="https://lunik.tiwabbit.fr/blog/vibe-coding-claude-code-openrouter#why-claude-code" class="hash-link" aria-label="Direct link to Why Claude Code?" title="Direct link to Why Claude Code?" translate="no">​</a></h2>
<p><a href="https://github.com/anthropics/claude-code" target="_blank" rel="noopener noreferrer" class="">Claude Code</a> has become my primary interface for AI-assisted coding because it offers something unique: a terminal-native experience that feels like pair programming with a knowledgeable colleague.</p>
<p>Key advantages:</p>
<ul>
<li class=""><strong>Seamless terminal integration</strong>: No context switching between IDE and chat interface</li>
<li class=""><strong>File-aware operations</strong>: Understands your project structure and can read/write files directly</li>
<li class=""><strong>Command-based workflow</strong>: Natural integration with existing development practices</li>
<li class=""><strong>Session memory</strong>: Remembers context across interactions within a session</li>
<li class=""><strong>Agentic loop</strong>: Claude Code continuously analyses context and iteratively proposes and executes actions (edits, reads, exec) for reactive and proactive assistance.</li>
<li class=""><strong>Tools and skills</strong>: Built‑in capabilities like ultrareview, planning, dependency management and other micro‑tools streamline the workflow.</li>
</ul>
<p>What really sets <a href="https://github.com/anthropics/claude-code" target="_blank" rel="noopener noreferrer" class="">Claude Code</a> apart is how it handles complex, multi-file operations. Rather than just suggesting code snippets, it can actually implement features across multiple files, run tests, and even debug issues.</p>
<p>I use a hybrid approach: a large model for the planning phase (Claude Code plan mode), then a cheap/free model for the actual development. This lets me structure the task with the power of a big model while keeping costs low during coding.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-openrouter-free-tier-advantage">The OpenRouter Free Tier Advantage<a href="https://lunik.tiwabbit.fr/blog/vibe-coding-claude-code-openrouter#the-openrouter-free-tier-advantage" class="hash-link" aria-label="Direct link to The OpenRouter Free Tier Advantage" title="Direct link to The OpenRouter Free Tier Advantage" translate="no">​</a></h2>
<p><a href="https://openrouter.ai/" target="_blank" rel="noopener noreferrer" class="">OpenRouter</a>'s free tier has been a game-changer. Through their platform, I get access to several high-quality models at zero cost:</p>
<p><strong>Models I regularly use:</strong></p>
<ul>
<li class=""><strong>Nemotron 3 Super</strong> (my current primary, listed as free on <a href="https://openrouter.ai/" target="_blank" rel="noopener noreferrer" class="">OpenRouter</a> but incurs a small per‑request fee or uses your credit balance, so it may cost dollars depending on your OpenRouter usage settings): Excellent for code generation and understanding - I chose this specifically for its thinking capabilities, excellent time to first token, and token throughput</li>
<li class=""><strong>Mistral models</strong>: Strong performance on coding tasks</li>
<li class=""><strong>Google's Gemma models</strong>: Surprisingly capable for their size</li>
<li class=""><strong>OpenAI GPT OSS (120b)</strong>: Large open‑source model with deep understanding of text and code, ideal for detailed prompts and tasks requiring extensive context.</li>
</ul>
<p><strong>Important note about free tier limits</strong>: While the models are free to use, <a href="https://openrouter.ai/" target="_blank" rel="noopener noreferrer" class="">OpenRouter</a> implements rate limits to prevent abuse. Free tier users are limited to 20 requests per minute. Additionally, there are daily limits based on purchased credits:</p>
<ul>
<li class="">Less than 10 credits: 50 free model requests per day</li>
<li class="">At least 10 credits: 1000 free model requests per day</li>
</ul>
<p>I've found that purchasing just $10 in credits <a href="https://openrouter.ai/" target="_blank" rel="noopener noreferrer" class="">OpenRouter</a> (which lasts a long time given how inexpensive these models are) gives me plenty of headroom for daily development work. The cost is negligible compared to traditional AI coding assistants.</p>
<p>The key insight is that different tasks benefit from different models:</p>
<ul>
<li class=""><strong>Architecture/design discussions</strong>: Use the most capable free model available</li>
<li class=""><strong>Straightforward code generation</strong>: Mid-tier models work perfectly</li>
<li class=""><strong>Debugging/log analysis</strong>: Even smaller models excel at pattern matching</li>
<li class=""><strong>Learning/exploration</strong>: Any model can help explain concepts</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="cost-analysis-nearly-free-development">Cost Analysis: Nearly Free Development<a href="https://lunik.tiwabbit.fr/blog/vibe-coding-claude-code-openrouter#cost-analysis-nearly-free-development" class="hash-link" aria-label="Direct link to Cost Analysis: Nearly Free Development" title="Direct link to Cost Analysis: Nearly Free Development" translate="no">​</a></h2>
<p>Let's break down the actual costs:</p>
<p><strong>Traditional approach:</strong></p>
<ul>
<li class="">Claude Pro: $20/month</li>
<li class="">GitHub Copilot: $10/month</li>
<li class="">OpenAI API usage: $20-50/month (depending on usage)</li>
<li class=""><strong>Total: $50-80/month</strong></li>
</ul>
<p><strong>My current approach:</strong></p>
<ul>
<li class=""><a href="https://github.com/anthropics/claude-code" target="_blank" rel="noopener noreferrer" class="">Claude Code</a>: Free (open-source CLI tool)</li>
<li class=""><a href="https://openrouter.ai/" target="_blank" rel="noopener noreferrer" class="">OpenRouter</a> free tier: $0</li>
<li class="">Occasional premium model usage for complex tasks: &lt;$2/month</li>
<li class=""><strong>Total: &lt;$2/month</strong></li>
</ul>
<p>That's a 95-97% cost reduction while maintaining excellent development velocity.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="quality-considerations">Quality Considerations<a href="https://lunik.tiwabbit.fr/blog/vibe-coding-claude-code-openrouter#quality-considerations" class="hash-link" aria-label="Direct link to Quality Considerations" title="Direct link to Quality Considerations" translate="no">​</a></h2>
<p>You might wonder: does using free models compromise quality? My experience says no, with some caveats:</p>
<p><strong>Where free models excel:</strong></p>
<ul>
<li class="">Boilerplate code generation</li>
<li class="">Standard algorithms and data structures</li>
<li class="">API integration code</li>
<li class="">Documentation and comments</li>
<li class="">Bug fixes and refactoring</li>
</ul>
<p><strong>Where you might want premium models:</strong></p>
<ul>
<li class="">Complex architectural decisions</li>
<li class="">Novel algorithm development</li>
<li class="">Edge case handling in security-critical code</li>
<li class="">When maximum reliability is required</li>
</ul>
<p>The trick is knowing when to use which. For 80% of my coding tasks, the free models on <a href="https://openrouter.ai/" target="_blank" rel="noopener noreferrer" class="">OpenRouter</a> are more than sufficient.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="tips-for-maximizing-free-tier-usage">Tips for Maximizing Free Tier Usage<a href="https://lunik.tiwabbit.fr/blog/vibe-coding-claude-code-openrouter#tips-for-maximizing-free-tier-usage" class="hash-link" aria-label="Direct link to Tips for Maximizing Free Tier Usage" title="Direct link to Tips for Maximizing Free Tier Usage" translate="no">​</a></h2>
<ol>
<li class=""><strong>Be specific in your prompts</strong>: Vague requests waste tokens and give poorer results</li>
<li class=""><strong>Break problems into smaller chunks</strong>: Easier for models to handle, less context needed</li>
<li class=""><strong>Use the right model for the task</strong>: Match model capability to task complexity</li>
<li class=""><strong>Leverage Claude Code's file operations</strong>: Let it read your codebase to provide better context</li>
<li class=""><strong>Cache good prompts</strong>: Save effective prompt patterns for reuse</li>
<li class=""><strong>Monitor usage</strong>: Keep an eye on which models you're using most effectively</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-bigger-picture-ai-for-all">The Bigger Picture: AI for All<a href="https://lunik.tiwabbit.fr/blog/vibe-coding-claude-code-openrouter#the-bigger-picture-ai-for-all" class="hash-link" aria-label="Direct link to The Bigger Picture: AI for All" title="Direct link to The Bigger Picture: AI for All" translate="no">​</a></h2>
<p>What excites me most about this setup isn't just the cost savings-it's the democratizing effect. When AI-assisted development becomes accessible to virtually anyone with an internet connection, we unlock tremendous creative potential.</p>
<p>Imagine:</p>
<ul>
<li class="">Students in developing countries learning to code with AI assistance</li>
<li class="">Hobbyists building projects without worrying about API costs</li>
<li class="">Entrepreneurs validating ideas quickly and cheaply</li>
<li class="">Open-source contributors able to contribute more effectively</li>
</ul>
<p>This isn't just about saving money-it's about lowering the barrier to entry for software creation.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="getting-started-yourself">Getting Started Yourself<a href="https://lunik.tiwabbit.fr/blog/vibe-coding-claude-code-openrouter#getting-started-yourself" class="hash-link" aria-label="Direct link to Getting Started Yourself" title="Direct link to Getting Started Yourself" translate="no">​</a></h2>
<p>If you want to try this approach:</p>
<ol>
<li class="">
<p><strong>Install Claude Code</strong>: <code>brew install claude-code</code> (via <a href="https://brew.sh/" target="_blank" rel="noopener noreferrer" class="">Homebrew</a>)</p>
</li>
<li class="">
<p><strong>Sign up for OpenRouter</strong>: <a href="https://openrouter.ai/" target="_blank" rel="noopener noreferrer" class="">OpenRouter</a> (free tier available immediately)</p>
</li>
<li class="">
<p><strong>Configure your OpenRouter API key</strong> in <a href="https://github.com/anthropics/claude-code" target="_blank" rel="noopener noreferrer" class="">Claude Code</a> settings</p>
<p>Claude Code talks to Anthropic by default. To route through OpenRouter you must set the following environment variables before running Claude Code:</p>
<div class="language-text codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-text codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">export ANTHROPIC_BASE_URL="https://openrouter.ai/api"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">export ANTHROPIC_AUTH_TOKEN="&lt;your‑OpenRouter‑API‑key&gt;"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">export ANTHROPIC_API_KEY=""</span><br></div></code></pre></div></div>
<p>The empty <code>ANTHROPIC_API_KEY</code> tells Claude Code to skip the built‑in Anthropic key and use the custom base URL and token above. This lets you alias any model name (e.g., <code>gpt-oss-120b:free</code>) to the OpenRouter endpoint.</p>
</li>
<li class="">
<p><strong>Start with simple tasks</strong> to get a feel for how the models respond</p>
</li>
<li class="">
<p><strong>Experiment with different models</strong> to find what works best for your workflow</p>
</li>
</ol>
<p><strong>Model configuration:</strong> I'm using <code>openai/gpt-oss-120b:free</code> with <code>maxContextTokens</code> set to 135000 in <code>~/.claude/settings.json</code> to stay within the model’s context window.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-future-of-affordable-ai-coding">The Future of Affordable AI Coding<a href="https://lunik.tiwabbit.fr/blog/vibe-coding-claude-code-openrouter#the-future-of-affordable-ai-coding" class="hash-link" aria-label="Direct link to The Future of Affordable AI Coding" title="Direct link to The Future of Affordable AI Coding" translate="no">​</a></h2>
<p>As open-source models continue to improve and platforms like <a href="https://openrouter.ai/" target="_blank" rel="noopener noreferrer" class="">OpenRouter</a> expand their free tiers, I believe we're heading toward a future where high-quality AI-assisted coding is accessible to everyone, regardless of budget.</p>
<p>The tools are already here. The cost barrier is falling. All that's left is to build.</p>]]></content>
        <author>
            <name>Guillaume MARTINEZ</name>
            <uri>https://github.com/Lunik</uri>
        </author>
        <category label="ai" term="ai"/>
        <category label="coding" term="coding"/>
        <category label="claude-code" term="claude-code"/>
        <category label="openrouter" term="openrouter"/>
        <category label="llm" term="llm"/>
        <category label="productivity" term="productivity"/>
        <category label="software-development" term="software-development"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Building Crypto Scripts: Stellar XLM & Ethereum]]></title>
        <id>https://lunik.tiwabbit.fr/blog/building-crypto-send-receive-scripts</id>
        <link href="https://lunik.tiwabbit.fr/blog/building-crypto-send-receive-scripts"/>
        <updated>2026-04-24T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[A practical guide to building safe, production-ready scripts for sending and receiving cryptocurrency across Stellar and Ethereum networks]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2026-04-24-building-crypto-send-receive-scripts/cover.png" alt="cover" class="img__Ss2"></p>
<p>I don't trust apps with my crypto. Every wallet, exchange, and dapp is a black box. You hand over your private key, click "send", and hope the code does what it claims. So I built my own scripts instead: small, auditable Python programs that I can read and understand completely.</p>
<p>The differences between Stellar and Ethereum force you to confront each blockchain's design philosophy. Here's what I learned building scripts for both.</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="getting-started-keypair-generation">Getting Started: Keypair Generation<a href="https://lunik.tiwabbit.fr/blog/building-crypto-send-receive-scripts#getting-started-keypair-generation" class="hash-link" aria-label="Direct link to Getting Started: Keypair Generation" title="Direct link to Getting Started: Keypair Generation" translate="no">​</a></h3>
<p>The first difference hit me immediately: <strong>how do you fund a testnet account?</strong></p>
<p>With Stellar, it's almost too easy. Generate a keypair, then call Friendbot once:</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">keypair </span><span class="token operator">=</span><span class="token plain"> Keypair</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">random</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">public_key </span><span class="token operator">=</span><span class="token plain"> keypair</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">public_key</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">url </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"https://friendbot.stellar.org?addr=</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">public_key</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">response </span><span class="token operator">=</span><span class="token plain"> urllib</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">urlopen</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">body </span><span class="token operator">=</span><span class="token plain"> json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">loads</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">read</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">funding_tx </span><span class="token operator">=</span><span class="token plain"> body</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"hash"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></div></code></pre></div></div>
<p>Boom. <code>10,000 XLM</code>, instantly. Stellar was designed for developers.</p>
<p>Ethereum? Different story entirely.</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">account </span><span class="token operator">=</span><span class="token plain"> Account</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">create</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">address </span><span class="token operator">=</span><span class="token plain"> account</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">address</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">private_key </span><span class="token operator">=</span><span class="token plain"> account</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">key</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token builtin" style="color:rgb(189, 147, 249)">hex</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></div></code></pre></div></div>
<p>Then you have to go find a faucet to fund the account on the testnet. I used the <a href="https://cloud.google.com/application/web3/faucet/ethereum/sepolia" target="_blank" rel="noopener noreferrer" class="">Ethereum Sepolia Faucet</a> from Google, which let me get started quickly with <code>0.05 Sepolia ETH</code>.</p>
<p>Ethereum's testnet requires you to prove you're human. This isn't a flaw—it's a design choice. Sepolia is scarce by design, and that scarcity forces you to be intentional about testing.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-decimal-problem">The Decimal Problem<a href="https://lunik.tiwabbit.fr/blog/building-crypto-send-receive-scripts#the-decimal-problem" class="hash-link" aria-label="Direct link to The Decimal Problem" title="Direct link to The Decimal Problem" translate="no">​</a></h2>
<p>I discovered the decimal limits problem by accident. I tried sending <code>10.12345678 XLM</code> and Stellar rejected it silently at the RPC level. Turns out Stellar stops at <code>7</code> decimals:</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">value </span><span class="token operator">=</span><span class="token plain"> Decimal</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">amount</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">decimals </span><span class="token operator">=</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">abs</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">value</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">as_tuple</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">exponent</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">  </span><span class="token comment" style="color:rgb(98, 114, 164)"># Stellar: max 7</span><br></div></code></pre></div></div>
<p>Ethereum goes much further. <code>18</code> decimals, because wei (<code>10^-18 ETH</code>) is the smallest unit:</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">value </span><span class="token operator">=</span><span class="token plain"> Decimal</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">amount</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">amount_wei </span><span class="token operator">=</span><span class="token plain"> Web3</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">to_wei</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">value</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"ether"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">  </span><span class="token comment" style="color:rgb(98, 114, 164)"># Ethereum: max 18</span><br></div></code></pre></div></div>
<p>This taught me something: <strong>blockchains aren't interchangeable</strong>. They have hard constraints baked into their protocol. You can't just move code from one to the other—you have to understand and respect each system's limits.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="memos-different-tradeoffs">Memos: Different Tradeoffs<a href="https://lunik.tiwabbit.fr/blog/building-crypto-send-receive-scripts#memos-different-tradeoffs" class="hash-link" aria-label="Direct link to Memos: Different Tradeoffs" title="Direct link to Memos: Different Tradeoffs" translate="no">​</a></h2>
<p>I wanted to include descriptive memos in transactions—something like "invoice #12345" to track payments. Stellar made this simple:</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">memo </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"payment"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">add_text_memo</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">memo</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">  </span><span class="token comment" style="color:rgb(98, 114, 164)"># 28 UTF-8 bytes, free</span><br></div></code></pre></div></div>
<p><code>28 bytes</code>, included in the base fee. Done.</p>
<p>Ethereum doesn't have native memos. Instead, you encode data into the transaction itself:</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">memo </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"payment"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">data </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"0x"</span><span class="token plain"> </span><span class="token operator">+</span><span class="token plain"> memo</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">encode</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"utf-8"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token builtin" style="color:rgb(189, 147, 249)">hex</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">tx</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"data"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> data  </span><span class="token comment" style="color:rgb(98, 114, 164)"># Each byte ≈ 16 gas</span><br></div></code></pre></div></div>
<p>This is expensive. A <code>256-byte</code> memo costs <code>~4,000 gas</code>, which at current rates is about <code>$0.10</code>. So on Ethereum, you think twice before adding metadata to a transaction. On Stellar, you just throw it in. Different design, different tradeoffs.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-destination-problem">The Destination Problem<a href="https://lunik.tiwabbit.fr/blog/building-crypto-send-receive-scripts#the-destination-problem" class="hash-link" aria-label="Direct link to The Destination Problem" title="Direct link to The Destination Problem" translate="no">​</a></h2>
<p>When I tested sending to a non-existent account, I got completely different behaviors.</p>
<p>On Stellar, it fails:</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">server</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">load_account</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">destination</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">  </span><span class="token comment" style="color:rgb(98, 114, 164)"># Check if account exists</span><br></div></code></pre></div></div>
<p>Stellar makes you prove the destination account is real. If it doesn't exist, you can't send. You need a <code>create_account</code> operation first, then send. Two steps. This forces you to think about what you're doing.</p>
<p>On Ethereum, any address is valid:</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">destination </span><span class="token operator">=</span><span class="token plain"> Web3</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">to_checksum_address</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">destination</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">  </span><span class="token comment" style="color:rgb(98, 114, 164)"># Normalize with checksum</span><br></div></code></pre></div></div>
<p>Ethereum will happily send funds to an address that doesn't exist yet. The address is valid, the transaction succeeds, and now those funds are locked in a contract address nobody owns. Forever. The checksum helps you avoid typos, but it can't save you from a wrong address.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="fee-models-the-biggest-difference">Fee Models: The Biggest Difference<a href="https://lunik.tiwabbit.fr/blog/building-crypto-send-receive-scripts#fee-models-the-biggest-difference" class="hash-link" aria-label="Direct link to Fee Models: The Biggest Difference" title="Direct link to Fee Models: The Biggest Difference" translate="no">​</a></h2>
<p>This is where Stellar and Ethereum revealed their fundamentally different philosophies.</p>
<p>Stellar's fee model is dead simple:</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">base_fee </span><span class="token operator">=</span><span class="token plain"> server</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">fetch_base_fee</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">ops_count </span><span class="token operator">=</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">len</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">transaction</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">transaction</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">operations</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">fee </span><span class="token operator">=</span><span class="token plain"> base_fee </span><span class="token operator">*</span><span class="token plain"> ops_count </span><span class="token operator">/</span><span class="token plain"> </span><span class="token number">10_000_000</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">print</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"Fee: </span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">fee</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)"> XLM (</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">ops_count</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)"> operations)"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></div></code></pre></div></div>
<p>Fetch the base fee, multiply by operation count, done. Fees are predictable. They scale gently with network load. You can estimate gas costs accurately before submitting.</p>
<p>Ethereum's model is... more complex:</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">gas_price </span><span class="token operator">=</span><span class="token plain"> w3</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">eth</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">gas_price</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">tx </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"from"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> source</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"to"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> dest</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"value"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> amount_wei</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">gas_limit </span><span class="token operator">=</span><span class="token plain"> w3</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">eth</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">estimate_gas</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">tx</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">fee </span><span class="token operator">=</span><span class="token plain"> gas_limit </span><span class="token operator">*</span><span class="token plain"> gas_price</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">print</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"Fee: ~</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">Web3</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token string-interpolation interpolation">from_wei</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string-interpolation interpolation">fee</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token string-interpolation interpolation"> </span><span class="token string-interpolation interpolation string" style="color:rgb(255, 121, 198)">'ether'</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token string-interpolation interpolation format-spec">.8f</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)"> ETH"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></div></code></pre></div></div>
<p>You have to estimate. And the estimate can change. The <code>~</code> in my output isn't cosmetic—it's a warning that this fee is approximate. Gas prices spike. Estimates are wrong. You have to check again right before submitting. This forces a different workflow: <strong>always dry-run first</strong>.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="sequences-and-nonces-the-sync-problem">Sequences and Nonces: The Sync Problem<a href="https://lunik.tiwabbit.fr/blog/building-crypto-send-receive-scripts#sequences-and-nonces-the-sync-problem" class="hash-link" aria-label="Direct link to Sequences and Nonces: The Sync Problem" title="Direct link to Sequences and Nonces: The Sync Problem" translate="no">​</a></h2>
<p>Both blockchains use counters to ensure transactions don't get duplicated or reordered. But they surface the problem differently.</p>
<p>Stellar's sequence numbers auto-increment:</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">source_account </span><span class="token operator">=</span><span class="token plain"> server</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">load_account</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">public_key</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">builder </span><span class="token operator">=</span><span class="token plain"> TransactionBuilder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">source_account</span><span class="token operator">=</span><span class="token plain">source_account</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">transaction </span><span class="token operator">=</span><span class="token plain"> builder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">build</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></div></code></pre></div></div>
<p>Convenient, but also dangerous. If you submit two transactions concurrently, they'll have the same sequence number and one will fail. For scripts, I document it as a limitation. For production systems, you'd need a queue or mutex.</p>
<p>Ethereum's nonce is something you fetch fresh:</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">nonce </span><span class="token operator">=</span><span class="token plain"> w3</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">eth</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">get_transaction_count</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">source_address</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">tx </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">"nonce"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> nonce</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Same problem, same solution needed, but now it's explicit. You're responsible for fetching the nonce. You see the problem immediately. In a way, Ethereum forces you to think about concurrency.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="key-management-two-philosophies">Key Management: Two Philosophies<a href="https://lunik.tiwabbit.fr/blog/building-crypto-send-receive-scripts#key-management-two-philosophies" class="hash-link" aria-label="Direct link to Key Management: Two Philosophies" title="Direct link to Key Management: Two Philosophies" translate="no">​</a></h2>
<p>For testing, both blockchains let you use plain text private keys. But Ethereum also shipped with a production-ready key format: the encrypted keystore.</p>
<p>Stellar: plain text with restricted permissions:</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">with</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">open</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"keys/</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">public_key</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">.secret"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"w"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">as</span><span class="token plain"> f</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    os</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">chmod</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">f</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token number">0o600</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">  </span><span class="token comment" style="color:rgb(98, 114, 164)"># chmod 600</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    f</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">write</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">secret_key</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></div></code></pre></div></div>
<p>For testnet, this is fine. For mainnet, you're on your own.</p>
<p>Ethereum: built-in encryption:</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">password </span><span class="token operator">=</span><span class="token plain"> getpass</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">getpass</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"Choose password:"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">keystore </span><span class="token operator">=</span><span class="token plain"> Account</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">encrypt</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">private_key</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> password</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># Use it later</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">python send_eth</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">py </span><span class="token operator">-</span><span class="token operator">-</span><span class="token plain">keystore keystores</span><span class="token operator">/</span><span class="token number">0x123</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">json</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># Prompts for password at runtime</span><br></div></code></pre></div></div>
<p>So I built both workflows. For Ethereum mainnet, I always use encrypted keystores. The password is never stored—I type it on each use. It's an extra step, but it matters.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="rpc-reliability-stellar-vs-ethereum">RPC Reliability: Stellar vs. Ethereum<a href="https://lunik.tiwabbit.fr/blog/building-crypto-send-receive-scripts#rpc-reliability-stellar-vs-ethereum" class="hash-link" aria-label="Direct link to RPC Reliability: Stellar vs. Ethereum" title="Direct link to RPC Reliability: Stellar vs. Ethereum" translate="no">​</a></h2>
<p>Stellar has one official endpoint: Horizon. Rock solid.</p>
<p>Ethereum has hundreds of public RPC endpoints, and they're all flaky:</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">rpcs </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"https://ethereum-sepolia-rpc.publicnode.com"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"https://rpc.sepolia.org"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">for</span><span class="token plain"> rpc_url </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">in</span><span class="token plain"> rpcs</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    w3 </span><span class="token operator">=</span><span class="token plain"> Web3</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">Web3</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">HTTPProvider</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">rpc_url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> w3</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">is_connected</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">break</span><br></div></code></pre></div></div>
<p>Public RPCs go down. They rate-limit. They're unreliable. So I retry multiple endpoints. It's annoying, but necessary.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="error-messages-structured-vs-parsed">Error Messages: Structured vs. Parsed<a href="https://lunik.tiwabbit.fr/blog/building-crypto-send-receive-scripts#error-messages-structured-vs-parsed" class="hash-link" aria-label="Direct link to Error Messages: Structured vs. Parsed" title="Direct link to Error Messages: Structured vs. Parsed" translate="no">​</a></h2>
<p>When a transaction fails, Stellar is explicit:</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"># Horizon returns structured codes like:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># "tx_insufficient_balance", "tx_bad_seq", "op_no_destination"</span><br></div></code></pre></div></div>
<p>Read the code, understand what went wrong, fix it.</p>
<p>Ethereum returns error messages:</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"># RPC errors arrive as strings like:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># "insufficient funds", "nonce too low", "gas too low"</span><br></div></code></pre></div></div>
<p>I had to write string parsing logic to extract the actual error. Not elegant, but it works.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="audit-logging-non-negotiable">Audit Logging: Non-Negotiable<a href="https://lunik.tiwabbit.fr/blog/building-crypto-send-receive-scripts#audit-logging-non-negotiable" class="hash-link" aria-label="Direct link to Audit Logging: Non-Negotiable" title="Direct link to Audit Logging: Non-Negotiable" translate="no">​</a></h2>
<p>After a successful transaction on either blockchain, I log it immediately:</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">timestamp </span><span class="token operator">=</span><span class="token plain"> datetime</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">now</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">timezone</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">utc</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">isoformat</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">log_line </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">timestamp</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)"> | </span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">network</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)"> | from=</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">source</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)"> | to=</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">dest</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)"> | amount=</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">amount</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)"> | tx=</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">tx_hash</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">\n"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">with</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">open</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"transactions.</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">network</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">.log"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"a"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">as</span><span class="token plain"> f</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    f</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">write</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">log_line</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></div></code></pre></div></div>
<p>Never log private keys—just source, destination, amount, and tx hash. This is your receipt. Months later, you'll need proof of what happened. The log is that proof.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="dry-run-always-test-first">Dry-Run: Always Test First<a href="https://lunik.tiwabbit.fr/blog/building-crypto-send-receive-scripts#dry-run-always-test-first" class="hash-link" aria-label="Direct link to Dry-Run: Always Test First" title="Direct link to Dry-Run: Always Test First" translate="no">​</a></h2>
<p>Both scripts support a <code>--dry-run</code> flag that simulates the transaction without submitting:</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> dry_run</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">print</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"From   : </span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">source</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">print</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"To     : </span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">destination</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">print</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"Amount : </span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">amount</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">print</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"Fee    : </span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">fee</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">print</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"Balance: </span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">balance</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><br></div></code></pre></div></div>
<p>On Ethereum especially, this is critical. You see the gas cost before committing. You verify the balance. You check the destination address. The workflow becomes: dry-run testnet, dry-run mainnet, submit mainnet.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-real-lesson">The Real Lesson<a href="https://lunik.tiwabbit.fr/blog/building-crypto-send-receive-scripts#the-real-lesson" class="hash-link" aria-label="Direct link to The Real Lesson" title="Direct link to The Real Lesson" translate="no">​</a></h2>
<p>I built these scripts because <strong>I don't trust black boxes with my crypto</strong>. Every exchange, every wallet, every dapp is a centralized point of failure. Any one of them could have a bug that drains your account. Any one could get hacked. Any one could disappear.</p>
<p>But when I read my own code—when I wrote every line—I know exactly what happens when I hit enter. No hidden endpoints. No intermediaries. Just Python + standard libraries + direct blockchain calls.</p>
<p>Stellar and Ethereum are solid platforms. But they can't protect you from:</p>
<ul>
<li class="">A buggy wallet that miscalculates gas</li>
<li class="">An exchange that locks your account</li>
<li class="">A dapp that requests too many permissions</li>
<li class="">A private key stolen from a browser cache</li>
</ul>
<p>Self-custody through auditable code is the answer.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="if-youre-building-this-too">If You're Building This Too<a href="https://lunik.tiwabbit.fr/blog/building-crypto-send-receive-scripts#if-youre-building-this-too" class="hash-link" aria-label="Direct link to If You're Building This Too" title="Direct link to If You're Building This Too" translate="no">​</a></h2>
<ol>
<li class=""><strong>Trust code, not UIs</strong>, You should understand every line that moves your assets.</li>
<li class=""><strong>Start on testnet</strong>, Free funds. Zero risk. Perfect for learning.</li>
<li class=""><strong>Log everything</strong>, You need proof. The log is your receipt.</li>
<li class=""><strong>Dry-run first</strong>, See the fee. See the structure. Before you commit.</li>
<li class=""><strong>Keep it simple</strong>, No abstractions. No magic. Just straightforward steps.</li>
</ol>
<p>If you're not comfortable reading the code that moves your assets, don't use it. And if you're using a service you <em>can't</em> read the code for, you're trusting someone else's judgment with your money.</p>
<p><strong>Read the code. Audit it. Modify it. Own it.</strong></p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="source-code">Source Code<a href="https://lunik.tiwabbit.fr/blog/building-crypto-send-receive-scripts#source-code" class="hash-link" aria-label="Direct link to Source Code" title="Direct link to Source Code" translate="no">​</a></h2>
<p>Both scripts are open source:</p>
<ul>
<li class=""><strong>Stellar XLM</strong>: <a href="https://github.com/Lunik/stellar_xlm" target="_blank" rel="noopener noreferrer" class="">https://github.com/Lunik/stellar_xlm</a></li>
<li class=""><strong>Ethereum</strong>: <a href="https://github.com/Lunik/ethereum" target="_blank" rel="noopener noreferrer" class="">https://github.com/Lunik/ethereum</a></li>
</ul>]]></content>
        <author>
            <name>Guillaume MARTINEZ</name>
            <uri>https://github.com/Lunik</uri>
        </author>
        <category label="blockchain" term="blockchain"/>
        <category label="ethereum" term="ethereum"/>
        <category label="stellar" term="stellar"/>
        <category label="python" term="python"/>
        <category label="cryptocurrency" term="cryptocurrency"/>
        <category label="scripting" term="scripting"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Refreshing the blog — again]]></title>
        <id>https://lunik.tiwabbit.fr/blog/refreshing-the-blog</id>
        <link href="https://lunik.tiwabbit.fr/blog/refreshing-the-blog"/>
        <updated>2026-04-15T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[How and why I migrated my personal website from MkDocs + raw HTML to a Docusaurus + Vite monorepo]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2026-04-15-refreshing-the-blog/cover.png" alt="cover" class="img__Ss2"></p>
<p>A while ago I wrote <a class="" href="https://lunik.tiwabbit.fr/blog/init">a blog post about how I created this blog</a>. Back then it was <a href="https://getpelican.com/" target="_blank" rel="noopener noreferrer" class="">Pelican</a>, a <a href="https://www.python.org/" target="_blank" rel="noopener noreferrer" class="">Python</a> static site generator. Then I migrated it to <a href="https://www.mkdocs.org/" target="_blank" rel="noopener noreferrer" class="">MkDocs</a> — also <a href="https://www.python.org/" target="_blank" rel="noopener noreferrer" class="">Python</a>, a bit more modern, better support for my bilingual setup. Now here we are again. Third time.</p>
<p>This is the story of why I did it and how it went.</p>
<div class="theme-admonition theme-admonition-info admonition_IZjC alert alert--info"><div class="admonitionHeading_uVvU"><span class="admonitionIcon_HiR3"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</div><div class="admonitionContent_bl22"><p>If you are reading this on the blog, it means the migration is done. Congratulations, you survived the downtime.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-old-setup">The old setup<a href="https://lunik.tiwabbit.fr/blog/refreshing-the-blog#the-old-setup" class="hash-link" aria-label="Direct link to The old setup" title="Direct link to The old setup" translate="no">​</a></h2>
<p>Let me describe what I had before so you understand why I wanted to change it.</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="the-portfolio">The portfolio<a href="https://lunik.tiwabbit.fr/blog/refreshing-the-blog#the-portfolio" class="hash-link" aria-label="Direct link to The portfolio" title="Direct link to The portfolio" translate="no">​</a></h3>
<p>A raw <a href="https://developer.mozilla.org/en-US/docs/Web/HTML" target="_blank" rel="noopener noreferrer" class="">HTML</a>/<a href="https://developer.mozilla.org/en-US/docs/Web/CSS" target="_blank" rel="noopener noreferrer" class="">CSS</a>/<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript" target="_blank" rel="noopener noreferrer" class="">JavaScript</a> website. No build step. No bundler. Just files on a disk that I uploaded to <a href="https://aws.amazon.com/s3/" target="_blank" rel="noopener noreferrer" class="">S3</a>.</p>
<p>It worked. But it was painful to maintain. All the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS" target="_blank" rel="noopener noreferrer" class="">CSS</a> was in one 600-line file mixing colors, layout and components. There was no clear separation between data and presentation. The <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript" target="_blank" rel="noopener noreferrer" class="">JavaScript</a> was procedural, with <code>eval()</code> calls to map string property names to DOM attributes. Dependencies were pulled from CDNs with hardcoded version numbers in <code>&lt;script&gt;</code> tags.</p>
<p>Every time I needed to touch something I had to rediscover how it was put together.</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="the-blog">The blog<a href="https://lunik.tiwabbit.fr/blog/refreshing-the-blog#the-blog" class="hash-link" aria-label="Direct link to The blog" title="Direct link to The blog" translate="no">​</a></h3>
<p><a href="https://www.mkdocs.org/" target="_blank" rel="noopener noreferrer" class="">MkDocs</a> is a perfectly fine static site generator. I have nothing bad to say about it. But it was a separate project, with its own <a href="https://www.python.org/" target="_blank" rel="noopener noreferrer" class="">Python</a> environment, its own <a href="https://docs.gitlab.com/ee/ci/" target="_blank" rel="noopener noreferrer" class="">GitLab CI</a> pipeline, and its own S3 deployment. Two projects, two pipelines, two deployment processes.</p>
<p>And <a href="https://www.mkdocs.org/" target="_blank" rel="noopener noreferrer" class="">MkDocs</a> is not really designed for a blog. I was using the <a href="https://squidfunk.github.io/mkdocs-material/plugins/blog/" target="_blank" rel="noopener noreferrer" class="">MkDocs Material Blog plugin</a> which is great but adds a layer on top of something that wasn't built for that use case from the start.</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="the-ci-situation">The CI situation<a href="https://lunik.tiwabbit.fr/blog/refreshing-the-blog#the-ci-situation" class="hash-link" aria-label="Direct link to The CI situation" title="Direct link to The CI situation" translate="no">​</a></h3>
<p>Two <code>.gitlab-ci.yml</code> files. Two separate cache strategies. Two separate deploy jobs. When I wanted to make a change that touched both the portfolio and the blog — like updating the footer links — I had to coordinate two separate pipelines and two separate deployments. It was annoying.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="why-docusaurus-and-vite">Why Docusaurus and Vite<a href="https://lunik.tiwabbit.fr/blog/refreshing-the-blog#why-docusaurus-and-vite" class="hash-link" aria-label="Direct link to Why Docusaurus and Vite" title="Direct link to Why Docusaurus and Vite" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="docusaurus">Docusaurus<a href="https://lunik.tiwabbit.fr/blog/refreshing-the-blog#docusaurus" class="hash-link" aria-label="Direct link to Docusaurus" title="Direct link to Docusaurus" translate="no">​</a></h3>
<p><a href="https://docusaurus.io/" target="_blank" rel="noopener noreferrer" class="">Docusaurus</a> is a <a href="https://react.dev/" target="_blank" rel="noopener noreferrer" class="">React</a>-based static site generator made by <a href="https://opensource.fb.com/" target="_blank" rel="noopener noreferrer" class="">Meta</a>, primarily designed for documentation. But it has a blog mode that is quite good. It supports:</p>
<ul>
<li class="">Bilingual content out of the box with its i18n system</li>
<li class="">Reading time estimation</li>
<li class="">Author profiles</li>
<li class="">RSS/Atom feeds</li>
<li class="">Tag pages</li>
</ul>
<p>It's built on top of <a href="https://nodejs.org/" target="_blank" rel="noopener noreferrer" class="">Node.js</a> and <a href="https://www.npmjs.com/" target="_blank" rel="noopener noreferrer" class="">npm</a>, which means I'm back in the ecosystem I hate but at least it's the same ecosystem as the portfolio build tooling. Consistency has a value.</p>
<p>I considered <a href="https://astro.build/" target="_blank" rel="noopener noreferrer" class="">Astro</a> and <a href="https://www.11ty.dev/" target="_blank" rel="noopener noreferrer" class="">11ty</a>. <a href="https://astro.build/" target="_blank" rel="noopener noreferrer" class="">Astro</a> is interesting but it felt like more configuration for the same result. <a href="https://www.11ty.dev/" target="_blank" rel="noopener noreferrer" class="">11ty</a> is elegant in its simplicity but the <a href="https://docusaurus.io/" target="_blank" rel="noopener noreferrer" class="">Docusaurus</a> i18n support is hard to beat without writing it yourself.</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="vite">Vite<a href="https://lunik.tiwabbit.fr/blog/refreshing-the-blog#vite" class="hash-link" aria-label="Direct link to Vite" title="Direct link to Vite" translate="no">​</a></h3>
<p><a href="https://vitejs.dev/" target="_blank" rel="noopener noreferrer" class="">Vite</a> is the obvious choice for the portfolio. It's fast, it handles multi-page apps well, and it has first-class support for <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules" target="_blank" rel="noopener noreferrer" class="">ES modules</a>. The migration from raw <a href="https://developer.mozilla.org/en-US/docs/Web/HTML" target="_blank" rel="noopener noreferrer" class="">HTML</a> + procedural <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript" target="_blank" rel="noopener noreferrer" class="">JavaScript</a> to a proper <a href="https://vitejs.dev/" target="_blank" rel="noopener noreferrer" class="">Vite</a> multi-page app gave me the structure I needed without forcing me to adopt a component framework.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-monorepo-structure">The monorepo structure<a href="https://lunik.tiwabbit.fr/blog/refreshing-the-blog#the-monorepo-structure" class="hash-link" aria-label="Direct link to The monorepo structure" title="Direct link to The monorepo structure" translate="no">​</a></h2>
<p>The decision to merge the two projects into one repository was the most important one. Everything else follows from it.</p>
<div class="language-text codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-text codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">my_website_v2/</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── package.json          # npm workspaces root</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── packages/</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── website/          # Vite portfolio</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   └── blog/             # Docusaurus blog</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">└── dist/                 # assembled output</span><br></div></code></pre></div></div>
<p>The root <code>package.json</code> declares both packages as <a href="https://docs.npmjs.com/cli/using-npm/workspaces" target="_blank" rel="noopener noreferrer" class="">npm workspaces</a> and orchestrates the build:</p>
<div class="language-json codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-json codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"scripts"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"build"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"npm run build:website &amp;&amp; npm run build:blog &amp;&amp; npm run assemble"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"assemble"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"mkdir -p dist &amp;&amp; cp -r packages/website/dist/. dist/ &amp;&amp; mkdir -p dist/blog &amp;&amp; cp -r packages/blog/build/. dist/blog/"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>The portfolio builds into <code>dist/</code>, the blog builds into <code>dist/blog/</code>. The URL structure is preserved: <code>/</code> for the portfolio, <code>/blog/</code> for the blog. One <code>rclone sync</code> command on the CI to push everything to S3.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="migrating-the-portfolio">Migrating the portfolio<a href="https://lunik.tiwabbit.fr/blog/refreshing-the-blog#migrating-the-portfolio" class="hash-link" aria-label="Direct link to Migrating the portfolio" title="Direct link to Migrating the portfolio" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="css-refactor">CSS refactor<a href="https://lunik.tiwabbit.fr/blog/refreshing-the-blog#css-refactor" class="hash-link" aria-label="Direct link to CSS refactor" title="Direct link to CSS refactor" translate="no">​</a></h3>
<p>The first thing I did was extract the <a href="https://github.com/morhetz/gruvbox" target="_blank" rel="noopener noreferrer" class="">Gruvbox</a> color palette into <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties" target="_blank" rel="noopener noreferrer" class="">CSS custom properties</a>:</p>
<div class="language-css codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-css codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token selector pseudo-class" style="color:rgb(255, 121, 198)">:root</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token variable" style="color:rgb(189, 147, 249);font-style:italic">--gb-bg</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">      </span><span class="token hexcode color">#1d2021</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token variable" style="color:rgb(189, 147, 249);font-style:italic">--gb-bg1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">     </span><span class="token hexcode color">#282828</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token variable" style="color:rgb(189, 147, 249);font-style:italic">--gb-fg</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">      </span><span class="token hexcode color">#ebdbb2</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token variable" style="color:rgb(189, 147, 249);font-style:italic">--gb-cyan</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">    </span><span class="token hexcode color">#689d6a</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token variable" style="color:rgb(189, 147, 249);font-style:italic">--gb-yellow</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">  </span><span class="token hexcode color">#d79921</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token comment" style="color:rgb(98, 114, 164)">/* ... */</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Then replaced every hardcoded color literal in the component stylesheets with these variables. No visual change, but now the theme is in one place.</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="javascript-modules">JavaScript modules<a href="https://lunik.tiwabbit.fr/blog/refreshing-the-blog#javascript-modules" class="hash-link" aria-label="Direct link to JavaScript modules" title="Direct link to JavaScript modules" translate="no">​</a></h3>
<p>The main challenge was the <code>eval()</code> pattern. The original code used string property names to set dynamic CSS variables:</p>
<div class="language-javascript codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-javascript codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// Before</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">config</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token method function property-access" style="color:rgb(80, 250, 123)">forEach</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">function</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token parameter">item</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token known-class-name class-name">Object</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token method function property-access" style="color:rgb(80, 250, 123)">keys</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">item</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token method function property-access" style="color:rgb(80, 250, 123)">forEach</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">function</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token parameter">key</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token function" style="color:rgb(80, 250, 123)">eval</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"element."</span><span class="token plain"> </span><span class="token operator">+</span><span class="token plain"> key </span><span class="token operator">+</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">" = item[key]"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>I replaced this with an explicit property map:</p>
<div class="language-javascript codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-javascript codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// After</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> </span><span class="token constant" style="color:rgb(189, 147, 249)">PROPERTY_MAP</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token function-variable function" style="color:rgb(80, 250, 123)">textContent</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token parameter">el</span><span class="token parameter punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token parameter"> v</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token arrow operator">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> el</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token property-access">textContent</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> v </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token function-variable function" style="color:rgb(80, 250, 123)">href</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token parameter">el</span><span class="token parameter punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token parameter"> v</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token arrow operator">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> el</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token property-access">href</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> v </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token function-variable function" style="color:rgb(80, 250, 123)">style</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token parameter">el</span><span class="token parameter punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token parameter"> v</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token arrow operator">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> el</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token method function property-access" style="color:rgb(80, 250, 123)">setAttribute</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'style'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> v</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>Not as clever, but it works without <code>eval()</code> and it's readable.</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="uaparserjs">UAParser.js<a href="https://lunik.tiwabbit.fr/blog/refreshing-the-blog#uaparserjs" class="hash-link" aria-label="Direct link to UAParser.js" title="Direct link to UAParser.js" translate="no">​</a></h3>
<p>The portfolio uses <a href="https://github.com/faisalman/ua-parser-js" target="_blank" rel="noopener noreferrer" class="">UAParser.js</a> to display the visitor's browser and OS on the terminal homepage. Before it was loaded from a CDN:</p>
<div class="language-html codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-html codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">script</span><span class="token tag" style="color:rgb(255, 121, 198)"> </span><span class="token tag attr-name" style="color:rgb(241, 250, 140)">src</span><span class="token tag attr-value punctuation attr-equals" style="color:rgb(248, 248, 242)">=</span><span class="token tag attr-value punctuation" style="color:rgb(248, 248, 242)">"</span><span class="token tag attr-value" style="color:rgb(255, 121, 198)">https://cdn.jsdelivr.net/npm/ua-parser-js@1/src/ua-parser.min.js</span><span class="token tag attr-value punctuation" style="color:rgb(248, 248, 242)">"</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token script"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">script</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><br></div></code></pre></div></div>
<p>Now it's a proper <a href="https://www.npmjs.com/" target="_blank" rel="noopener noreferrer" class="">npm</a> dependency imported as an ES module:</p>
<div class="language-javascript codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-javascript codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token keyword module" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">UAParser</span><span class="token imports"> </span><span class="token imports punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token keyword module" style="color:rgb(189, 147, 249);font-style:italic">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'ua-parser-js'</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="migrating-the-blog-content">Migrating the blog content<a href="https://lunik.tiwabbit.fr/blog/refreshing-the-blog#migrating-the-blog-content" class="hash-link" aria-label="Direct link to Migrating the blog content" title="Direct link to Migrating the blog content" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="the-migration-script">The migration script<a href="https://lunik.tiwabbit.fr/blog/refreshing-the-blog#the-migration-script" class="hash-link" aria-label="Direct link to The migration script" title="Direct link to The migration script" translate="no">​</a></h3>
<p>I wrote a one-shot <a href="https://nodejs.org/" target="_blank" rel="noopener noreferrer" class="">Node.js</a> script to migrate all the <a href="https://www.mkdocs.org/" target="_blank" rel="noopener noreferrer" class="">MkDocs</a> posts to <a href="https://docusaurus.io/" target="_blank" rel="noopener noreferrer" class="">Docusaurus</a> format. The main things it had to handle:</p>
<p><strong>Frontmatter</strong>: <a href="https://www.mkdocs.org/" target="_blank" rel="noopener noreferrer" class="">MkDocs</a> Material uses <code>categories</code> and <code>tags</code> as separate fields, <a href="https://docusaurus.io/" target="_blank" rel="noopener noreferrer" class="">Docusaurus</a> only has <code>tags</code>. The script merges them and deduplicates. It also removes the <code>readtime</code> field since <a href="https://docusaurus.io/" target="_blank" rel="noopener noreferrer" class="">Docusaurus</a> computes reading time automatically.</p>
<p><strong>Admonitions</strong>: <a href="https://www.mkdocs.org/" target="_blank" rel="noopener noreferrer" class="">MkDocs</a> uses <code>!!! warning "Title"</code> syntax, <a href="https://docusaurus.io/" target="_blank" rel="noopener noreferrer" class="">Docusaurus</a> uses <code>:::warning[Title]</code>. A regex handles the conversion:</p>
<div class="language-javascript codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-javascript codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">content </span><span class="token operator">=</span><span class="token plain"> content</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token method function property-access" style="color:rgb(80, 250, 123)">replace</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token regex regex-delimiter">/</span><span class="token regex regex-source language-regex anchor function" style="color:rgb(80, 250, 123)">^</span><span class="token regex regex-source language-regex">!!!</span><span class="token regex regex-source language-regex char-set class-name">\s</span><span class="token regex regex-source language-regex quantifier number">+</span><span class="token regex regex-source language-regex group punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token regex regex-source language-regex char-set class-name">\w</span><span class="token regex regex-source language-regex quantifier number">+</span><span class="token regex regex-source language-regex group punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token regex regex-source language-regex group punctuation" style="color:rgb(248, 248, 242)">(?:</span><span class="token regex regex-source language-regex char-set class-name">\s</span><span class="token regex regex-source language-regex quantifier number">+</span><span class="token regex regex-source language-regex">"</span><span class="token regex regex-source language-regex group punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token regex regex-source language-regex char-class char-class-punctuation punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token regex regex-source language-regex char-class char-class-negation operator">^</span><span class="token regex regex-source language-regex char-class">"</span><span class="token regex regex-source language-regex char-class char-class-punctuation punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token regex regex-source language-regex quantifier number">*</span><span class="token regex regex-source language-regex group punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token regex regex-source language-regex">"</span><span class="token regex regex-source language-regex group punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token regex regex-source language-regex quantifier number">?</span><span class="token regex regex-source language-regex escape">\n</span><span class="token regex regex-source language-regex escape">\n</span><span class="token regex regex-source language-regex quantifier number">?</span><span class="token regex regex-source language-regex group punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token regex regex-source language-regex group punctuation" style="color:rgb(248, 248, 242)">(?:</span><span class="token regex regex-source language-regex group punctuation" style="color:rgb(248, 248, 242)">(?:</span><span class="token regex regex-source language-regex">    </span><span class="token regex regex-source language-regex alternation keyword" style="color:rgb(189, 147, 249);font-style:italic">|</span><span class="token regex regex-source language-regex escape">\t</span><span class="token regex regex-source language-regex group punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token regex regex-source language-regex char-class char-class-punctuation punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token regex regex-source language-regex char-class char-class-negation operator">^</span><span class="token regex regex-source language-regex char-class escape">\n</span><span class="token regex regex-source language-regex char-class char-class-punctuation punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token regex regex-source language-regex quantifier number">*</span><span class="token regex regex-source language-regex escape">\n</span><span class="token regex regex-source language-regex quantifier number">?</span><span class="token regex regex-source language-regex group punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token regex regex-source language-regex quantifier number">+</span><span class="token regex regex-source language-regex group punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token regex regex-delimiter">/</span><span class="token regex regex-flags">gm</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token parameter">_</span><span class="token parameter punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token parameter"> type</span><span class="token parameter punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token parameter"> title</span><span class="token parameter punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token parameter"> body</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token arrow operator">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> dedented </span><span class="token operator">=</span><span class="token plain"> body</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token method function property-access" style="color:rgb(80, 250, 123)">replace</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token regex regex-delimiter">/</span><span class="token regex regex-source language-regex anchor function" style="color:rgb(80, 250, 123)">^</span><span class="token regex regex-source language-regex group punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token regex regex-source language-regex">    </span><span class="token regex regex-source language-regex alternation keyword" style="color:rgb(189, 147, 249);font-style:italic">|</span><span class="token regex regex-source language-regex escape">\t</span><span class="token regex regex-source language-regex group punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token regex regex-delimiter">/</span><span class="token regex regex-flags">gm</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">''</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> header </span><span class="token operator">=</span><span class="token plain"> title </span><span class="token operator">?</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(255, 121, 198)">`</span><span class="token template-string string" style="color:rgb(255, 121, 198)">:::</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">${</span><span class="token template-string interpolation">type</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token template-string string" style="color:rgb(255, 121, 198)">[</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">${</span><span class="token template-string interpolation">title</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token template-string string" style="color:rgb(255, 121, 198)">]</span><span class="token template-string template-punctuation string" style="color:rgb(255, 121, 198)">`</span><span class="token plain"> </span><span class="token operator">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(255, 121, 198)">`</span><span class="token template-string string" style="color:rgb(255, 121, 198)">:::</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">${</span><span class="token template-string interpolation">type</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token template-string template-punctuation string" style="color:rgb(255, 121, 198)">`</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword control-flow" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(255, 121, 198)">`</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">${</span><span class="token template-string interpolation">header</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token template-string string" style="color:rgb(255, 121, 198)">\n</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">${</span><span class="token template-string interpolation">dedented</span><span class="token template-string interpolation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token template-string interpolation method function property-access" style="color:rgb(80, 250, 123)">trim</span><span class="token template-string interpolation punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token template-string interpolation punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token template-string string" style="color:rgb(255, 121, 198)">\n:::\n</span><span class="token template-string template-punctuation string" style="color:rgb(255, 121, 198)">`</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></div></code></pre></div></div>
<p><strong>Directory structure</strong>: <a href="https://www.mkdocs.org/" target="_blank" rel="noopener noreferrer" class="">MkDocs</a> posts are in <code>docs/en/posts/slug/index.md</code> with no date in the path. <a href="https://docusaurus.io/" target="_blank" rel="noopener noreferrer" class="">Docusaurus</a> wants <code>blog/YYYY-MM-DD-slug/index.md</code>. The script reads the <code>date:</code> field from frontmatter and prefixes the directory name.</p>
<p><strong>Truncation markers</strong>: <a href="https://www.mkdocs.org/" target="_blank" rel="noopener noreferrer" class="">MkDocs</a> uses <code>&lt;!-- more --&gt;</code> for the post excerpt cutoff. <a href="https://docusaurus.io/" target="_blank" rel="noopener noreferrer" class="">Docusaurus</a> uses <code>&lt;!-- truncate --&gt;</code>. A simple find and replace.</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="bilingual-content">Bilingual content<a href="https://lunik.tiwabbit.fr/blog/refreshing-the-blog#bilingual-content" class="hash-link" aria-label="Direct link to Bilingual content" title="Direct link to Bilingual content" translate="no">​</a></h3>
<p>The EN posts go in <code>packages/blog/blog/</code>. The FR posts go in <code>packages/blog/i18n/fr/docusaurus-plugin-content-blog/</code>. Images are stored with the EN post and FR posts reference them with the same relative path — <a href="https://docusaurus.io/" target="_blank" rel="noopener noreferrer" class="">Docusaurus</a> resolves them correctly at build time.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-fun-problems">The fun problems<a href="https://lunik.tiwabbit.fr/blog/refreshing-the-blog#the-fun-problems" class="hash-link" aria-label="Direct link to The fun problems" title="Direct link to The fun problems" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="node-25-and-the-localstorage-error">Node 25 and the localStorage error<a href="https://lunik.tiwabbit.fr/blog/refreshing-the-blog#node-25-and-the-localstorage-error" class="hash-link" aria-label="Direct link to Node 25 and the localStorage error" title="Direct link to Node 25 and the localStorage error" translate="no">​</a></h3>
<p>The <a href="https://docusaurus.io/" target="_blank" rel="noopener noreferrer" class="">Docusaurus</a> SSG build crashed on <a href="https://nodejs.org/" target="_blank" rel="noopener noreferrer" class="">Node.js</a> 25 with:</p>
<div class="language-text codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-text codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Cannot initialize local storage without a --localstorage-file path.</span><br></div></code></pre></div></div>
<p><a href="https://nodejs.org/" target="_blank" rel="noopener noreferrer" class="">Node.js</a> 25 ships the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API" target="_blank" rel="noopener noreferrer" class="">Web Storage API</a> natively, but unlike browsers, it requires an explicit file path to persist data. <a href="https://docusaurus.io/" target="_blank" rel="noopener noreferrer" class="">Docusaurus</a> uses <code>localStorage</code> internally during SSG for theme persistence and had no idea this API now needed initialization.</p>
<p>The fix is a <a href="https://nodejs.org/" target="_blank" rel="noopener noreferrer" class="">Node.js</a> CLI flag:</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">NODE_OPTIONS</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">'--localstorage-file=/tmp/docusaurus-ls'</span><span class="token plain"> docusaurus build</span><br></div></code></pre></div></div>
<p>It's in the <code>package.json</code> build script and in the <a href="https://docs.gitlab.com/ee/ci/" target="_blank" rel="noopener noreferrer" class="">GitLab CI</a> <code>variables</code> block.</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="non-breaking-spaces">Non-breaking spaces<a href="https://lunik.tiwabbit.fr/blog/refreshing-the-blog#non-breaking-spaces" class="hash-link" aria-label="Direct link to Non-breaking spaces" title="Direct link to Non-breaking spaces" translate="no">​</a></h3>
<p>HTML collapses consecutive regular spaces into one. Everyone knows that. Fewer people remember it when writing strings that will be injected as <code>innerHTML</code>.</p>
<p>The terminal homepage has two commands whose output relies on column alignment: <code>locate</code> (links padded to a common width before the <code>#</code> comment) and <code>ls -l /bin</code> (file sizes and dates right-aligned). The original source used U+00A0 non-breaking spaces for that padding, not regular spaces. In HTML, <code>&amp;nbsp;</code> or its Unicode equivalent is the only whitespace character that does not collapse.</p>
<p>During the migration, every text editor, diff tool, and copy-paste involved in moving those strings treated U+00A0 as an ordinary space. They all came out as U+0020. The columns disappeared silently — no build error, no test failure, just a cosmetic regression that you only notice when you look at the page.</p>
<p>The fix is surgical: find the original source, extract the raw bytes, restore them. A one-liner in Python to grep the original file and count <code>\xa0</code> occurrences confirmed it, then a direct byte-level substitution put them back.</p>
<p>Lesson: if column alignment in HTML matters, use a <code>&lt;pre&gt;</code> or <code>white-space: pre</code> and keep the string in a separate file where a linter cannot touch it.</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="webpack-5106-and-webpackbar">webpack 5.106 and webpackbar<a href="https://lunik.tiwabbit.fr/blog/refreshing-the-blog#webpack-5106-and-webpackbar" class="hash-link" aria-label="Direct link to webpack 5.106 and webpackbar" title="Direct link to webpack 5.106 and webpackbar" translate="no">​</a></h3>
<p><a href="https://docusaurus.io/" target="_blank" rel="noopener noreferrer" class="">Docusaurus</a> 3 uses <a href="https://webpack.js.org/" target="_blank" rel="noopener noreferrer" class="">webpack</a> 5 internally. <a href="https://github.com/unjs/webpackbar" target="_blank" rel="noopener noreferrer" class="">webpackbar</a> — the progress bar plugin — was updated to 6.0.1, which passed options (<code>name</code>, <code>color</code>, <code>reporters</code>) to <a href="https://webpack.js.org/" target="_blank" rel="noopener noreferrer" class="">webpack</a>'s <code>ProgressPlugin</code>. But <a href="https://webpack.js.org/" target="_blank" rel="noopener noreferrer" class="">webpack</a> 5.100+ removed those options from <code>ProgressPlugin</code>.</p>
<p>The result: the blog build crashed at startup with an obscure plugin options error.</p>
<p>The fix was to pin <a href="https://webpack.js.org/" target="_blank" rel="noopener noreferrer" class="">webpack</a> to <code>5.95.0</code> as a direct <code>devDependency</code> in <code>packages/blog/package.json</code>. npm workspace resolution picks it up and uses it instead of resolving the latest 5.x version.</p>
<div class="language-json codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-json codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"devDependencies"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"webpack"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"5.95.0"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Not great, but it works until <a href="https://docusaurus.io/" target="_blank" rel="noopener noreferrer" class="">Docusaurus</a> ships a fix upstream.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-ci-pipeline">The CI pipeline<a href="https://lunik.tiwabbit.fr/blog/refreshing-the-blog#the-ci-pipeline" class="hash-link" aria-label="Direct link to The CI pipeline" title="Direct link to The CI pipeline" translate="no">​</a></h2>
<p>The new <code>.gitlab-ci.yml</code> is three stages: <code>install</code>, <code>build</code>, <code>deploy</code>.</p>
<div class="language-text codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-text codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">install → build → pages (develop branch)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                → publish (master branch)</span><br></div></code></pre></div></div>
<p>The <code>pages</code> job copies <code>dist/</code> to <code>public/</code> for <a href="https://docs.gitlab.com/ee/user/project/pages/" target="_blank" rel="noopener noreferrer" class="">GitLab Pages</a> on the <code>develop</code> branch. The <code>publish</code> job runs <code>rclone sync</code> to push <code>dist/</code> to S3 on the <code>master</code> branch.</p>
<p>Before this, I had two pipelines with two separate S3 deploy jobs, one of which used an exclusion pattern to avoid overwriting the blog that was deployed from the other pipeline. Now it's a single <code>rclone sync</code> with no exclusions:</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">rclone </span><span class="token function" style="color:rgb(80, 250, 123)">sync</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--checksum</span><span class="token plain"> --delete-during </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--quiet</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  --s3-provider AWS </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  --s3-access-key-id </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$AWS_ACCESS_KEY_ID</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  --s3-secret-access-key </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$AWS_SECRET_ACCESS_KEY</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  --s3-region eu-west-3 </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  ./dist :s3:website-lunik.tiwabbit.fr</span><br></div></code></pre></div></div>
<p><code>--delete-during</code> removes files from the bucket that no longer exist in <code>dist/</code> as the sync runs. <code>--checksum</code> uses checksums rather than modification times to detect changes — more reliable for files that get reassembled from the same source. Clean.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="conclusion">Conclusion<a href="https://lunik.tiwabbit.fr/blog/refreshing-the-blog#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>Three iterations of this blog. <a href="https://getpelican.com/" target="_blank" rel="noopener noreferrer" class="">Pelican</a>, then <a href="https://www.mkdocs.org/" target="_blank" rel="noopener noreferrer" class="">MkDocs</a>, now <a href="https://docusaurus.io/" target="_blank" rel="noopener noreferrer" class="">Docusaurus</a>. Each time the tooling gets better and the maintenance overhead gets lower.</p>
<p>The monorepo was the right call. Having the portfolio and the blog in the same repository, with the same build pipeline, is genuinely less annoying than maintaining two separate projects. I should have done it earlier.</p>
<p>The source is at <a href="https://gitlab.com/Lunik/my_website_v2" target="_blank" rel="noopener noreferrer" class="">gitlab.com/Lunik/my_website_v2</a>.</p>]]></content>
        <author>
            <name>Guillaume MARTINEZ</name>
            <uri>https://github.com/Lunik</uri>
        </author>
        <category label="blog" term="blog"/>
        <category label="website" term="website"/>
        <category label="docusaurus" term="docusaurus"/>
        <category label="vite" term="vite"/>
        <category label="npm" term="npm"/>
        <category label="nodejs" term="nodejs"/>
        <category label="monorepo" term="monorepo"/>
        <category label="gitlab-ci" term="gitlab-ci"/>
        <category label="devops" term="devops"/>
        <category label="sysops" term="sysops"/>
        <category label="it" term="it"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Apple Keyframer Saturn SVG]]></title>
        <id>https://lunik.tiwabbit.fr/blog/apple-keyframer-saturn-svg</id>
        <link href="https://lunik.tiwabbit.fr/blog/apple-keyframer-saturn-svg"/>
        <updated>2024-05-22T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Simply the SVG used by Apple in their Keyframer demo application]]></summary>
        <content type="html"><![CDATA[<p>I have been following Apple machine learning research for a while now and I have been stunned by their <a href="https://machinelearning.apple.com/research/keyframer" target="_blank" rel="noopener noreferrer" class="">Keyframer demo application</a>. I have been trying to replicate the Saturn SVG used in the demo and I think I have come up with a pretty good approximation.</p>
<p>Right click on the image below and save it as an SVG file.</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2024-05-22-apple-keyframer-saturn-svg/saturn.svg" alt="saturn" class="img__Ss2"></p>
<p><strong>Don't forget to credit Apple for the original design. All right reserved. And me for the approximation.</strong></p>]]></content>
        <author>
            <name>Guillaume MARTINEZ</name>
            <uri>https://github.com/Lunik</uri>
        </author>
        <category label="apple" term="apple"/>
        <category label="svg" term="svg"/>
        <category label="genai" term="genai"/>
        <category label="keyframer" term="keyframer"/>
        <category label="saturn" term="saturn"/>
        <category label="transform" term="transform"/>
        <category label="animation" term="animation"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Python Virtual Environments Paradise]]></title>
        <id>https://lunik.tiwabbit.fr/blog/python-venv-paradise</id>
        <link href="https://lunik.tiwabbit.fr/blog/python-venv-paradise"/>
        <updated>2024-02-20T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Why you should use Python Virtual Environments and how to use them.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2024-02-20-python-venv-paradise/cover.png" alt="cover" class="img__Ss2"></p>
<p>In this article, I will explain why Python Virtual Environments are so cool when you know how to use them properly. I will also show you how to use them the right way.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="what-are-we-talking-about-">What are we talking about ?<a href="https://lunik.tiwabbit.fr/blog/python-venv-paradise#what-are-we-talking-about-" class="hash-link" aria-label="Direct link to What are we talking about ?" title="Direct link to What are we talking about ?" translate="no">​</a></h2>
<p>Python Virtual Environments are a way to isolate your Python environment from the system's Python environment. This is useful when you are working on multiple projects that require different versions of Python or different versions of the same package.</p>
<p>I have heard many times that people are afraid of using Python Virtual Environments because they think it is complicated and unnecessary. But at the same time, there are more than hundreds of python packages that tries to solve the same problem : pyenv, virtualenv, pipenv, conda, poetry, pew, and many more.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="a-little-bit-of-history">A little bit of history<a href="https://lunik.tiwabbit.fr/blog/python-venv-paradise#a-little-bit-of-history" class="hash-link" aria-label="Direct link to A little bit of history" title="Direct link to A little bit of history" translate="no">​</a></h2>
<p>Let's go back to the basics. Wow there is a built-in module for that : <code>venv</code> ! Who knew ?! It was first introduced in Python <code>3.3</code> with the <a href="https://peps.python.org/pep-0405/" target="_blank" rel="noopener noreferrer" class="">PEP 405 – Python Virtual Environments</a>.</p>
<blockquote>
<p>This PEP proposes to add to Python a mechanism for lightweight “virtual environments” with their own site directories, optionally isolated from system site directories. Each virtual environment has its own Python binary (allowing creation of environments with various Python versions) and can have its own independent set of installed Python packages in its site directories, but shares the standard library with the base installed Python.</p>
</blockquote>
<p>So what about all these people trying to solve the same problem ? Well, they are trying to make it easier to use and to manage. But in the end, they all provide the same thing : a way to isolate your Python environment.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="how-to-use-them-the-right-way-">How to use them the right way ?<a href="https://lunik.tiwabbit.fr/blog/python-venv-paradise#how-to-use-them-the-right-way-" class="hash-link" aria-label="Direct link to How to use them the right way ?" title="Direct link to How to use them the right way ?" translate="no">​</a></h2>
<p>You only need to know 3 commands to use Python Virtual Environments the right way.</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="create-a-new-virtual-environment">Create a new virtual environment<a href="https://lunik.tiwabbit.fr/blog/python-venv-paradise#create-a-new-virtual-environment" class="hash-link" aria-label="Direct link to Create a new virtual environment" title="Direct link to Create a new virtual environment" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bash codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">python </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-m</span><span class="token plain"> venv myenv</span><br></div></code></pre></div></div>
<p>This command will create a new directory called <code>myenv</code> that contains a Python environment. You can replace <code>myenv</code> with any name you want.
The most common name is <code>venv</code> of <code>.venv</code>. (Don't forget to add it to your <code>.gitignore</code> file if you are using git).</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="activate-the-virtual-environment">Activate the virtual environment<a href="https://lunik.tiwabbit.fr/blog/python-venv-paradise#activate-the-virtual-environment" class="hash-link" aria-label="Direct link to Activate the virtual environment" title="Direct link to Activate the virtual environment" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bash codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token builtin class-name" style="color:rgb(189, 147, 249)">source</span><span class="token plain"> myenv/bin/activate</span><br></div></code></pre></div></div>
<p>This command will activate the virtual environment. You can see that the prompt has changed. It now contains the name of the virtual environment in the beginning of the line.</p>
<div class="language-bash codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bash codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">myenv</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> user@host:~$</span><br></div></code></pre></div></div>
<p>You can check the Python binaries that are used by the virtual environment with the following command.</p>
<div class="language-bash codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bash codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">which</span><span class="token plain"> python</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="deactivate-the-virtual-environment">Deactivate the virtual environment<a href="https://lunik.tiwabbit.fr/blog/python-venv-paradise#deactivate-the-virtual-environment" class="hash-link" aria-label="Direct link to Deactivate the virtual environment" title="Direct link to Deactivate the virtual environment" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bash codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">deactivate</span><br></div></code></pre></div></div>
<p>This command will deactivate the virtual environment. You are now back to the system's Python environment.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="now-what-">Now what ?<a href="https://lunik.tiwabbit.fr/blog/python-venv-paradise#now-what-" class="hash-link" aria-label="Direct link to Now what ?" title="Direct link to Now what ?" translate="no">​</a></h2>
<p>Now that you know how to use Python Virtual Environments, you can use them in your projects.</p>
<p>In my routine, when I start a new project or when I work on an existing project, the first thing I do is to create a new virtual environment. Then I activate it and I install the required packages.</p>
<div class="language-bash codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bash codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">python </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-m</span><span class="token plain"> venv .venv</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">source</span><span class="token plain"> .venv/bin/activate</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">pip </span><span class="token function" style="color:rgb(80, 250, 123)">install</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-r</span><span class="token plain"> requirements.txt</span><br></div></code></pre></div></div>
<p>This allows me to work on multiple projects that require different versions of Python or different versions of the same package without any conflicts.
Without this technique, I would have to deal with a lot of conflicts and I would have to uninstall and reinstall packages all the time.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="conclusion">Conclusion<a href="https://lunik.tiwabbit.fr/blog/python-venv-paradise#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>Python Virtual Environments are a great way to isolate your Python environment from the system's Python environment. They are easy to use and they are very useful when you are working on multiple projects that require different versions of Python or different versions of the same package. You should use them in your projects. It will save you a lot of time and headaches.</p>
<p>And remember, Python already provides a built-in module to create virtual environments. You don't need to use any third-party package to do that. <strong>Trust Python, not some random package you found on the internet.</strong></p>]]></content>
        <author>
            <name>Guillaume MARTINEZ</name>
            <uri>https://github.com/Lunik</uri>
        </author>
        <category label="python" term="python"/>
        <category label="development" term="development"/>
        <category label="venv" term="venv"/>
        <category label="virtualenv" term="virtualenv"/>
        <category label="pip" term="pip"/>
        <category label="local" term="local"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Configure default boot image in Grub2]]></title>
        <id>https://lunik.tiwabbit.fr/blog/config-default-grub2-boot-img</id>
        <link href="https://lunik.tiwabbit.fr/blog/config-default-grub2-boot-img"/>
        <updated>2024-02-08T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Simple guide to configure the default boot image in Grub2]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2024-02-08-config-default-grub2-boot-img/cover.jpg" alt="cover" class="img__Ss2"></p>
<p>Today I have upgraded my <a href="https://fedoraproject.org/" target="_blank" rel="noopener noreferrer" class="">Fedora</a> server from version <code>33</code> to <code>38</code> but all did not go as planned.</p>
<p>I have <a href="https://github.com/openzfs/zfs" target="_blank" rel="noopener noreferrer" class="">OpenZFS</a> installed to handle my storage and the current released version of <a href="https://github.com/openzfs/zfs" target="_blank" rel="noopener noreferrer" class="">OpenZFS</a> (<a href="https://github.com/openzfs/zfs/releases/tag/zfs-2.2.2" target="_blank" rel="noopener noreferrer" class="">2.2.2</a>) is not compatible with the latest <a href="https://www.kernel.org/" target="_blank" rel="noopener noreferrer" class="">kernel</a> (<code>6.7</code>). I was stuck with my new <a href="https://www.kernel.org/" target="_blank" rel="noopener noreferrer" class="">kernel</a> and unable to access my data.</p>
<p>For reference here is the error message I was getting:</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">zfs list</span><br></div></code></pre></div></div>
<p>Output:</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">The ZFS modules cannot be auto-loaded.</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Try running </span><span class="token string" style="color:rgb(255, 121, 198)">'modprobe zfs'</span><span class="token plain"> as root to manually load them.</span><br></div></code></pre></div></div>
<p>And:</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">modprobe zfs</span><br></div></code></pre></div></div>
<p>Output:</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">modprobe: FATAL: Module zfs not found </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">in</span><span class="token plain"> directory /lib/modules/6.7.3-100.fc38.x86_64</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="troubleshooting">Troubleshooting<a href="https://lunik.tiwabbit.fr/blog/config-default-grub2-boot-img#troubleshooting" class="hash-link" aria-label="Direct link to Troubleshooting" title="Direct link to Troubleshooting" translate="no">​</a></h2>
<p>The first thing I did was to check what <a href="https://www.kernel.org/" target="_blank" rel="noopener noreferrer" class="">kernel</a> version is supported by the latest ZFS release (<a href="https://github.com/openzfs/zfs/releases/tag/zfs-2.2.2" target="_blank" rel="noopener noreferrer" class="">2.2.2</a>) and I found out that the latest supported <a href="https://www.kernel.org/" target="_blank" rel="noopener noreferrer" class="">kernel</a> is <code>6.6</code>. I missed it by one minor version.</p>
<p>Well that's not a big deal, I can just install the previous <a href="https://www.kernel.org/" target="_blank" rel="noopener noreferrer" class="">kernel</a> and boot on it, right?</p>
<p>Let's see what kernels are available:</p>
<div class="language-bash codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bash codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">dnf </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--releasever</span><span class="token operator">=</span><span class="token number">38</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--showduplicates</span><span class="token plain"> list kernel</span><br></div></code></pre></div></div>
<p>Output:</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Paquets disponibles</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">kernel.x86_64          </span><span class="token number">6.2</span><span class="token plain">.9-300.fc38          fedora  </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">kernel.x86_64          </span><span class="token number">6.7</span><span class="token plain">.3-100.fc38          pdates </span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="install-previous-kernel">Install previous kernel<a href="https://lunik.tiwabbit.fr/blog/config-default-grub2-boot-img#install-previous-kernel" class="hash-link" aria-label="Direct link to Install previous kernel" title="Direct link to Install previous kernel" translate="no">​</a></h2>
<p>Not really what I was expecting. I was hoping to see the previous <a href="https://www.kernel.org/" target="_blank" rel="noopener noreferrer" class="">kernel</a> version <code>6.6</code> but it's not there.
Let's try to install the <code>6.2.9</code> version and see what happens:</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">dnf </span><span class="token function" style="color:rgb(80, 250, 123)">install</span><span class="token plain"> kernel-6.2.9-300.fc38</span><br></div></code></pre></div></div>
<p>Let's reboot and see if it works.</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">uname</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-r</span><br></div></code></pre></div></div>
<p>Output:</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token number">6.7</span><span class="token plain">.3-100.fc38.x86_64</span><br></div></code></pre></div></div>
<p>After rebooting I was still booting on the latest <a href="https://www.kernel.org/" target="_blank" rel="noopener noreferrer" class="">kernel</a> <code>6.7</code>.</p>
<p>Let's plug a monitor and see what's happening during the boot process.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="grub2">Grub2<a href="https://lunik.tiwabbit.fr/blog/config-default-grub2-boot-img#grub2" class="hash-link" aria-label="Direct link to Grub2" title="Direct link to Grub2" translate="no">​</a></h2>
<p>During the boot process, I got the Grub2 menu and I was able to select the previous <a href="https://www.kernel.org/" target="_blank" rel="noopener noreferrer" class="">kernel</a> version <code>6.2.9</code> but it was not the default boot image. I had to manually select it every time I rebooted the server.</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="troubleshooting-again">Troubleshooting again<a href="https://lunik.tiwabbit.fr/blog/config-default-grub2-boot-img#troubleshooting-again" class="hash-link" aria-label="Direct link to Troubleshooting again" title="Direct link to Troubleshooting again" translate="no">​</a></h3>
<p>Let's have a look at the Grub2 configuration to see what's the default configuration:</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">cat</span><span class="token plain"> /etc/default/grub</span><br></div></code></pre></div></div>
<p>Output:</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">GRUB_TIMEOUT</span><span class="token operator">=</span><span class="token number">5</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">GRUB_DISTRIBUTOR</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">$(</span><span class="token string variable function" style="color:rgb(80, 250, 123);font-style:italic">sed</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic"> </span><span class="token string variable string" style="color:rgb(255, 121, 198);font-style:italic">'s, release .*$,,g'</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic"> /etc/system-release</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">)</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">GRUB_DEFAULT</span><span class="token operator">=</span><span class="token plain">saved</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">GRUB_DISABLE_SUBMENU</span><span class="token operator">=</span><span class="token plain">true</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">GRUB_TERMINAL_OUTPUT</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"console"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">GRUB_CMDLINE_LINUX</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"resume=/dev/mapper/fedora_nubs-swap rd.lvm.lv=fedora_nubs/root rd.lvm.lv=fedora_nubs/swap rhgb quiet"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">GRUB_DISABLE_RECOVERY</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"true"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">GRUB_ENABLE_BLSCFG</span><span class="token operator">=</span><span class="token plain">true</span><br></div></code></pre></div></div>
<p>Hum. <code>GRUB_DEFAULT=saved</code>. Loooks like the default boot image is saved somewhere. Let's have a look at the Grub2 configuration file:</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">grep</span><span class="token plain"> saved /etc/grub2.cfg</span><br></div></code></pre></div></div>
<p>Output:</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">   </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">set</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">${saved_entry}</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">${prev_saved_entry}</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">then</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">set</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">saved_entry</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">${prev_saved_entry}</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  save_env saved_entry</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">set</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">prev_saved_entry</span><span class="token operator">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  save_env prev_saved_entry</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">function</span><span class="token plain"> </span><span class="token function-name function" style="color:rgb(80, 250, 123)">savedefault</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">saved_entry</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token string variable" style="color:rgb(189, 147, 249);font-style:italic">${chosen}</span><span class="token string" style="color:rgb(255, 121, 198)">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    save_env saved_entry</span><br></div></code></pre></div></div>
<p>The grub2 configuration file is quite complex and I don't want to mess with it but it seeams that it sets the default boot image to the <code>saved_entry</code> variable.</p>
<p>I found out there is a binary to work with grub2 environment variables: <code>grub2-editenv</code>.</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">grub2-editenv list</span><br></div></code></pre></div></div>
<p>Output:</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">saved_entry</span><span class="token operator">=</span><span class="token plain">8bfb6e63623d4ee6aab1634ebafdd5d4-5.6.15-200.fc31.x86_64</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">kernelopts</span><span class="token operator">=</span><span class="token plain">root</span><span class="token operator">=</span><span class="token plain">/dev/mapper/fedora_nubs-root ro </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">resume</span><span class="token operator">=</span><span class="token plain">/dev/mapper/fedora_nubs-swap </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">rd.lvm.lv</span><span class="token operator">=</span><span class="token plain">fedora_nubs/root </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">rd.lvm.lv</span><span class="token operator">=</span><span class="token plain">fedora_nubs/swap rhgb quiet </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">systemd.unified_cgroup_hierarchy</span><span class="token operator">=</span><span class="token number">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">boot_success</span><span class="token operator">=</span><span class="token number">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">boot_indeterminate</span><span class="token operator">=</span><span class="token number">1</span><br></div></code></pre></div></div>
<p><code>5.6</code> ! That's an old kernel version. I guess my previous upgrade from <a href="https://fedoraproject.org/" target="_blank" rel="noopener noreferrer" class="">Fedora</a> 31 was not as clean as I thought.</p>
<p>How can I change the default boot image ? And what is this long hexadecimal string at the beginning of the <code>saved_entry</code> variable ?</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="changing-the-default-boot-image">Changing the default boot image<a href="https://lunik.tiwabbit.fr/blog/config-default-grub2-boot-img#changing-the-default-boot-image" class="hash-link" aria-label="Direct link to Changing the default boot image" title="Direct link to Changing the default boot image" translate="no">​</a></h3>
<p>We first need to find the list of available boot images. I found another handy binary to do that <code>grubby</code>.</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">grubby </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--info</span><span class="token plain"> ALL</span><br></div></code></pre></div></div>
<p>Output:</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">index</span><span class="token operator">=</span><span class="token number">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">kernel</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"/boot/vmlinuz-6.7.3-100.fc38.x86_64"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">args</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"ro resume=/dev/mapper/fedora_nubs-swap rd.lvm.lv=fedora_nubs/root rd.lvm.lv=fedora_nubs/swap rhgb quiet systemd.unified_cgroup_hierarchy=0"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">root</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"/dev/mapper/fedora_nubs-root"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">initrd</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"/boot/initramfs-6.7.3-100.fc38.x86_64.img"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">title</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"Fedora Linux (6.7.3-100.fc38.x86_64) 38 (Server Edition)"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">id</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"8bfb6e63623d4ee6aab1634ebafdd5d4-6.7.3-100.fc38.x86_64"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">index</span><span class="token operator">=</span><span class="token number">1</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">kernel</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"/boot/vmlinuz-6.5.12-100.fc37.x86_64"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">args</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"ro resume=/dev/mapper/fedora_nubs-swap rd.lvm.lv=fedora_nubs/root rd.lvm.lv=fedora_nubs/swap rhgb quiet systemd.unified_cgroup_hierarchy=0"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">root</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"/dev/mapper/fedora_nubs-root"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">initrd</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"/boot/initramfs-6.5.12-100.fc37.x86_64.img"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">title</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"Fedora Linux (6.5.12-100.fc37.x86_64) 37 (Server Edition)"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">id</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"8bfb6e63623d4ee6aab1634ebafdd5d4-6.5.12-100.fc37.x86_64"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">index</span><span class="token operator">=</span><span class="token number">2</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">kernel</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"/boot/vmlinuz-6.2.9-300.fc38.x86_64"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">args</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"ro resume=/dev/mapper/fedora_nubs-swap rd.lvm.lv=fedora_nubs/root rd.lvm.lv=fedora_nubs/swap rhgb quiet systemd.unified_cgroup_hierarchy=0"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">root</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"/dev/mapper/fedora_nubs-root"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">initrd</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"/boot/initramfs-6.2.9-300.fc38.x86_64.img"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">title</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"Fedora Linux (6.2.9-300.fc38.x86_64) 38 (Server Edition)"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">id</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"8bfb6e63623d4ee6aab1634ebafdd5d4-6.2.9-300.fc38.x86_64"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">index</span><span class="token operator">=</span><span class="token number">3</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">kernel</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"/boot/vmlinuz-0-rescue-8bfb6e63623d4ee6aab1634ebafdd5d4"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">args</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"ro resume=/dev/mapper/fedora_nubs-swap rd.lvm.lv=fedora_nubs/root rd.lvm.lv=fedora_nubs/swap rhgb quiet systemd.unified_cgroup_hierarchy=0"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">root</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"/dev/mapper/fedora_nubs-root"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">initrd</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"/boot/initramfs-0-rescue-8bfb6e63623d4ee6aab1634ebafdd5d4.img"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">title</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"Fedora (0-rescue-8bfb6e63623d4ee6aab1634ebafdd5d4) 30 (Thirty)"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">id</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"8bfb6e63623d4ee6aab1634ebafdd5d4-0-rescue"</span><br></div></code></pre></div></div>
<p>Wait a minute. The output list is really similar to what I have on my monitor during the boot process. And the <code>id</code> field as the same format as the <code>saved_entry</code> variable.</p>
<p>I guess if I change the <code>saved_entry</code> variable to the <code>id</code> of the boot image I want to boot on, it should work.</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">grub2-editenv - </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">set</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">saved_entry</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">'8bfb6e63623d4ee6aab1634ebafdd5d4-6.2.9-300.fc38.x86_64'</span><br></div></code></pre></div></div>
<p>Checking the <code>saved_entry</code> variable:</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">grub2-editenv list</span><br></div></code></pre></div></div>
<p>Output:</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">saved_entry</span><span class="token operator">=</span><span class="token plain">8bfb6e63623d4ee6aab1634ebafdd5d4-6.2.9-300.fc38.x86_64</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">kernelopts</span><span class="token operator">=</span><span class="token plain">root</span><span class="token operator">=</span><span class="token plain">/dev/mapper/fedora_nubs-root ro </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">resume</span><span class="token operator">=</span><span class="token plain">/dev/mapper/fedora_nubs-swap </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">rd.lvm.lv</span><span class="token operator">=</span><span class="token plain">fedora_nubs/root </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">rd.lvm.lv</span><span class="token operator">=</span><span class="token plain">fedora_nubs/swap rhgb quiet </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">systemd.unified_cgroup_hierarchy</span><span class="token operator">=</span><span class="token number">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">boot_success</span><span class="token operator">=</span><span class="token number">1</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">boot_indeterminate</span><span class="token operator">=</span><span class="token number">1</span><br></div></code></pre></div></div>
<p>Looks good. Time for the final test. Rebooting the server...</p>
<p>I see a the Grub2 menu and the default selected image is the one I set. Don't move ... 5 ... 4 ... 3 ... 2 ... 1 ... 0.</p>
<p>It boots !</p>
<p>Last check:</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">uname</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-r</span><br></div></code></pre></div></div>
<p>Output:</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token number">6.2</span><span class="token plain">.9-300.fc38.x86_64</span><br></div></code></pre></div></div>
<p>And my ZFS ?</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">zfs list</span><br></div></code></pre></div></div>
<p>Output:</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">The ZFS modules cannot be auto-loaded.</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Try running </span><span class="token string" style="color:rgb(255, 121, 198)">'modprobe zfs'</span><span class="token plain"> as root to manually load them.</span><br></div></code></pre></div></div>
<p>Argh. I forgot to reinstall the ZFS modules for the new <a href="https://www.kernel.org/" target="_blank" rel="noopener noreferrer" class="">kernel</a>. Let's do that.</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">dnf </span><span class="token function" style="color:rgb(80, 250, 123)">install</span><span class="token plain"> zfs</span><br></div></code></pre></div></div>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">zfs list</span><br></div></code></pre></div></div>
<p>Output:</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">NAME                           USED  AVAIL  REFER  MOUNTPOINT</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">poule                         </span><span class="token number">3</span><span class="token plain">.64T  </span><span class="token number">3</span><span class="token plain">.47T   141K  /poule</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token plain">.</span><br></div></code></pre></div></div>
<p>It's alive !</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="conclusion">Conclusion<a href="https://lunik.tiwabbit.fr/blog/config-default-grub2-boot-img#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>I hope this guide will help you to configure the default boot image in Grub2. It's a simple task but it can be tricky if you don't know where to look. Also be very careful when playing with Grub2, you can easily break your system. Always have a backup plan (I didn't ... YOLO).</p>]]></content>
        <author>
            <name>Guillaume MARTINEZ</name>
            <uri>https://github.com/Lunik</uri>
        </author>
        <category label="linux" term="linux"/>
        <category label="grub" term="grub"/>
        <category label="boot" term="boot"/>
        <category label="grub2" term="grub2"/>
        <category label="initramfs" term="initramfs"/>
        <category label="kernel" term="kernel"/>
        <category label="image" term="image"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Force network link speed on Linux]]></title>
        <id>https://lunik.tiwabbit.fr/blog/focre-network-link-speed-linux</id>
        <link href="https://lunik.tiwabbit.fr/blog/focre-network-link-speed-linux"/>
        <updated>2024-01-14T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Step-by-step guide to troubleshooting and forcing network interface link speed on Linux using ethtool, covering common issues with auto-negotiation.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2024-01-14-force-network-link-speed/cover.png" alt="cover" class="img__Ss2"></p>
<p>Today I was confronted with a quite annoying problem. I have a server with a 1Gbps network card connected to a 1Gbps switch. I was trying to backup my data as usual but the transfer was slower than usual. Using <code>ethtool</code> I saw that the link speed was only 100Mbps !</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="debug-the-current-link-speed">Debug the current link speed<a href="https://lunik.tiwabbit.fr/blog/focre-network-link-speed-linux#debug-the-current-link-speed" class="hash-link" aria-label="Direct link to Debug the current link speed" title="Direct link to Debug the current link speed" translate="no">​</a></h2>
<p>First of all, I need to retreive the name of the network interface. I tougth it was <code>eth0</code> but it was <code>enp2s0</code>. Thank's Fedora for the new naming convention !</p>
<p>So using the <code>ip</code> command I can get list of all network interfaces and their current status.</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">ip</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">link</span><span class="token plain"> show</span><br></div></code></pre></div></div>
<p>Output :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token number">1</span><span class="token plain">: lo: </span><span class="token operator">&lt;</span><span class="token plain">LOOPBACK,UP,LOWER_UP</span><span class="token operator">&gt;</span><span class="token plain"> mtu </span><span class="token number">65536</span><span class="token plain"> qdisc noqueue state UNKNOWN mode DEFAULT group default qlen </span><span class="token number">1000</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token number">2</span><span class="token plain">: enp2s0: </span><span class="token operator">&lt;</span><span class="token plain">BROADCAST,MULTICAST,UP,LOWER_UP</span><span class="token operator">&gt;</span><span class="token plain"> mtu </span><span class="token number">1500</span><span class="token plain"> qdisc fq_codel state UP mode DEFAULT group default qlen </span><span class="token number">1000</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    link/ether </span><span class="token number">12</span><span class="token plain">:34:56:78:9a:bc brd ff:ff:ff:ff:ff:ff</span><br></div></code></pre></div></div>
<p>The <code>enp2s0</code> interface is the one I'm looking for. I can now use <code>ethtool</code> to get the current link speed.</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">ethtool</span><span class="token plain"> enp2s0</span><br></div></code></pre></div></div>
<p>Output :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Settings </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">for</span><span class="token plain"> enp2s0:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Supported ports: </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> TP	 MII </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Supported </span><span class="token function" style="color:rgb(80, 250, 123)">link</span><span class="token plain"> modes:   10baseT/Half 10baseT/Full</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	                        100baseT/Half 100baseT/Full</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	                        1000baseT/Full</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Supported pause frame use: Symmetric Receive-only</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Supports auto-negotiation: Yes</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Supported FEC modes: Not reported</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Advertised </span><span class="token function" style="color:rgb(80, 250, 123)">link</span><span class="token plain"> modes:  100baseT/Full</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Advertised pause frame use: Symmetric Receive-only</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Advertised auto-negotiation: Yes</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Advertised FEC modes: Not reported</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Link partner advertised </span><span class="token function" style="color:rgb(80, 250, 123)">link</span><span class="token plain"> modes:  10baseT/Half 10baseT/Full</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	                                     100baseT/Half 100baseT/Full</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	                                     1000baseT/Half 1000baseT/Full</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Link partner advertised pause frame use: Symmetric Receive-only</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Link partner advertised auto-negotiation: Yes</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Link partner advertised FEC modes: Not reported</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Speed: 100Mb/s</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Duplex: Full</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Auto-negotiation: on</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	master-slave cfg: preferred slave</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	master-slave status: slave</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Port: Twisted Pair</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	PHYAD: </span><span class="token number">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Transceiver: external</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	MDI-X: Unknown</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Supports Wake-on: pumbg</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Wake-on: g</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Link detected: </span><span class="token function" style="color:rgb(80, 250, 123)">yes</span><br></div></code></pre></div></div>
<p>The <code>Speed</code> line is the one I'm looking for. It says <code>1000b/s</code> which is not what I'm expecting. <code>Auto-negotiation</code> is <code>on</code> so the network card should be able to negociate the link speed with the switch.</p>
<p>My card supports <code>1000baseT/Full</code> as seen in the <code>Supported link modes</code> line. The switch also supports <code>1000baseT/Full</code> as seen in the <code>Link partner advertised link modes</code> line. So why is the link speed only 100Mbps ?</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="force-the-link-speed">Force the link speed<a href="https://lunik.tiwabbit.fr/blog/focre-network-link-speed-linux#force-the-link-speed" class="hash-link" aria-label="Direct link to Force the link speed" title="Direct link to Force the link speed" translate="no">​</a></h2>
<p>I can force the link speed using the <code>ethtool</code> command.</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">ethtool</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-s</span><span class="token plain"> enp2s0 speed </span><span class="token number">1000</span><br></div></code></pre></div></div>
<p>Let's check the link speed again.</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">ethtool</span><span class="token plain"> enp2s0</span><br></div></code></pre></div></div>
<p>Output :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Settings </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">for</span><span class="token plain"> enp2s0:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Supported ports: </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> TP	 MII </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Supported </span><span class="token function" style="color:rgb(80, 250, 123)">link</span><span class="token plain"> modes:   10baseT/Half 10baseT/Full</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	                        100baseT/Half 100baseT/Full</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	                        1000baseT/Full</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Supported pause frame use: Symmetric Receive-only</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Supports auto-negotiation: Yes</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Supported FEC modes: Not reported</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Advertised </span><span class="token function" style="color:rgb(80, 250, 123)">link</span><span class="token plain"> modes:  1000baseT/Full</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Advertised pause frame use: Symmetric Receive-only</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Advertised auto-negotiation: Yes</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Advertised FEC modes: Not reported</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Link partner advertised </span><span class="token function" style="color:rgb(80, 250, 123)">link</span><span class="token plain"> modes:  10baseT/Half 10baseT/Full</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	                                     100baseT/Half 100baseT/Full</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	                                     1000baseT/Half 1000baseT/Full</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Link partner advertised pause frame use: Symmetric Receive-only</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Link partner advertised auto-negotiation: Yes</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Link partner advertised FEC modes: Not reported</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Speed: 1000Mb/s</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Duplex: Full</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Auto-negotiation: on</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	master-slave cfg: preferred slave</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	master-slave status: slave</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Port: Twisted Pair</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	PHYAD: </span><span class="token number">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Transceiver: external</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	MDI-X: Unknown</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Supports Wake-on: pumbg</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Wake-on: g</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">	Link detected: </span><span class="token function" style="color:rgb(80, 250, 123)">yes</span><br></div></code></pre></div></div>
<p>The <code>Speed</code> line now says <code>1000Mb/s</code> which is what I'm expecting.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="conclusion">Conclusion<a href="https://lunik.tiwabbit.fr/blog/focre-network-link-speed-linux#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>I don't really know why the link speed was only 100Mbps. Maybe there was a problem with the switch or the cable. I'll investigate later. For now, I'm happy with my 1Gbps link speed.</p>]]></content>
        <author>
            <name>Guillaume MARTINEZ</name>
            <uri>https://github.com/Lunik</uri>
        </author>
        <category label="linux" term="linux"/>
        <category label="network" term="network"/>
        <category label="speed" term="speed"/>
        <category label="link" term="link"/>
        <category label="ethtool" term="ethtool"/>
        <category label="ip" term="ip"/>
        <category label="ifconfig" term="ifconfig"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[SSH Authentication with a CA]]></title>
        <id>https://lunik.tiwabbit.fr/blog/ssh-ca-authentication</id>
        <link href="https://lunik.tiwabbit.fr/blog/ssh-ca-authentication"/>
        <updated>2023-09-19T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[How to use a SSH CA Authority to authenticate to SSH servers]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-09-19-ssh-ca-authentication/cover.jpg" alt="cover" class="img__Ss2"></p>
<p>This article describes how to use a SSH CA to authenticate to SSH servers. This is particularly useful when you have a lot of servers to manage and you want to avoid the hassle of managing a lot of SSH keys.</p>
<p>The basic idea is to have a CA (Certificate Authority) that will sign the public keys of the users. Then, the users will be able to authenticate to the servers using their signed public key. This way, you don't have to manage the public keys of the users on the servers, you only have to manage the public keys of the CA. You can also limit the validity of the signed public keys to a certain amount of time or to a certain set of servers and users.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="prerequisites">Prerequisites<a href="https://lunik.tiwabbit.fr/blog/ssh-ca-authentication#prerequisites" class="hash-link" aria-label="Direct link to Prerequisites" title="Direct link to Prerequisites" translate="no">​</a></h2>
<p>This article assumes that you have a basic knowledge of SSH and that you have a SSH server running on a Linux machine.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-theory">The theory<a href="https://lunik.tiwabbit.fr/blog/ssh-ca-authentication#the-theory" class="hash-link" aria-label="Direct link to The theory" title="Direct link to The theory" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="what-is-a-ssh-ca-">What is a SSH CA ?<a href="https://lunik.tiwabbit.fr/blog/ssh-ca-authentication#what-is-a-ssh-ca-" class="hash-link" aria-label="Direct link to What is a SSH CA ?" title="Direct link to What is a SSH CA ?" translate="no">​</a></h3>
<p>A SSH CA is simply an basic SSH key pair (public and private) that is used to sign other SSH public keys. The public key of the CA is then distributed to the servers and the private key is kept secret.</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="how-does-it-work-">How does it work ?<a href="https://lunik.tiwabbit.fr/blog/ssh-ca-authentication#how-does-it-work-" class="hash-link" aria-label="Direct link to How does it work ?" title="Direct link to How does it work ?" translate="no">​</a></h3>
<p>When a user wants to authenticate to a server, the server will ask the user to provide a public key. The user will then provide a public key that has been signed by the CA. The server will then check the signature of the public key using the public key of the CA. If the signature is valid, the user will be authenticated.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-practice">The practice<a href="https://lunik.tiwabbit.fr/blog/ssh-ca-authentication#the-practice" class="hash-link" aria-label="Direct link to The practice" title="Direct link to The practice" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="generate-the-ca-key-pair">Generate the CA key pair<a href="https://lunik.tiwabbit.fr/blog/ssh-ca-authentication#generate-the-ca-key-pair" class="hash-link" aria-label="Direct link to Generate the CA key pair" title="Direct link to Generate the CA key pair" translate="no">​</a></h3>
<p>The first step is to generate the CA key pair. To do so, you can use the <code>ssh-keygen</code> command:</p>
<div class="language-bash codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bash codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">ssh-keygen </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-t</span><span class="token plain"> rsa </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-b</span><span class="token plain"> </span><span class="token number">4096</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-f</span><span class="token plain"> my_ssh_ca</span><br></div></code></pre></div></div>
<table><thead><tr><th>Parameter</th><th>Description</th></tr></thead><tbody><tr><td><code>-t</code></td><td>The algorithm to use to generate the key pair.</td></tr><tr><td><code>-b</code></td><td>The size (in bits) of the key pair.</td></tr><tr><td><code>-f</code></td><td>The output file name (without the extensions).</td></tr></tbody></table>
<p>This will generate two files: <code>my_ssh_ca</code> and <code>my_ssh_ca.pub</code>. The first one is the private key of the CA and the second one is the public key of the CA.</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="generate-a-personal-ssh-key-pair-optional">Generate a personal SSH key pair (optional)<a href="https://lunik.tiwabbit.fr/blog/ssh-ca-authentication#generate-a-personal-ssh-key-pair-optional" class="hash-link" aria-label="Direct link to Generate a personal SSH key pair (optional)" title="Direct link to Generate a personal SSH key pair (optional)" translate="no">​</a></h3>
<p>This step assumes that you already have a personal SSH key pair. If you don't, you can generate one using the <code>ssh-keygen</code> command:</p>
<div class="language-bash codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bash codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">ssh-keygen </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-t</span><span class="token plain"> ed25519 </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-b</span><span class="token plain"> </span><span class="token number">256</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-f</span><span class="token plain"> my_ssh_key</span><br></div></code></pre></div></div>
<table><thead><tr><th>Parameter</th><th>Description</th></tr></thead><tbody><tr><td><code>-t</code></td><td>The algorithm to use to generate the key pair.</td></tr><tr><td><code>-b</code></td><td>The size (in bits) of the key pair.</td></tr><tr><td><code>-f</code></td><td>The output file name (without the extensions).</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="distribute-the-ca-public-key">Distribute the CA public key<a href="https://lunik.tiwabbit.fr/blog/ssh-ca-authentication#distribute-the-ca-public-key" class="hash-link" aria-label="Direct link to Distribute the CA public key" title="Direct link to Distribute the CA public key" translate="no">​</a></h3>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="configure-the-ssh-server">Configure the SSH server<a href="https://lunik.tiwabbit.fr/blog/ssh-ca-authentication#configure-the-ssh-server" class="hash-link" aria-label="Direct link to Configure the SSH server" title="Direct link to Configure the SSH server" translate="no">​</a></h4>
<p>First, you need to configure the SSH server to accept the SSH CA public key. To do so, you need to authenticate to the server and become root. Then, you need to edit the <code>/etc/ssh/sshd_config</code> file and add the following line at the end of the file:</p>
<div class="language-text codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-text codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Match Address 192.168.42.*</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  TrustedUserCAKeys /etc/ssh/ssh_user_ca</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  AuthorizedPrincipalsFile /etc/ssh/authorized_principals/%u</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  RevokedKeys /etc/ssh/revoked_keys</span><br></div></code></pre></div></div>
<table><thead><tr><th>Parameter</th><th>Description</th></tr></thead><tbody><tr><td><code>Match Address 192.168.42.*</code></td><td>This line is used to apply the following configuration only to the clients that match the given address. In this case, we only want to apply the following configuration to the clients that match the <code>192.168.42.*</code> address.</td></tr><tr><td><code>TrustedUserCAKeys</code></td><td>The path to the SSH CA public key.</td></tr><tr><td><code>AuthorizedPrincipalsFile</code></td><td>The path to the file containing the list of SSH principals allowed to authenticate to users.</td></tr><tr><td><code>RevokedKeys</code></td><td>The path to the file containing the list of revoked SSH public keys.</td></tr></tbody></table>
<p>Then, you need to create the <code>/etc/ssh/ssh_user_ca</code> file and add the public key of the SSH CA to it:</p>
<div class="language-bash codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bash codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">cat</span><span class="token plain"> my_ssh_ca.pub </span><span class="token operator">&gt;</span><span class="token plain"> /etc/ssh/ssh_user_ca</span><br></div></code></pre></div></div>
<p>For now we don't have any SSH CA public key to revoke, so we only need to create an empty file for the <code>RevokedKeys</code> parameter:</p>
<div class="language-bash codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bash codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">touch</span><span class="token plain"> /etc/ssh/revoked_keys</span><br></div></code></pre></div></div>
<p>Finally, we need to create the <code>/etc/ssh/authorized_principals</code> directory to store the list of SSH principals allowed to authenticate to users:</p>
<div class="language-bash codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bash codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">mkdir</span><span class="token plain"> /etc/ssh/authorized_principals</span><br></div></code></pre></div></div>
<h5 class="anchor anchorTargetStickyNavbar_SAay" id="configure-principal-based-authentication">Configure principal-based authentication<a href="https://lunik.tiwabbit.fr/blog/ssh-ca-authentication#configure-principal-based-authentication" class="hash-link" aria-label="Direct link to Configure principal-based authentication" title="Direct link to Configure principal-based authentication" translate="no">​</a></h5>
<p>As an example, we will configure the SSH server to allow me to authenticate to the <code>wabbit</code> user. To do so, we need to create the <code>/etc/ssh/authorized_principals/wabbit</code> file and add the following line to it:</p>
<div class="language-text codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-text codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">wabbit_ops</span><br></div></code></pre></div></div>
<p>This will allow me to authenticate to the <code>wabbit</code> user using the <code>wabbit_ops</code> principal. Attention, a principal is not a username, it's just a name that will be used to identify the user or a role. You can use whatever name you want, but it's better to use a name that is easy to remember.</p>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="apply-the-configuration">Apply the configuration<a href="https://lunik.tiwabbit.fr/blog/ssh-ca-authentication#apply-the-configuration" class="hash-link" aria-label="Direct link to Apply the configuration" title="Direct link to Apply the configuration" translate="no">​</a></h4>
<p>Once the configuration is done, you need to restart the SSH server to apply the changes:</p>
<div class="language-bash codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bash codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">systemctl restart sshd</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="configure-the-ssh-client">Configure the SSH client<a href="https://lunik.tiwabbit.fr/blog/ssh-ca-authentication#configure-the-ssh-client" class="hash-link" aria-label="Direct link to Configure the SSH client" title="Direct link to Configure the SSH client" translate="no">​</a></h3>
<p>Now that the SSH server is configured, we need to configure the SSH client to use the SSH CA to authenticate to the SSH server.</p>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="sign-the-personal-ssh-public-key">Sign the personal SSH public key<a href="https://lunik.tiwabbit.fr/blog/ssh-ca-authentication#sign-the-personal-ssh-public-key" class="hash-link" aria-label="Direct link to Sign the personal SSH public key" title="Direct link to Sign the personal SSH public key" translate="no">​</a></h4>
<p>First, we need to sign the personal SSH public key using the CA private key. To do so, we need to use the <code>ssh-keygen</code> command:</p>
<div class="language-bash codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bash codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">ssh-keygen </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-s</span><span class="token plain"> my_ssh_ca </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-I</span><span class="token plain"> lunik@my_server </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-n</span><span class="token plain"> wabbit_ops </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-V</span><span class="token plain"> +1d </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  my_ssh_key.pub</span><br></div></code></pre></div></div>
<table><thead><tr><th>Parameter</th><th>Description</th></tr></thead><tbody><tr><td><code>-s</code></td><td>The path to the CA private key.</td></tr><tr><td><code>-I</code></td><td>The identity of the signed public key. This is used to identify the signed public key. It's a good idea to use the username, the hostname and the name of the server. It doesn't have any impact on the authentication.</td></tr><tr><td><code>-n</code></td><td>The principal allowed during the authentication.</td></tr><tr><td><code>-V</code></td><td>The validity of the signed public key.</td></tr><tr><td><code>my_ssh_key.pub</code></td><td>The path to the public key to sign.</td></tr></tbody></table>
<p>This will generate a <code>my_ssh_key-cert.pub</code> file that contains the signed public key.</p>
<p>To check the signature of the signed public key, you can use the <code>ssh-keygen</code> command:</p>
<div class="language-bash codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bash codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">ssh-keygen </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-L</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-f</span><span class="token plain"> my_ssh_key-cert.pub</span><br></div></code></pre></div></div>
<table><thead><tr><th>Parameter</th><th>Description</th></tr></thead><tbody><tr><td><code>-L</code></td><td>Check the signature of the public key.</td></tr><tr><td><code>-f</code></td><td>The path to the public key to check.</td></tr></tbody></table>
<p>This will print the following output:</p>
<div class="language-text codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-text codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">my_ssh_key-cert.pub:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        Type: ssh-ed25519-cert-v01@openssh.com user certificate</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        Public key: ED25519-CERT SHA256:Nbe7SXv788c0t3g8D8GQ/5dwHXX4zXtoaEC7r97h5oM</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        Signing CA: RSA SHA256:jitBsmIydxjgdW7DttD9piKIWmb4RPQQ7i7wfQ+M4Vg (using rsa-sha2-512)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        Key ID: "lunik@my_server"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        Serial: 0</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        Valid: from 2023-09-19T19:13:00 to 2023-09-20T19:14:07</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        Principals: </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                wabbit_ops</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        Critical Options: (none)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        Extensions: </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                permit-X11-forwarding</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                permit-agent-forwarding</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                permit-port-forwarding</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                permit-pty</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                permit-user-rc</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="authenticate-to-the-ssh-server">Authenticate to the SSH server<a href="https://lunik.tiwabbit.fr/blog/ssh-ca-authentication#authenticate-to-the-ssh-server" class="hash-link" aria-label="Direct link to Authenticate to the SSH server" title="Direct link to Authenticate to the SSH server" translate="no">​</a></h3>
<p>Now that the SSH client is configured, we can authenticate to the SSH server using the signed public key. To do so, we need to use the <code>ssh</code> command:</p>
<div class="language-bash codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bash codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">ssh</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-i</span><span class="token plain"> my_ssh_key </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  wabbit@my_server</span><br></div></code></pre></div></div>
<table><thead><tr><th>Parameter</th><th>Description</th></tr></thead><tbody><tr><td><code>-i</code></td><td>The path to the private key to use to authenticate.</td></tr><tr><td><code>wabbit@my_server</code></td><td>The username and the hostname of the SSH server.</td></tr></tbody></table>]]></content>
        <author>
            <name>Guillaume MARTINEZ</name>
            <uri>https://github.com/Lunik</uri>
        </author>
        <category label="ssh" term="ssh"/>
        <category label="security" term="security"/>
        <category label="ca" term="ca"/>
        <category label="authority" term="authority"/>
        <category label="authentication" term="authentication"/>
        <category label="public" term="public"/>
        <category label="private" term="private"/>
        <category label="key" term="key"/>
        <category label="keys" term="keys"/>
        <category label="ssh-keygen" term="ssh-keygen"/>
        <category label="ssh-keyscan" term="ssh-keyscan"/>
        <category label="ssh-copy-id" term="ssh-copy-id"/>
        <category label="ssh-agent" term="ssh-agent"/>
        <category label="ssh-add" term="ssh-add"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Automated media center]]></title>
        <id>https://lunik.tiwabbit.fr/blog/auto-media-center</id>
        <link href="https://lunik.tiwabbit.fr/blog/auto-media-center"/>
        <updated>2023-06-21T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Fully automated media center step by step from requesting to watching in less than 5 minutes]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/cover.png" alt="cover" class="img__Ss2"></p>
<p>I have a lot of media to watch and I don't want to spend time searching for it, downloading it, renaming it, moving it, etc. I want to be able to request a movie or a TV show and watch it in less than 5 minutes. I also want to be able to watch it on any device, anywhere, anytime.</p>
<div class="theme-admonition theme-admonition-warning admonition_IZjC alert alert--warning"><div class="admonitionHeading_uVvU"><span class="admonitionIcon_HiR3"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>warning</div><div class="admonitionContent_bl22"><p>This article is for educational purposes only. I does not encourage the piracy of licenced content. Doing what is described in this article is illegal in some countries. I am not responsible for the use you make of this article.</p></div></div>
<p>I do not use this setup to download and watch pirated content. No pirated content was downloaded during the writing of this article. Placeolder files were used instead.</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="requirements">Requirements<a href="https://lunik.tiwabbit.fr/blog/auto-media-center#requirements" class="hash-link" aria-label="Direct link to Requirements" title="Direct link to Requirements" translate="no">​</a></h3>
<p>We are going to use the following tools :</p>
<ul>
<li class=""><a href="https://www.plex.tv/" target="_blank" rel="noopener noreferrer" class="">Plex</a> : Media server and player</li>
<li class=""><a href="https://sonarr.tv/" target="_blank" rel="noopener noreferrer" class="">Sonarr</a> : TV shows manager</li>
<li class=""><a href="https://radarr.video/" target="_blank" rel="noopener noreferrer" class="">Radarr</a> : Movies manager</li>
<li class=""><a href="https://prowlarr.com/" target="_blank" rel="noopener noreferrer" class="">Prowlarr</a> : Torrents indexer</li>
<li class=""><a href="https://overseerr.dev/" target="_blank" rel="noopener noreferrer" class="">Overseerr</a> : Requests manager</li>
<li class=""><a href="https://www.qbittorrent.org/" target="_blank" rel="noopener noreferrer" class="">qBittorrent</a> : Torrents client</li>
</ul>
<p>We are also going to need a linux based server with for hosting all of this.</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="deployment">Deployment<a href="https://lunik.tiwabbit.fr/blog/auto-media-center#deployment" class="hash-link" aria-label="Direct link to Deployment" title="Direct link to Deployment" translate="no">​</a></h3>
<p>For the deployment, we are going to use <a href="https://www.docker.com/" target="_blank" rel="noopener noreferrer" class="">Docker</a> and <a href="https://docs.docker.com/compose/" target="_blank" rel="noopener noreferrer" class="">Docker Compose</a>.</p>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="docker-compose">Docker Compose<a href="https://lunik.tiwabbit.fr/blog/auto-media-center#docker-compose" class="hash-link" aria-label="Direct link to Docker Compose" title="Direct link to Docker Compose" translate="no">​</a></h4>
<p>The full <code>docker-compose.yml</code> file is available <a class="" href="https://lunik.tiwabbit.fr/blog/assets/docker-compose.yml">here</a>.</p>
<p>Then we can start the stack with the following command :</p>
<div class="language-bash codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bash codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">docker</span><span class="token plain"> compose up </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-d</span><br></div></code></pre></div></div>
<p>Then we can check that everything is running with :</p>
<div class="language-bash codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bash codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">docker</span><span class="token plain"> compose </span><span class="token function" style="color:rgb(80, 250, 123)">ps</span><br></div></code></pre></div></div>
<p>The output should look like this :</p>
<div class="language-bash codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bash codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">NAME                              IMAGE                                    COMMAND             SERVICE             CREATED             STATUS              PORTS</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">auto-media-center-prowlarr-1      lscr.io/linuxserver/prowlarr:latest      </span><span class="token string" style="color:rgb(255, 121, 198)">"/init"</span><span class="token plain">             prowlarr            </span><span class="token number">20</span><span class="token plain"> minutes ago      Up </span><span class="token number">20</span><span class="token plain"> minutes       </span><span class="token number">0.0</span><span class="token plain">.0.0:9696-</span><span class="token operator">&gt;</span><span class="token number">9696</span><span class="token plain">/tcp</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">auto-media-center-overseerr-1     lscr.io/linuxserver/overseerr:latest     </span><span class="token string" style="color:rgb(255, 121, 198)">"/init"</span><span class="token plain">             overseerr           </span><span class="token number">9</span><span class="token plain"> minutes ago       Up </span><span class="token number">9</span><span class="token plain"> minutes        </span><span class="token number">0.0</span><span class="token plain">.0.0:5055-</span><span class="token operator">&gt;</span><span class="token number">5055</span><span class="token plain">/tcp</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">auto-media-center-plex-1          lscr.io/linuxserver/plex:latest          </span><span class="token string" style="color:rgb(255, 121, 198)">"/init"</span><span class="token plain">             plex                </span><span class="token number">9</span><span class="token plain"> minutes ago       Up </span><span class="token number">9</span><span class="token plain"> minutes        </span><span class="token number">0.0</span><span class="token plain">.0.0:3005-</span><span class="token operator">&gt;</span><span class="token number">3005</span><span class="token plain">/tcp, </span><span class="token number">0.0</span><span class="token plain">.0.0:8324-</span><span class="token operator">&gt;</span><span class="token number">8324</span><span class="token plain">/tcp, </span><span class="token number">0.0</span><span class="token plain">.0.0:1900-</span><span class="token operator">&gt;</span><span class="token number">1900</span><span class="token plain">/udp, </span><span class="token number">0.0</span><span class="token plain">.0.0:32410-</span><span class="token operator">&gt;</span><span class="token number">32410</span><span class="token plain">/udp, </span><span class="token number">0.0</span><span class="token plain">.0.0:32400-</span><span class="token operator">&gt;</span><span class="token number">32400</span><span class="token plain">/tcp, </span><span class="token number">0.0</span><span class="token plain">.0.0:32412-32414-</span><span class="token operator">&gt;</span><span class="token number">32412</span><span class="token plain">-32414/udp, </span><span class="token number">0.0</span><span class="token plain">.0.0:32469-</span><span class="token operator">&gt;</span><span class="token number">32469</span><span class="token plain">/tcp, </span><span class="token number">5353</span><span class="token plain">/udp</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">auto-media-center-qbittorrent-1   lscr.io/linuxserver/qbittorrent:latest   </span><span class="token string" style="color:rgb(255, 121, 198)">"/init"</span><span class="token plain">             qbittorrent         </span><span class="token number">20</span><span class="token plain"> minutes ago      Up </span><span class="token number">20</span><span class="token plain"> minutes       </span><span class="token number">0.0</span><span class="token plain">.0.0:6881-</span><span class="token operator">&gt;</span><span class="token number">6881</span><span class="token plain">/tcp, </span><span class="token number">0.0</span><span class="token plain">.0.0:8080-</span><span class="token operator">&gt;</span><span class="token number">8080</span><span class="token plain">/tcp, </span><span class="token number">0.0</span><span class="token plain">.0.0:6881-</span><span class="token operator">&gt;</span><span class="token number">6881</span><span class="token plain">/udp</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">auto-media-center-radarr-1        lscr.io/linuxserver/radarr:latest        </span><span class="token string" style="color:rgb(255, 121, 198)">"/init"</span><span class="token plain">             radarr              </span><span class="token number">20</span><span class="token plain"> minutes ago      Up </span><span class="token number">19</span><span class="token plain"> minutes       </span><span class="token number">0.0</span><span class="token plain">.0.0:7878-</span><span class="token operator">&gt;</span><span class="token number">7878</span><span class="token plain">/tcp</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">auto-media-center-sonarr-1        lscr.io/linuxserver/sonarr:latest        </span><span class="token string" style="color:rgb(255, 121, 198)">"/init"</span><span class="token plain">             sonarr              </span><span class="token number">20</span><span class="token plain"> minutes ago      Up </span><span class="token number">19</span><span class="token plain"> minutes       </span><span class="token number">0.0</span><span class="token plain">.0.0:8989-</span><span class="token operator">&gt;</span><span class="token number">8989</span><span class="token plain">/tcp</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="setup">Setup<a href="https://lunik.tiwabbit.fr/blog/auto-media-center#setup" class="hash-link" aria-label="Direct link to Setup" title="Direct link to Setup" translate="no">​</a></h3>
<p>Now that everything is running, we can start the setup.</p>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="plex">Plex<a href="https://lunik.tiwabbit.fr/blog/auto-media-center#plex" class="hash-link" aria-label="Direct link to Plex" title="Direct link to Plex" translate="no">​</a></h4>
<p>This is the first thing we are going to setup. <a href="https://www.plex.tv/" target="_blank" rel="noopener noreferrer" class="">Plex</a> is the media server and player. It is the core of our media center.</p>
<p>First we need to access the service through the web interface. The default port is <code>32400</code>. So we can access it at <code>http://&lt;server-ip&gt;:32400/web</code>.</p>
<p>If you don't have a <a href="https://www.plex.tv/" target="_blank" rel="noopener noreferrer" class="">Plex</a> account, you can create one for free. Then you will be able to access the web interface.</p>
<p>After going through the wizard, you should see something like this :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/plex-web-interface-init.png" alt="Plex web interface init" class="img__Ss2"></p>
<p>We can now add our media libraries. For this example, I will add a TV shows library and a movies library.</p>
<p>First the movies library configuration :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/plex-movie-library-config-01.png" alt="Plex web config movies library 01" class="img__Ss2">
<img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/plex-movie-library-config-02.png" alt="Plex web config movies library 02" class="img__Ss2"></p>
<p>Then the TV shows library configuration :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/plex-tv-library-config-01.png" alt="Plex web config tv shows library 01" class="img__Ss2">
<img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/plex-tv-library-config-02.png" alt="Plex web config tv shows library 02" class="img__Ss2"></p>
<p>Finally <a href="https://www.plex.tv/" target="_blank" rel="noopener noreferrer" class="">Plex</a> server should look like this :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/plex-library-config.png" alt="Plex web interface final" class="img__Ss2"></p>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="qbittorrent">QBittorrent<a href="https://lunik.tiwabbit.fr/blog/auto-media-center#qbittorrent" class="hash-link" aria-label="Direct link to QBittorrent" title="Direct link to QBittorrent" translate="no">​</a></h4>
<p><a href="https://www.qbittorrent.org/" target="_blank" rel="noopener noreferrer" class="">qBittorrent</a> is a torrents client. It will allow us to download torrents.</p>
<p>First we need to access the service through the web interface. The default port is <code>8080</code>. So we can access it at <code>http://&lt;server-ip&gt;:8080</code>.
The default username is <code>admin</code> and the default password is <code>adminadmin</code>.</p>
<p>You should see something like this :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/qbittorrent-web-interface-init.png" alt="QBittorrent web interface init" class="img__Ss2"></p>
<p>Then we need to configure the download folder in (<strong>Options</strong> &gt; <strong>Downloads</strong>). For this example, I will use <code>/downloads</code> as the download folder and <code>/incomplete-downloads</code> as the incomplete downloads folder. Don't forget to click <code>Save</code> at the bottom of the page.</p>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="sonarr">Sonarr<a href="https://lunik.tiwabbit.fr/blog/auto-media-center#sonarr" class="hash-link" aria-label="Direct link to Sonarr" title="Direct link to Sonarr" translate="no">​</a></h4>
<p><a href="https://sonarr.tv/" target="_blank" rel="noopener noreferrer" class="">Sonarr</a> is a TV shows manager. It will allow us to search for TV shows and download them automatically.</p>
<p>First we need to access the service through the web interface. The default port is <code>8989</code>. So we can access it at <code>http://&lt;server-ip&gt;:8989</code>.</p>
<p>You should see something like this :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/sonarr-web-interface-init.png" alt="Sonarr web interface init" class="img__Ss2"></p>
<h5 class="anchor anchorTargetStickyNavbar_SAay" id="indexers">Indexers<a href="https://lunik.tiwabbit.fr/blog/auto-media-center#indexers" class="hash-link" aria-label="Direct link to Indexers" title="Direct link to Indexers" translate="no">​</a></h5>
<p>We are going to see the configuration of the indexers in (<strong>Settings</strong> &gt; <strong>Indexers</strong>). This will be automatically configured by <a href="https://lunik.tiwabbit.fr/blog/auto-media-center#prowlarr" class="">Prowlarr</a> later.</p>
<h5 class="anchor anchorTargetStickyNavbar_SAay" id="download-client">Download client<a href="https://lunik.tiwabbit.fr/blog/auto-media-center#download-client" class="hash-link" aria-label="Direct link to Download client" title="Direct link to Download client" translate="no">​</a></h5>
<p>We are going to configure the download client in (<strong>Settings</strong> &gt; <strong>Download Client</strong>). For this example, I will use the following configuration :</p>
<table><thead><tr><th style="text-align:left">Field</th><th style="text-align:right">Value</th></tr></thead><tbody><tr><td style="text-align:left">Type</td><td style="text-align:right"><code>qBittorrent</code></td></tr><tr><td style="text-align:left">Enable</td><td style="text-align:right"><code>Yes</code></td></tr><tr><td style="text-align:left">Host</td><td style="text-align:right"><code>qbittorrent</code></td></tr><tr><td style="text-align:left">Port</td><td style="text-align:right"><code>8080</code></td></tr><tr><td style="text-align:left">UseSsl</td><td style="text-align:right"><code>No</code></td></tr><tr><td style="text-align:left">Username</td><td style="text-align:right"><code>admin</code></td></tr><tr><td style="text-align:left">Password</td><td style="text-align:right"><code>adminadmin</code></td></tr></tbody></table>
<p>We can test the connection by clicking on <code>Test</code> at the bottom of the page.</p>
<p><strong>Note :</strong> You can set the <strong>Initial State</strong> to <code>Paused</code> if you don't want <a href="https://sonarr.tv/" target="_blank" rel="noopener noreferrer" class="">Sonarr</a> to download all the episodes of the TV shows you add.</p>
<h5 class="anchor anchorTargetStickyNavbar_SAay" id="media-management">Media management<a href="https://lunik.tiwabbit.fr/blog/auto-media-center#media-management" class="hash-link" aria-label="Direct link to Media management" title="Direct link to Media management" translate="no">​</a></h5>
<p>We are going to configure the media management in (<strong>Settings</strong> &gt; <strong>Media Management</strong>). This will allow <a href="https://sonarr.tv/" target="_blank" rel="noopener noreferrer" class="">Sonarr</a> to rename and move the downloaded episodes from qBittorret in the right folder for <a href="https://www.plex.tv/" target="_blank" rel="noopener noreferrer" class="">Plex</a> to find them.</p>
<p>For this example, I will use the following configuration :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/sonarr-media-management-config-01.png" alt="Sonarr media management config 01" class="img__Ss2"></p>
<h5 class="anchor anchorTargetStickyNavbar_SAay" id="test">Test<a href="https://lunik.tiwabbit.fr/blog/auto-media-center#test" class="hash-link" aria-label="Direct link to Test" title="Direct link to Test" translate="no">​</a></h5>
<p>Now we should be able to add a TV show. In (<strong>Series</strong> &gt; <strong>Add Series</strong>), we can search for <code>Game of Thrones</code> and add it :</p>
<table><thead><tr><th style="text-align:left">Field</th><th style="text-align:right">Value</th></tr></thead><tbody><tr><td style="text-align:left">Root Folder</td><td style="text-align:right"><code>/media/Series/Game of Thrones</code></td></tr><tr><td style="text-align:left">Monitor</td><td style="text-align:right"><code>All Episodes</code></td></tr><tr><td style="text-align:left">Quality Profile</td><td style="text-align:right"><code>HD-1080p</code></td></tr></tbody></table>
<p>Then we click on the <code>Game of Thrones</code> card and we should see something like this :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/sonarr-tv-example.png" alt="Sonarr tv example" class="img__Ss2"></p>
<p>Then we can click on the <code>Interractive Search</code> button (small person icon) to search the first season torrents :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/sonarr-tv-example-search.png" alt="Sonarr tv example search" class="img__Ss2"></p>
<p>We can click on the <code>Download</code> button to trigger the download of the first season.</p>
<p>On the (<strong>Activity</strong> &gt; <strong>Queue</strong>) we can see the download progress :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/sonarr-tv-example-download.png" alt="Sonarr tv example download" class="img__Ss2"></p>
<p>And on <a href="https://www.qbittorrent.org/" target="_blank" rel="noopener noreferrer" class="">qBittorrent</a> we can see the download progress :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/qbittorrent-tv-example-download.png" alt="QBittorrent tv example download" class="img__Ss2"></p>
<p>And finally when the download is finished, <a href="https://sonarr.tv/" target="_blank" rel="noopener noreferrer" class="">Sonarr</a> will rename and move the episode in the right folder. And <a href="https://www.plex.tv/" target="_blank" rel="noopener noreferrer" class="">Plex</a> will detect the new episode and add it to the library.</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/sonarr-tv-example-final.png" alt="Sonarr tv example final" class="img__Ss2"></p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/plex-tv-example-final.png" alt="Plex tv example final" class="img__Ss2"></p>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="radarr">Radarr<a href="https://lunik.tiwabbit.fr/blog/auto-media-center#radarr" class="hash-link" aria-label="Direct link to Radarr" title="Direct link to Radarr" translate="no">​</a></h4>
<p><a href="https://radarr.video/" target="_blank" rel="noopener noreferrer" class="">Radarr</a> is a movies manager. It will allow us to search for movies and download them automatically.</p>
<p>First we need to access the service through the web interface. The default port is <code>7878</code>. So we can access it at <code>http://&lt;server-ip&gt;:7878</code>.</p>
<p>You should see something like this :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/radarr-web-interface-init.png" alt="Radarr web interface init" class="img__Ss2"></p>
<h5 class="anchor anchorTargetStickyNavbar_SAay" id="indexers-1">Indexers<a href="https://lunik.tiwabbit.fr/blog/auto-media-center#indexers-1" class="hash-link" aria-label="Direct link to Indexers" title="Direct link to Indexers" translate="no">​</a></h5>
<p>We are going to see the configuration of the indexers in (<strong>Settings</strong> &gt; <strong>Indexers</strong>). This will be automatically configured by <a href="https://lunik.tiwabbit.fr/blog/auto-media-center#prowlarr" class="">Prowlarr</a> later.</p>
<h5 class="anchor anchorTargetStickyNavbar_SAay" id="download-client-1">Download client<a href="https://lunik.tiwabbit.fr/blog/auto-media-center#download-client-1" class="hash-link" aria-label="Direct link to Download client" title="Direct link to Download client" translate="no">​</a></h5>
<p>We are going to configure the download client in (<strong>Settings</strong> &gt; <strong>Download Client</strong>). For this example, I will use the following configuration :</p>
<table><thead><tr><th style="text-align:left">Field</th><th style="text-align:right">Value</th></tr></thead><tbody><tr><td style="text-align:left">Type</td><td style="text-align:right"><code>qBittorrent</code></td></tr><tr><td style="text-align:left">Enable</td><td style="text-align:right"><code>Yes</code></td></tr><tr><td style="text-align:left">Host</td><td style="text-align:right"><code>qbittorrent</code></td></tr><tr><td style="text-align:left">Port</td><td style="text-align:right"><code>8080</code></td></tr><tr><td style="text-align:left">UseSsl</td><td style="text-align:right"><code>No</code></td></tr><tr><td style="text-align:left">Username</td><td style="text-align:right"><code>admin</code></td></tr><tr><td style="text-align:left">Password</td><td style="text-align:right"><code>adminadmin</code></td></tr></tbody></table>
<p>We can test the connection by clicking on <em>Test</em> at the bottom of the page.</p>
<p><strong>Note :</strong> You can set the <strong>Initial State</strong> to <code>Paused</code> if you don't want <a href="https://radarr.video/" target="_blank" rel="noopener noreferrer" class="">Radarr</a> to download all the Films you add.</p>
<h5 class="anchor anchorTargetStickyNavbar_SAay" id="media-management-1">Media management<a href="https://lunik.tiwabbit.fr/blog/auto-media-center#media-management-1" class="hash-link" aria-label="Direct link to Media management" title="Direct link to Media management" translate="no">​</a></h5>
<p>We are going to configure the media management in (<strong>Settings</strong> &gt; <strong>Media Management</strong>). This will allow <a href="https://radarr.video/" target="_blank" rel="noopener noreferrer" class="">Radarr</a> to rename and move the downloaded films from qBittorret in the right folder for <a href="https://www.plex.tv/" target="_blank" rel="noopener noreferrer" class="">Plex</a> to find them.</p>
<p>For this example, I will use the following configuration :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/radarr-media-management-config-01.png" alt="Radarr media management config 01" class="img__Ss2"></p>
<h5 class="anchor anchorTargetStickyNavbar_SAay" id="test-1">Test<a href="https://lunik.tiwabbit.fr/blog/auto-media-center#test-1" class="hash-link" aria-label="Direct link to Test" title="Direct link to Test" translate="no">​</a></h5>
<p>Now we should be able to add a TV show. In (<strong>Series</strong> &gt; <strong>Add Series</strong>), we can search for <code>Avengers</code> and add it :</p>
<table><thead><tr><th style="text-align:left">Field</th><th style="text-align:right">Value</th></tr></thead><tbody><tr><td style="text-align:left">Root Folder</td><td style="text-align:right"><code>/media/Films/The Avengers (2012)</code></td></tr><tr><td style="text-align:left">Monitor</td><td style="text-align:right"><code>Movie Only</code></td></tr><tr><td style="text-align:left">Quality Profile</td><td style="text-align:right"><code>HD-1080p</code></td></tr></tbody></table>
<p>Then we click on the <code>The Avengers</code> card and we should see something like this :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/radarr-movie-example.png" alt="Sonarr movie example" class="img__Ss2"></p>
<p>Then we can click on the <code>Search</code> tab to search the film torrents :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/radarr-movie-example-search.png" alt="Sonarr movie example search" class="img__Ss2"></p>
<p>We can click on the <code>Download</code> button to trigger the download of the first season.</p>
<p>On the (<strong>Activity</strong> &gt; <strong>Queue</strong>) we can see the download progress :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/radarr-movie-example-download.png" alt="Radarr movie example download" class="img__Ss2"></p>
<p>And on <a href="https://www.qbittorrent.org/" target="_blank" rel="noopener noreferrer" class="">qBittorrent</a> we can see the download progress :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/qbittorrent-movie-example-download.png" alt="QBittorrent movie example download" class="img__Ss2"></p>
<p>And finally when the download is finished, <a href="https://radarr.video/" target="_blank" rel="noopener noreferrer" class="">Radarr</a> will rename and move the episode in the right folder. And <a href="https://www.plex.tv/" target="_blank" rel="noopener noreferrer" class="">Plex</a> will detect the new episode and add it to the library.</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/radarr-movie-example-final.png" alt="Radarr movie example final" class="img__Ss2"></p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/plex-movie-example-final.png" alt="Plex movie example final" class="img__Ss2"></p>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="prowlarr">Prowlarr<a href="https://lunik.tiwabbit.fr/blog/auto-media-center#prowlarr" class="hash-link" aria-label="Direct link to Prowlarr" title="Direct link to Prowlarr" translate="no">​</a></h4>
<p><a href="https://prowlarr.com/" target="_blank" rel="noopener noreferrer" class="">Prowlarr</a> is a torrents indexer. It will allow us to search for torrents on multiple trackers at once.</p>
<p>First we need to access the service through the web interface. The default port is <code>9696</code>. So we can access it at <code>http://&lt;server-ip&gt;:9696</code>.</p>
<p>You should see something like this :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/prowlarr-web-interface-init.png" alt="Prowlarr web interface init" class="img__Ss2"></p>
<p>Then we need to add some indexers. For this example, I will add the following indexers :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/prowlarr-indexer-config.png" alt="Prowlarr indexer config" class="img__Ss2"></p>
<h5 class="anchor anchorTargetStickyNavbar_SAay" id="apps">Apps<a href="https://lunik.tiwabbit.fr/blog/auto-media-center#apps" class="hash-link" aria-label="Direct link to Apps" title="Direct link to Apps" translate="no">​</a></h5>
<p>This section in <strong>Settings</strong> will allow us to link Prowlarr to <a href="https://lunik.tiwabbit.fr/blog/auto-media-center#radarr" class="">Radarr</a> and <a href="https://lunik.tiwabbit.fr/blog/auto-media-center#sonarr" class="">Sonarr</a>. This way, all the indexers we add in Prowlarr will be automatically added in <a href="https://lunik.tiwabbit.fr/blog/auto-media-center#radarr" class="">Radarr</a> and <a href="https://lunik.tiwabbit.fr/blog/auto-media-center#sonarr" class="">Sonarr</a>.</p>
<p>Add two applications :</p>
<table><thead><tr><th style="text-align:left">Field</th><th style="text-align:right">Value</th></tr></thead><tbody><tr><td style="text-align:left">Name</td><td style="text-align:right"><code>Radarr</code></td></tr><tr><td style="text-align:left">Sync Level</td><td style="text-align:right"><code>Full Sync</code></td></tr><tr><td style="text-align:left">Prowlarr Server</td><td style="text-align:right"><code>http://prowlarr:9696</code></td></tr><tr><td style="text-align:left">Radarr Server</td><td style="text-align:right"><code>http://radarr:7878</code></td></tr><tr><td style="text-align:left">API Key</td><td style="text-align:right">Copied API key from <a href="https://radarr.video/" target="_blank" rel="noopener noreferrer" class="">Radarr</a> web interface (<strong>Settings</strong> &gt; <strong>Général</strong>).</td></tr></tbody></table>
<table><thead><tr><th style="text-align:left">Field</th><th style="text-align:right">Value</th></tr></thead><tbody><tr><td style="text-align:left">Name</td><td style="text-align:right"><code>Sonarr</code></td></tr><tr><td style="text-align:left">Sync Level</td><td style="text-align:right"><code>Full Sync</code></td></tr><tr><td style="text-align:left">Prowlarr Server</td><td style="text-align:right"><code>http://prowlarr:9696</code></td></tr><tr><td style="text-align:left">Sonarr Server</td><td style="text-align:right"><code>http://sonarr:8989</code></td></tr><tr><td style="text-align:left">API Key</td><td style="text-align:right">Copied API key from <a href="https://sonarr.tv/" target="_blank" rel="noopener noreferrer" class="">Sonarr</a> web interface (<strong>Settings</strong> &gt; <strong>Général</strong>).</td></tr></tbody></table>
<p>By checking in <a href="https://lunik.tiwabbit.fr/blog/auto-media-center#radarr" class="">Radarr</a> and <a href="https://lunik.tiwabbit.fr/blog/auto-media-center#sonarr" class="">Sonarr</a>, we can see that the indexers have been successfully added in the <strong>Indexers</strong> section.</p>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="overseerr">Overseerr<a href="https://lunik.tiwabbit.fr/blog/auto-media-center#overseerr" class="hash-link" aria-label="Direct link to Overseerr" title="Direct link to Overseerr" translate="no">​</a></h4>
<p>As you can see, <a href="https://radarr.video/" target="_blank" rel="noopener noreferrer" class="">Radarr</a> and <a href="https://sonarr.tv/" target="_blank" rel="noopener noreferrer" class="">Sonarr</a> are great tools. But they are not very user friendly. <a href="https://overseerr.dev/" target="_blank" rel="noopener noreferrer" class="">Overseerr</a> will allow us to request TV shows and movies in a nice web interface.</p>
<p>First we need to access the service through the web interface. The default port is <code>5055</code>. So we can access it at <code>http://&lt;server-ip&gt;:5055</code>.</p>
<p>During the first setup, we need to configure the following fields :</p>
<p><strong>Plex Settings</strong></p>
<p>This will allow <a href="https://overseerr.dev/" target="_blank" rel="noopener noreferrer" class="">Overseerr</a> to connect to <a href="https://www.plex.tv/" target="_blank" rel="noopener noreferrer" class="">Plex</a> and look if media are already available in the library.</p>
<table><thead><tr><th style="text-align:left">Field</th><th style="text-align:right">Value</th></tr></thead><tbody><tr><td style="text-align:left">Server</td><td style="text-align:right"><code>Your Plex server</code></td></tr><tr><td style="text-align:left">Hostname or IP Address</td><td style="text-align:right"><code>http://plex</code></td></tr><tr><td style="text-align:left">Port</td><td style="text-align:right"><code>32400</code></td></tr><tr><td style="text-align:left">Use SSL</td><td style="text-align:right"><code>No</code></td></tr><tr><td style="text-align:left"><a href="https://www.plex.tv/" target="_blank" rel="noopener noreferrer" class="">Plex</a> Libraries</td><td style="text-align:right"><code>Films,Series</code></td></tr></tbody></table>
<p><strong>Radarr Settings</strong></p>
<p>This will allow <a href="https://overseerr.dev/" target="_blank" rel="noopener noreferrer" class="">Overseerr</a> to connect to <a href="https://radarr.video/" target="_blank" rel="noopener noreferrer" class="">Radarr</a> and trigger the download of requested movies.</p>
<table><thead><tr><th style="text-align:left">Field</th><th style="text-align:right">Value</th></tr></thead><tbody><tr><td style="text-align:left">Default Server</td><td style="text-align:right"><code>Yes</code></td></tr><tr><td style="text-align:left">Server Name</td><td style="text-align:right"><code>Radarr</code></td></tr><tr><td style="text-align:left">Hostname or IP Address</td><td style="text-align:right"><code>http://radarr</code></td></tr><tr><td style="text-align:left">Port</td><td style="text-align:right"><code>7878</code></td></tr><tr><td style="text-align:left">API Key</td><td style="text-align:right">Copied API key from <a href="https://radarr.video/" target="_blank" rel="noopener noreferrer" class="">Radarr</a> web interface (<strong>Settings</strong> &gt; <strong>Général</strong>).</td></tr><tr><td style="text-align:left">Quality Profile</td><td style="text-align:right"><code>HD-1080p</code></td></tr><tr><td style="text-align:left">Root Folder</td><td style="text-align:right"><code>/media/Films</code></td></tr></tbody></table>
<p><strong>Sonarr Settings</strong></p>
<p>This will allow <a href="https://overseerr.dev/" target="_blank" rel="noopener noreferrer" class="">Overseerr</a> to connect to <a href="https://sonarr.tv/" target="_blank" rel="noopener noreferrer" class="">Sonarr</a> and trigger the download of requested TV shows.</p>
<table><thead><tr><th style="text-align:left">Field</th><th style="text-align:right">Value</th></tr></thead><tbody><tr><td style="text-align:left">Default Server</td><td style="text-align:right"><code>Yes</code></td></tr><tr><td style="text-align:left">Server Name</td><td style="text-align:right"><code>Sonarr</code></td></tr><tr><td style="text-align:left">Hostname or IP Address</td><td style="text-align:right"><code>http://sonarr</code></td></tr><tr><td style="text-align:left">Port</td><td style="text-align:right"><code>8989</code></td></tr><tr><td style="text-align:left">API Key</td><td style="text-align:right">Copied API key from <a href="https://sonarr.tv/" target="_blank" rel="noopener noreferrer" class="">Sonarr</a> web interface (<strong>Settings</strong> &gt; <strong>Général</strong>).</td></tr><tr><td style="text-align:left">Quality Profile</td><td style="text-align:right"><code>HD-1080p</code></td></tr><tr><td style="text-align:left">Root Folder</td><td style="text-align:right"><code>/media/Series</code></td></tr></tbody></table>
<p>If everything is configured correctly, you should see something like this in the Discovery tab :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/overseerr-discovery.png" alt="Overseerr discovery" class="img__Ss2"></p>
<p>We can see that the movie <code>Avengers</code> and TV show <code>Game of Thrones</code> are already available in <a href="https://www.plex.tv/" target="_blank" rel="noopener noreferrer" class="">Plex</a>.</p>
<p><strong>Notes :</strong>
User that have access to your <a href="https://www.plex.tv/" target="_blank" rel="noopener noreferrer" class="">Plex</a> server will be able to request movies and TV shows. They can login with their <a href="https://www.plex.tv/" target="_blank" rel="noopener noreferrer" class="">Plex</a> account.</p>
<p>You can configure the users in the <strong>Users</strong> tab. You can for example create a local users that will be able to acces <a href="https://overseerr.dev/" target="_blank" rel="noopener noreferrer" class="">Overseerr</a> without a <a href="https://www.plex.tv/" target="_blank" rel="noopener noreferrer" class="">Plex</a> account.</p>
<h5 class="anchor anchorTargetStickyNavbar_SAay" id="requesting-media">Requesting media<a href="https://lunik.tiwabbit.fr/blog/auto-media-center#requesting-media" class="hash-link" aria-label="Direct link to Requesting media" title="Direct link to Requesting media" translate="no">​</a></h5>
<p>We will now try to request a movie from <a href="https://overseerr.dev/" target="_blank" rel="noopener noreferrer" class="">Overseerr</a>. We will use a local user for this example.</p>
<p>Let's try to request the movie <code>Deerskin</code>. From the <strong>Discover</strong> tab, we can search for <code>Deerskin</code> and we should see something like this :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/overseerr-movie-search.png" alt="Overseerr movie search" class="img__Ss2"></p>
<p>Then we can click on the <code>Request</code> button to request the movie.</p>
<p>Going back to the <strong>Requests</strong> tab with our admin account, we can see the request :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-06-21-auto-media-center/overseerr-movie-request.png" alt="Overseerr movie request" class="img__Ss2"></p>
<p>We can click on the <code>Approve</code> button to approve the request. This will trigger the download of the movie in <a href="https://radarr.video/" target="_blank" rel="noopener noreferrer" class="">Radarr</a> and download it like we saw earlier in the <a href="https://radarr.video/" target="_blank" rel="noopener noreferrer" class="">Radarr</a> section.</p>
<p>And finally when all the process is finished, we can see the movie is available in <a href="https://overseerr.dev/" target="_blank" rel="noopener noreferrer" class="">Overseerr</a> and <a href="https://www.plex.tv/" target="_blank" rel="noopener noreferrer" class="">Plex</a>.</p>]]></content>
        <author>
            <name>Guillaume MARTINEZ</name>
            <uri>https://github.com/Lunik</uri>
        </author>
        <category label="media" term="media"/>
        <category label="media-center" term="media-center"/>
        <category label="plex" term="plex"/>
        <category label="svod" term="svod"/>
        <category label="streaming" term="streaming"/>
        <category label="radarr" term="radarr"/>
        <category label="sonarr" term="sonarr"/>
        <category label="prowlarr" term="prowlarr"/>
        <category label="overseerr" term="overseerr"/>
        <category label="qbitorrent" term="qbitorrent"/>
        <category label="torrent" term="torrent"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Install custom CA on IOS]]></title>
        <id>https://lunik.tiwabbit.fr/blog/install-custom-ca-on-ios</id>
        <link href="https://lunik.tiwabbit.fr/blog/install-custom-ca-on-ios"/>
        <updated>2023-01-28T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Step by step installation of a root CA certificate on IOS]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-01-28-install-custom-ca-on-ios/cover.jpg" alt="cover" class="img__Ss2"></p>
<p>Now that I have generated by own custom certification chain in the previous article "x509 certificat managment" I need to install the Certificate Authority (CA) on my iPhone.</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="install-the-certificate">Install the certificate<a href="https://lunik.tiwabbit.fr/blog/install-custom-ca-on-ios#install-the-certificate" class="hash-link" aria-label="Direct link to Install the certificate" title="Direct link to Install the certificate" translate="no">​</a></h3>
<p>First I need to install the CA certificate on the device.</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-01-28-install-custom-ca-on-ios/1_download_folder.jpg" alt="1_download_folder" class="img__Ss2"></p>
<p>The file was on the device and I opened it from the file explorer.</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-01-28-install-custom-ca-on-ios/2_open_file.jpg" alt="2_open_file" class="img__Ss2"></p>
<p>Then I need to go in IOS settings. In the "Général" section.</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-01-28-install-custom-ca-on-ios/3_general_settings.jpg" alt="3_general_settings" class="img__Ss2"></p>
<p>Then in the "VPN et gestion de l'appareil" section.</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-01-28-install-custom-ca-on-ios/4_device_managment.jpg" alt="4_device_managment" class="img__Ss2"></p>
<p>Click on the CA in the "Profil téléchargé" section.</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-01-28-install-custom-ca-on-ios/5_downloaded_profile.jpg" alt="5_downloaded_profile" class="img__Ss2"></p>
<p>I have followed the install process.</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-01-28-install-custom-ca-on-ios/6_install_profile_01.jpg" alt="6_install_profile_01" class="img__Ss2">
<img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-01-28-install-custom-ca-on-ios/7_install_profile_02.jpg" alt="7_install_profile_02" class="img__Ss2"></p>
<p>Then verified the installation.</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-01-28-install-custom-ca-on-ios/8_verify_install.jpg" alt="8_verify_install" class="img__Ss2"></p>
<p>Finally I need to approve the CA as a root authority on the device.
By going back to the "Général" section. Now in the "Informations" section.</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-01-28-install-custom-ca-on-ios/9_informations.jpg" alt="9_informations" class="img__Ss2"></p>
<p>Then in the "Réglages des certificats" section.</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-01-28-install-custom-ca-on-ios/10_certificats_settings.jpg" alt="10_certificats_settings" class="img__Ss2"></p>
<p>Finally toggled the certificate and completed the installation.</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-01-28-install-custom-ca-on-ios/11_toggle_certificate.jpg" alt="11_toggle_certificate" class="img__Ss2">
<img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2023-01-28-install-custom-ca-on-ios/12_validate_toggle_certificate.jpg" alt="12_validate_toggle_certificate" class="img__Ss2"></p>]]></content>
        <author>
            <name>Guillaume MARTINEZ</name>
            <uri>https://github.com/Lunik</uri>
        </author>
        <category label="tls" term="tls"/>
        <category label="certificate" term="certificate"/>
        <category label="ios" term="ios"/>
        <category label="iphone" term="iphone"/>
        <category label="ca" term="ca"/>
        <category label="root" term="root"/>
        <category label="install" term="install"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Decrypt Terraform States in GitLab Backend]]></title>
        <id>https://lunik.tiwabbit.fr/blog/decrypt-terraform-state-gitlab-backend</id>
        <link href="https://lunik.tiwabbit.fr/blog/decrypt-terraform-state-gitlab-backend"/>
        <updated>2022-10-05T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[This article explain how you can retreive and decrypt Terraform states stored in GitLab backend from a GitLab backup]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2022-10-05-decrypt-terraform-state-gitlab-backend/cover.jpeg" alt="cover" class="img__Ss2"></p>
<p>Assuming you are using the <a href="https://docs.gitlab.com/ee/user/infrastructure/iac/terraform_state.html" target="_blank" rel="noopener noreferrer" class="">GitLab Terraform state feature</a> in your self managed instance and you are using the embded <a href="https://docs.gitlab.com/charts/backup-restore/" target="_blank" rel="noopener noreferrer" class="">backup utility provided by GitLab</a>.</p>
<p>The Terraform state files are encrypted before they are stored. This means that you cannot retreiv the content at rest.
For this purpose, GitLab use <a href="https://docs.gitlab.com/ee/development/application_secrets.html" target="_blank" rel="noopener noreferrer" class="">application secrets</a> (and derive new secrets from thoses keys when needed) to encrypt sensitive content.</p>
<p>You want to retreiv the content of a state file from a GitLab backup. Like explained in <a href="https://gitlab.com/gitlab-org/gitlab/-/issues/335739" target="_blank" rel="noopener noreferrer" class="">this issue</a>, it's not possible to easily retreiv a decrypted content is the instance is offline.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="recover-procedure">Recover procedure<a href="https://lunik.tiwabbit.fr/blog/decrypt-terraform-state-gitlab-backend#recover-procedure" class="hash-link" aria-label="Direct link to Recover procedure" title="Direct link to Recover procedure" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="retreiving-gitlab-instance-secrets">Retreiving GitLab instance secrets<a href="https://lunik.tiwabbit.fr/blog/decrypt-terraform-state-gitlab-backend#retreiving-gitlab-instance-secrets" class="hash-link" aria-label="Direct link to Retreiving GitLab instance secrets" title="Direct link to Retreiving GitLab instance secrets" translate="no">​</a></h3>
<p>Using the <a href="https://docs.gitlab.com/ee/development/application_secrets.html" target="_blank" rel="noopener noreferrer" class="">GitLab documentation about application secrets</a> you need to retreiv the value of the <code>db_key_base</code>.</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="retreiving-project-informations">Retreiving project informations<a href="https://lunik.tiwabbit.fr/blog/decrypt-terraform-state-gitlab-backend#retreiving-project-informations" class="hash-link" aria-label="Direct link to Retreiving project informations" title="Direct link to Retreiving project informations" translate="no">​</a></h3>
<p>Terraform state storage is linked to a GitLab project. If the project still exists in your instance, save the <code>project_id</code> for later.</p>
<p>If you have deleted the project we are going to need the database backup (also stored in the GitLab backup).</p>
<p>First search in the <code>public.routes</code> table for your project. You can filter by the <code>source_type=Project</code> and <code>path=path/of/your/project</code>. Write down the value in the <code>namespace_id</code> column.</p>
<p>Then look at the <code>public.projects</code> table filtering the <code>namespace_id</code> retreived just before. Write down the <code>id</code> of the project.</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="calculating-the-state-storage-path">Calculating the state storage path<a href="https://lunik.tiwabbit.fr/blog/decrypt-terraform-state-gitlab-backend#calculating-the-state-storage-path" class="hash-link" aria-label="Direct link to Calculating the state storage path" title="Direct link to Calculating the state storage path" translate="no">​</a></h3>
<p>States are stored with hashed path.</p>
<p>The first part is the hash256 sum of the project <code>id</code>. You can use the following command to calculate it :</p>
<div class="language-bash codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bash codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-n</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"&lt;project_id&gt;"</span><span class="token plain"> </span><span class="token operator">|</span><span class="token plain"> openssl dgst </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-sha256</span><br></div></code></pre></div></div>
<p>The second part can be retreived from the database in the table <code>public.terraform_states</code>. Filter by the <code>project_id</code> and state <code>name</code> save the <code>uuid</code>.</p>
<p>The full folder path of the state file is now :</p>
<div class="language-text codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-text codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">&lt;part_1[0:1]&gt;/&lt;part_1[2:3]&gt;/&lt;part_1&gt;/&lt;part_2&gt;</span><br></div></code></pre></div></div>
<p>If you have enabled versionning state are stored with the filename <code>&lt;serial_number&gt;.tfstate</code></p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="retreiving-the-content">Retreiving the content<a href="https://lunik.tiwabbit.fr/blog/decrypt-terraform-state-gitlab-backend#retreiving-the-content" class="hash-link" aria-label="Direct link to Retreiving the content" title="Direct link to Retreiving the content" translate="no">​</a></h3>
<p>This is the hardest part and you need to be a little familiar with <a href="https://www.ruby-lang.org/" target="_blank" rel="noopener noreferrer" class="">Ruby</a>.</p>
<p>GitLab use a tool named <a href="https://github.com/ankane/lockbox" target="_blank" rel="noopener noreferrer" class="">Lockbox</a> to encrypt Terraform state content before storing them. We are going to use the same tool to decrypt our files.</p>
<p>Fill and use the following script :</p>
<div class="language-ruby codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-ruby codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">#################</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"># Configuration #</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">#################</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"># Retreived from GitLab rails secrets</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"># https://docs.gitlab.com/ee/development/application_secrets.html</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"># This is a dummy key base. Don't bother using it</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">db_key_base = "&lt;FILL_DB_KEY_BASE&gt;"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"># The project ID in GitLab</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">project_id = "&lt;FILL_PROJECT_ID&gt;"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"># The file to decrypt</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">input_file = "&lt;FILL_ENCRYPTED_STATE_FILE_PATH&gt;"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"># The file where to write the terraform state content</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">output_file = "&lt;FILL_DECRYPTED_STATE_FILE_PATH&gt;"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">#############</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"># ALGORITHM #</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">#############</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"># Compute encryption key</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">key = OpenSSL::HMAC.digest('SHA256', db_key_base, project_id)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"># Generate LockBox tool</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"># https://github.com/ankane/lockbox</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">lockbox = Lockbox.new(key: key)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">encrypted_state_content = File.binread(input_file)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">state_content = lockbox.decrypt_str(encrypted_state_content)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">File.write(output_file, state_content)</span><br></div></code></pre></div></div>
<p>I'm not an expert in <a href="https://www.ruby-lang.org/" target="_blank" rel="noopener noreferrer" class="">Ruby</a> but here is a simple bash script to setup the correct environment :</p>
<div class="language-bash codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bash codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token shebang important">#!/bin/bash</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">rails new myapp</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">cd</span><span class="token plain"> myapp/</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'gem "lockbox"'</span><span class="token plain"> </span><span class="token operator">&gt;&gt;</span><span class="token plain"> Gemfile</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">bundle </span><span class="token function" style="color:rgb(80, 250, 123)">install</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">bundle </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">exec</span><span class="token plain"> rails runner decode.rb</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="resources">Resources<a href="https://lunik.tiwabbit.fr/blog/decrypt-terraform-state-gitlab-backend#resources" class="hash-link" aria-label="Direct link to Resources" title="Direct link to Resources" translate="no">​</a></h2>
<ul>
<li class=""><a href="https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/uploaders/terraform/state_uploader.rb" target="_blank" rel="noopener noreferrer" class="">GitLab source code used to encrypt state files</a></li>
<li class=""><a href="https://gitlab.com/gitlab-org/gitlab/-/issues/342225" target="_blank" rel="noopener noreferrer" class="">GitLab issue for documenting the recover procedure</a></li>
</ul>]]></content>
        <author>
            <name>Guillaume MARTINEZ</name>
            <uri>https://github.com/Lunik</uri>
        </author>
        <category label="terraform" term="terraform"/>
        <category label="gitlab" term="gitlab"/>
        <category label="terraform-state" term="terraform-state"/>
        <category label="state" term="state"/>
        <category label="backend" term="backend"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Properly handle Git flow]]></title>
        <id>https://lunik.tiwabbit.fr/blog/properly-handle-git-flow</id>
        <link href="https://lunik.tiwabbit.fr/blog/properly-handle-git-flow"/>
        <updated>2022-09-11T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Full example of a Git repository lifecycle]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2022-09-11-properly-handle-git-flow/cover.png" alt="cover" class="img__Ss2"></p>
<p>Any developper use or will use <a href="https://git-scm.com/" target="_blank" rel="noopener noreferrer" class="">Git</a> at a point in is career. Most of the time they will have to work with other people on the same <a href="https://git-scm.com/" target="_blank" rel="noopener noreferrer" class="">Git</a> repository. To avoid it to be branch and commit battlefield here is a simple guide on how to contribute properly on a <a href="https://git-scm.com/" target="_blank" rel="noopener noreferrer" class="">Git</a> repository.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="basics">Basics<a href="https://lunik.tiwabbit.fr/blog/properly-handle-git-flow#basics" class="hash-link" aria-label="Direct link to Basics" title="Direct link to Basics" translate="no">​</a></h2>
<p>First of all, the <a href="https://git-scm.com/" target="_blank" rel="noopener noreferrer" class="">Git</a> repository should have a default branch often called <code>master</code> or <code>main</code> (it may be anything else as long as everyone agree on the name).</p>
<p>Secondly, a second branch used for development purpose. This one will follow the default branch pretty closely. It will be a receptacle for any new development. This branch is the only one allowed to be merged on the default branch.</p>
<p>Finally, all other branch fall into the last category. They are features, bugs fix and other.</p>
<p><strong>Note :</strong> Only HotFix branches are allowed to bypass the development branch.</p>
<p>Here is an example of a simple <a href="https://git-scm.com/" target="_blank" rel="noopener noreferrer" class="">Git</a> repository</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2022-09-11-properly-handle-git-flow/01_example-git-repository-graph.png" alt="example-git-repository-graph" class="img__Ss2"></p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="full-example-of-a-repository-lifecycle">Full example of a repository lifecycle<a href="https://lunik.tiwabbit.fr/blog/properly-handle-git-flow#full-example-of-a-repository-lifecycle" class="hash-link" aria-label="Direct link to Full example of a repository lifecycle" title="Direct link to Full example of a repository lifecycle" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="initializing-a-repository">Initializing a repository<a href="https://lunik.tiwabbit.fr/blog/properly-handle-git-flow#initializing-a-repository" class="hash-link" aria-label="Direct link to Initializing a repository" title="Direct link to Initializing a repository" translate="no">​</a></h3>
<p>The first thing to do when creating a new repository, is to initialize the base structure.</p>
<p>Create the <a href="https://git-scm.com/" target="_blank" rel="noopener noreferrer" class="">Git</a> repository :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">mkdir</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-p</span><span class="token plain"> myrepository</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">cd</span><span class="token plain"> myrepository</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> init</span><br></div></code></pre></div></div>
<p>You may want to create your first files. Like the <code>README.md</code>/<code>.gitignore</code> and some package file like <code>package.json</code> or <code>pom.xml</code>.</p>
<p>Then create the initial commit of the repository with those files :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">add</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> commit </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-m</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Initial commit"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> tag v0.0.0</span><br></div></code></pre></div></div>
<p>Finally create the development branch :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> branch develop</span><br></div></code></pre></div></div>
<p>The repository should looks like :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2022-09-11-properly-handle-git-flow/02_init-git-repository-graph.png" alt="init-git-repository-graph" class="img__Ss2"></p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="initializing-the-development-branch">Initializing the development branch<a href="https://lunik.tiwabbit.fr/blog/properly-handle-git-flow#initializing-the-development-branch" class="hash-link" aria-label="Direct link to Initializing the development branch" title="Direct link to Initializing the development branch" translate="no">​</a></h3>
<p>Now that you have ou base structure on the <a href="https://git-scm.com/" target="_blank" rel="noopener noreferrer" class="">Git</a> repository, it's time initialize the development branch.</p>
<p>First checkout to the development branch :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> checkout develop</span><br></div></code></pre></div></div>
<p>Sometimes you may want to add an initial commit on the development branch with the modification of the current version in your package file <code>package.json</code>, <code>pom.xml</code> or other.</p>
<p>Edit thoses files then create a commit :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">add</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> commit </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-m</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Prepare development version"</span><br></div></code></pre></div></div>
<p>The repository should looks like :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2022-09-11-properly-handle-git-flow/03_init-develop-git-repository-graph.png" alt="init-develop-git-repository-graph" class="img__Ss2"></p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="add-the-first-feature">Add the first feature<a href="https://lunik.tiwabbit.fr/blog/properly-handle-git-flow#add-the-first-feature" class="hash-link" aria-label="Direct link to Add the first feature" title="Direct link to Add the first feature" translate="no">​</a></h3>
<p>Now let's create the first feature in our application.</p>
<p>Verify that your are on the development branch :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> branch</span><br></div></code></pre></div></div>
<p>Result :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">* develop</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  master</span><br></div></code></pre></div></div>
<p>If not checkout on the development branch.</p>
<p>Then checkout to a feature branch :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> checkout </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-b</span><span class="token plain"> feature/my-first-feature</span><br></div></code></pre></div></div>
<p>Now write the feature and do commit from time to time with :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">add</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> commit </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-m</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"save dev"</span><br></div></code></pre></div></div>
<p>The repository should looks like :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2022-09-11-properly-handle-git-flow/04_feature-branch-git-repository-graph.png" alt="feature-branch-git-repository-graph" class="img__Ss2"></p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="work-on-a-required-feature">Work on a required feature<a href="https://lunik.tiwabbit.fr/blog/properly-handle-git-flow#work-on-a-required-feature" class="hash-link" aria-label="Direct link to Work on a required feature" title="Direct link to Work on a required feature" translate="no">​</a></h3>
<p>You where working on the first feature but you realize that you needed another one to continue.</p>
<p><strong>/!\ Make sure that you don't have any unstaged changes before switching branches</strong></p>
<p>Reproduce the same commands as for the first feature :</p>
<ul>
<li class="">Checkout to the development branch</li>
<li class="">Then checkout to a feature branch</li>
<li class="">Write your code and create commits</li>
</ul>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> checkout develop</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> checkout </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-b</span><span class="token plain"> feature/my-required-feature</span><br></div></code></pre></div></div>
<p>The repository should looks like :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2022-09-11-properly-handle-git-flow/05_feature2-branch-git-repository-graph.png" alt="feature2-branch-git-repository-graph" class="img__Ss2"></p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="merging-the-required-feature">Merging the required feature<a href="https://lunik.tiwabbit.fr/blog/properly-handle-git-flow#merging-the-required-feature" class="hash-link" aria-label="Direct link to Merging the required feature" title="Direct link to Merging the required feature" translate="no">​</a></h3>
<p>Now that you have finished your work on the required feature comes the time to merge the code to the development branch.</p>
<p>First you need to rebase the branch to remove all unecessary commits :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> rebase </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-i</span><span class="token plain"> develop</span><br></div></code></pre></div></div>
<p>The first commit should always be picked. All other commits can be squashed.</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">pick fa71872 save dev</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">squash 5d9a97a save dev</span><br></div></code></pre></div></div>
<p>The repository should looks like :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2022-09-11-properly-handle-git-flow/06_feature2-rebase-git-repository-graph.png" alt="feature2-rebase-git-repository-graph" class="img__Ss2"></p>
<p>Checkout to the development branch and merge the require feature branch :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> checkout develop</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> merge --no-ff feature/my-required-feature</span><br></div></code></pre></div></div>
<p>The repository should looks like :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2022-09-11-properly-handle-git-flow/07_feature2-merge-git-repository-graph.png" alt="feature2-merge-git-repository-graph" class="img__Ss2"></p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="continuing-the-work-on-the-first-feature">Continuing the work on the first feature<a href="https://lunik.tiwabbit.fr/blog/properly-handle-git-flow#continuing-the-work-on-the-first-feature" class="hash-link" aria-label="Direct link to Continuing the work on the first feature" title="Direct link to Continuing the work on the first feature" translate="no">​</a></h3>
<p>Now that you have finished the required feature, you want to rebase your current work on the first feature to retrieve the content of the required feature.</p>
<ul>
<li class="">Checkout to the feature branch</li>
<li class="">Rebase it on the developement branch</li>
</ul>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> checkout feature/my-first-feature</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> rebase develop</span><br></div></code></pre></div></div>
<p>What this command is doing is taking all the commit from the feature branch and apply them at the end of the developement branch.</p>
<p>The repository should looks like :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2022-09-11-properly-handle-git-flow/08_feature-rebase-git-repository-graph.png" alt="feature-rebase-git-repository-graph" class="img__Ss2"></p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="merging-the-first-feature">Merging the first feature<a href="https://lunik.tiwabbit.fr/blog/properly-handle-git-flow#merging-the-first-feature" class="hash-link" aria-label="Direct link to Merging the first feature" title="Direct link to Merging the first feature" translate="no">​</a></h3>
<p>Now that you have finished your work on the first feature comes the time to merge the code to the development branch.</p>
<p>First you need to rebase the branch to remove all unecessary commits :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> rebase </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-i</span><span class="token plain"> develop</span><br></div></code></pre></div></div>
<p>The first commit should always be picked. All other commits can be squashed.</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">pick 717051b save dev</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">squash 7d39273 save dev</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">squash 7b700f1 save dev</span><br></div></code></pre></div></div>
<p>The repository should looks like :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2022-09-11-properly-handle-git-flow/09_feature-rebase2-git-repository-graph.png" alt="feature-rebase2-git-repository-graph" class="img__Ss2"></p>
<p>Checkout to the development branch and merge the require feature branch :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> checkout develop</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> merge --no-ff feature/my-first-feature</span><br></div></code></pre></div></div>
<p>The repository should looks like :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2022-09-11-properly-handle-git-flow/10_feature-merge-git-repository-graph.png" alt="feature-merge-git-repository-graph" class="img__Ss2"></p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="hotfix-critical-issue">Hotfix critical issue<a href="https://lunik.tiwabbit.fr/blog/properly-handle-git-flow#hotfix-critical-issue" class="hash-link" aria-label="Direct link to Hotfix critical issue" title="Direct link to Hotfix critical issue" translate="no">​</a></h3>
<p>A critical issue have been discovered on the production application an you need to quickly produce a patch. You don't have the time to go through all the release process.</p>
<ul>
<li class="">Checkout from the default branch</li>
<li class="">Create a new hotfix branch and checkout to it</li>
<li class="">Make the correction</li>
<li class="">Merge into the default branch</li>
<li class="">Tag your new release</li>
</ul>
<p>The repository should looks like :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2022-09-11-properly-handle-git-flow/11_hotfix-git-repository-graph.png" alt="hotfix-git-repository-graph" class="img__Ss2"></p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="releasing-your-work">Releasing your work<a href="https://lunik.tiwabbit.fr/blog/properly-handle-git-flow#releasing-your-work" class="hash-link" aria-label="Direct link to Releasing your work" title="Direct link to Releasing your work" translate="no">​</a></h3>
<p>It's now time to release all that hard work.</p>
<p>First you need to merge the default branch to retreiv all hotfix corrections.</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> checkout develop</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> merge --no-ff master</span><br></div></code></pre></div></div>
<p>The repository should looks like :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2022-09-11-properly-handle-git-flow/12_develop-rebase-git-repository-graph.png" alt="develop-rebase-git-repository-graph" class="img__Ss2"></p>
<p>And finaly, merge your development on the default branch.</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> checkout master</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> merge --no-ff develop</span><br></div></code></pre></div></div>
<p>The repository should looks like :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2022-09-11-properly-handle-git-flow/13_develop-merge-git-repository-graph.png" alt="develop-merge-git-repository-graph" class="img__Ss2"></p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="cleanup">Cleanup<a href="https://lunik.tiwabbit.fr/blog/properly-handle-git-flow#cleanup" class="hash-link" aria-label="Direct link to Cleanup" title="Direct link to Cleanup" translate="no">​</a></h3>
<p>Let's do some cleanup by removing some unused references like feature and hotfix branches.</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> branch </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-D</span><span class="token plain"> feature/my-first-feature</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> branch </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-D</span><span class="token plain"> feature/my-required-feature</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">git</span><span class="token plain"> branch </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-D</span><span class="token plain"> hotfix/correct-critical-issue  </span><br></div></code></pre></div></div>
<p>The repository should looks like :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2022-09-11-properly-handle-git-flow/14_cleanup-git-repository-graph.png" alt="cleanup-git-repository-graph" class="img__Ss2"></p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="notes">Notes<a href="https://lunik.tiwabbit.fr/blog/properly-handle-git-flow#notes" class="hash-link" aria-label="Direct link to Notes" title="Direct link to Notes" translate="no">​</a></h2>
<p>All Git graph have been generated with <a href="https://bit-booster.com/" target="_blank" rel="noopener noreferrer" class="">Bit-Booster app</a></p>]]></content>
        <author>
            <name>Guillaume MARTINEZ</name>
            <uri>https://github.com/Lunik</uri>
        </author>
        <category label="git" term="git"/>
        <category label="gitflow" term="gitflow"/>
        <category label="merge request" term="merge request"/>
        <category label="pull request" term="pull request"/>
        <category label="feature" term="feature"/>
        <category label="merge" term="merge"/>
        <category label="pull" term="pull"/>
        <category label="request" term="request"/>
        <category label="branche" term="branche"/>
        <category label="rebase" term="rebase"/>
        <category label="checkout" term="checkout"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Zero trust deployment with Kubernetes]]></title>
        <id>https://lunik.tiwabbit.fr/blog/zero-trust-kubernetes-deployment</id>
        <link href="https://lunik.tiwabbit.fr/blog/zero-trust-kubernetes-deployment"/>
        <updated>2022-08-11T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[How to securely deploy application inside a Kubernetes Cluster. Especially with untrusted application or OpenSource software.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2022-08-11-zero-trust-kubernetes-deployment/cover.jpg" alt="cover" class="img__Ss2"></p>
<p>Using <a href="https://en.wikipedia.org/wiki/Open-source_model" target="_blank" rel="noopener noreferrer" class="">OpenSource</a> software written by unkown people sometimes can be a little scary. Even more when I deploy them I a production environment in my company.
On my case, I have created a brand new <a href="https://kubernetes.io/" target="_blank" rel="noopener noreferrer" class="">Kubernetes</a> cluster to host some private services on my local network and I wanted to be sure that they don't do anything malicious on my network.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="isolate-applications-and-services">Isolate applications and services<a href="https://lunik.tiwabbit.fr/blog/zero-trust-kubernetes-deployment#isolate-applications-and-services" class="hash-link" aria-label="Direct link to Isolate applications and services" title="Direct link to Isolate applications and services" translate="no">​</a></h2>
<p>First thing to do is to isolate each of the services inside a <a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/" target="_blank" rel="noopener noreferrer" class="">Kubernetes namespace</a>. This is a core resource of <a href="https://kubernetes.io/" target="_blank" rel="noopener noreferrer" class="">Kubernetes</a> that allow to isolate groups of resources within a single cluster. It then allow to have a more fine control on access, permissions, network on the resources.</p>
<p>A <a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/" target="_blank" rel="noopener noreferrer" class="">Namespace</a> can be create pretty easily with the following command :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">kubectl create namespace </span><span class="token operator">&lt;</span><span class="token plain">insert-namespace-name-here</span><span class="token operator">&gt;</span><br></div></code></pre></div></div>
<p>Then I can list <a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/" target="_blank" rel="noopener noreferrer" class="">Namespaces</a> with :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">kubectl get namespaces</span><br></div></code></pre></div></div>
<p>Result :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">NAME              STATUS   AGE</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">default           Active   4h52m</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">kube-system       Active   4h52m</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">kube-public       Active   4h52m</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">kube-node-lease   Active   4h52m</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">tiwabbit-prod     Active   2s</span><br></div></code></pre></div></div>
<p><em>Ignore <a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/" target="_blank" rel="noopener noreferrer" class="">Namespaces</a> prefixed with <code>kube-</code> who are defined for the <a href="https://kubernetes.io/docs/concepts/overview/components/#control-plane-components" target="_blank" rel="noopener noreferrer" class="">Kubernetes control plane</a></em></p>
<p>I can now create resources inside my <a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/" target="_blank" rel="noopener noreferrer" class="">Namespace</a>. Let's start with a <a href="https://kubernetes.io/docs/concepts/configuration/secret/" target="_blank" rel="noopener noreferrer" class="">Secret</a> for example :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">kubectl </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--namespace</span><span class="token plain"> tiwabbit-prod </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  create secret generic </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  db-credentials </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  --from-literal</span><span class="token operator">=</span><span class="token plain">username</span><span class="token operator">=</span><span class="token plain">tiwabbit </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  --from-literal</span><span class="token operator">=</span><span class="token plain">password</span><span class="token operator">=</span><span class="token plain">mysecurepassword</span><br></div></code></pre></div></div>
<p>If I list all <a href="https://kubernetes.io/docs/concepts/configuration/secret/" target="_blank" rel="noopener noreferrer" class="">Secrets</a> in my cluster :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">kubectl get secrets --all-namespaces</span><br></div></code></pre></div></div>
<p>Result :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">NAMESPACE       NAME                  TYPE                                  DATA   AGE</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token plain">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">default         default-token-m59jl   kubernetes.io/service-account-token   </span><span class="token number">3</span><span class="token plain">      4h58m</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">tiwabbit-prod   default-token-8zkr2   kubernetes.io/service-account-token   </span><span class="token number">3</span><span class="token plain">      3m15s</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">tiwabbit-prod   db-credentials        Opaque                                </span><span class="token number">2</span><span class="token plain">      49s</span><br></div></code></pre></div></div>
<p>In theory, only <a href="https://kubernetes.io/docs/concepts/workloads/pods/" target="_blank" rel="noopener noreferrer" class="">Pods</a> running inside my <a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/" target="_blank" rel="noopener noreferrer" class="">Namespace</a> (<code>tiwabbit-prod</code>) can mount this secret and read it.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="rbac-securization">RBAC securization<a href="https://lunik.tiwabbit.fr/blog/zero-trust-kubernetes-deployment#rbac-securization" class="hash-link" aria-label="Direct link to RBAC securization" title="Direct link to RBAC securization" translate="no">​</a></h2>
<p>By default all <a href="https://kubernetes.io/docs/concepts/workloads/pods/" target="_blank" rel="noopener noreferrer" class="">Pods</a> without specific configuration use the <code>default</code> <a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/" target="_blank" rel="noopener noreferrer" class="">ServiceAccount</a> of the <a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/" target="_blank" rel="noopener noreferrer" class="">Namespace</a> they are running in.
This last one dosn't have any right on the <a href="https://kubernetes.io/docs/concepts/overview/kubernetes-api/" target="_blank" rel="noopener noreferrer" class="">Kubernetes API</a> witch is a great thing and should not be changed.</p>
<p>How ever in some scenarios my application may needs to call the <a href="https://kubernetes.io/docs/concepts/overview/kubernetes-api/" target="_blank" rel="noopener noreferrer" class="">Kubernetes API</a>. For exemple if it need to create batch using a <a href="https://kubernetes.io/docs/concepts/workloads/controllers/job/" target="_blank" rel="noopener noreferrer" class="">Kubernetes Job</a>. Let's take that last exemple to create a <a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/" target="_blank" rel="noopener noreferrer" class="">ServiceAccount</a> with those permission an assign it to my application <a href="https://kubernetes.io/docs/concepts/workloads/pods/" target="_blank" rel="noopener noreferrer" class="">Pod</a>.</p>
<p>First I need to create a new <a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/" target="_blank" rel="noopener noreferrer" class="">ServiceAccount</a> :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">kubectl </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--namespace</span><span class="token plain"> tiwabbit-prod </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  create serviceaccount </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  my-application</span><br></div></code></pre></div></div>
<p>Then I need a <a href="https://kubernetes.io/docs/reference/access-authn-authz/rbac/" target="_blank" rel="noopener noreferrer" class="">Role</a> that implement the level of permission that my <a href="https://kubernetes.io/docs/concepts/workloads/pods/" target="_blank" rel="noopener noreferrer" class="">Pod</a> need. here is the manifest :</p>
<div class="language-yaml codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-yaml codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token key atrule">apiVersion</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> rbac.authorization.k8s.io/v1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">kind</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Role</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">metadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">application</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">batch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">creator</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">namespace</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> tiwabbit</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">prod</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">rules</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">apiGroups</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">resources</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> pods</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> pods/status</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> pods/log </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">verbs</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> list</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> watch </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">apiGroups</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> batch </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">resources</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> jobs </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">verbs</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"> create</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> list</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> watch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> patch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> update</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> delete </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><br></div></code></pre></div></div>
<p>Finally, I need to assign the <a href="https://kubernetes.io/docs/reference/access-authn-authz/rbac/" target="_blank" rel="noopener noreferrer" class="">Role</a> to my <a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/" target="_blank" rel="noopener noreferrer" class="">ServiceAccount</a> using a <a href="https://kubernetes.io/docs/reference/access-authn-authz/rbac/" target="_blank" rel="noopener noreferrer" class="">RoleBinding</a> with the following manifest definition :</p>
<div class="language-yaml codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-yaml codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token key atrule">apiVersion</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> rbac.authorization.k8s.io/v1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">kind</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> RoleBinding</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">metadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">application</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">permissions</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">namespace</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> tiwabbit</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">prod</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">roleRef</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">apiGroup</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> rbac.authorization.k8s.io</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">kind</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Role</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">application</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">batch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">creator</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">subjects</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">kind</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> ServiceAccount</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">application</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">namespace</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> tiwabbit</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">prod</span><br></div></code></pre></div></div>
<p>Now let's create a <a href="https://kubernetes.io/docs/concepts/workloads/pods/" target="_blank" rel="noopener noreferrer" class="">Pod</a> with the <a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/" target="_blank" rel="noopener noreferrer" class="">ServiceAccount</a> and review the actions allowed by the role. Here is a <a href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/" target="_blank" rel="noopener noreferrer" class="">Deployment</a> manifest :</p>
<div class="language-yaml codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-yaml codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token key atrule">apiVersion</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> apps/v1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">kind</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Deployment</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">metadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">application</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">namespace</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> tiwabbit</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">prod</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">spec</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">replicas</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">1</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">selector</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">matchLabels</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">app</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">application</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">template</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">metadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">labels</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token key atrule">app</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">application</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">spec</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">serviceAccountName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">application</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">containers</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">command</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> sleep</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> 1d</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token key atrule">image</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> alpine</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">latest</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> alpine</span><br></div></code></pre></div></div>
<p>Geting inside the <a href="https://kubernetes.io/docs/concepts/workloads/pods/" target="_blank" rel="noopener noreferrer" class="">Pod</a> and follow the <a href="https://kubernetes.io/docs/tasks/tools/#kubectl" target="_blank" rel="noopener noreferrer" class="">Kubernetes documentation to install <code>kubectl</code></a>.</p>
<p>Let's check if I can list <a href="https://kubernetes.io/docs/concepts/workloads/pods/" target="_blank" rel="noopener noreferrer" class="">Pods</a> by querying the Kubernetes API :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">kubectl get pods</span><br></div></code></pre></div></div>
<p>Result :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">NAME                             READY   STATUS    RESTARTS   AGE</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">my-application-df5b5cb75-8twc5   </span><span class="token number">1</span><span class="token plain">/1     Running   </span><span class="token number">0</span><span class="token plain">          5m21s</span><br></div></code></pre></div></div>
<p>Then I should try to create a batch execution with a <a href="https://kubernetes.io/docs/concepts/workloads/controllers/job/" target="_blank" rel="noopener noreferrer" class="">Job</a> :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">kubectl create job my-application-batch </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--image</span><span class="token plain"> alpine:latest -- </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">echo</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Hello World"</span><br></div></code></pre></div></div>
<p>Listing all the <a href="https://kubernetes.io/docs/concepts/workloads/controllers/job/" target="_blank" rel="noopener noreferrer" class="">Jobs</a> in the <a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/" target="_blank" rel="noopener noreferrer" class="">Namespace</a> :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">kubectl get </span><span class="token function" style="color:rgb(80, 250, 123)">jobs</span><br></div></code></pre></div></div>
<p>Result :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">NAME                   COMPLETIONS   DURATION   AGE</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">my-application-batch   </span><span class="token number">0</span><span class="token plain">/1           2s         2s</span><br></div></code></pre></div></div>
<p>Listing the <a href="https://kubernetes.io/docs/concepts/workloads/pods/" target="_blank" rel="noopener noreferrer" class="">Pods</a> in the <a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/" target="_blank" rel="noopener noreferrer" class="">Namespace</a> :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">kubectl get pods</span><br></div></code></pre></div></div>
<p>Result :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">NAME                             READY   STATUS    RESTARTS   AGE</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">my-application-df5b5cb75-8twc5   </span><span class="token number">1</span><span class="token plain">/1     Running   </span><span class="token number">0</span><span class="token plain">          11m</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">my-application-batch-f2pf6       </span><span class="token number">1</span><span class="token plain">/1     Running   </span><span class="token number">0</span><span class="token plain">          3s</span><br></div></code></pre></div></div>
<p>My <a href="https://kubernetes.io/docs/concepts/workloads/controllers/job/" target="_blank" rel="noopener noreferrer" class="">Job</a> finished and I can delete it with :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">kubectl delete job/my-application-batch</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="conclusions">Conclusions<a href="https://lunik.tiwabbit.fr/blog/zero-trust-kubernetes-deployment#conclusions" class="hash-link" aria-label="Direct link to Conclusions" title="Direct link to Conclusions" translate="no">​</a></h3>
<p>If the process in my <a href="https://kubernetes.io/docs/concepts/workloads/pods/" target="_blank" rel="noopener noreferrer" class="">Pod</a> doesn't need to communicate with the <a href="https://kubernetes.io/docs/concepts/overview/kubernetes-api/" target="_blank" rel="noopener noreferrer" class="">Kubernetes API</a> (for creating, querying, deleting ressources), use the <code>default</code> <a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/" target="_blank" rel="noopener noreferrer" class="">ServiceAccount</a> witch give zero permission to my <a href="https://kubernetes.io/docs/concepts/workloads/pods/" target="_blank" rel="noopener noreferrer" class="">Pod</a>. In other case, use a custom <a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/" target="_blank" rel="noopener noreferrer" class="">ServiceAccount</a> for each of my apps and multiple <a href="https://kubernetes.io/docs/reference/access-authn-authz/rbac/" target="_blank" rel="noopener noreferrer" class="">Role</a> and <a href="https://kubernetes.io/docs/reference/access-authn-authz/rbac/" target="_blank" rel="noopener noreferrer" class="">RoleBinding</a> for each of my application use case.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="prevent-usage-of-root-user-or-privileged-escalation">Prevent usage of root user or privileged escalation<a href="https://lunik.tiwabbit.fr/blog/zero-trust-kubernetes-deployment#prevent-usage-of-root-user-or-privileged-escalation" class="hash-link" aria-label="Direct link to Prevent usage of root user or privileged escalation" title="Direct link to Prevent usage of root user or privileged escalation" translate="no">​</a></h2>
<p>Kubernetes allow by default multiple security options that can be applied to pods and underlaying containers. Most of them can be configured with the <a href="https://kubernetes.io/docs/tasks/configure-pod-container/security-context/" target="_blank" rel="noopener noreferrer" class="">SecurityContext</a> block as follow :</p>
<div class="language-yaml codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-yaml codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token key atrule">apiVersion</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> v1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">kind</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Pod</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">metadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">secure</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">pod</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">spec</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">securityContext</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">containers</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">secure</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">container</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">image</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> nginx</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">latest</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">securityContext</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">second</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">secure</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">container</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">image</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> busybox</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">latest</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">command</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"sleep"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"infinity"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">securityContext</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Obviously such options can be added to a <a href="https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/" target="_blank" rel="noopener noreferrer" class="">StatefulSet</a> or <a href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/" target="_blank" rel="noopener noreferrer" class="">Deployment</a> template.</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="running-pod-as-non-root">Running pod as non root<a href="https://lunik.tiwabbit.fr/blog/zero-trust-kubernetes-deployment#running-pod-as-non-root" class="hash-link" aria-label="Direct link to Running pod as non root" title="Direct link to Running pod as non root" translate="no">​</a></h3>
<p>The easiest setup to secure the <a href="https://kubernetes.io/docs/concepts/workloads/pods/" target="_blank" rel="noopener noreferrer" class="">Pod</a> is to overwrite the <a href="https://en.wikipedia.org/wiki/User_identifier" target="_blank" rel="noopener noreferrer" class="">UID</a> and <a href="https://en.wikipedia.org/wiki/Group_identifier" target="_blank" rel="noopener noreferrer" class="">GID</a> of the user running the main process. This way, what ever the default user in the image, <a href="https://kubernetes.io/" target="_blank" rel="noopener noreferrer" class="">Kubernetes</a> will bypass it. An <a href="https://en.wikipedia.org/wiki/User_identifier" target="_blank" rel="noopener noreferrer" class="">UID</a> and <a href="https://en.wikipedia.org/wiki/Group_identifier" target="_blank" rel="noopener noreferrer" class="">GID</a> superior or equal to <code>1000</code> should be used.</p>
<p>Three parameters can be used :</p>
<ul>
<li class=""><code>runAsUser</code> : Allow to change the <a href="https://en.wikipedia.org/wiki/User_identifier" target="_blank" rel="noopener noreferrer" class="">UID</a> of the default process</li>
<li class=""><code>runAsGroup</code> : Allow to change the <a href="https://en.wikipedia.org/wiki/Group_identifier" target="_blank" rel="noopener noreferrer" class="">GID</a> of the default process</li>
<li class=""><code>fsGroup</code> : If specified, the user will also be in that group and all files and directories created will take that <a href="https://en.wikipedia.org/wiki/Group_identifier" target="_blank" rel="noopener noreferrer" class="">GID</a> as owner.<!-- -->
<ul>
<li class="">This last parameter can increase the mount time of a given external volume because <a href="https://kubernetes.io/" target="_blank" rel="noopener noreferrer" class="">Kubernetes</a> ensure that files are owned by the group defined by <code>fsGroup</code>. Resulting on an <code>chmod -R</code> of all the filesystem.</li>
<li class=""><code>fsGroupChangePolicy</code> can be used to change this behaviour with those values :<!-- -->
<ul>
<li class=""><code>OnRootMismatch</code> : only check the root directory and change all filesystem if the group mismatch</li>
<li class=""><code>Always</code> : it's in the option name</li>
</ul>
</li>
</ul>
</li>
</ul>
<div class="language-yaml codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-yaml codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token key atrule">apiVersion</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> v1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">kind</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Pod</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">metadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">secure</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">pod</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">spec</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">securityContext</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">runAsUser</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">1000</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">runAsGroup</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">1000</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">fsGroup</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">2000</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">fsGroupChangePolicy</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> OnRootMismatch</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">containers</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">secure</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">container</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">image</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> busybox</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">latest</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">command</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"sleep"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"infinity"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">securityContext</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">runAsUser</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">2000</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">runAsGroup</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">2000</span><br></div></code></pre></div></div>
<p>Enforcing running as non <code>root</code> could be achieved with <code>runAsNonRoot</code> parameter :</p>
<div class="language-yaml codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-yaml codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token key atrule">apiVersion</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> v1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">kind</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Pod</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">metadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">secure</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">pod</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">spec</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">securityContext</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">runAsNonRoot</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token boolean important">true</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">containers</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">secure</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">container</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">image</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> busybox</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">latest</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">command</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"sleep"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"infinity"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">securityContext</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="use-readonly-filesystem">Use readonly filesystem<a href="https://lunik.tiwabbit.fr/blog/zero-trust-kubernetes-deployment#use-readonly-filesystem" class="hash-link" aria-label="Direct link to Use readonly filesystem" title="Direct link to Use readonly filesystem" translate="no">​</a></h3>
<p>Preventing all write/update/delete operation on the root filesystem can prevent malicious process (or breached application) to take advantage of the container environment and modify it.
The <code>readOnlyRootFilesystem</code> option allow to achieve that :</p>
<div class="language-yaml codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-yaml codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token key atrule">apiVersion</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> v1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">kind</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Pod</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">metadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">secure</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">pod</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">spec</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">securityContext</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">containers</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">secure</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">container</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">image</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> busybox</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">latest</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">command</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"sleep"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"infinity"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">securityContext</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">readOnlyRootFilesystem</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token boolean important">true</span><br></div></code></pre></div></div>
<p>If the application need to write temporary or application data during the runtime, I can still create an <a href="https://kubernetes.io/docs/concepts/storage/volumes/#emptydir" target="_blank" rel="noopener noreferrer" class="">EmptyDir</a> volume and mounting it inside the container :</p>
<div class="language-yaml codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-yaml codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token key atrule">apiVersion</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> v1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">kind</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Pod</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">metadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">secure</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">pod</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">spec</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">securityContext</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">containers</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">secure</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">container</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">image</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> busybox</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">latest</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">command</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"sleep"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"infinity"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">securityContext</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">readOnlyRootFilesystem</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token boolean important">true</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">volumeMounts</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">mountPath</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> /data</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">data</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">volumes</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">data</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">emptyDir</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="prevent-priviled-escalation">Prevent priviled escalation<a href="https://lunik.tiwabbit.fr/blog/zero-trust-kubernetes-deployment#prevent-priviled-escalation" class="hash-link" aria-label="Direct link to Prevent priviled escalation" title="Direct link to Prevent priviled escalation" translate="no">​</a></h3>
<p>Linux kernel expose low level function for a process to change is current <a href="https://en.wikipedia.org/wiki/User_identifier" target="_blank" rel="noopener noreferrer" class="">UID</a> or <a href="https://en.wikipedia.org/wiki/Group_identifier" target="_blank" rel="noopener noreferrer" class="">GID</a>. For example using [setuid][linux-setuid-wikipedia] or <a href="https://en.wikipedia.org/wiki/Setuid" target="_blank" rel="noopener noreferrer" class="">setgid</a> privitive functions.</p>
<p>Using the option <code>allowPrivilegeEscalation</code> can prevent this to happens.</p>
<div class="language-yaml codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-yaml codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token key atrule">apiVersion</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> v1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">kind</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Pod</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">metadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">secure</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">pod</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">spec</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">securityContext</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">containers</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">secure</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">container</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">image</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> busybox</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">latest</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">command</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"sleep"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"infinity"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">securityContext</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">allowPrivilegeEscalation</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token boolean important">false</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="dropping-all-capabilities">Dropping all capabilities<a href="https://lunik.tiwabbit.fr/blog/zero-trust-kubernetes-deployment#dropping-all-capabilities" class="hash-link" aria-label="Direct link to Dropping all capabilities" title="Direct link to Dropping all capabilities" translate="no">​</a></h3>
<p>As for privileged escalation, Linux give <a href="https://manpages.ubuntu.com/manpages/jammy/en/man7/capabilities.7.html" target="_blank" rel="noopener noreferrer" class="">Capabilities</a> to process allowing them to make high privileged system calls. Like binding network ports under <code>1024</code>.</p>
<p>We want to only give our process only the necessary capabilities for it to run.
In the cas of the exemple we can have :</p>
<div class="language-yaml codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-yaml codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token key atrule">apiVersion</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> v1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">kind</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Pod</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">metadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">secure</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">pod</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">spec</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">securityContext</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">containers</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">second</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">secure</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">container</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">image</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> busybox</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">latest</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">command</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"sleep"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"infinity"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">securityContext</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">capabilities</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token key atrule">drop</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> ALL</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token key atrule">add</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> NET_BIND_SERVICE</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="network-communication-hardening">Network communication hardening<a href="https://lunik.tiwabbit.fr/blog/zero-trust-kubernetes-deployment#network-communication-hardening" class="hash-link" aria-label="Direct link to Network communication hardening" title="Direct link to Network communication hardening" translate="no">​</a></h2>
<p>In the world of pods, by default every pods in every namespace can talk to each other. If you have multiple application or even multiple clients on the same Kubernetes cluster, you should not allow them to communicate (at least not by default).</p>
<p>That's why, <a href="https://kubernetes.io/docs/concepts/services-networking/network-policies/" target="_blank" rel="noopener noreferrer" class="">NetworkPolicies</a> must be created with <code>deny all</code> by default. Then open point to point connections if services/clients needs to communicates with each others.</p>
<p>The default <code>deny all</code> config will looks like this :</p>
<div class="language-yaml codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-yaml codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token key atrule">apiVersion</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> networking.k8s.io/v1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">kind</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> NetworkPolicy</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">metadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> default</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">deny</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">ingress</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">spec</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">podSelector</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">policyTypes</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> Ingress</span><br></div></code></pre></div></div>
<p>Then if you have a third party application that needs to access your application pods :</p>
<div class="language-yaml codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-yaml codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token key atrule">apiVersion</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> networking.k8s.io/v1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">kind</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> NetworkPolicy</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">metadata</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> test</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">network</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">policy</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">namespace</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">namespace</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">spec</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">podSelector</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">matchLabels</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">application</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">application</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">component</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> api</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">policyTypes</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> Ingress</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">ingress</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">from</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">namespaceSelector</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token key atrule">matchLabels</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">              </span><span class="token key atrule">project</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">friend</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">namespace</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">podSelector</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token key atrule">matchLabels</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">              </span><span class="token key atrule">application</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> his</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">application</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">ports</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">protocol</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> TCP</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token key atrule">port</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">8080</span><br></div></code></pre></div></div>]]></content>
        <author>
            <name>Guillaume MARTINEZ</name>
            <uri>https://github.com/Lunik</uri>
        </author>
        <category label="kubernetes" term="kubernetes"/>
        <category label="security" term="security"/>
        <category label="firewall" term="firewall"/>
        <category label="network" term="network"/>
        <category label="zero-trust" term="zero-trust"/>
        <category label="namespace" term="namespace"/>
        <category label="capabilities" term="capabilities"/>
        <category label="system" term="system"/>
        <category label="privileged" term="privileged"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Understanding Time-based One-time-password]]></title>
        <id>https://lunik.tiwabbit.fr/blog/understanding-totp</id>
        <link href="https://lunik.tiwabbit.fr/blog/understanding-totp"/>
        <updated>2022-04-28T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Deep dive inside the TOTP protocol]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2022-04-28-understanding-totp/cover.png" alt="cover" class="img__Ss2"></p>
<p>Now that every web service encourage you, more and more, to use <a href="https://en.wikipedia.org/wiki/Multi-factor_authentication" target="_blank" rel="noopener noreferrer" class="">MFA</a> to secure your account, one of them is used most than others : <a href="https://en.wikipedia.org/wiki/Time-based_one-time_password" target="_blank" rel="noopener noreferrer" class="">Time-based one-time password or TOTP</a> generate a unique code of <code>6</code> or more numbers to enter just after typing your password.</p>
<p>The server or web app allowing to setup <a href="https://en.wikipedia.org/wiki/Time-based_one-time_password" target="_blank" rel="noopener noreferrer" class="">TOTP</a> give a <a href="https://en.wikipedia.org/wiki/QR_code" target="_blank" rel="noopener noreferrer" class="">QRCode</a> to scan (or a <a href="https://tools.ietf.org/html/rfc4648" target="_blank" rel="noopener noreferrer" class="">Base32</a> string) to configure in a <a href="https://en.wikipedia.org/wiki/Time-based_one-time_password" target="_blank" rel="noopener noreferrer" class="">TOTP</a> generator app like <a href="https://www.microsoft.com/en-us/security/mobile-authenticator-app" target="_blank" rel="noopener noreferrer" class="">Microsoft Authenticator</a>, <a href="https://support.google.com/accounts/answer/1066447" target="_blank" rel="noopener noreferrer" class="">Google Authenticator</a>, <a href="https://bitwarden.com/" target="_blank" rel="noopener noreferrer" class="">Bitwarden</a> or more.</p>
<p>We all use it, but how does it work ? Is it secure ? Is my account secure when using third-party <a href="https://en.wikipedia.org/wiki/Time-based_one-time_password" target="_blank" rel="noopener noreferrer" class="">TOTP</a> generator ??</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-protocol">The protocol<a href="https://lunik.tiwabbit.fr/blog/understanding-totp#the-protocol" class="hash-link" aria-label="Direct link to The protocol" title="Direct link to The protocol" translate="no">​</a></h2>
<p>I will get deeps into the details of the underlying protocol allowing you to generated <a href="https://en.wikipedia.org/wiki/Time-based_one-time_password" target="_blank" rel="noopener noreferrer" class="">TOTP</a> and how the server verify it.</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="basics">Basics<a href="https://lunik.tiwabbit.fr/blog/understanding-totp#basics" class="hash-link" aria-label="Direct link to Basics" title="Direct link to Basics" translate="no">​</a></h3>
<p>The basic idea is pretty straight forward. You and the server shared a secret, a <code>16</code> character long <a href="https://tools.ietf.org/html/rfc4648" target="_blank" rel="noopener noreferrer" class="">Base32</a> string (sometimes stored in the <a href="https://en.wikipedia.org/wiki/QR_code" target="_blank" rel="noopener noreferrer" class="">QRCode</a>). Then you both synchronize your clocks.</p>
<p>Finally when you want to login, you execute a fancy magical function and ultimately you get a <a href="https://en.wikipedia.org/wiki/Time-based_one-time_password" target="_blank" rel="noopener noreferrer" class="">TOTP</a>.
The server compute the <a href="https://en.wikipedia.org/wiki/Time-based_one-time_password" target="_blank" rel="noopener noreferrer" class="">TOTP</a> on his side and ask you the one you generated on your side. If both of the <a href="https://en.wikipedia.org/wiki/Time-based_one-time_password" target="_blank" rel="noopener noreferrer" class="">TOTP</a> match, the server allow you in.</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="totp-protocol">TOTP protocol<a href="https://lunik.tiwabbit.fr/blog/understanding-totp#totp-protocol" class="hash-link" aria-label="Direct link to TOTP protocol" title="Direct link to TOTP protocol" translate="no">​</a></h3>
<p>Let's dig into the <a href="https://tools.ietf.org/html/rfc6238" target="_blank" rel="noopener noreferrer" class="">TOTP</a> protocol. According to the <a href="https://tools.ietf.org/html/rfc6238" target="_blank" rel="noopener noreferrer" class="">RFC 6238</a> you need :</p>
<ul>
<li class="">A moving <code>counter</code> that must be synchronized between the two parties</li>
<li class="">A <code>secret</code> shared between the two parties</li>
</ul>
<p>The <code>counter</code> is calculated base on <a href="https://en.wikipedia.org/wiki/Epoch_(computing)" target="_blank" rel="noopener noreferrer" class="">epoch</a> time (That way everybody is synchronized) with the following formula :</p>
<div class="language-text codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-text codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">C = (T - T0) / X</span><br></div></code></pre></div></div>
<p>With :</p>
<ul>
<li class=""><code>C</code> the counter value</li>
<li class=""><code>T</code> the current Unix time in seconds since <a href="https://en.wikipedia.org/wiki/Epoch_(computing)" target="_blank" rel="noopener noreferrer" class="">epoch</a></li>
<li class=""><code>T0</code> an arbitrary start Unix time. <code>0</code> by default</li>
<li class=""><code>X</code> the time step in seconds or the frequency of code update. <code>30</code> by default</li>
</ul>
<p>If we try to make a <a href="https://www.python.org/" target="_blank" rel="noopener noreferrer" class="">Python</a> implementation, it will looks like :</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">t0 </span><span class="token operator">=</span><span class="token plain"> </span><span class="token number">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># https://docs.python.org/3/library/time.html#time.time</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">current_unix_time </span><span class="token operator">=</span><span class="token plain"> time</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">time</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">time_step </span><span class="token operator">=</span><span class="token plain"> </span><span class="token number">30</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">counter </span><span class="token operator">=</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">int</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">current_unix_time </span><span class="token operator">-</span><span class="token plain"> t0</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">/</span><span class="token plain"> time_step</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></div></code></pre></div></div>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">current_unix_time</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">1651094239.491242</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">counter </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token number">55036474</span><br></div></code></pre></div></div>
<p>Now the <a href="https://tools.ietf.org/html/rfc6238" target="_blank" rel="noopener noreferrer" class="">TOTP</a> value can be retrieved with the following formula :</p>
<div class="language-text codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-text codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">TOTP = HOTP(K, C, D)</span><br></div></code></pre></div></div>
<p>With:</p>
<ul>
<li class=""><code>C</code> the counter value</li>
<li class=""><code>K</code> the secret key</li>
<li class=""><code>D</code> the number of digits in our <a href="https://tools.ietf.org/html/rfc6238" target="_blank" rel="noopener noreferrer" class="">TOTP</a>s</li>
<li class=""><code>HOTP</code> the magical function that calculate the OTP</li>
</ul>
<p>In <a href="https://www.python.org/" target="_blank" rel="noopener noreferrer" class="">Python</a>, we get :</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">counter </span><span class="token operator">=</span><span class="token plain"> </span><span class="token number">55036474</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">key </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"ABCDEFGHYJKLMNOP"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">digits </span><span class="token operator">=</span><span class="token plain"> </span><span class="token number">6</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">totp </span><span class="token operator">=</span><span class="token plain"> hotp</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">key</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> counter</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> digits</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></div></code></pre></div></div>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">totp </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token number">934929</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="hotp-protocol">HOTP protocol<a href="https://lunik.tiwabbit.fr/blog/understanding-totp#hotp-protocol" class="hash-link" aria-label="Direct link to HOTP protocol" title="Direct link to HOTP protocol" translate="no">​</a></h3>
<p>This second protocol is used to calculate a unique value a counter value and the secret key. From the <a href="https://tools.ietf.org/html/rfc4226" target="_blank" rel="noopener noreferrer" class="">RFC rfc4226</a>, we learn that its based on <a href="https://tools.ietf.org/html/rfc2104" target="_blank" rel="noopener noreferrer" class="">HMAC hash protocol</a>.</p>
<p>Sit tight because it will begin to be challenging and I hope that your are comfortable with binaries operations.</p>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="handle-inputs">Handle inputs<a href="https://lunik.tiwabbit.fr/blog/understanding-totp#handle-inputs" class="hash-link" aria-label="Direct link to Handle inputs" title="Direct link to Handle inputs" translate="no">​</a></h4>
<p>First of all, we need handle the 2 of our 3 input parameters : <code>counter</code>, <code>key</code></p>
<ul>
<li class=""><code>counter</code> needs to be converted into an <code>unsigned long long</code> and <code>big-endian</code> bytes array value.</li>
</ul>
<p>In <a href="https://www.python.org/" target="_blank" rel="noopener noreferrer" class="">Python</a> :</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> struct</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># https://docs.python.org/3/library/struct.html#struct.pack</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># https://docs.python.org/3/library/struct.html#byte-order-size-and-alignment</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># https://docs.python.org/3/library/struct.html#format-characters</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">bytes_counter </span><span class="token operator">=</span><span class="token plain"> struct</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">pack</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'&gt;Q'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> counter</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></div></code></pre></div></div>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">counter</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">55036474</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">bytes_counter</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token builtin" style="color:rgb(189, 147, 249)">bin</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token number">00000011</span><span class="token plain"> </span><span class="token number">01000111</span><span class="token plain"> </span><span class="token number">11001010</span><span class="token plain"> </span><span class="token number">00111010</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">bytes_counter</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token builtin" style="color:rgb(189, 147, 249)">hex</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token number">03</span><span class="token plain"> </span><span class="token number">47</span><span class="token plain"> ca 3a</span><br></div></code></pre></div></div>
<ul>
<li class=""><code>key</code> should be a valid <a href="https://tools.ietf.org/html/rfc4648" target="_blank" rel="noopener noreferrer" class="">Base32</a> string. The <a href="https://tools.ietf.org/html/rfc4648" target="_blank" rel="noopener noreferrer" class="">RFC 4648</a> tells us that the string length need to be a factor of <code>8</code>. So padding can be added with char <code>=</code> at the end if it's too short. For example : <code>AA</code> =&gt; <code>AA======</code>, <code>BBBBBBBBBB</code> =&gt; <code>BBBBBBBBBB======</code>, <code>CCCCCCCC</code> =&gt; <code>CCCCCCCC</code></li>
</ul>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">padding </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'='</span><span class="token plain"> </span><span class="token operator">*</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">8</span><span class="token plain"> </span><span class="token operator">-</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">len</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">key</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">%</span><span class="token plain"> </span><span class="token number">8</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">valid_key </span><span class="token operator">=</span><span class="token plain"> key </span><span class="token operator">+</span><span class="token plain"> padding</span><br></div></code></pre></div></div>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">key</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> ABCDEFGHIJKLMNOP</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">valid_key </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> ABCDEFGHIJKLMNOP</span><br></div></code></pre></div></div>
<p>Then we convert the updated key in bytes array value :</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> base64</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">bytes_key </span><span class="token operator">=</span><span class="token plain"> base64</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">b32decode</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">key</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></div></code></pre></div></div>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">valid_key</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> ABCDEFGHIJKLMNOP</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">bytes_key</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token builtin" style="color:rgb(189, 147, 249)">bin</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token number">10001000</span><span class="token plain"> </span><span class="token number">01100100</span><span class="token plain"> </span><span class="token number">00101001</span><span class="token plain"> </span><span class="token number">10001110</span><span class="token plain"> </span><span class="token number">10000100</span><span class="token plain"> </span><span class="token number">10101001</span><span class="token plain"> </span><span class="token number">01101100</span><span class="token plain"> </span><span class="token number">0110101</span><span class="token plain"> </span><span class="token number">11001111</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">bytes_key</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token builtin" style="color:rgb(189, 147, 249)">hex</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token number">44</span><span class="token plain"> </span><span class="token number">32</span><span class="token plain"> </span><span class="token number">14</span><span class="token plain"> c7 </span><span class="token number">42</span><span class="token plain"> </span><span class="token number">54</span><span class="token plain"> b6 </span><span class="token number">35</span><span class="token plain"> cf</span><br></div></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="calulate-hmac">Calulate HMAC<a href="https://lunik.tiwabbit.fr/blog/understanding-totp#calulate-hmac" class="hash-link" aria-label="Direct link to Calulate HMAC" title="Direct link to Calulate HMAC" translate="no">​</a></h4>
<p>Using the <a href="https://tools.ietf.org/html/rfc2104" target="_blank" rel="noopener noreferrer" class="">RFC 2104</a> we can understand how the <a href="https://tools.ietf.org/html/rfc2104" target="_blank" rel="noopener noreferrer" class="">HMAC function</a> work. We need to choose an hash function. <a href="https://en.wikipedia.org/wiki/HMAC-based_one-time_password" target="_blank" rel="noopener noreferrer" class="">HOTP</a> or <a href="https://tools.ietf.org/html/rfc4226" target="_blank" rel="noopener noreferrer" class="">RFC 4226</a> use the <a href="https://tools.ietf.org/html/rfc3174" target="_blank" rel="noopener noreferrer" class="">SHA-1</a> cryptographic hash function.</p>
<p><a href="https://www.python.org/" target="_blank" rel="noopener noreferrer" class="">Python</a> implement <a href="https://docs.python.org/3/library/hmac.html" target="_blank" rel="noopener noreferrer" class="">HMAC library</a> :</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> hmac</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># Calculate the HMAC-SHA1 value</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># Produce a 20 bytes (or 160 bites) long string</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">mac </span><span class="token operator">=</span><span class="token plain"> hmac</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">new</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">bytes_key</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> bytes_counter</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"SHA1"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">digest</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></div></code></pre></div></div>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">bytes_counter</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token builtin" style="color:rgb(189, 147, 249)">hex</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">03</span><span class="token plain"> </span><span class="token number">47</span><span class="token plain"> ca 3a</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">bytes_key</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token builtin" style="color:rgb(189, 147, 249)">hex</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">44</span><span class="token plain"> </span><span class="token number">32</span><span class="token plain"> </span><span class="token number">14</span><span class="token plain"> c7 </span><span class="token number">42</span><span class="token plain"> </span><span class="token number">54</span><span class="token plain"> b6 </span><span class="token number">35</span><span class="token plain"> cf</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">mac</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token builtin" style="color:rgb(189, 147, 249)">bin</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token number">10110100</span><span class="token plain"> </span><span class="token number">11010010</span><span class="token plain"> </span><span class="token number">01111010</span><span class="token plain"> </span><span class="token number">10110100</span><span class="token plain"> </span><span class="token number">10111101</span><span class="token plain"> </span><span class="token number">00110101</span><span class="token plain"> </span><span class="token number">11111110</span><span class="token plain"> </span><span class="token number">00100011</span><span class="token plain"> </span><span class="token number">11101101</span><span class="token plain"> </span><span class="token number">01011001</span><span class="token plain"> </span><span class="token number">01111110</span><span class="token plain"> </span><span class="token number">10111100</span><span class="token plain"> </span><span class="token number">11110000</span><span class="token plain"> </span><span class="token number">01111001</span><span class="token plain"> </span><span class="token number">11000001</span><span class="token plain"> </span><span class="token number">01001010</span><span class="token plain"> </span><span class="token number">00000110</span><span class="token plain"> </span><span class="token number">01101100</span><span class="token plain"> </span><span class="token number">01010001</span><span class="token plain"> </span><span class="token number">00101111</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">mac</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token builtin" style="color:rgb(189, 147, 249)">hex</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> b4 d2 7a b4 bd </span><span class="token number">35</span><span class="token plain"> fe </span><span class="token number">23</span><span class="token plain"> ed </span><span class="token number">59</span><span class="token plain"> 7e bc f0 </span><span class="token number">79</span><span class="token plain"> c1 4a </span><span class="token number">06</span><span class="token plain"> 6c </span><span class="token number">51</span><span class="token plain"> 2f</span><br></div></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="calculate-hotp">Calculate HOTP<a href="https://lunik.tiwabbit.fr/blog/understanding-totp#calculate-hotp" class="hash-link" aria-label="Direct link to Calculate HOTP" title="Direct link to Calculate HOTP" translate="no">​</a></h4>
<p>Following the <a href="https://tools.ietf.org/html/rfc4226" target="_blank" rel="noopener noreferrer" class="">RFC 4226</a> section <code>5.3</code>.</p>
<h5 class="anchor anchorTargetStickyNavbar_SAay" id="finding-offset">Finding offset<a href="https://lunik.tiwabbit.fr/blog/understanding-totp#finding-offset" class="hash-link" aria-label="Direct link to Finding offset" title="Direct link to Finding offset" translate="no">​</a></h5>
<p>We first need to find the <code>offset</code> value by getting the least significant <code>4</code> bits of the <code>MAC</code> (The <code>4</code> on the right)
using <a href="https://en.wikipedia.org/wiki/Bitwise_operation#AND" target="_blank" rel="noopener noreferrer" class="">bitwise AND operation</a></p>
<p>In <a href="https://www.python.org/" target="_blank" rel="noopener noreferrer" class="">Python</a> <code>&amp;</code> can be used to make binary <code>AND</code> operation between two number. With the value <code>0x0F</code> (in hexadecimal and equivalent to <code>00001111</code> in binary) we can extract two <code>4</code> last bits. Since <code>offset</code> is an integer stored on <code>4</code> bits, the value is between <code>0</code> and <code>15</code>. In <a href="https://www.python.org/" target="_blank" rel="noopener noreferrer" class="">Python</a> we can use <code>mac[-1]</code> to get the last byte of the bytes array converted in an integer.</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">offset </span><span class="token operator">=</span><span class="token plain"> mac</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token operator">&amp;</span><span class="token plain"> </span><span class="token number">0x0F</span><br></div></code></pre></div></div>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">mac</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token builtin" style="color:rgb(189, 147, 249)">bin</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">10110100</span><span class="token plain"> </span><span class="token number">11010010</span><span class="token plain"> </span><span class="token number">01111010</span><span class="token plain"> </span><span class="token number">10110100</span><span class="token plain"> </span><span class="token number">10111101</span><span class="token plain"> </span><span class="token number">00110101</span><span class="token plain"> </span><span class="token number">11111110</span><span class="token plain"> </span><span class="token number">00100011</span><span class="token plain"> </span><span class="token number">11101101</span><span class="token plain"> </span><span class="token number">01011001</span><span class="token plain"> </span><span class="token number">01111110</span><span class="token plain"> </span><span class="token number">10111100</span><span class="token plain"> </span><span class="token number">11110000</span><span class="token plain"> </span><span class="token number">01111001</span><span class="token plain"> </span><span class="token number">11000001</span><span class="token plain"> </span><span class="token number">01001010</span><span class="token plain"> </span><span class="token number">00000110</span><span class="token plain"> </span><span class="token number">01101100</span><span class="token plain"> </span><span class="token number">01010001</span><span class="token plain"> </span><span class="token number">00101111</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">mac</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token builtin" style="color:rgb(189, 147, 249)">hex</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> b4 d2 7a b4 bd </span><span class="token number">35</span><span class="token plain"> fe </span><span class="token number">23</span><span class="token plain"> ed </span><span class="token number">59</span><span class="token plain"> 7e bc f0 </span><span class="token number">79</span><span class="token plain"> c1 4a </span><span class="token number">06</span><span class="token plain"> 6c </span><span class="token number">51</span><span class="token plain"> 2f</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">offset</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token builtin" style="color:rgb(189, 147, 249)">bin</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token number">1111</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">offset</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token builtin" style="color:rgb(189, 147, 249)">hex</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> f</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">offset</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">dec</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token number">15</span><br></div></code></pre></div></div>
<h5 class="anchor anchorTargetStickyNavbar_SAay" id="finding-the-dynamic-truncation">Finding the dynamic truncation<a href="https://lunik.tiwabbit.fr/blog/understanding-totp#finding-the-dynamic-truncation" class="hash-link" aria-label="Direct link to Finding the dynamic truncation" title="Direct link to Finding the dynamic truncation" translate="no">​</a></h5>
<p>Using the previously found <code>offset</code>, we are going to extract the <code>Dynamic Truncation</code> from the <code>MAC</code>. We need to retrieve the <code>4</code> bytes at position <code>offset</code>, <code>offset+1</code>, <code>offset+2</code> and <code>offset+3</code> in the <code>MAC</code> bytes array.</p>
<p>We got :</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">dynamic_truncation </span><span class="token operator">=</span><span class="token plain"> mac</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">offset</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">offset</span><span class="token operator">+</span><span class="token number">3</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token operator">+</span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><br></div></code></pre></div></div>
<p>In <a href="https://www.python.org/" target="_blank" rel="noopener noreferrer" class="">Python</a> the value after the <code>:</code> is not included. In order to extract <code>offset+3</code> value, we need to put <code>(offset+3)+1</code></p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">offset</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token builtin" style="color:rgb(189, 147, 249)">bin</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">1111</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">offset</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token builtin" style="color:rgb(189, 147, 249)">hex</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> f</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">offset</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">dec</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">15</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">offset</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">dec</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">from</span><span class="token plain"> </span><span class="token number">15</span><span class="token plain"> to </span><span class="token number">18</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">mac</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token builtin" style="color:rgb(189, 147, 249)">bin</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">10110100</span><span class="token plain"> </span><span class="token number">11010010</span><span class="token plain"> </span><span class="token number">01111010</span><span class="token plain"> </span><span class="token number">10110100</span><span class="token plain"> </span><span class="token number">10111101</span><span class="token plain"> </span><span class="token number">00110101</span><span class="token plain"> </span><span class="token number">11111110</span><span class="token plain"> </span><span class="token number">00100011</span><span class="token plain"> </span><span class="token number">11101101</span><span class="token plain"> </span><span class="token number">01011001</span><span class="token plain"> </span><span class="token number">01111110</span><span class="token plain"> </span><span class="token number">10111100</span><span class="token plain"> </span><span class="token number">11110000</span><span class="token plain"> </span><span class="token number">01111001</span><span class="token plain"> </span><span class="token number">11000001</span><span class="token plain"> </span><span class="token number">01001010</span><span class="token plain"> </span><span class="token number">00000110</span><span class="token plain"> </span><span class="token number">01101100</span><span class="token plain"> </span><span class="token number">01010001</span><span class="token plain"> </span><span class="token number">00101111</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">mac</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token builtin" style="color:rgb(189, 147, 249)">hex</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> b4 d2 7a b4 bd </span><span class="token number">35</span><span class="token plain"> fe </span><span class="token number">23</span><span class="token plain"> ed </span><span class="token number">59</span><span class="token plain"> 7e bc f0 </span><span class="token number">79</span><span class="token plain"> c1 4a </span><span class="token number">06</span><span class="token plain"> 6c </span><span class="token number">51</span><span class="token plain"> 2f</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">dynamic_truncation</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token builtin" style="color:rgb(189, 147, 249)">bin</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token number">10010100</span><span class="token plain"> </span><span class="token number">00001100</span><span class="token plain"> </span><span class="token number">11011000</span><span class="token plain"> </span><span class="token number">1010001</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">dynamic_truncation</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token builtin" style="color:rgb(189, 147, 249)">hex</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> 4a </span><span class="token number">06</span><span class="token plain"> 6c </span><span class="token number">51</span><br></div></code></pre></div></div>
<h5 class="anchor anchorTargetStickyNavbar_SAay" id="extract-the-31-bits">Extract the 31 bits<a href="https://lunik.tiwabbit.fr/blog/understanding-totp#extract-the-31-bits" class="hash-link" aria-label="Direct link to Extract the 31 bits" title="Direct link to Extract the 31 bits" translate="no">​</a></h5>
<p>Finally we are going to extract the least significant <code>31</code> bits from our <code>Dynamic Truncation</code>
The solution would be to convert the result bytes array into un signed long witch is <code>32</code> bits with the most significant bit (on the left) containing the signed value (<code>+</code> or <code>-</code>).
Using the same trick as earlier, we use <a href="https://en.wikipedia.org/wiki/Bitwise_operation#AND" target="_blank" rel="noopener noreferrer" class="">bitwise AND operation</a> to extract them.
<code>0x7fffffff</code> (in hexadecimal and equivalent to <code>01111111 11111111 11111111 11111111</code> in binary). Resulting in a 31 bit value when ignoring leading 0.</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"># https://docs.python.org/3/library/struct.html#struct.unpack</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">extract_31 </span><span class="token operator">=</span><span class="token plain"> struct</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">unpack</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'&gt;L'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> dynamic_truncation</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token number">0</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token operator">&amp;</span><span class="token plain"> </span><span class="token number">0x7fffffff</span><br></div></code></pre></div></div>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">dynamic_truncation</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token builtin" style="color:rgb(189, 147, 249)">bin</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">10010100</span><span class="token plain"> </span><span class="token number">00001100</span><span class="token plain"> </span><span class="token number">11011000</span><span class="token plain"> </span><span class="token number">1010001</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">dynamic_truncation</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token builtin" style="color:rgb(189, 147, 249)">hex</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> 4a </span><span class="token number">06</span><span class="token plain"> 6c </span><span class="token number">51</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">extract_31</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token builtin" style="color:rgb(189, 147, 249)">bin</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token number">10010100</span><span class="token plain"> </span><span class="token number">00001100</span><span class="token plain"> </span><span class="token number">11011000</span><span class="token plain"> </span><span class="token number">1010001</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">extract_31</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token builtin" style="color:rgb(189, 147, 249)">hex</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> 4a </span><span class="token number">06</span><span class="token plain"> 6c </span><span class="token number">51</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">extract_31</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">dec</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token number">1241934929</span><br></div></code></pre></div></div>
<h5 class="anchor anchorTargetStickyNavbar_SAay" id="truncate-digits">Truncate digits<a href="https://lunik.tiwabbit.fr/blog/understanding-totp#truncate-digits" class="hash-link" aria-label="Direct link to Truncate digits" title="Direct link to Truncate digits" translate="no">​</a></h5>
<p>Now that we have our final number after extracting the last <code>31</code> bits, we need to truncate the decimal value this time. Based on the configured <code>digits</code> value, we want to only keep the <code>X</code> last numbers of the decimal value of the <code>extract_31</code>.
<code>X</code> is the length of the <a href="https://tools.ietf.org/html/rfc6238" target="_blank" rel="noopener noreferrer" class="">TOTP</a> code we want and should be between <code>6</code> and <code>10</code>.
<code>6</code> minimum because of security requirement and <code>10</code> maximum because the max value of a <code>32</code> bit signed integer is <code>2147483647</code>, a <code>10</code> number value.</p>
<p>Using a length of <code>10</code> over <code>9</code> doesn't add that much security because the leading number can only take <code>0</code>, <code>1</code>, <code>2</code> value.</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">digits_count </span><span class="token operator">=</span><span class="token plain"> </span><span class="token number">6</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">hotp </span><span class="token operator">=</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">str</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">extract_31</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token operator">-</span><span class="token plain">digits_count</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><br></div></code></pre></div></div>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">extract_31</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">dec</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">1241934929</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">digits_count</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">6</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">hotp </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token number">934929</span><br></div></code></pre></div></div>
<p>In the case where <code>extract_31</code> is a too low value where the number of digits is lower than <code>digits</code>, we add leading <code>0</code> at the left of the <a href="https://tools.ietf.org/html/rfc4226" target="_blank" rel="noopener noreferrer" class="">HOTP</a> code to make it the right length.</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">hotp </span><span class="token operator">=</span><span class="token plain"> hotp</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">zfill</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">digits_count</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></div></code></pre></div></div>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">hotp</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">934929</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">hotp </span><span class="token operator">=</span><span class="token operator">&gt;</span><span class="token plain"> </span><span class="token number">934929</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="conclusion">Conclusion<a href="https://lunik.tiwabbit.fr/blog/understanding-totp#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>Now you really know how <a href="https://tools.ietf.org/html/rfc6238" target="_blank" rel="noopener noreferrer" class="">TOTP</a> codes are generates. As we can see, it's pretty simple and you can develop your own script to generate them.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="full-code">Full code<a href="https://lunik.tiwabbit.fr/blog/understanding-totp#full-code" class="hash-link" aria-label="Direct link to Full code" title="Direct link to Full code" translate="no">​</a></h2>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> base64</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> hmac</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> struct</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> time</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">def</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">hotp</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">counter</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> key</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> digits_count</span><span class="token operator">=</span><span class="token number">6</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  bytes_counter </span><span class="token operator">=</span><span class="token plain"> struct</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">pack</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'&gt;Q'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> counter</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  key </span><span class="token operator">=</span><span class="token plain"> key </span><span class="token operator">+</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'='</span><span class="token plain"> </span><span class="token operator">*</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">8</span><span class="token plain"> </span><span class="token operator">-</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">len</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">key</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">%</span><span class="token plain"> </span><span class="token number">8</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  bytes_key </span><span class="token operator">=</span><span class="token plain"> base64</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">b32decode</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">key</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  mac </span><span class="token operator">=</span><span class="token plain"> hmac</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">new</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">bytes_key</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> bytes_counter</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"SHA1"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">digest</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  offset </span><span class="token operator">=</span><span class="token plain"> mac</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token operator">&amp;</span><span class="token plain"> </span><span class="token number">0x0F</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  dynamic_truncation </span><span class="token operator">=</span><span class="token plain"> mac</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">offset</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain">offset</span><span class="token operator">+</span><span class="token number">4</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  extract_31 </span><span class="token operator">=</span><span class="token plain"> struct</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">unpack</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'&gt;L'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> dynamic_truncation</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token number">0</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token operator">&amp;</span><span class="token plain"> </span><span class="token number">0x7fffffff</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">str</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">extract_31</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token operator">-</span><span class="token plain">digits_count</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">zfill</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">digits_count</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">def</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">totp</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">key</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  counter </span><span class="token operator">=</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">int</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">time</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">time</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">/</span><span class="token plain"> </span><span class="token number">30</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> hotp</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">counter</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> key</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> __name__ </span><span class="token operator">==</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"__main__"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  key </span><span class="token operator">=</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">input</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"Enter secret key : "</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">upper</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">print</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"TOTP code :"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> totp</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">key</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="references">References<a href="https://lunik.tiwabbit.fr/blog/understanding-totp#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h2>
<ul>
<li class="">
<p><a href="https://tools.ietf.org/html/rfc6238" target="_blank" rel="noopener noreferrer" class="">TOTP: Time-Based One-Time Password Algorithm</a></p>
</li>
<li class="">
<p><a href="https://tools.ietf.org/html/rfc4226" target="_blank" rel="noopener noreferrer" class="">HOTP: An HMAC-Based One-Time Password Algorithm</a></p>
</li>
<li class="">
<p><a href="https://tools.ietf.org/html/rfc4648" target="_blank" rel="noopener noreferrer" class="">The Base16, Base32, and Base64 Data Encodings</a></p>
</li>
<li class="">
<p><a href="https://tools.ietf.org/html/rfc2104" target="_blank" rel="noopener noreferrer" class="">HMAC: Keyed-Hashing for Message Authentication</a></p>
</li>
<li class="">
<p><a href="https://tools.ietf.org/html/rfc3174" target="_blank" rel="noopener noreferrer" class="">US Secure Hash Algorithm 1 (SHA1)</a></p>
</li>
<li class="">
<p><a href="https://en.wikipedia.org/wiki/Multi-factor_authentication" target="_blank" rel="noopener noreferrer" class="">Multi-factor authentication</a></p>
</li>
<li class="">
<p><a href="https://en.wikipedia.org/wiki/Time-based_one-time_password" target="_blank" rel="noopener noreferrer" class="">Time-based one-time password</a></p>
</li>
<li class="">
<p><a href="https://en.wikipedia.org/wiki/HMAC-based_one-time_password" target="_blank" rel="noopener noreferrer" class="">HMAC-based one-time password</a></p>
</li>
<li class="">
<p><a href="https://en.wikipedia.org/wiki/QR_code" target="_blank" rel="noopener noreferrer" class="">QR code</a></p>
</li>
<li class="">
<p><a href="https://en.wikipedia.org/wiki/SHA-1" target="_blank" rel="noopener noreferrer" class="">SHA-1</a></p>
</li>
<li class="">
<p><a href="https://en.wikipedia.org/wiki/Bitwise_operation#AND" target="_blank" rel="noopener noreferrer" class="">Bitwise operation</a></p>
</li>
<li class="">
<p><a href="https://en.wikipedia.org/wiki/Epoch_(computing)" target="_blank" rel="noopener noreferrer" class="">epoch</a></p>
</li>
<li class="">
<p><a href="https://docs.python.org/3/library/hmac.html" target="_blank" rel="noopener noreferrer" class="">Python HMAC lib</a></p>
</li>
</ul>]]></content>
        <author>
            <name>Guillaume MARTINEZ</name>
            <uri>https://github.com/Lunik</uri>
        </author>
        <category label="security" term="security"/>
        <category label="2fa" term="2fa"/>
        <category label="totp" term="totp"/>
        <category label="two-factor" term="two-factor"/>
        <category label="authentication" term="authentication"/>
        <category label="otp" term="otp"/>
        <category label="hmac" term="hmac"/>
        <category label="hotp" term="hotp"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[x509 certificat managment]]></title>
        <id>https://lunik.tiwabbit.fr/blog/manage-x509-certs</id>
        <link href="https://lunik.tiwabbit.fr/blog/manage-x509-certs"/>
        <updated>2022-02-06T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Presentation and managment of a Root Certificate Authority]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2022-02-06-manage-x509-certs/cover.png" alt="cover" class="img__Ss2"></p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="what-are-x509-certificates-">What are x509 certificates ?<a href="https://lunik.tiwabbit.fr/blog/manage-x509-certs#what-are-x509-certificates-" class="hash-link" aria-label="Direct link to What are x509 certificates ?" title="Direct link to What are x509 certificates ?" translate="no">​</a></h2>
<p><a href="https://en.wikipedia.org/wiki/X.509" target="_blank" rel="noopener noreferrer" class="">According to Wikipedia</a>, <a href="https://datatracker.ietf.org/doc/html/rfc5280" target="_blank" rel="noopener noreferrer" class="">x509</a> is a standard defining the format of public key certificates. They are used in many Internet protocols, including <a href="https://datatracker.ietf.org/doc/html/rfc8446" target="_blank" rel="noopener noreferrer" class="">TLS/SSL</a>, which is the basis for <a href="https://datatracker.ietf.org/doc/html/rfc2818" target="_blank" rel="noopener noreferrer" class="">HTTPS</a> (the secure protocol for browsing the web).</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="purpose">Purpose<a href="https://lunik.tiwabbit.fr/blog/manage-x509-certs#purpose" class="hash-link" aria-label="Direct link to Purpose" title="Direct link to Purpose" translate="no">​</a></h2>
<p>The purpose of having my own Certification Authority (CA) allow me to generate self signed certificates for non public uses. Hosted services on my own private network prevent me from getting free certificates from well known <a href="https://letsencrypt.org/" target="_blank" rel="noopener noreferrer" class="">Let's Encrypt authority</a>.</p>
<p>Since I host more than one service and I want that all my devices (laptop, phone, ...) can have access to them without having to install certificate every 3 weeks (when I'm updating hostnames for exemple). I have decided to create my own CA and generate signed certificates validated by it. That way, I only have to install the root CA on all my device to make it work.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="making-my-own-certification-authority">Making my own certification authority<a href="https://lunik.tiwabbit.fr/blog/manage-x509-certs#making-my-own-certification-authority" class="hash-link" aria-label="Direct link to Making my own certification authority" title="Direct link to Making my own certification authority" translate="no">​</a></h2>
<p>All I need is a linux environment and <a href="https://www.openssl.org/" target="_blank" rel="noopener noreferrer" class=""><code>openssl</code></a>.</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="generating-the-secret">Generating the secret<a href="https://lunik.tiwabbit.fr/blog/manage-x509-certs#generating-the-secret" class="hash-link" aria-label="Direct link to Generating the secret" title="Direct link to Generating the secret" translate="no">​</a></h3>
<p>First let's generate the private key with the following command :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">openssl genrsa </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-out</span><span class="token plain"> root-CA.key </span><span class="token number">4096</span><br></div></code></pre></div></div>
<p>Ouput :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Generating RSA private key, </span><span class="token number">4096</span><span class="token plain"> bit long modulus</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token plain">.++</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token plain">++</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">e is </span><span class="token number">65537</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">0x10001</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></div></code></pre></div></div>
<p>I now have a nice looking private key :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">-----BEGIN RSA PRIVATE KEY-----</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">MIIJKAIBAAKCAgEA2IxiR6U/B6Ypd1/3KG5Pxk/BY47HzHpBAHnEFsMHdcpCNNXO</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">GxqhUsIHBGMc0I2xa0WiyNJB4k5qGwL58J1TK/kKaW8yOOGajawvhjL/6bYXgN1I</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token plain">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">rcL3YJyl/m0mlYl80syPPViHM9TD7uwQi/M9DPeOGLwVuCYzpl3qAkQRaTwQc2Ev</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">5hffBNz+evhf2Q7iUCmWQFizIFlFpOCq1f/jvpleqf8ChG6EhNRgvAFIUMc</span><span class="token operator">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">-----END RSA PRIVATE KEY-----</span><br></div></code></pre></div></div>
<p><strong>This file should be kept secret and securelly stored offline.</strong></p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="generating-the-certificate-configuration">Generating the certificate configuration<a href="https://lunik.tiwabbit.fr/blog/manage-x509-certs#generating-the-certificate-configuration" class="hash-link" aria-label="Direct link to Generating the certificate configuration" title="Direct link to Generating the certificate configuration" translate="no">​</a></h3>
<p>Now that I have the secret part, I need to generate the public part : the <a href="https://datatracker.ietf.org/doc/html/rfc5280" target="_blank" rel="noopener noreferrer" class="">x509 certificate</a>. Using the <a href="https://github.com/openssl/openssl/blob/master/apps/openssl.cnf" target="_blank" rel="noopener noreferrer" class="">default OpenSSL configuration</a>:</p>
<div class="language-toml codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-toml codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">[ req ]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">default_bits       = 4096</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">default_keyfile    = root-CA.key</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">encrypt_key        = no</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">utf8               = yes</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">distinguished_name = req_distinguished_name</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">prompt             = no</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">x509_extensions    = v3_ca</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">[ req_distinguished_name ]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">countryName         = FR</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">stateOrProvinceName = France</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">localityName        = Paris</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">organizationName    = Example</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">commonName          = example.org</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">[ v3_ca ]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">subjectKeyIdentifier = hash</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">basicConstraints     = critical,CA:true,pathlen:1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">keyUsage             = critical,keyCertSign</span><br></div></code></pre></div></div>
<p>This configuration should be kept for the next time I need to regenerate the root certificate. Keep in mind that this cert shouldn't be changed too often otherwise it make the main purpose useless.</p>
<p>You can adjust this configuration based on your need.</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="generating-the-root-certificate">Generating the root certificate<a href="https://lunik.tiwabbit.fr/blog/manage-x509-certs#generating-the-root-certificate" class="hash-link" aria-label="Direct link to Generating the root certificate" title="Direct link to Generating the root certificate" translate="no">​</a></h3>
<p>Using the previous configuration, I have generated the self signed root CA :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">openssl req </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-x509</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-new</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-nodes</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-config</span><span class="token plain"> root-CA-openssl.cnf </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-key</span><span class="token plain"> root-CA.key </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-sha256</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-days</span><span class="token plain"> </span><span class="token number">1024</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-out</span><span class="token plain"> root-CA.crt</span><br></div></code></pre></div></div>
<p>Checking the content of the certificate :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">openssl x509 </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-in</span><span class="token plain"> root-CA.crt </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-noout</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-text</span><br></div></code></pre></div></div>
<p>Output :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Certificate:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    Data:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        Version: </span><span class="token number">1</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">0x0</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        Serial Number: </span><span class="token number">11676879669831540892</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">0xa20c9c31654a209c</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    Signature Algorithm: sha256WithRSAEncryption</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        Issuer: </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">C</span><span class="token operator">=</span><span class="token plain">FR, </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">ST</span><span class="token operator">=</span><span class="token plain">France, </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">L</span><span class="token operator">=</span><span class="token plain">Paris, </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">O</span><span class="token operator">=</span><span class="token plain">Example, </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">CN</span><span class="token operator">=</span><span class="token plain">example.org</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        Validity</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            Not Before: Feb  </span><span class="token number">6</span><span class="token plain"> </span><span class="token number">15</span><span class="token plain">:53:58 </span><span class="token number">2022</span><span class="token plain"> GMT</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            Not After </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">:</span><span class="token plain"> Nov </span><span class="token number">26</span><span class="token plain"> </span><span class="token number">15</span><span class="token plain">:53:58 </span><span class="token number">2024</span><span class="token plain"> GMT</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        Subject: </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">C</span><span class="token operator">=</span><span class="token plain">FR, </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">ST</span><span class="token operator">=</span><span class="token plain">France, </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">L</span><span class="token operator">=</span><span class="token plain">Paris, </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">O</span><span class="token operator">=</span><span class="token plain">Example, </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">CN</span><span class="token operator">=</span><span class="token plain">example.org</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token plain">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        X509v3 extensions:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            X509v3 Subject Key Identifier: </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                F1:FA:C4:50:A4:7F:53:D3:6C:D5:EF:48:72:24:B8:CE:08:10:3F:94</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            X509v3 Basic Constraints: critical</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                CA:TRUE, pathlen:1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            X509v3 Key Usage: critical</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">                Certificate Sign</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token plain">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><br></div></code></pre></div></div>
<p>This command generate the certificate file and sign its content with the private key.</p>
<p>I now have a Authority Certificate, I can generate all my apps certificates and sign them with it.</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="verifying-that-the-certificate-and-the-private-key-match">Verifying that the certificate and the private key match<a href="https://lunik.tiwabbit.fr/blog/manage-x509-certs#verifying-that-the-certificate-and-the-private-key-match" class="hash-link" aria-label="Direct link to Verifying that the certificate and the private key match" title="Direct link to Verifying that the certificate and the private key match" translate="no">​</a></h3>
<p>Get the <em>checkum</em> of the certificat <strong>modulus</strong> :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">openssl x509 </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-in</span><span class="token plain"> root-CA.crt </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-noout</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-modulus</span><span class="token plain"> </span><span class="token operator">|</span><span class="token plain"> sha256sum</span><br></div></code></pre></div></div>
<p>Do the same for the private key :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">openssl rsa </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-in</span><span class="token plain"> root-CA.key </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-noout</span><span class="token plain"> -modulus</span><span class="token operator">|</span><span class="token plain"> sha256sum</span><br></div></code></pre></div></div>
<p>You should get exactly the same output with both commands :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">dc51b8c96c2d745df3bd5590d990230a482fd247123599548e0632fdbf97fc22  -</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="creating-certificates-for-my-apps">Creating certificates for my apps<a href="https://lunik.tiwabbit.fr/blog/manage-x509-certs#creating-certificates-for-my-apps" class="hash-link" aria-label="Direct link to Creating certificates for my apps" title="Direct link to Creating certificates for my apps" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="generating-the-secret-1">Generating the secret<a href="https://lunik.tiwabbit.fr/blog/manage-x509-certs#generating-the-secret-1" class="hash-link" aria-label="Direct link to Generating the secret" title="Direct link to Generating the secret" translate="no">​</a></h3>
<p>Same as for the root CA, I need to generate a secret key.</p>
<p>I can use an identical command as earlier :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">openssl genrsa </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-out</span><span class="token plain"> my-app.key </span><span class="token number">2048</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="generating-the-openssl-config">Generating the openssl config<a href="https://lunik.tiwabbit.fr/blog/manage-x509-certs#generating-the-openssl-config" class="hash-link" aria-label="Direct link to Generating the openssl config" title="Direct link to Generating the openssl config" translate="no">​</a></h3>
<p>I now need to create the OpenSSL configuration for my certificate using the <a href="https://github.com/openssl/openssl/blob/master/apps/openssl.cnf" target="_blank" rel="noopener noreferrer" class="">default OpenSSL configuration</a> :</p>
<div class="language-toml codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-toml codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">[ req ]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">default_bits       = 2048</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">default_keyfile    = my-app.key</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">encrypt_key        = no</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">utf8               = yes</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">distinguished_name = req_distinguished_name</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">req_extensions     = v3_req</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">prompt             = no</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">[ req_distinguished_name ]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">countryName         = FR</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">stateOrProvinceName = France</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">localityName        = Paris</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">organizationName    = Example</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">commonName          = my-app.example.org</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">[v3_req]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">subjectKeyIdentifier = hash</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">subjectAltName       = @alt_names</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">[ alt_names ]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">DNS.1 = my-app.example.org</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">DNS.2 = extra.my-app.example.org</span><br></div></code></pre></div></div>
<p>Note here that the <code>subjectAltName</code> parameter and the <code>alt_names</code> are very important. Now every browser or phone application require that the hostname exposed by the web application should be specified in this section.</p>
<p>You can still add more than one alternative name to the certificate. This allow you to use the same certificate for multiple application. <strong>Make sure to use different certificate based on the security level you need for your apps.</strong></p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="generating-the-certificate-request">Generating the certificate request<a href="https://lunik.tiwabbit.fr/blog/manage-x509-certs#generating-the-certificate-request" class="hash-link" aria-label="Direct link to Generating the certificate request" title="Direct link to Generating the certificate request" translate="no">​</a></h3>
<p>In order to generate a certificate that is signed by my root CA, I need to generate a certificate request.
Using the following command, I can generate the request and sign it with the app private key :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">openssl req </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-sha256</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-new</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-key</span><span class="token plain"> my-app.key </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-config</span><span class="token plain"> my-app.cnf </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-out</span><span class="token plain"> my-app.csr</span><br></div></code></pre></div></div>
<p>I can review the content of my request with :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">openssl req </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-in</span><span class="token plain"> my-app.csr </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-noout</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-text</span><br></div></code></pre></div></div>
<p>Output :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Certificate Request:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    Data:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        Version: </span><span class="token number">0</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">0x0</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        Subject: </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">C</span><span class="token operator">=</span><span class="token plain">FR, </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">ST</span><span class="token operator">=</span><span class="token plain">France, </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">L</span><span class="token operator">=</span><span class="token plain">Paris, </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">O</span><span class="token operator">=</span><span class="token plain">Example, </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">CN</span><span class="token operator">=</span><span class="token plain">my-app.example.org</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token plain">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="signing-my-app-certificate-with-the-root-ca">Signing my app certificate with the root CA<a href="https://lunik.tiwabbit.fr/blog/manage-x509-certs#signing-my-app-certificate-with-the-root-ca" class="hash-link" aria-label="Direct link to Signing my app certificate with the root CA" title="Direct link to Signing my app certificate with the root CA" translate="no">​</a></h3>
<p>Lastly I need to use my root CA to generate my application certificate based on the request and ensure its authenticity.</p>
<p>I only need to use the following command :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">openssl x509 </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-req</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-CA</span><span class="token plain"> root-CA.crt </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-CAkey</span><span class="token plain"> root-CA.key </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-CAcreateserial</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-in</span><span class="token plain"> my-app.csr </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-days</span><span class="token plain"> </span><span class="token number">500</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-sha256</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-out</span><span class="token plain"> my-app.crt</span><br></div></code></pre></div></div>
<p>To verify the content of the newly generated certificate, I can run the following conmmand :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">openssl x509 </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-in</span><span class="token plain"> my-app.crt </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-noout</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-text</span><br></div></code></pre></div></div>
<p>Output :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Certificate:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    Data:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        Version: </span><span class="token number">1</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">0x0</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        Serial Number: </span><span class="token number">15807445207358136962</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">0xdb5f53aa27086282</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    Signature Algorithm: sha256WithRSAEncryption</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        Issuer: </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">C</span><span class="token operator">=</span><span class="token plain">FR, </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">ST</span><span class="token operator">=</span><span class="token plain">France, </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">L</span><span class="token operator">=</span><span class="token plain">Paris, </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">O</span><span class="token operator">=</span><span class="token plain">Example, </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">CN</span><span class="token operator">=</span><span class="token plain">example.org</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        Validity</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            Not Before: Feb  </span><span class="token number">6</span><span class="token plain"> </span><span class="token number">16</span><span class="token plain">:22:38 </span><span class="token number">2022</span><span class="token plain"> GMT</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">            Not After </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">:</span><span class="token plain"> Jun </span><span class="token number">21</span><span class="token plain"> </span><span class="token number">16</span><span class="token plain">:22:38 </span><span class="token number">2023</span><span class="token plain"> GMT</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        Subject: </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">C</span><span class="token operator">=</span><span class="token plain">FR, </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">ST</span><span class="token operator">=</span><span class="token plain">France, </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">L</span><span class="token operator">=</span><span class="token plain">Paris, </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">O</span><span class="token operator">=</span><span class="token plain">Example, </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">CN</span><span class="token operator">=</span><span class="token plain">my-app.example.org</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="verifiying-the-full-trust-chain">Verifiying the full trust chain<a href="https://lunik.tiwabbit.fr/blog/manage-x509-certs#verifiying-the-full-trust-chain" class="hash-link" aria-label="Direct link to Verifiying the full trust chain" title="Direct link to Verifiying the full trust chain" translate="no">​</a></h3>
<p>Now that I have the certificate for my application, I need to make sure that the root CA validate the authenticity of it.</p>
<p>Using one last <code>openssl</code> command :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">openssl verify </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-CAfile</span><span class="token plain"> root-CA.crt </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  my-app.crt</span><br></div></code></pre></div></div>
<p>Ouput :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">my-app.crt: OK</span><br></div></code></pre></div></div>
<p>Also with an intermediate certificate :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">openssl verify </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-CAfile</span><span class="token plain"> root-CA.crt </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">-untrusted</span><span class="token plain"> intermediate.pem </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  my-app.crt</span><br></div></code></pre></div></div>]]></content>
        <author>
            <name>Guillaume MARTINEZ</name>
            <uri>https://github.com/Lunik</uri>
        </author>
        <category label="certificate" term="certificate"/>
        <category label="openssl" term="openssl"/>
        <category label="x509" term="x509"/>
        <category label="private_key" term="private_key"/>
        <category label="public_key" term="public_key"/>
        <category label="rsa" term="rsa"/>
        <category label="pem" term="pem"/>
        <category label="cert" term="cert"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Create and Expose a public DNS service]]></title>
        <id>https://lunik.tiwabbit.fr/blog/create-public-dns-service</id>
        <link href="https://lunik.tiwabbit.fr/blog/create-public-dns-service"/>
        <updated>2021-12-24T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[I'm explaining how I have created an exposed a public DNS service]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2021-12-24-create-public-dns-service/cover.png" alt="cover" class="img__Ss2"></p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="what-is-dns-">What is DNS ?<a href="https://lunik.tiwabbit.fr/blog/create-public-dns-service#what-is-dns-" class="hash-link" aria-label="Direct link to What is DNS ?" title="Direct link to What is DNS ?" translate="no">​</a></h2>
<p>The <a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">Domain Name System (DNS)</a> is a system that provided human readable names for computers, services and other resources connected to the internet. Basic records allow to translate a <a href="https://en.wikipedia.org/wiki/Domain_name" target="_blank" rel="noopener noreferrer" class="">Domain Name</a> (that humans can understand) into an <a href="https://en.wikipedia.org/wiki/IP_address" target="_blank" rel="noopener noreferrer" class="">IP Address</a> (that computer understand for routing).</p>
<p><a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">DNS</a> is used all the time, like right now, for accessing this blog, your computer make a <a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">DNS</a> request to translate <code>lunik.tiwabbit.fr</code> into an <a href="https://en.wikipedia.org/wiki/IP_address" target="_blank" rel="noopener noreferrer" class="">IP</a>. Then it requested the web page to the <a href="https://en.wikipedia.org/wiki/IP_address" target="_blank" rel="noopener noreferrer" class="">IP address</a> resolved by the <a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">DNS</a> query.
You can view this behavior by opening the <a href="https://en.wikipedia.org/wiki/Web_development_tools" target="_blank" rel="noopener noreferrer" class="">developper console</a> on your web browser :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2021-12-24-create-public-dns-service/dns_safari_debug.jpg" alt="blog-dns-debug-through-developper-console" class="img__Ss2"></p>
<div class="theme-admonition theme-admonition-warning admonition_IZjC alert alert--warning"><div class="admonitionHeading_uVvU"><span class="admonitionIcon_HiR3"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>warning</div><div class="admonitionContent_bl22"><p>All the IP address and <a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">DNS</a> names used in this blog post are no longer in use by my project. Please don't make stupid things and ignore them.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="make-dns-query">Make DNS query<a href="https://lunik.tiwabbit.fr/blog/create-public-dns-service#make-dns-query" class="hash-link" aria-label="Direct link to Make DNS query" title="Direct link to Make DNS query" translate="no">​</a></h3>
<p>You can make basic <a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">DNS</a> query using <code>nslookup</code> on your terminal :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">nslookup</span><span class="token plain"> lunik.tiwabbit.fr</span><br></div></code></pre></div></div>
<p>You will get something like :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Name: lunik.tiwabbit.fr</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Address: </span><span class="token number">13.224</span><span class="token plain">.247.30</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Name: lunik.tiwabbit.fr</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Address: </span><span class="token number">13.224</span><span class="token plain">.247.31</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Name: lunik.tiwabbit.fr</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Address: </span><span class="token number">13.224</span><span class="token plain">.247.51</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Name: lunik.tiwabbit.fr</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Address: </span><span class="token number">13.224</span><span class="token plain">.247.100</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="where-to-deploy-the-service-">Where to deploy the service ?<a href="https://lunik.tiwabbit.fr/blog/create-public-dns-service#where-to-deploy-the-service-" class="hash-link" aria-label="Direct link to Where to deploy the service ?" title="Direct link to Where to deploy the service ?" translate="no">​</a></h2>
<p>I have decided to host my <a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">DNS</a> service on a public cloud provider named <a href="https://www.scaleway.com/en/elements/" target="_blank" rel="noopener noreferrer" class="">Scaleway</a>.</p>
<p>Here is a global view of the architecture :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2021-12-24-create-public-dns-service/scaleway-arch.jpg" alt="scalway-architecture" class="img__Ss2"></p>
<p>I have decided to expose two public <a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">DNS</a> servers one in each region : France (Paris 1) and Netherlands (Amsterdam 1).</p>
<p>I choose those two regions because theire are the only one to propose <a href="https://www.scaleway.com/en/stardust-instances/" target="_blank" rel="noopener noreferrer" class="">Stardust Instances</a>. <a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">DNS</a> servers doesn't require a tons of resources, it allow me to reduce the project cost to a minimum.</p>
<p>The two servers are exposed with public <code>flexible</code> <a href="https://en.wikipedia.org/wiki/IP_address" target="_blank" rel="noopener noreferrer" class="">IP</a>s (one <a href="https://en.wikipedia.org/wiki/IPv4" target="_blank" rel="noopener noreferrer" class="">v4</a> and one <a href="https://en.wikipedia.org/wiki/IPv6" target="_blank" rel="noopener noreferrer" class="">v6</a> each) address that can be resolved via two <a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">DNS</a> entries for more convenience.</p>
<p>The security was my main concern so I have attached a security group to my instances allowing me to filter incoming and outgoing traffic.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="deploying-the-infrastructure">Deploying the infrastructure<a href="https://lunik.tiwabbit.fr/blog/create-public-dns-service#deploying-the-infrastructure" class="hash-link" aria-label="Direct link to Deploying the infrastructure" title="Direct link to Deploying the infrastructure" translate="no">​</a></h2>
<p>Now that I know what architecture I want for the service. I need to deploy it on the cloud provider.
I decided to use <a href="https://www.scaleway.com/en/terraform/" target="_blank" rel="noopener noreferrer" class="">Terraform</a> to create all the resources and bind them together.</p>
<p>Following the <a href="https://registry.terraform.io/providers/scaleway/scaleway/latest" target="_blank" rel="noopener noreferrer" class="">Terraform documentation</a> for creating my ressource is pretty straight forward. After 1 hour I had the necessary code. Now I just needed to apply it.</p>
<p>Here is what the <code>plan</code> looked like (truncated) :</p>
<div class="language-hcl codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-hcl codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Terraform will perform the following actions:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token comment" style="color:rgb(98, 114, 164)"># scaleway_instance_ip.france["tiwabbit-dns-01"] will be created</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  + </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">resource </span><span class="token keyword type variable" style="color:rgb(189, 147, 249);font-style:italic">"scaleway_instance_ip"</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"france"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">address</span><span class="token plain">         </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> (known after apply)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">id</span><span class="token plain">              </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> (known after apply)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">reverse</span><span class="token plain">         </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"dns01.tiwabbit.fr"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">server_id</span><span class="token plain">       </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> (known after apply)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">zone</span><span class="token plain">            </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"fr-par-1"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token comment" style="color:rgb(98, 114, 164)"># scaleway_instance_security_group.france will be created</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  + </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">resource </span><span class="token keyword type variable" style="color:rgb(189, 147, 249);font-style:italic">"scaleway_instance_security_group"</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"france"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">description</span><span class="token plain">             </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"dns"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">enable_default_security</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">external_rules</span><span class="token plain">          </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token boolean">false</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">id</span><span class="token plain">                      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> (known after apply)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">inbound_default_policy</span><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"drop"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">name</span><span class="token plain">                    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"public-dns"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">outbound_default_policy</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"accept"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">stateful</span><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">zone</span><span class="token plain">                    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"fr-par-1"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">inbound_rule</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          + </span><span class="token property">action</span><span class="token plain">   </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"accept"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          + </span><span class="token property">ip</span><span class="token plain">       </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"X.X.X.X"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          + </span><span class="token property">port</span><span class="token plain">     </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token number">22</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          + </span><span class="token property">protocol</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"TCP"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">inbound_rule</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          + </span><span class="token property">action</span><span class="token plain">   </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"accept"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          + </span><span class="token property">ip_range</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"0.0.0.0/0"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          + </span><span class="token property">port</span><span class="token plain">     </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token number">53</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          + </span><span class="token property">protocol</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"UDP"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">inbound_rule</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          + </span><span class="token property">action</span><span class="token plain">   </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"accept"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          + </span><span class="token property">ip_range</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"::/0"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          + </span><span class="token property">port</span><span class="token plain">     </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token number">53</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          + </span><span class="token property">protocol</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"UDP"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token comment" style="color:rgb(98, 114, 164)"># scaleway_instance_server.france["tiwabbit-dns-01"] will be created</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  + </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">resource </span><span class="token keyword type variable" style="color:rgb(189, 147, 249);font-style:italic">"scaleway_instance_server"</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"france"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">enable_dynamic_ip</span><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token boolean">false</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">enable_ipv6</span><span class="token plain">                      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">id</span><span class="token plain">                               </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> (known after apply)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">image</span><span class="token plain">                            </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"fr-par-1/5c8bbf4b-10f0-4cac-863b-4561781043ff"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">ip_id</span><span class="token plain">                            </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> (known after apply)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">ipv6_address</span><span class="token plain">                     </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> (known after apply)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">ipv6_gateway</span><span class="token plain">                     </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> (known after apply)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">ipv6_prefix_length</span><span class="token plain">               </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> (known after apply)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">name</span><span class="token plain">                             </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"tiwabbit-dns-01"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">private_ip</span><span class="token plain">                       </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> (known after apply)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">public_ip</span><span class="token plain">                        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> (known after apply)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">security_group_id</span><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"fr-par-1/dns"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">state</span><span class="token plain">                            </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"started"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">type</span><span class="token plain">                             </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"STARDUST1-S"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token property">zone</span><span class="token plain">                             </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"fr-par-1"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      + </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">root_volume</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          + </span><span class="token property">size_in_gb</span><span class="token plain">            </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"10"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          + </span><span class="token property">volume_id</span><span class="token plain">             </span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token plain"> (known after apply)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">...</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Plan: </span><span class="token number">8</span><span class="token plain"> to add, </span><span class="token number">0</span><span class="token plain"> to change, </span><span class="token number">0</span><span class="token plain"> to destroy.</span><br></div></code></pre></div></div>
<p>After completing the <code>apply</code> this is what I had on Scaleway Console :</p>
<p><strong>Instances :</strong>
<img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2021-12-24-create-public-dns-service/scaleway-console-instances.png" alt="scaleway-console-instances" class="img__Ss2"></p>
<p><strong>Volumes :</strong>
<img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2021-12-24-create-public-dns-service/scaleway-console-volumes.png" alt="scaleway-console-volumes" class="img__Ss2"></p>
<p><strong><a href="https://en.wikipedia.org/wiki/IP_address" target="_blank" rel="noopener noreferrer" class="">IP addresses</a> :</strong>
<img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2021-12-24-create-public-dns-service/scaleway-console-flexible-ips.png" alt="scaleway-console-flexible-ips" class="img__Ss2"></p>
<p><strong>Security groups :</strong>
<img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2021-12-24-create-public-dns-service/scaleway-console-security-group.png" alt="scaleway-console-security-group" class="img__Ss2">
Here, you can see that I'm only allowing inbound traffic on the port <code>53</code> which is the one used by <a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">DNS</a> servers. (The first rule with port <code>22</code> allow me to manage the server from a private location using SSH)</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="installing-and-configuring-the-service">Installing and configuring the service<a href="https://lunik.tiwabbit.fr/blog/create-public-dns-service#installing-and-configuring-the-service" class="hash-link" aria-label="Direct link to Installing and configuring the service" title="Direct link to Installing and configuring the service" translate="no">​</a></h2>
<p>Now that I have two brand new server at my disposition, I need to install and configure the <a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">DNS</a> software to run on.
There are running <a href="https://getfedora.org/en/" target="_blank" rel="noopener noreferrer" class="">Fedora 32</a> with a <code>5.6</code> <a href="https://github.com/torvalds/linux" target="_blank" rel="noopener noreferrer" class="">Linux Kernel</a> :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">root@tiwabbit-dns-01 ~</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token comment" style="color:rgb(98, 114, 164)"># screenfetch</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">           /:-------------:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain">          root@tiwabbit-dns-01</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        :-------------------::        OS: Fedora </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      :-----------/shhOHbmp---:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain">      Kernel: x86_64 Linux </span><span class="token number">5.6</span><span class="token plain">.6-300.fc32.x86_64</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    /-----------omMMMNNNMMD  ---:     Uptime: 18m</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   :-----------sMMMMNMNMP.    ---:    Packages: </span><span class="token number">405</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  :-----------:MMMdP-------    ---</span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain">   Shell: </span><span class="token function" style="color:rgb(80, 250, 123)">bash</span><span class="token plain"> </span><span class="token number">5.0</span><span class="token plain">.11</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> ,------------:MMMd--------    ---:   Disk: </span><span class="token number">1</span><span class="token plain">.0G / </span><span class="token number">9</span><span class="token plain">.6G </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">11</span><span class="token plain">%</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> :------------:MMMd-------    .---:   CPU: AMD EPYC </span><span class="token number">7281</span><span class="token plain"> </span><span class="token number">16</span><span class="token plain">-Core @ </span><span class="token number">2</span><span class="token plain">.096GHz</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> :----    oNMMMMMMMMMNho     .----:   RAM: 293MiB / 969MiB</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> :--     .+shhhMMMmhhy++   .------/  </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> :-    -------:MMMd--------------:   </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> :-   --------/MMMd-------------</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">    </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> :-    ------/hMMMy------------:     </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> :-- :dMNdhhdNMMNo------------</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">      </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> :---:sdNMMMMNds:------------:       </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> :------:://:-------------::         </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> :---------------------://  </span><br></div></code></pre></div></div>
<p>Fun fact <a href="https://www.scaleway.com/en/stardust-instances/" target="_blank" rel="noopener noreferrer" class="">Scaleway Stardust instance</a> runs on <a href="https://www.amd.com/en/products/epyc" target="_blank" rel="noopener noreferrer" class="">AMD EPYC</a> <a href="https://en.wikipedia.org/wiki/System_on_a_chip" target="_blank" rel="noopener noreferrer" class="">SoC</a> !</p>
<p>I have decided to use <a href="https://bind9.net/" target="_blank" rel="noopener noreferrer" class="">Bind9</a> <a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">DNS</a> server because it's already packaged in many distributions, there are a lot of documentation and the community is strong.</p>
<p>I'm using <a href="https://www.ansible.com/" target="_blank" rel="noopener noreferrer" class="">Ansible</a> to deploy all the stack : base linux config, <a href="https://bind9.net/" target="_blank" rel="noopener noreferrer" class="">Bind9</a>, <a href="https://firewalld.org/" target="_blank" rel="noopener noreferrer" class="">Firewalld</a>, <a href="https://www.fail2ban.org/" target="_blank" rel="noopener noreferrer" class="">Fail2Ban</a></p>
<div class="theme-admonition theme-admonition-tip admonition_IZjC alert alert--success"><div class="admonitionHeading_uVvU"><span class="admonitionIcon_HiR3"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>tip</div><div class="admonitionContent_bl22"><p>I have alreay talk about <a href="https://firewalld.org/" target="_blank" rel="noopener noreferrer" class="">Firewalld</a> and <a href="https://www.fail2ban.org/" target="_blank" rel="noopener noreferrer" class="">Fail2Ban</a> in another blog post : Securing web entrypoint from external threats</p></div></div>
<p>But for the purpose of this blog post I will detail equivalent <code>bash</code> commands that can be used.</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="bind9-installation-and-configuration">Bind9 installation and configuration<a href="https://lunik.tiwabbit.fr/blog/create-public-dns-service#bind9-installation-and-configuration" class="hash-link" aria-label="Direct link to Bind9 installation and configuration" title="Direct link to Bind9 installation and configuration" translate="no">​</a></h3>
<p>The installation of <a href="https://bind9.net/" target="_blank" rel="noopener noreferrer" class="">Bind9</a> is pretty strait forward. Since it's already packaged I only need to make a simple command to install it :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">root@tiwabbit-dns-01 ~</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token comment" style="color:rgb(98, 114, 164)"># dnf install bind bind-utils</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Last metadata expiration check: </span><span class="token number">0</span><span class="token plain">:02:41 ago on Fri 05 Nov </span><span class="token number">2021</span><span class="token plain"> 09:49:15 AM UTC.</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Dependencies resolved.</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> Package                                                       Architecture                            Version                                               Repository                                Size</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Installing:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">bind</span><span class="token plain">                                                          x86_64                                  </span><span class="token number">32</span><span class="token plain">:9.11.28-1.fc32                                     updates                                  </span><span class="token number">2.0</span><span class="token plain"> M</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> bind-utils                                                    x86_64                                  </span><span class="token number">32</span><span class="token plain">:9.11.28-1.fc32                                     updates                                  </span><span class="token number">233</span><span class="token plain"> k</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Installing dependencies:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> bind-dnssec-doc                                               noarch                                  </span><span class="token number">32</span><span class="token plain">:9.11.28-1.fc32                                     updates                                   </span><span class="token number">46</span><span class="token plain"> k</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> bind-libs                                                     x86_64                                  </span><span class="token number">32</span><span class="token plain">:9.11.28-1.fc32                                     updates                                   </span><span class="token number">90</span><span class="token plain"> k</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> bind-libs-lite                                                x86_64                                  </span><span class="token number">32</span><span class="token plain">:9.11.28-1.fc32                                     updates                                  </span><span class="token number">1.1</span><span class="token plain"> M</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> bind-license                                                  noarch                                  </span><span class="token number">32</span><span class="token plain">:9.11.28-1.fc32                                     updates                                   </span><span class="token number">16</span><span class="token plain"> k</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> fstrm                                                         x86_64                                  </span><span class="token number">0.5</span><span class="token plain">.0-2.fc32                                          fedora                                    </span><span class="token number">28</span><span class="token plain"> k</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> mariadb-connector-c                                           x86_64                                  </span><span class="token number">3.1</span><span class="token plain">.12-1.fc32                                         updates                                  </span><span class="token number">203</span><span class="token plain"> k</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> mariadb-connector-c-config                                    noarch                                  </span><span class="token number">3.1</span><span class="token plain">.12-1.fc32                                         updates                                   </span><span class="token number">11</span><span class="token plain"> k</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> policycoreutils-python-utils                                  noarch                                  </span><span class="token number">3.0</span><span class="token plain">-2.fc32                                            fedora                                    </span><span class="token number">83</span><span class="token plain"> k</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> protobuf-c                                                    x86_64                                  </span><span class="token number">1.3</span><span class="token plain">.2-2.fc32                                          fedora                                    </span><span class="token number">35</span><span class="token plain"> k</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> python3-bind                                                  noarch                                  </span><span class="token number">32</span><span class="token plain">:9.11.28-1.fc32                                     updates                                   </span><span class="token number">64</span><span class="token plain"> k</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Installing weak dependencies:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"> bind-dnssec-utils                                             x86_64                                  </span><span class="token number">32</span><span class="token plain">:9.11.28-1.fc32                                     updates                                  </span><span class="token number">128</span><span class="token plain"> k</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Transaction Summary</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token operator">==</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Install  </span><span class="token number">13</span><span class="token plain"> Packages</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Total download size: </span><span class="token number">4.0</span><span class="token plain"> M</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Installed size: </span><span class="token number">10</span><span class="token plain"> M</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Is this ok </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">y/N</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain">:</span><br></div></code></pre></div></div>
<p>Once it's installed, using the <a href="https://bind9.readthedocs.io/en/latest/reference.html" target="_blank" rel="noopener noreferrer" class="">Bind9 documentation</a> I have put the following configuration in <code>/etc/named.conf</code> :</p>
<div class="language-bind9 codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bind9 codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">acl "managment" {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    X.X.X.X/32;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">};</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">acl "public" {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    0.0.0.0/0;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    ::/0;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">};</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">options {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  dump-file          "/etc/named/data/cache_dump.db";</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  statistics-file    "/etc/named/data/named_stats.txt";</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  memstatistics-file "/etc/named/data/named_mem_stats.txt";</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  secroots-file      "/etc/named/data/named.secroots";</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  recursing-file     "/etc/named/data/named.recursing";</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  listen-on port 53 { any; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  listen-on-v6 port 53 { any; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  allow-transfer { none; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  max-cache-size 70%;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  allow-query-cache {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    127.0.0.1;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    localhost;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    managment;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    public;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  allow-query {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    127.0.0.1;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    localhost;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    managment;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    public;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  recursion yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  allow-recursion {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    127.0.0.1;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    localhost;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    managment;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    public;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  dnssec-enable yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  dnssec-validation yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  prefetch 4 10;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  rate-limit {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    ipv4-prefix-length 28;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    ipv6-prefix-length 56;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    responses-per-second 20;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    window 5;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    slip 3;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  managed-keys-directory "/var/named/dynamic";</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  geoip-directory        "/usr/share/GeoIP";</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  pid-file        "/run/named/named.pid";</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  session-keyfile "/run/named/session.key";</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  hostname "dns01.tiwabbit.fr";</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  server-id "dns01.tiwabbit.fr";</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  /* https://fedoraproject.org/wiki/Changes/CryptoPolicy */</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  include "/etc/crypto-policies/back-ends/bind.config";</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">};</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">statistics-channels {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  inet 127.0.0.1 port 8053 allow { 127.0.0.1; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">};</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">logging {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  channel client_file {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    file "/var/log/named/client.log" versions 3 size 5m;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    severity dynamic;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-time yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-category yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-severity yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  channel cname_file {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    file "/var/log/named/cname.log" versions 3 size 5m;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    severity dynamic;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-time yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-category yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-severity yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  channel config_file {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    file "/var/log/named/config.log" versions 3 size 5m;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    severity dynamic;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-time yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-category yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-severity yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  channel database_file {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    file "/var/log/named/database.log" versions 3 size 5m;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    severity dynamic;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-time yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-category yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-severity yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  channel default_file {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    file "/var/log/named/default.log" versions 3 size 5m;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    severity dynamic;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-time yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-category yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-severity yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  channel delegation-only_file {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    file "/var/log/named/delegation-only.log" versions 3 size 5m;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    severity dynamic;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-time yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-category yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-severity yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  channel dispatch_file {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    file "/var/log/named/dispatch.log" versions 3 size 5m;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    severity dynamic;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-time yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-category yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-severity yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  channel dnssec_file {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    file "/var/log/named/dnssec.log" versions 3 size 5m;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    severity dynamic;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-time yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-category yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-severity yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  channel dnstap_file {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    file "/var/log/named/dnstap.log" versions 3 size 5m;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    severity dynamic;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-time yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-category yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-severity yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  channel edns-disabled_file {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    file "/var/log/named/edns-disabled.log" versions 3 size 5m;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    severity dynamic;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-time yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-category yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-severity yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  channel general_file {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    file "/var/log/named/general.log" versions 3 size 5m;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    severity dynamic;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-time yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-category yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-severity yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  channel lame-servers_file {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    file "/var/log/named/lame-servers.log" versions 3 size 5m;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    severity dynamic;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-time yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-category yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-severity yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  channel network_file {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    file "/var/log/named/network.log" versions 3 size 5m;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    severity dynamic;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-time yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-category yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-severity yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  channel notify_file {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    file "/var/log/named/notify.log" versions 3 size 5m;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    severity dynamic;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-time yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-category yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-severity yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  channel queries_file {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    file "/var/log/named/queries.log" versions 3 size 5m;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    severity dynamic;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-time yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-category yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-severity yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  channel query-errors_file {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    file "/var/log/named/query-errors.log" versions 3 size 5m;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    severity dynamic;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-time yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-category yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-severity yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  channel rate-limit_file {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    file "/var/log/named/rate-limit.log" versions 3 size 5m;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    severity dynamic;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-time yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-category yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-severity yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  channel resolver_file {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    file "/var/log/named/resolver.log" versions 3 size 5m;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    severity dynamic;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-time yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-category yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-severity yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  channel rpz_file {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    file "/var/log/named/rpz.log" versions 3 size 5m;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    severity dynamic;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-time yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-category yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-severity yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  channel security_file {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    file "/var/log/named/security.log" versions 3 size 5m;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    severity dynamic;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-time yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-category yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-severity yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  channel spill_file {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    file "/var/log/named/spill.log" versions 3 size 5m;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    severity dynamic;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-time yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-category yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-severity yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  channel trust-anchor-telemetry_file {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    file "/var/log/named/trust-anchor-telemetry.log" versions 3 size 5m;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    severity dynamic;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-time yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-category yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-severity yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  channel unmatched_file {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    file "/var/log/named/unmatched.log" versions 3 size 5m;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    severity dynamic;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-time yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-category yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-severity yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  channel update_file {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    file "/var/log/named/update.log" versions 3 size 5m;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    severity dynamic;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-time yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-category yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-severity yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  channel update-security_file {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    file "/var/log/named/update-security.log" versions 3 size 5m;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    severity dynamic;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-time yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-category yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-severity yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  channel xfer-in_file {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    file "/var/log/named/xfer-in.log" versions 3 size 5m;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    severity dynamic;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-time yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-category yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-severity yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  channel xfer-out_file {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    file "/var/log/named/xfer-out.log" versions 3 size 5m;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    severity dynamic;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-time yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-category yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    print-severity yes;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  category client { client_file; default_debug; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  category cname { cname_file; default_debug; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  category config { config_file; default_debug; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  category database { database_file; default_debug; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  category default { default_file; default_debug; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  category delegation-only { delegation-only_file; default_debug; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  category dispatch { dispatch_file; default_debug; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  category dnssec { dnssec_file; default_debug; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  category dnstap { dnstap_file; default_debug; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  category edns-disabled { edns-disabled_file; default_debug; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  category general { general_file; default_debug; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  category lame-servers { lame-servers_file; default_debug; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  category network { network_file; default_debug; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  category notify { notify_file; default_debug; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  category queries { queries_file; default_debug; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  category query-errors { query-errors_file; default_debug; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  category rate-limit { rate-limit_file; default_debug; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  category resolver { resolver_file; default_debug; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  category rpz { rpz_file; default_debug; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  category security { security_file; default_debug; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  category spill { spill_file; default_debug; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  category trust-anchor-telemetry { trust-anchor-telemetry_file; default_debug; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  category unmatched { unmatched_file; default_debug; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  category update { update_file; default_debug; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  category update-security { update-security_file; default_debug; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  category xfer-in { xfer-in_file; default_debug; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  category xfer-out { xfer-out_file; default_debug; };</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">};</span><br></div></code></pre></div></div>
<p>Wow that's a lot !</p>
<p>The important line to configure are :</p>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="access-control-lists">Access-control lists<a href="https://lunik.tiwabbit.fr/blog/create-public-dns-service#access-control-lists" class="hash-link" aria-label="Direct link to Access-control lists" title="Direct link to Access-control lists" translate="no">​</a></h4>
<p><a href="https://en.wikipedia.org/wiki/Access-control_list" target="_blank" rel="noopener noreferrer" class="">ACL</a>s allow you to choose the behavior of the service depending on the client <a href="https://en.wikipedia.org/wiki/IP_address" target="_blank" rel="noopener noreferrer" class="">IP address</a>.</p>
<p>Let's take a look at my configuration :</p>
<div class="language-bind9 codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bind9 codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">acl "managment" {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    X.X.X.X/32;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">};</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">acl "public" {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    0.0.0.0/0;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    ::/0;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">};</span><br></div></code></pre></div></div>
<p>Here I'm creating two <a href="https://en.wikipedia.org/wiki/Access-control_list" target="_blank" rel="noopener noreferrer" class="">ACL</a> groups : <code>public</code> and <code>managment</code>. In each of these <code>acl</code> block I can put as many <a href="https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing" target="_blank" rel="noopener noreferrer" class="">CIDR</a>s as I want.
<code>public</code> contains the <a href="https://en.wikipedia.org/wiki/IPv4" target="_blank" rel="noopener noreferrer" class="">IPv4</a> and <a href="https://en.wikipedia.org/wiki/IPv6" target="_blank" rel="noopener noreferrer" class="">IPv6</a> global <a href="https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing" target="_blank" rel="noopener noreferrer" class="">CIDR</a>. <code>managment</code> contains a unique <a href="https://en.wikipedia.org/wiki/IP_address" target="_blank" rel="noopener noreferrer" class="">IP</a> <a href="https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing" target="_blank" rel="noopener noreferrer" class="">CIDR</a> (the one used to configure the service).</p>
<p>The you can reuse those <a href="https://en.wikipedia.org/wiki/Access-control_list" target="_blank" rel="noopener noreferrer" class="">ACL</a>s groups in other part of the configuration like in <code>allow-query</code>, <code>allow-query-cache</code>, <code>allow-recursion</code>.</p>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="query-behavior">Query behavior<a href="https://lunik.tiwabbit.fr/blog/create-public-dns-service#query-behavior" class="hash-link" aria-label="Direct link to Query behavior" title="Direct link to Query behavior" translate="no">​</a></h4>
<p>I choose to implement only three query behaviors to keep it simple :</p>
<ul>
<li class=""><code>allow-query</code> define who is allow to make query to my <a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">DNS</a> service. If the client <a href="https://en.wikipedia.org/wiki/IP_address" target="_blank" rel="noopener noreferrer" class="">IP</a> is not in the list, the server will not respond.</li>
<li class=""><code>allow-query-cache</code> define from witch client the server should cache the responses. This allow the query to be resolved quicker the next time.</li>
<li class=""><code>allow-recursion</code> define who can make <a href="https://www.cloudflare.com/learning/dns/what-is-recursive-dns" target="_blank" rel="noopener noreferrer" class="">recursive queries</a>. <a href="https://www.cloudflare.com/learning/dns/what-is-recursive-dns" target="_blank" rel="noopener noreferrer" class="">Query recursion</a> is a <a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">DNS</a> mechanism that find the <a href="https://en.wikipedia.org/wiki/IP_address" target="_blank" rel="noopener noreferrer" class="">IP</a> associated with a <a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">DNS</a> entry by making all the necessary requests one by one from the root servers. This allow to be independent when resolving queries (you don't need to forward the request to another public <a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">DNS</a> server like <code>8.8.8.8</code> or <code>1.1.1.1</code>)</li>
</ul>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="prefetching">Prefetching<a href="https://lunik.tiwabbit.fr/blog/create-public-dns-service#prefetching" class="hash-link" aria-label="Direct link to Prefetching" title="Direct link to Prefetching" translate="no">​</a></h4>
<p>The name says it all. This configuration allow the server to make <a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">DNS</a> request on is own in anticipation of other requests. This allow to be quicker to response most of the time if a <a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">DNS</a> entry is requested very often.</p>
<p>Each <a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">DNS</a> entries have a <a href="https://en.wikipedia.org/wiki/Time_to_live" target="_blank" rel="noopener noreferrer" class="">Time To Live (or TTL)</a>. You can get it using <code>dig</code> :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">dig</span><span class="token plain"> lunik.tiwabbit.fr</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token operator">&lt;&lt;</span><span class="token operator">&gt;&gt;</span><span class="token plain"> DiG </span><span class="token number">9.11</span><span class="token plain">.28-RedHat-9.11.28-1.fc32 </span><span class="token operator">&lt;&lt;</span><span class="token operator">&gt;&gt;</span><span class="token plain"> lunik.tiwabbit.fr</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> global options: +cmd</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> Got answer:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> -</span><span class="token operator">&gt;&gt;</span><span class="token plain">HEADER</span><span class="token operator">&lt;&lt;-</span><span class="token plain"> opcode: QUERY, status: NOERROR, id: </span><span class="token number">41640</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> flags: qr rd ra</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> QUERY: </span><span class="token number">1</span><span class="token plain">, ANSWER: </span><span class="token number">4</span><span class="token plain">, AUTHORITY: </span><span class="token number">0</span><span class="token plain">, ADDITIONAL: </span><span class="token number">1</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> OPT PSEUDOSECTION:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> EDNS: version: </span><span class="token number">0</span><span class="token plain">, flags:</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> udp: </span><span class="token number">1232</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> QUESTION SECTION:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain">lunik.tiwabbit.fr.   IN  A</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> ANSWER SECTION:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">lunik.tiwabbit.fr.  </span><span class="token number">66</span><span class="token plain">  IN  A </span><span class="token number">52.222</span><span class="token plain">.158.86</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">lunik.tiwabbit.fr.  </span><span class="token number">66</span><span class="token plain">  IN  A </span><span class="token number">52.222</span><span class="token plain">.158.38</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">lunik.tiwabbit.fr.  </span><span class="token number">66</span><span class="token plain">  IN  A </span><span class="token number">52.222</span><span class="token plain">.158.55</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">lunik.tiwabbit.fr.  </span><span class="token number">66</span><span class="token plain">  IN  A </span><span class="token number">52.222</span><span class="token plain">.158.103</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> Query time: </span><span class="token number">0</span><span class="token plain"> msec</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> SERVER: </span><span class="token number">10.194</span><span class="token plain">.3.3</span><span class="token comment" style="color:rgb(98, 114, 164)">#53(10.194.3.3)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> WHEN: Fri Nov 05 </span><span class="token number">10</span><span class="token plain">:13:42 UTC </span><span class="token number">2021</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> MSG SIZE  rcvd: </span><span class="token number">110</span><br></div></code></pre></div></div>
<p>In the <code>ANSWER SECTION</code> :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">lunik.tiwabbit.fr.  </span><span class="token number">66</span><span class="token plain">  IN  A </span><span class="token number">52.222</span><span class="token plain">.158.86</span><br></div></code></pre></div></div>
<p><code>66</code> is the <a href="https://en.wikipedia.org/wiki/Time_to_live" target="_blank" rel="noopener noreferrer" class="">TTL</a> of this <a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">DNS</a> entry. This means that in <code>66</code> seconds it is not valid anymore and the client need to make another request to get the new <a href="https://en.wikipedia.org/wiki/IP_address" target="_blank" rel="noopener noreferrer" class="">IP</a> (most of the time, it doesn't change).</p>
<p>I my configuration I have :</p>
<div class="language-bind9 codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bind9 codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">prefetch 4 10;</span><br></div></code></pre></div></div>
<p>If the server have cached a <a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">DNS</a> entry with <code>4</code> or less <a href="https://en.wikipedia.org/wiki/Time_to_live" target="_blank" rel="noopener noreferrer" class="">TTL</a> remaining then it will make a <a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">DNS</a> query to refresh the cached one. <code>10</code> is a optional parameter which define the eligibility of the record for prefetching.</p>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="rate-limiting">Rate limiting<a href="https://lunik.tiwabbit.fr/blog/create-public-dns-service#rate-limiting" class="hash-link" aria-label="Direct link to Rate limiting" title="Direct link to Rate limiting" translate="no">​</a></h4>
<p>This is maybe one of the most important parameter of a public <a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">DNS</a> server. The goal of this configuration is to limit the number of response the server make if a client request to many time the same <a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">DNS</a> entry. It is very useful to mitigate <a href="https://www.cloudflare.com/learning/ddos/dns-amplification-ddos-attack" target="_blank" rel="noopener noreferrer" class="">DNS amplification attacks</a>.</p>
<p>Here is the configuration I have :</p>
<div class="language-bind9 codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bind9 codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">rate-limit {</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  ipv4-prefix-length 28;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  ipv6-prefix-length 56;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  responses-per-second 20;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  window 5;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  slip 3;</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">};</span><br></div></code></pre></div></div>
<p>If a client request more than <code>20</code> time the same <a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">DNS</a> entry in a period of <code>5</code> seconds, the server drop <code>3</code> responses before responding (indefinitely).
To prevent attack from large subnets the rate limite extends to a <code>/28</code> <a href="https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#IPv4_CIDR_blocks" target="_blank" rel="noopener noreferrer" class="">IPv4 subnet</a> and a <code>/56</code> <a href="https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#IPv6_CIDR_blocks" target="_blank" rel="noopener noreferrer" class="">IPv6 subnet</a>.</p>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="logging">Logging<a href="https://lunik.tiwabbit.fr/blog/create-public-dns-service#logging" class="hash-link" aria-label="Direct link to Logging" title="Direct link to Logging" translate="no">​</a></h4>
<p>I made the choice to be very verbose with the logs that's why I have configured all possible channels.</p>
<p>The more interesting are</p>
<ul>
<li class=""><code>queries</code> which log every query resolved by the server :</li>
</ul>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"># /var/log/named/queries.log</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">05-Nov-2021 </span><span class="token number">10</span><span class="token plain">:21:43.536 queries: info: client @0x7f76073dfe50 X.X.X.X</span><span class="token comment" style="color:rgb(98, 114, 164)">#14821 (mms-iad.sp-prod.net): query: mms-iad.sp-prod.net IN A +E(0)DV (10.70.2.235)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">05-Nov-2021 </span><span class="token number">10</span><span class="token plain">:21:43.545 queries: info: client @0x7f760793d450 X.X.X.X</span><span class="token comment" style="color:rgb(98, 114, 164)">#47583 (psja.isd.us): query: psja.isd.us IN MX +E(0)DV (10.70.2.235)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">05-Nov-2021 </span><span class="token number">10</span><span class="token plain">:21:43.547 queries: info: client @0x7f760759b400 X.X.X.X</span><span class="token comment" style="color:rgb(98, 114, 164)">#50020 (global.reputation.invincea.com): query: global.reputation.invincea.com IN A +E(0)DV (10.70.2.235)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">05-Nov-2021 </span><span class="token number">10</span><span class="token plain">:21:43.556 queries: info: client @0x7f76075dddc0 X.X.X.X</span><span class="token comment" style="color:rgb(98, 114, 164)">#38383 (forconzoomnyc233mmr.zoom.us): query: forconzoomnyc233mmr.zoom.us IN A + (10.70.2.235)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">05-Nov-2021 </span><span class="token number">10</span><span class="token plain">:21:43.566 queries: info: client @0x7f76077ba820 X.X.X.X</span><span class="token comment" style="color:rgb(98, 114, 164)">#39161 (ps-membership.us-ctkip-ps3.dell.com): query: ps-membership.us-ctkip-ps3.dell.com IN A + (10.70.2.235)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">05-Nov-2021 </span><span class="token number">10</span><span class="token plain">:21:43.618 queries: info: client @0x7f7607a9f2c0 X.X.X.X</span><span class="token comment" style="color:rgb(98, 114, 164)">#39161 (ps-membership.usgit.dell.com): query: ps-membership.usgit.dell.com IN A + (10.70.2.235)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">05-Nov-2021 </span><span class="token number">10</span><span class="token plain">:21:43.622 queries: info: client @0x7f76077fcbc0 X.X.X.X</span><span class="token comment" style="color:rgb(98, 114, 164)">#56424 (icn.intl-global-adns.alibabacloud.com): query: icn.intl-global-adns.alibabacloud.com IN A + (10.70.2.235)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">05-Nov-2021 </span><span class="token number">10</span><span class="token plain">:21:43.623 queries: info: client @0x7f760765f400 X.X.X.X</span><span class="token comment" style="color:rgb(98, 114, 164)">#38383 (forcon-zoomca193-123-14-158mmr.zoom.us): query: forcon-zoomca193-123-14-158mmr.zoom.us IN A + (10.70.2.235)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">05-Nov-2021 </span><span class="token number">10</span><span class="token plain">:21:43.635 queries: info: client @0x7f75f0ce5910 X.X.X.X</span><span class="token comment" style="color:rgb(98, 114, 164)">#56548 (sfc-idzwww.riotgames.roblox.com.ru): query: sfc-idzwww.riotgames.roblox.com.ru IN A + (10.70.2.235)</span><br></div></code></pre></div></div>
<ul>
<li class=""><code>rate-limit</code> which log every events regarding rate limiting behavior :</li>
</ul>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"># /var/log/named/rate-limit.log</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">05-Nov-2021 </span><span class="token number">10</span><span class="token plain">:21:39.631 rate-limit: info: client @0x7f76077cdd80 X.X.X.X</span><span class="token comment" style="color:rgb(98, 114, 164)">#38383 (zoomff134-224-74-182mmrforcon.zoom.us): rate limit drop NXDOMAIN response to X.X.X.X/28 for zoom.us  (4ef96ce9)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">05-Nov-2021 </span><span class="token number">10</span><span class="token plain">:21:39.694 rate-limit: info: client @0x7f75f0cf3f10 X.X.X.X</span><span class="token comment" style="color:rgb(98, 114, 164)">#38383 (bisdtx-orgforcon.zoom.us): rate limit drop NXDOMAIN response to X.X.X.X/28 for zoom.us  (4ef96ce9)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">05-Nov-2021 </span><span class="token number">10</span><span class="token plain">:21:39.752 rate-limit: info: client @0x7f76077fcbc0 X.X.X.X</span><span class="token comment" style="color:rgb(98, 114, 164)">#38383 (kirklandwa-forcon.zoom.us): rate limit slip NXDOMAIN response to X.X.X.X/28 for zoom.us  (4ef96ce9)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">05-Nov-2021 </span><span class="token number">10</span><span class="token plain">:21:39.863 rate-limit: info: client @0x7f76077eab00 X.X.X.X</span><span class="token comment" style="color:rgb(98, 114, 164)">#38383 (friendsnrcforcon.zoom.us): rate limit drop NXDOMAIN response to X.X.X.X/28 for zoom.us  (4ef96ce9)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">05-Nov-2021 </span><span class="token number">10</span><span class="token plain">:21:39.942 rate-limit: info: client @0x7f76078e8610 X.X.X.X</span><span class="token comment" style="color:rgb(98, 114, 164)">#38383 (deltatrust-org-uk-forcon.zoom.us): rate limit drop NXDOMAIN response to X.X.X.X/28 for zoom.us  (4ef96ce9)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">05-Nov-2021 </span><span class="token number">10</span><span class="token plain">:21:40.857 rate-limit: info: client @0x7f7607a13120 X.X.X.X</span><span class="token comment" style="color:rgb(98, 114, 164)">#38383 (12mmrforcon.zoom.us): rate limit slip NXDOMAIN response to X.X.X.X/28 for zoom.us  (4ef96ce9)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">05-Nov-2021 </span><span class="token number">10</span><span class="token plain">:21:40.871 rate-limit: info: client @0x7f76076509a0 X.X.X.X</span><span class="token comment" style="color:rgb(98, 114, 164)">#38383 (datamarkgisforcon.zoom.us): rate limit drop NXDOMAIN response to X.X.X.X/28 for zoom.us  (4ef96ce9)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">05-Nov-2021 </span><span class="token number">10</span><span class="token plain">:21:40.891 rate-limit: info: client @0x7f7607866d60 X.X.X.X</span><span class="token comment" style="color:rgb(98, 114, 164)">#38383 (wvsd208-forcon.zoom.us): rate limit drop NXDOMAIN response to X.X.X.X/28 for zoom.us  (4ef96ce9)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">05-Nov-2021 </span><span class="token number">10</span><span class="token plain">:21:40.907 rate-limit: info: client @0x7f7607146a70 X.X.X.X</span><span class="token comment" style="color:rgb(98, 114, 164)">#38383 (zoomva198-251-217-184mmrforcon.zoom.us): rate limit slip NXDOMAIN response to X.X.X.X/28 for zoom.us  (4ef96ce9)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">05-Nov-2021 </span><span class="token number">10</span><span class="token plain">:21:40.968 rate-limit: info: client @0x7f760713b020 X.X.X.X</span><span class="token comment" style="color:rgb(98, 114, 164)">#38383 (zoomdvs185mmr-forcon.zoom.us): rate limit drop NXDOMAIN response to X.X.X.X/28 for zoom.us  (4ef96ce9)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">05-Nov-2021 </span><span class="token number">10</span><span class="token plain">:21:43.761 rate-limit: info: *stop limiting NXDOMAIN responses to X.X.X.X/28 </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">for</span><span class="token plain"> buffer.com  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">bc75c42e</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">05-Nov-2021 </span><span class="token number">10</span><span class="token plain">:21:43.761 rate-limit: info: *stop limiting error responses to X.X.X.X/28</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">05-Nov-2021 </span><span class="token number">10</span><span class="token plain">:21:43.761 rate-limit: info: *stop limiting NXDOMAIN responses to X.X.X.X/28 </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">for</span><span class="token plain"> restintergamma.nl  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">97dd2767</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">05-Nov-2021 </span><span class="token number">10</span><span class="token plain">:21:43.761 rate-limit: info: *stop limiting NXDOMAIN responses to X.X.X.X/28 </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">for</span><span class="token plain"> alibabacloud.com  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">d22fa033</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">05-Nov-2021 </span><span class="token number">10</span><span class="token plain">:21:43.761 rate-limit: info: *stop limiting NXDOMAIN responses to X.X.X.X/28 </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">for</span><span class="token plain"> dell.com  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">98e605b5</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">05-Nov-2021 </span><span class="token number">10</span><span class="token plain">:21:43.761 rate-limit: info: *stop limiting error responses to X.X.X.X/28</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">05-Nov-2021 </span><span class="token number">10</span><span class="token plain">:21:43.761 rate-limit: info: *stop limiting NXDOMAIN responses to X.X.X.X/28 </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">for</span><span class="token plain"> zoom.us  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">4ef96ce9</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="monitoring">Monitoring<a href="https://lunik.tiwabbit.fr/blog/create-public-dns-service#monitoring" class="hash-link" aria-label="Direct link to Monitoring" title="Direct link to Monitoring" translate="no">​</a></h2>
<p>I choose to use <a href="https://www.datadoghq.com/" target="_blank" rel="noopener noreferrer" class="">Datadog</a> to monitor the <a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">DNS</a> servers.</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="datadog-agent-installation">Datadog agent installation<a href="https://lunik.tiwabbit.fr/blog/create-public-dns-service#datadog-agent-installation" class="hash-link" aria-label="Direct link to Datadog agent installation" title="Direct link to Datadog agent installation" translate="no">​</a></h3>
<p>The installation is pretty simple with <a href="https://www.ansible.com/" target="_blank" rel="noopener noreferrer" class="">Ansible</a> since they provide a <a href="https://galaxy.ansible.com/datadog/datadog" target="_blank" rel="noopener noreferrer" class="">galaxy role</a>.
I used it with the following configuration :</p>
<div class="language-yaml codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-yaml codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">---</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">datadog_api_key</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"{{ vault_datadog_api_key }}"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">datadog_site</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"datadoghq.eu"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">datadog_agent_flavor</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"datadog-agent"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">datadog_agent_major_version</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">7</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">datadog_enabled</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> yes</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">datadog_bind9_integration_version</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> 1.0.0</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">datadog_config</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">logs_enabled</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token boolean important">true</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">datadog_checks</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">bind9</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">init_config</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">instances</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> bind9</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token key atrule">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"http://127.0.0.1:{{ bind_statistics_port }}"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">logs</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> file</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token key atrule">path</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> /var/log/named/queries.log</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token key atrule">service</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> named</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token key atrule">source</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> bind9</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token key atrule">sourcecategory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> queries</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> file</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token key atrule">path</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> /var/log/named/rate</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">limit.log</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token key atrule">service</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> named</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token key atrule">source</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> bind9</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token key atrule">sourcecategory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> rate</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">limit</span><br></div></code></pre></div></div>
<p>I'm activating the <a href="https://bind9.net/" target="_blank" rel="noopener noreferrer" class="">Bind9</a> and logs integrations.</p>
<p>Once deployed, I can see my agents through the <a href="https://www.datadoghq.com/" target="_blank" rel="noopener noreferrer" class="">Datadog</a> web console in the <code>Infrastructure</code> panel :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2021-12-24-create-public-dns-service/datadog-host-map.png" alt="datadog-host-map" class="img__Ss2"></p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="datadog-metrics-and-logs">Datadog metrics and logs<a href="https://lunik.tiwabbit.fr/blog/create-public-dns-service#datadog-metrics-and-logs" class="hash-link" aria-label="Direct link to Datadog metrics and logs" title="Direct link to Datadog metrics and logs" translate="no">​</a></h3>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="metrics">Metrics<a href="https://lunik.tiwabbit.fr/blog/create-public-dns-service#metrics" class="hash-link" aria-label="Direct link to Metrics" title="Direct link to Metrics" translate="no">​</a></h4>
<p>Since I have activated the <a href="https://docs.datadoghq.com/integrations/bind9/" target="_blank" rel="noopener noreferrer" class="">Bind9 integration</a> in the agent configuration, I need to do the same in the web console. In the <code>Integrations</code> panel, search for <code>Bind9</code> and follow the configuration guide.</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2021-12-24-create-public-dns-service/datadog-bind9-integration-01.png" alt="datadog-bind-integration" class="img__Ss2"></p>
<p>Now I can see metrics showing up.</p>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="logs">Logs<a href="https://lunik.tiwabbit.fr/blog/create-public-dns-service#logs" class="hash-link" aria-label="Direct link to Logs" title="Direct link to Logs" translate="no">​</a></h4>
<p>The logs are already showing up in the log explorer in the <code>Logs</code> panel :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2021-12-24-create-public-dns-service/datadog-log-explorer.png" alt="datadog-log-explorer" class="img__Ss2"></p>
<p>But <a href="https://www.datadoghq.com/" target="_blank" rel="noopener noreferrer" class="">Datadog</a> doesn't know how to understand those logs. Now he only sees a large string of characters.
If I want analyse those logs, <a href="https://www.datadoghq.com/" target="_blank" rel="noopener noreferrer" class="">Datadog</a> needs to parse them for me.</p>
<p>In <a href="https://www.datadoghq.com/" target="_blank" rel="noopener noreferrer" class="">Datadog</a> builtin logs pipeline in the <code>Logs</code> panel, I can define a list of pattern and action to parse the log file produced by the <a href="https://bind9.net/" target="_blank" rel="noopener noreferrer" class="">Bind9</a> service. On some more popular software <a href="https://www.datadoghq.com/" target="_blank" rel="noopener noreferrer" class="">Datadog</a> has already made the work for you but it seems <a href="https://bind9.net/" target="_blank" rel="noopener noreferrer" class="">Bind9</a> is an exception.</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2021-12-24-create-public-dns-service/datadog-log-pipeline-library.png" alt="datadog-pipeline-library" class="img__Ss2"></p>
<h5 class="anchor anchorTargetStickyNavbar_SAay" id="defining-logs-parsing-pipelines">Defining logs parsing pipelines<a href="https://lunik.tiwabbit.fr/blog/create-public-dns-service#defining-logs-parsing-pipelines" class="hash-link" aria-label="Direct link to Defining logs parsing pipelines" title="Direct link to Defining logs parsing pipelines" translate="no">​</a></h5>
<p>First I created a new pipeline and use predefined filters : <code>source:bind9</code> and <code>sourcecategory:queries</code>.
Those two filters are deducted from the <a href="https://www.datadoghq.com/" target="_blank" rel="noopener noreferrer" class="">Datadog</a> agent configuration from earlier :</p>
<div class="language-yaml codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-yaml codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token key atrule">datadog_checks</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">bind9</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">...</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">logs</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">...</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> file</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token key atrule">path</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> /var/log/named/queries.log</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token key atrule">service</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> named</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token key atrule">source</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> bind9</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token key atrule">sourcecategory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> queries</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">...</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><br></div></code></pre></div></div>
<p>Now that I'm only going to parse logs from the right source, I need a <a href="https://docs.datadoghq.com/logs/log_configuration/parsing" target="_blank" rel="noopener noreferrer" class="">Grok parser</a> will parse the log string.
<a href="https://docs.datadoghq.com/logs/log_configuration/parsing" target="_blank" rel="noopener noreferrer" class="">Grok parser</a> define blocks in the string and put them into variables. When the line of log is parsed, it return a beautifully formatted <a href="https://tools.ietf.org/html/rfc8259" target="_blank" rel="noopener noreferrer" class="">JSON</a> object.</p>
<p>Blocks are defined by "simplified" regex : <code>number</code>, <code>word</code>, <code>date</code>, <code>ip</code>, ...</p>
<p><a href="https://www.datadoghq.com/" target="_blank" rel="noopener noreferrer" class="">Datadog</a> allow me to do it really simply by have a "live" parsing view were you can see in real time what part of the log is parsed by which bloc.</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2021-12-24-create-public-dns-service/datadog-grok-parser.png" alt="datadog-grok-parser" class="img__Ss2"></p>
<p>So here is the final <a href="https://docs.datadoghq.com/logs/log_configuration/parsing" target="_blank" rel="noopener noreferrer" class="">Grok parsing</a> rule for <code>queries</code> logs :</p>
<div class="language-grok codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-grok codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">default %{number}-%{word}-%{number}\s+%{date("HH:mm:ss.SSS"):timestamp}\s+queries:\s+%{word:status}:\s+client\s+@%{word:client.data}\s+%{ip:client.ip}\#%{number:client.port}\s+\(%{hostname:query.hostname}\):\s+query:\s+%{hostname:query.fqdn}\s+%{word:query.location}\s+%{word:query.qcode}\s+.*\s+\(%{ip:server.ip}\).*</span><br></div></code></pre></div></div>
<p>With this example :</p>
<div class="language-bind9 codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bind9 codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">27-Oct-2021 16:53:43.594 queries: info: client @0x7fad52ee1fd0 127.0.0.1#64318 (google.fr): query: google.fr IN A +E(0) (10.69.86.243)</span><br></div></code></pre></div></div>
<p>I get :</p>
<div class="language-json codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-json codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"status"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"info"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"query"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"qcode"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"A"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"location"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"IN"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"hostname"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"google.fr"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"fqdn"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"google.fr"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"client"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"ip"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"127.0.0.1"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"data"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"0x7fad52ee1fd0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"port"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">64318</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"timestamp"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">1636131223594</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"server"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"ip"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"10.69.86.243"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Pretty neat !</p>
<p><a href="https://www.datadoghq.com/" target="_blank" rel="noopener noreferrer" class="">Datadog</a> allow to enhance that new <a href="https://tools.ietf.org/html/rfc8259" target="_blank" rel="noopener noreferrer" class="">JSON</a> object with extra metadata. I my case, since I have the client <a href="https://en.wikipedia.org/wiki/IP_address" target="_blank" rel="noopener noreferrer" class="">IP addresse</a>, I can use the GeoIP parser to find metadata about that <a href="https://en.wikipedia.org/wiki/IP_address" target="_blank" rel="noopener noreferrer" class="">IP</a>. Now I can determine the ISP, the country and even the city of that client.</p>
<p>Here is an example :</p>
<div class="language-bind9 codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bind9 codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">05-Nov-2021 14:07:16.442 queries: info: client @0x7f6b462b2900 X.X.X.X#38383 (zoom.us): query: zoom.us IN A + (10.70.2.235)</span><br></div></code></pre></div></div>
<div class="language-json codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-json codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"client"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">  </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"geoip"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"as"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">  </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"domain"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"online.net"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"ONLINE S.A.S."</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"number"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"AS12876"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"route"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"51.15.0.0/16"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"isp"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"city"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Paris"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"continent"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"code"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"EU"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Europe"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"country"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"iso_code"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"FR"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"France"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"ipAddress"</span><span class="token plain"> </span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"X.X.X.X"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"location"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"latitude"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"48.85341"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token string" style="color:rgb(255, 121, 198)">"longitude"</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2.3488"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"subdivision"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Île-de-France"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"timezone"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Europe/Paris"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="dashboard">Dashboard<a href="https://lunik.tiwabbit.fr/blog/create-public-dns-service#dashboard" class="hash-link" aria-label="Direct link to Dashboard" title="Direct link to Dashboard" translate="no">​</a></h3>
<p>I can now begin the fun part of using a monitoring service : Make Dashboard and Graphs !</p>
<p>Here are some that I have created :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2021-12-24-create-public-dns-service/datadog-dashboard-01.png" alt="datadog-dashboard-overview" class="img__Ss2">
<img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2021-12-24-create-public-dns-service/datadog-dashboard-02.png" alt="datadog-dashboard-queries-by-code" class="img__Ss2">
<img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2021-12-24-create-public-dns-service/datadog-dashboard-03.png" alt="datadog-dashboard-client-by-location" class="img__Ss2"></p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="conclusions">Conclusions<a href="https://lunik.tiwabbit.fr/blog/create-public-dns-service#conclusions" class="hash-link" aria-label="Direct link to Conclusions" title="Direct link to Conclusions" translate="no">​</a></h2>
<p>Now I have a fully operational public <a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">DNS</a> server. I can now configure enchanced bahaviour if I want, like blocking some domains names (malware, illegal stuff, ...).</p>
<p>Note : If you want to make your public <a href="https://tools.ietf.org/html/rfc2929" target="_blank" rel="noopener noreferrer" class="">DNS</a> available for anyone, you can publish it on <a href="https://public-dns.info/" target="_blank" rel="noopener noreferrer" class="">public-dns.info</a></p>]]></content>
        <author>
            <name>Guillaume MARTINEZ</name>
            <uri>https://github.com/Lunik</uri>
        </author>
        <category label="dns" term="dns"/>
        <category label="server" term="server"/>
        <category label="public" term="public"/>
        <category label="bind9" term="bind9"/>
        <category label="bind" term="bind"/>
        <category label="named" term="named"/>
        <category label="cloud" term="cloud"/>
        <category label="scaleway" term="scaleway"/>
        <category label="datadog" term="datadog"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Why pylint rule `W1203` is important]]></title>
        <id>https://lunik.tiwabbit.fr/blog/python-logging-fstring-interpolation</id>
        <link href="https://lunik.tiwabbit.fr/blog/python-logging-fstring-interpolation"/>
        <updated>2021-10-13T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[`W1203: logging-fstring-interpolation` is a pylint rule that is a warning about potential performance issues]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2021-10-13-python-logging-fstring-interpolation/cover.jpg" alt="cover" class="img__Ss2"></p>
<p>If you have been using <a href="https://pylint.pycqa.org/" target="_blank" rel="noopener noreferrer" class="">pylint</a> tool to check the format of your Python code you may have encounterd the rule <code>W1203: logging-fstring-interpolation</code> but you didn't really understand why it's important.</p>
<p>The warning looks like :</p>
<div class="language-bash codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-bash codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">path/to/my/file.py:37:4: W1203: Use lazy % formatting </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">in</span><span class="token plain"> logging functions </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">logging-fstring-interpolation</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></div></code></pre></div></div>
<p>It appears when you are using the <a href="https://docs.python.org/3/library/logging.html" target="_blank" rel="noopener noreferrer" class="">Python <code>logging</code> library</a>. On the <a href="https://pylint.pycqa.org/" target="_blank" rel="noopener noreferrer" class="">pylint</a> documentation about this rule it is said that you shouldn't use <code>f-string</code> formated strings.</p>
<p>The exemple given is :</p>
<p><strong>Problematic code:</strong></p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> logging</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> sys</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">logging</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">error</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f'Python version: </span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">sys</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token string-interpolation interpolation">version</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">  </span><span class="token comment" style="color:rgb(98, 114, 164)"># [logging-fstring-interpolation]</span><br></div></code></pre></div></div>
<p><strong>Correct code:</strong></p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> logging</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> sys</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">logging</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">error</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'Python version: %s'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> sys</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">version</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></div></code></pre></div></div>
<p><strong>But why using <code>f-string</code> string in the logging context is a bad idea an can a huge impact on the performance of your application ??</strong></p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="the-logging-library">The logging library<a href="https://lunik.tiwabbit.fr/blog/python-logging-fstring-interpolation#the-logging-library" class="hash-link" aria-label="Direct link to The logging library" title="Direct link to The logging library" translate="no">​</a></h2>
<p>The <a href="https://docs.python.org/3/library/logging.html" target="_blank" rel="noopener noreferrer" class="">Python <code>logging</code> library</a> is the standard library to implement logging in an application or a script. It replace the traditional <code>print</code> statement.
It allow to have an unified log output format and select multiple level of verbosity on the fly.</p>
<p>Using the <a href="https://docs.python.org/3/library/logging.html" target="_blank" rel="noopener noreferrer" class=""><code>logging</code> library</a> you display logs (on the <code>stdout</code> or in a file) with multiple levels like : <code>info</code>, <code>warning</code>, <code>error</code>, <code>debug</code>, <a href="https://docs.python.org/3/library/logging.html#levels" target="_blank" rel="noopener noreferrer" class="">see more</a>.
You can then set the logging level of the logger to one of them.</p>
<p>For example if you decided that you want only usefull logs when running in production, you will set the logging level to <code>error</code>. Only the logs with level <code>error</code> or <code>critical</code> will be printed. But if you are in development environment, you may want to be in the <code>debug</code> logging level to show all possible logs.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="what-is-the-problem-then-">What is the problem then ?<a href="https://lunik.tiwabbit.fr/blog/python-logging-fstring-interpolation#what-is-the-problem-then-" class="hash-link" aria-label="Direct link to What is the problem then ?" title="Direct link to What is the problem then ?" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="full-example">Full example<a href="https://lunik.tiwabbit.fr/blog/python-logging-fstring-interpolation#full-example" class="hash-link" aria-label="Direct link to Full example" title="Direct link to Full example" translate="no">​</a></h3>
<p>In a production environment, if you have a logs of type <code>debug</code> that call other functions, thoses will interpolated even if it will never be printed.</p>
<p>For exemple you have a simple code :</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> logging</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> sys</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">from</span><span class="token plain"> datetime </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> datetime</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">logger </span><span class="token operator">=</span><span class="token plain"> logging</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">getLogger</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">handler </span><span class="token operator">=</span><span class="token plain"> logging</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">StreamHandler</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">sys</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">stdout</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">logger</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">addHandler</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">handler</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># In production</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">logger</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">setLevel</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"INFO"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># In development</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># logger.setLevel("DEBUG")</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">def</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">handle_incoming_message</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">message</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  logger</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">debug</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"[</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">datetime</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token string-interpolation interpolation">now</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">] New message received : '</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">message</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">'"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">len</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">message</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> __name__ </span><span class="token operator">==</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"__main__"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  total </span><span class="token operator">=</span><span class="token plain"> </span><span class="token number">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">for</span><span class="token plain"> i </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">in</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">range</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">10000000</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    total </span><span class="token operator">+=</span><span class="token plain"> handle_incoming_message</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"my custom message"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  logger</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">info</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"The total is : </span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">total</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></div></code></pre></div></div>
<p>You call a function <code>handle_incoming_message</code> on each incoming message received by your app. You count the number of letters in the message an return it. Finally you count the total of letters in all messages.</p>
<p>In development environement, you want to have a debug output each time you receive a message (for debuging purpose). But you don't care about this log when running in production.</p>
<p>Runnig this code in development took <code>116,40s</code> with <code>10000001</code> log output</p>
<p>Runnig this code in production took <code>16,99s</code> with only on log output (the last one)</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="can-we-optimize-the-script-duration-in-production-">Can we optimize the script duration in production ?<a href="https://lunik.tiwabbit.fr/blog/python-logging-fstring-interpolation#can-we-optimize-the-script-duration-in-production-" class="hash-link" aria-label="Direct link to Can we optimize the script duration in production ?" title="Direct link to Can we optimize the script duration in production ?" translate="no">​</a></h3>
<p>Changing the line :</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">logger</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">debug</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"[</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">datetime</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token string-interpolation interpolation">now</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">] New message received : '</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">message</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">'"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></div></code></pre></div></div>
<p>By :</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">logger</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">debug</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"[%s] New message received : '%s'"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> datetime</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">now</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> message</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></div></code></pre></div></div>
<p>Should produce the exact same output but in less time.</p>
<p>Runnig this code in development took <code>112,81s</code> with <code>10000001</code> log output</p>
<p>Runnig this code in production took <code>8,54s</code> with only on log output (the last one)</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="how-did-we-save-time-">How did we save time ?<a href="https://lunik.tiwabbit.fr/blog/python-logging-fstring-interpolation#how-did-we-save-time-" class="hash-link" aria-label="Direct link to How did we save time ?" title="Direct link to How did we save time ?" translate="no">​</a></h3>
<p>How can just changing the format of the log string we can run the same script in half the time ?</p>
<p>In the first solution :</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">logger</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">debug</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"[</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">datetime</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token string-interpolation interpolation">now</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">] New message received : '</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">message</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">'"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></div></code></pre></div></div>
<p>Python will firstly call the string format function to render the string, then call the <code>logger.debug</code></p>
<p>In the second example :</p>
<div class="language-python codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-python codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">logger</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">debug</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"[%s] New message received : '%s'"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> datetime</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">now</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> message</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></div></code></pre></div></div>
<p>We directly call the <code>logger.debug</code> with multiple argument. In the algorithm of the logging function, the string format function will be called with all the argument passed to the function <strong>only if</strong> the log should be showed to the user (depending of the logging level).</p>
<p>By chosing the second code over the other save us one function call and then time.
In the example, the time consuming call is <code>datetime.now()</code> but you have to imagin that this call could may be another more time ungry call in your application !</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="conclusion">Conclusion<a href="https://lunik.tiwabbit.fr/blog/python-logging-fstring-interpolation#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>By using bulting <a href="https://docs.python.org/3/library/logging.html" target="_blank" rel="noopener noreferrer" class=""><code>logging</code> library</a> string formating capabilities, you can save a lot of time and make your application or script more fast and responsive.</p>
<p>Thank's pylint for the warning.</p>]]></content>
        <author>
            <name>Guillaume MARTINEZ</name>
            <uri>https://github.com/Lunik</uri>
        </author>
        <category label="python" term="python"/>
        <category label="pylint" term="pylint"/>
        <category label="performance" term="performance"/>
        <category label="logging" term="logging"/>
        <category label="log" term="log"/>
        <category label="error" term="error"/>
        <category label="warning" term="warning"/>
        <category label="debug" term="debug"/>
        <category label="info" term="info"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Securing web entrypoint from external threats]]></title>
        <id>https://lunik.tiwabbit.fr/blog/secure-home-entrypoint</id>
        <link href="https://lunik.tiwabbit.fr/blog/secure-home-entrypoint"/>
        <updated>2021-10-13T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[I'm explaining how I have secured a web entrypoint from external threats]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2021-10-13-secure-home-entrypoint/cover.jpg" alt="cover" class="img__Ss2"></p>
<p>I'm currently hosting some private web services accessible from internet. In order to protect those apps, I needed a very secure way to protect the access to them.</p>
<p>As you may already know, there are tons of bots that continuously scan all public internet IPs for potential vulnerabilities. From open ports, insecure web services or security breach.
There are private organizations that allow to discover those vulnerabilities like <a href="https://www.shodan.io/" target="_blank" rel="noopener noreferrer" class="">Shodan</a>. In my particular case, this is the only information that they could collect from my IP gateway :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2021-10-13-secure-home-entrypoint/shodan-private-gateway-summary.png" alt="Showdan private gateway summary" class="img__Ss2"></p>
<p>So I need to expose my services and access them from anywhere on internet without too much complexity while making sure I'm the only on that can use them and nobody else could access my data.</p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="theory">Theory<a href="https://lunik.tiwabbit.fr/blog/secure-home-entrypoint#theory" class="hash-link" aria-label="Direct link to Theory" title="Direct link to Theory" translate="no">​</a></h2>
<p>I'm using <a href="https://en.wikipedia.org/wiki/Zero_trust_security_model" target="_blank" rel="noopener noreferrer" class="">Zero trust security model</a>. So by default no one and no device are trusted.</p>
<p>The first step is to set firewall to drop all incoming traffic, close all network port, disable login for all users, remove all unnecessary package and running demons.</p>
<blockquote>
<p><strong>Warning :</strong> You need to be extremely careful when you close everything or you could be locked out of your network/server without possibility to regain access.</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="layout">Layout<a href="https://lunik.tiwabbit.fr/blog/secure-home-entrypoint#layout" class="hash-link" aria-label="Direct link to Layout" title="Direct link to Layout" translate="no">​</a></h2>
<p>So this is a Top Level View of my network layout :</p>
<p><strong>Internet</strong> --&gt; <strong>Public IP</strong> --&gt; <strong>Firewall</strong> --&gt; <strong>Gateway Server</strong> --&gt; <strong>Ingress service</strong> --&gt; <strong>Private Network</strong></p>
<h2 class="anchor anchorTargetStickyNavbar_SAay" id="implementation">Implementation<a href="https://lunik.tiwabbit.fr/blog/secure-home-entrypoint#implementation" class="hash-link" aria-label="Direct link to Implementation" title="Direct link to Implementation" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="step-by-step">Step by step<a href="https://lunik.tiwabbit.fr/blog/secure-home-entrypoint#step-by-step" class="hash-link" aria-label="Direct link to Step by step" title="Direct link to Step by step" translate="no">​</a></h3>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="public-ip">Public IP<a href="https://lunik.tiwabbit.fr/blog/secure-home-entrypoint#public-ip" class="hash-link" aria-label="Direct link to Public IP" title="Direct link to Public IP" translate="no">​</a></h4>
<p>Well here there is not much to secure. The IP is statically provided by my <a href="https://en.wikipedia.org/wiki/Internet_service_provider" target="_blank" rel="noopener noreferrer" class="">ISP</a> and is publicly available and reachable.</p>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="firewall">Firewall<a href="https://lunik.tiwabbit.fr/blog/secure-home-entrypoint#firewall" class="hash-link" aria-label="Direct link to Firewall" title="Direct link to Firewall" translate="no">​</a></h4>
<p>This device if the first line of security.</p>
<p>As mentioned before, I'm using <a href="https://en.wikipedia.org/wiki/Zero_trust_security_model" target="_blank" rel="noopener noreferrer" class="">Zero trust security model</a>, so the first step is to drop all incoming traffic by default.
Now nobody can reach me from the outside but the returning traffic can't either. When I make a request to an outside service, the returned data is dropped ate the firewall level. Not very useful indeed.</p>
<p>The second step is then to allow returning traffic (while keep dropping all other traffic). I can be done with simple <a href="https://en.wikipedia.org/wiki/Network_address_translation" target="_blank" rel="noopener noreferrer" class="">Network Address Translation or NAT</a>. This way when a server within the private network make a request to a public service, the response can flow trough the firewall securely. This can come handy if you want to update packages on your server !
All firewall support natively <a href="https://en.wikipedia.org/wiki/Network_address_translation" target="_blank" rel="noopener noreferrer" class="">NAT</a> so it's not very hard to activate it.</p>
<p>Then I want to allow public access to my services which expose <a href="https://tools.ietf.org/html/rfc2818" target="_blank" rel="noopener noreferrer" class="">HTTPS</a> ports, so <code>443/tcp</code>. I have enabled <a href="https://en.wikipedia.org/wiki/Port_forwarding" target="_blank" rel="noopener noreferrer" class="">port forwarding</a> on my firewall so that when incoming traffic/request arrive on the port <code>443</code> with the <a href="https://tools.ietf.org/html/rfc793" target="_blank" rel="noopener noreferrer" class="">TCP</a> protocol, it is forwarded to my gateway server within my private network.</p>
<blockquote>
<p><strong>Note:</strong> The port exposed by my gateway server has no impact on the forward rule. It could be <code>443</code> to be coherent or it could be <code>8888</code>. It doesn't matter.</p>
</blockquote>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="gateway-server">Gateway Server<a href="https://lunik.tiwabbit.fr/blog/secure-home-entrypoint#gateway-server" class="hash-link" aria-label="Direct link to Gateway Server" title="Direct link to Gateway Server" translate="no">​</a></h4>
<p>My current gateway server is a <a href="https://en.wikipedia.org/wiki/Linux" target="_blank" rel="noopener noreferrer" class="">Linux</a> based server. The distribution used in my case is irrelevant.</p>
<h5 class="anchor anchorTargetStickyNavbar_SAay" id="local-firewall">Local firewall<a href="https://lunik.tiwabbit.fr/blog/secure-home-entrypoint#local-firewall" class="hash-link" aria-label="Direct link to Local firewall" title="Direct link to Local firewall" translate="no">​</a></h5>
<p>It ship with <a href="https://firewalld.org/" target="_blank" rel="noopener noreferrer" class="">firewalld</a> pre-installed witch allow me to have a local firewall on my server. Since my gateway receive traffic from the outside world, I need to control what can enter and leave this server.</p>
<p>For my setup I choose to keep it simple. I'm only allowing incoming traffic from port <code>443/tcp</code> (and <code>22/tcp</code> from my private network to administrate the server with <a href="https://tools.ietf.org/html/rfc4253" target="_blank" rel="noopener noreferrer" class="">SSH</a>). Then I'm allowing outgoing traffic (like <a href="https://en.wikipedia.org/wiki/Network_address_translation" target="_blank" rel="noopener noreferrer" class="">NAT</a>) for the returning flux.</p>
<p>With the <a href="https://firewalld.org/" target="_blank" rel="noopener noreferrer" class="">firewalld</a> following command line I can see my current configuration :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">firewall-cmd </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--zone</span><span class="token plain"> public --list-all</span><br></div></code></pre></div></div>
<p>I get :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">public </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">active</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  target: default</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  icmp-block-inversion: no</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  interfaces: eth0</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  sources: </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  services: https </span><span class="token function" style="color:rgb(80, 250, 123)">ssh</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  ports: </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  protocols: </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  forward: no</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  masquerade: no</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  forward-ports: </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  source-ports: </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  icmp-blocks: </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  rich rules: </span><br></div></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="ingress-service">Ingress service<a href="https://lunik.tiwabbit.fr/blog/secure-home-entrypoint#ingress-service" class="hash-link" aria-label="Direct link to Ingress service" title="Direct link to Ingress service" translate="no">​</a></h4>
<p>I'm using <a href="https://www.nginx.com/" target="_blank" rel="noopener noreferrer" class="">NGINX</a> as my ingress service. It runs on my gateway server.
His purpose is to route and filter incoming <a href="https://tools.ietf.org/html/rfc2818" target="_blank" rel="noopener noreferrer" class="">HTTPS</a> traffic.</p>
<h5 class="anchor anchorTargetStickyNavbar_SAay" id="filter-non-wanted-traffic">Filter non wanted traffic<a href="https://lunik.tiwabbit.fr/blog/secure-home-entrypoint#filter-non-wanted-traffic" class="hash-link" aria-label="Direct link to Filter non wanted traffic" title="Direct link to Filter non wanted traffic" translate="no">​</a></h5>
<p>First step is to filter out all <a href="https://tools.ietf.org/html/rfc2818" target="_blank" rel="noopener noreferrer" class="">HTTPS</a> that is not going to my exposed service. A simple first step is to drop by default all incoming requests. In a default site configuration <code>/etc/nginx/conf.d/default.conf</code>, I have :</p>
<div class="language-nginx codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-nginx codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">server</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">listen</span><span class="token directive">      </span><span class="token directive number">443</span><span class="token directive"> default_server ssl</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">server_name</span><span class="token directive"> _</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token directive">      </span><span class="token directive number">444</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>The first line with le <code>listen</code> keyword tells <a href="https://www.nginx.com/" target="_blank" rel="noopener noreferrer" class="">NGINX</a> to listen on port <code>443</code> and to begin a secure connexion with the client.</p>
<p>In this configuration <code>server_name _;</code> match all/undefined <a href="https://en.wikipedia.org/wiki/Server_Name_Indication" target="_blank" rel="noopener noreferrer" class="">Server Name Identification or SNI</a> within incoming requests.</p>
<p>The <code>return 444;</code> line return the <a href="https://en.wikipedia.org/wiki/List_of_HTTP_status_codes" target="_blank" rel="noopener noreferrer" class="">HTTP code</a> <code>444</code> which tells <a href="https://www.nginx.com/" target="_blank" rel="noopener noreferrer" class="">NGINX</a> to close the connection immediately.</p>
<p>Since I'm using <a href="https://tools.ietf.org/html/rfc2818" target="_blank" rel="noopener noreferrer" class="">HTTPS</a>, I need to provide a <a href="https://en.wikipedia.org/wiki/X.509" target="_blank" rel="noopener noreferrer" class="">X.509 certificate</a> for <a href="https://tools.ietf.org/html/rfc8446" target="_blank" rel="noopener noreferrer" class="">TLS</a> communications. I made a little joke on this one because, you know, if you are a stranger trying to access my private stuff <em>GTFO</em>. This give me :</p>
<div class="language-nginx codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-nginx codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">server</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">listen</span><span class="token directive">      </span><span class="token directive number">443</span><span class="token directive"> default_server ssl</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">server_name</span><span class="token directive"> _</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">ssl_certificate</span><span class="token directive">     /etc/pki/tls/certs/go.fuck.yourself.now.crt</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">ssl_certificate_key</span><span class="token directive"> /etc/pki/tls/private/go.fuck.yourself.now.key</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token directive">      </span><span class="token directive number">444</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>I'm currently using a overkill <a href="https://tools.ietf.org/html/rfc8017" target="_blank" rel="noopener noreferrer" class="">RSA</a> <code>4096</code> bits long private key.</p>
<p>Now if I try to make a dumb request to my ingress service like :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">curl</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--insecure</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--verbose</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--header</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Host: dumb.example.org"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  https://mygateway.local</span><br></div></code></pre></div></div>
<p>I get :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">*   Trying XXX.XXX.XXX.XXX</span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token plain">.</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">* TCP_NODELAY </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">set</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">* Connected to mygateway.local </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">XXX.XXX.XXX.XXX</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> port </span><span class="token number">443</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token comment" style="color:rgb(98, 114, 164)">#0)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">* ALPN, offering h2</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">* ALPN, offering http/1.1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">* successfully </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">set</span><span class="token plain"> certificate verify locations:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">*   CAfile: /etc/ssl/cert.pem</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  CApath: none</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">* TLSv1.2 </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">OUT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">, TLS handshake, Client hello </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">* TLSv1.2 </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">IN</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">, TLS handshake, Server hello </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">2</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">* TLSv1.2 </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">IN</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">, TLS handshake, Certificate </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">11</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">* TLSv1.2 </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">IN</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">, TLS handshake, Server key exchange </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">12</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">* TLSv1.2 </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">IN</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">, TLS handshake, Server finished </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">14</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">* TLSv1.2 </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">OUT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">, TLS handshake, Client key exchange </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">16</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">* TLSv1.2 </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">OUT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">, TLS change cipher, Change cipher spec </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">* TLSv1.2 </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">OUT</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">, TLS handshake, Finished </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">20</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">* TLSv1.2 </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">IN</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">, TLS change cipher, Change cipher spec </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">* TLSv1.2 </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">IN</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">, TLS handshake, Finished </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">20</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain">:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">* ALPN, server accepted to use http/1.1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">* Server certificate:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">*  subject: </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">C</span><span class="token operator">=</span><span class="token plain">FR</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">L</span><span class="token operator">=</span><span class="token plain">Paris</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">O</span><span class="token operator">=</span><span class="token plain">Wabbit</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">CN</span><span class="token operator">=</span><span class="token plain">go.fuck.yourself.now</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">emailAddress</span><span class="token operator">=</span><span class="token plain">admin@wabbit</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">*  start date: May </span><span class="token number">29</span><span class="token plain"> </span><span class="token number">15</span><span class="token plain">:55:36 </span><span class="token number">2021</span><span class="token plain"> GMT</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">*  expire date: May </span><span class="token number">29</span><span class="token plain"> </span><span class="token number">15</span><span class="token plain">:55:36 </span><span class="token number">2022</span><span class="token plain"> GMT</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">*  issuer: </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">C</span><span class="token operator">=</span><span class="token plain">FR</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">L</span><span class="token operator">=</span><span class="token plain">Paris</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">O</span><span class="token operator">=</span><span class="token plain">Wabbit</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">CN</span><span class="token operator">=</span><span class="token plain">wabbit</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">emailAddress</span><span class="token operator">=</span><span class="token plain">admin@wabbit</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">*  SSL certificate verify ok.</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token operator">&gt;</span><span class="token plain"> GET / HTTP/1.1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token operator">&gt;</span><span class="token plain"> Host: dumb.example.org</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token operator">&gt;</span><span class="token plain"> User-Agent: curl/7.64.1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token operator">&gt;</span><span class="token plain"> Accept: */*</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token operator">&gt;</span><span class="token plain"> </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">* Empty reply from server</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">* Connection </span><span class="token comment" style="color:rgb(98, 114, 164)">#0 to host mygateway.local left intact</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">curl: </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">52</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> Empty reply from server</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">* Closing connection </span><span class="token number">0</span><br></div></code></pre></div></div>
<p>My ingress server send me the certificate with Common Name <code>go.fuck.yourself.now</code> for secure communication. My client make a <code>GET</code> request on the <code>/</code> URI path with the host <code>dumb.example.org</code>.</p>
<p>Since my ingress server is not configured to accept request for any service with the name <code>dumb.example.org</code>, it close the connexion without warning and my client get an <code>Empty reply from server</code>.</p>
<h5 class="anchor anchorTargetStickyNavbar_SAay" id="enhance-secure-connexion">Enhance secure connexion<a href="https://lunik.tiwabbit.fr/blog/secure-home-entrypoint#enhance-secure-connexion" class="hash-link" aria-label="Direct link to Enhance secure connexion" title="Direct link to Enhance secure connexion" translate="no">​</a></h5>
<p>By default <a href="https://www.nginx.com/" target="_blank" rel="noopener noreferrer" class="">NGINX</a> is very open in term of protocols and cipher allowed to make secure connexion. Inside the <a href="https://www.nginx.com/" target="_blank" rel="noopener noreferrer" class="">NGINX</a> main configuration file, located at <code>/etc/nginx/nginx.conf</code>, in the <code>http</code> section, I have replaced the <code>ssl_</code> options with :</p>
<div class="language-nginx codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-nginx codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">ssl_protocols</span><span class="token directive"> TLSv1.2 TLSv1.3</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">ssl_prefer_server_ciphers</span><span class="token directive"> </span><span class="token directive boolean">on</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">ssl_ciphers</span><span class="token directive">         HIGH:!aNULL:!MD5</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>Now my ingress service will only allow only <a href="https://tools.ietf.org/html/rfc8446" target="_blank" rel="noopener noreferrer" class="">TLS</a> <code>1.2</code> minimum (up to <code>1.3</code>) with only <code>HIGH</code>, not <code>NULL</code> and not <code>MD5</code> ciphers to establish secure connexions with the client. The <code>ssl_prefer_server_ciphers</code> for the client to tries ciphers with the order provided by the ingress service.</p>
<p><a href="https://www.ssi.gouv.fr/uploads/2017/02/security-recommendations-for-tls_v1.1.pdf" target="_blank" rel="noopener noreferrer" class="">Additional TLS recomendations</a></p>
<h5 class="anchor anchorTargetStickyNavbar_SAay" id="log-everything">Log everything<a href="https://lunik.tiwabbit.fr/blog/secure-home-entrypoint#log-everything" class="hash-link" aria-label="Direct link to Log everything" title="Direct link to Log everything" translate="no">​</a></h5>
<p>By default <a href="https://www.nginx.com/" target="_blank" rel="noopener noreferrer" class="">NGINX</a> logs all request inside the <code>/var/log/nginx/access.log</code> file and all errors in <code>/var/log/nginx/error.log</code>. I have checked if it is the case for my instance. In the main configuration file <code>/etc/nginx/nginx.conf</code>, I have :</p>
<div class="language-nginx codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-nginx codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">error_log</span><span class="token directive"> /var/log/nginx/error.log</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>and :</p>
<div class="language-nginx codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-nginx codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">log_format</span><span class="token directive">  main  </span><span class="token directive string" style="color:rgb(255, 121, 198)">'</span><span class="token directive string variable" style="color:rgb(189, 147, 249);font-style:italic">$remote_addr</span><span class="token directive string" style="color:rgb(255, 121, 198)"> - </span><span class="token directive string variable" style="color:rgb(189, 147, 249);font-style:italic">$remote_user</span><span class="token directive string" style="color:rgb(255, 121, 198)"> [</span><span class="token directive string variable" style="color:rgb(189, 147, 249);font-style:italic">$time_local]</span><span class="token directive string" style="color:rgb(255, 121, 198)"> "</span><span class="token directive string variable" style="color:rgb(189, 147, 249);font-style:italic">$request</span><span class="token directive string" style="color:rgb(255, 121, 198)">" '</span><span class="token directive"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token directive">                  </span><span class="token directive string" style="color:rgb(255, 121, 198)">'</span><span class="token directive string variable" style="color:rgb(189, 147, 249);font-style:italic">$status</span><span class="token directive string" style="color:rgb(255, 121, 198)"> </span><span class="token directive string variable" style="color:rgb(189, 147, 249);font-style:italic">$body_bytes_sent</span><span class="token directive string" style="color:rgb(255, 121, 198)"> "</span><span class="token directive string variable" style="color:rgb(189, 147, 249);font-style:italic">$http_referer</span><span class="token directive string" style="color:rgb(255, 121, 198)">" '</span><span class="token directive"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token directive">                  </span><span class="token directive string" style="color:rgb(255, 121, 198)">'"</span><span class="token directive string variable" style="color:rgb(189, 147, 249);font-style:italic">$http_user_agent</span><span class="token directive string" style="color:rgb(255, 121, 198)">" "</span><span class="token directive string variable" style="color:rgb(189, 147, 249);font-style:italic">$http_x_forwarded_for</span><span class="token directive string" style="color:rgb(255, 121, 198)">"'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">access_log</span><span class="token directive">  /var/log/nginx/access.log  main</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>In <code>/var/log/nginx/access.log</code> I can see my test request from earlier :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">XXX.XXX.XXX.XXX - - </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token number">10</span><span class="token plain">/Oct/2021:15:55:36 +0000</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"GET / HTTP/1.1"</span><span class="token plain"> </span><span class="token number">444</span><span class="token plain"> </span><span class="token number">0</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"-"</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"curl/7.64.1"</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"-"</span><br></div></code></pre></div></div>
<h5 class="anchor anchorTargetStickyNavbar_SAay" id="expose-service">Expose service<a href="https://lunik.tiwabbit.fr/blog/secure-home-entrypoint#expose-service" class="hash-link" aria-label="Direct link to Expose service" title="Direct link to Expose service" translate="no">​</a></h5>
<p>Now that I have all my connexions filtered, I want to expose my services. To do that I added a new config file inside <code>/etc/nginx/conf.d</code> with the following content :</p>
<div class="language-nginx codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-nginx codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">server</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">listen</span><span class="token directive">      </span><span class="token directive number">443</span><span class="token directive"> ssl</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">server_name</span><span class="token directive"> myapp.example.org</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">ssl_certificate</span><span class="token directive">     /etc/pki/tls/certs/myapp.example.org.crt</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">ssl_certificate_key</span><span class="token directive"> /etc/pki/tls/private/myapp.example.org.key</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">location</span><span class="token directive"> /</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">proxy_pass</span><span class="token directive"> https://myapp.local</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">proxy_set_header</span><span class="token directive"> Host </span><span class="token directive variable" style="color:rgb(189, 147, 249);font-style:italic">$host</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">proxy_set_header</span><span class="token directive"> X-Real-IP </span><span class="token directive variable" style="color:rgb(189, 147, 249);font-style:italic">$remote_addr</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">proxy_set_header</span><span class="token directive"> X-Forwarded-For </span><span class="token directive variable" style="color:rgb(189, 147, 249);font-style:italic">$proxy_add_x_forwarded_for</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>If a request arrives with the <a href="https://en.wikipedia.org/wiki/Server_Name_Indication" target="_blank" rel="noopener noreferrer" class="">SNI</a> <code>myapp.example.org</code> it get accepted by the ingress service. <a href="https://www.nginx.com/" target="_blank" rel="noopener noreferrer" class="">NGINX</a> is going to use the provided <code>myapp.example.org.crt</code> certificate to establish secure connexion with the client. Then it will forward all the traffic to my private app at <code>https://myapp.local</code> using <a href="https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/" target="_blank" rel="noopener noreferrer" class="">NGINX proxy configuration</a>.</p>
<h5 class="anchor anchorTargetStickyNavbar_SAay" id="identifying-clients">Identifying clients<a href="https://lunik.tiwabbit.fr/blog/secure-home-entrypoint#identifying-clients" class="hash-link" aria-label="Direct link to Identifying clients" title="Direct link to Identifying clients" translate="no">​</a></h5>
<p>Since I want to access my apps from anywhere outside my private network, I need to allow incoming requests from any potential public IPs.
But I still want to be able to identify my devices among all public IPs.</p>
<p>I have decided to implement <a href="https://en.wikipedia.org/wiki/Client_certificate" target="_blank" rel="noopener noreferrer" class="">client certificate</a> authentication. I have generated a unique private <a href="https://en.wikipedia.org/wiki/X.509" target="_blank" rel="noopener noreferrer" class="">X.509 certificate</a> for each of my devices (PC, phone, ...) and installed them.</p>
<p>I have then configured my ingress service to request <a href="https://en.wikipedia.org/wiki/Client_certificate" target="_blank" rel="noopener noreferrer" class="">client certificate</a> when a client wish to access an exposed service. Enhancing the previous configuration I get :</p>
<div class="language-nginx codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-nginx codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">server</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">listen</span><span class="token directive">      </span><span class="token directive number">443</span><span class="token directive"> ssl</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">server_name</span><span class="token directive"> myapp.example.org</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">ssl_certificate</span><span class="token directive">     /etc/pki/tls/certs/myapp.example.org.crt</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">ssl_certificate_key</span><span class="token directive"> /etc/pki/tls/private/myapp.example.org.key</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token comment" style="color:rgb(98, 114, 164)"># make verification optional, so we can display a 403 message to those</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token comment" style="color:rgb(98, 114, 164)"># who fail authentication</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">ssl_verify_client</span><span class="token directive"> </span><span class="token directive boolean">on</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">ssl_client_certificate</span><span class="token directive"> /etc/pki/tls/certs/users.example.org.crt</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">location</span><span class="token directive"> /</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">proxy_pass</span><span class="token directive"> https://myapp.local</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">proxy_set_header</span><span class="token directive"> Host </span><span class="token directive variable" style="color:rgb(189, 147, 249);font-style:italic">$host</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">proxy_set_header</span><span class="token directive"> X-Real-IP </span><span class="token directive variable" style="color:rgb(189, 147, 249);font-style:italic">$remote_addr</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token directive keyword" style="color:rgb(189, 147, 249);font-style:italic">proxy_set_header</span><span class="token directive"> X-Forwarded-For </span><span class="token directive variable" style="color:rgb(189, 147, 249);font-style:italic">$proxy_add_x_forwarded_for</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p><code>ssl_verify_client</code> force user to provide a <a href="https://en.wikipedia.org/wiki/Client_certificate" target="_blank" rel="noopener noreferrer" class="">client certificate</a> for authentication. If not, the connexion is closed with code <code>400</code>.</p>
<p><code>ssl_certificate_key</code> provides the <a href="https://en.wikipedia.org/wiki/Certificate_authority" target="_blank" rel="noopener noreferrer" class="">Certificate Authority or CA</a> who have signed the clients certificates.</p>
<p>Now if I try to make a request on <code>myapp.example.org</code> without the <a href="https://en.wikipedia.org/wiki/Client_certificate" target="_blank" rel="noopener noreferrer" class="">client certificate</a> :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">curl</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--insecure</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--include</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  https://myapp.example.org</span><br></div></code></pre></div></div>
<p>I get :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">HTTP/1.1 </span><span class="token number">400</span><span class="token plain"> Bad Request</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Server: nginx/1.20.1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Date: Sun, </span><span class="token number">10</span><span class="token plain"> Oct </span><span class="token number">2021</span><span class="token plain"> </span><span class="token number">17</span><span class="token plain">:35:05 GMT</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Content-Type: text/html</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Content-Length: </span><span class="token number">237</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Connection: close</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token operator">&lt;</span><span class="token plain">html</span><span class="token operator">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token operator">&lt;</span><span class="token plain">head</span><span class="token operator">&gt;</span><span class="token operator">&lt;</span><span class="token plain">title</span><span class="token operator">&gt;</span><span class="token number">400</span><span class="token plain"> No required SSL certificate was sent</span><span class="token operator">&lt;</span><span class="token plain">/title</span><span class="token operator">&gt;</span><span class="token operator">&lt;</span><span class="token plain">/head</span><span class="token operator">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token operator">&lt;</span><span class="token plain">body</span><span class="token operator">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token operator">&lt;</span><span class="token plain">center</span><span class="token operator">&gt;</span><span class="token operator">&lt;</span><span class="token plain">h</span><span class="token operator file-descriptor important">1</span><span class="token operator">&gt;</span><span class="token number">400</span><span class="token plain"> Bad Request</span><span class="token operator">&lt;</span><span class="token plain">/h</span><span class="token operator file-descriptor important">1</span><span class="token operator">&gt;</span><span class="token operator">&lt;</span><span class="token plain">/center</span><span class="token operator">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token operator">&lt;</span><span class="token plain">center</span><span class="token operator">&gt;</span><span class="token plain">No required SSL certificate was sent</span><span class="token operator">&lt;</span><span class="token plain">/center</span><span class="token operator">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token operator">&lt;</span><span class="token plain">hr</span><span class="token operator">&gt;</span><span class="token operator">&lt;</span><span class="token plain">center</span><span class="token operator">&gt;</span><span class="token plain">nginx/1.20.</span><span class="token operator file-descriptor important">1</span><span class="token operator">&lt;</span><span class="token plain">/center</span><span class="token operator">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token operator">&lt;</span><span class="token plain">/body</span><span class="token operator">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token operator">&lt;</span><span class="token plain">/html</span><span class="token operator">&gt;</span><br></div></code></pre></div></div>
<p>The ingress server reject my request with code <code>400</code> because I have not provided the required <a href="https://en.wikipedia.org/wiki/Client_certificate" target="_blank" rel="noopener noreferrer" class="">client certificate</a>.</p>
<p>Let's try again while providing the <a href="https://en.wikipedia.org/wiki/Client_certificate" target="_blank" rel="noopener noreferrer" class="">client certificate</a> :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">curl</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--insecure</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--include</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--cert</span><span class="token plain"> lunik.pem </span><span class="token punctuation" style="color:rgb(248, 248, 242)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  https://myapp.example.org</span><br></div></code></pre></div></div>
<p>I get :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">HTTP/1.1 </span><span class="token number">302</span><span class="token plain"> Found</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Server: nginx/1.20.1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Date: Sun, </span><span class="token number">10</span><span class="token plain"> Oct </span><span class="token number">2021</span><span class="token plain"> </span><span class="token number">17</span><span class="token plain">:35:36 GMT</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Content-Type: text/plain</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">charset</span><span class="token operator">=</span><span class="token plain">utf-8</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Content-Length: </span><span class="token number">30</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Connection: keep-alive</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Content-Language: en</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Location: /login</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Referrer-Policy: same-origin</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Vary: Accept, Accept-Encoding</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">X-Content-Type-Options: nosniff</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">X-Frame-Options: deny</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">X-Xss-Protection: </span><span class="token number">1</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">mode</span><span class="token operator">=</span><span class="token plain">block</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Found. Redirecting to /login</span><br></div></code></pre></div></div>
<p>It works !</p>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="fail2ban">Fail2ban<a href="https://lunik.tiwabbit.fr/blog/secure-home-entrypoint#fail2ban" class="hash-link" aria-label="Direct link to Fail2ban" title="Direct link to Fail2ban" translate="no">​</a></h4>
<p>Now that I can securely access my private services, I still want to prevent unwanted users or bots to <a href="https://en.wikipedia.org/wiki/Brute-force_attack" target="_blank" rel="noopener noreferrer" class="">brute force</a> or <a href="https://en.wikipedia.org/wiki/Denial-of-service_attack" target="_blank" rel="noopener noreferrer" class="">DDOS</a> my gateway server.</p>
<p>I'm currently using <a href="https://www.fail2ban.org/" target="_blank" rel="noopener noreferrer" class="">Fail2ban</a> to block those behaviours. It constantly scan my ingress service access logs to identify malicious requests made by public clients.</p>
<p>I have created a custom <code>filter</code> that match all request returning with a <code>444</code> code. If you remember well this code is returned by my <a href="https://www.nginx.com/" target="_blank" rel="noopener noreferrer" class="">NGINX</a> configuration if client request an unknown <a href="https://en.wikipedia.org/wiki/Server_Name_Indication" target="_blank" rel="noopener noreferrer" class="">SNI</a>.
The filter configuration (<code>/etc/fail2ban/filter.d/nginx-444.conf</code>) looks like :</p>
<div class="language-toml codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-toml codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">[Definition]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">failregex = ^&lt;HOST&gt;.*"(\w+).*" (444) .*$</span><br></div></code></pre></div></div>
<p>Then I have a <code>jail</code> that implement this <code>filter</code>. The <code>/etc/fail2ban/jail.d/block-malicious-users.conf</code> contains :</p>
<div class="language-toml codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-toml codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">[DEFAULT]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">ignoreip = 127.0.0.1 XXX.XXX.XXX.XXX YYY.YYY.YYY.YYY</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">findtime = 3600</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">bantime = 31536000</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">maxretry = 1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">[nginx-444]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">enabled = true</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">logpath  = /var/log/nginx/*.log</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"># Ban IP</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">action = %(banaction_allports)s </span><br></div></code></pre></div></div>
<p>If <a href="https://www.fail2ban.org/" target="_blank" rel="noopener noreferrer" class="">Fail2ban</a> found, in the <code>logpath</code> file at least <code>maxretry</code> failed attempts in a <code>findtime</code> window of time. It will ban the IP for <code>bantime</code> seconds.</p>
<blockquote>
<p><strong>Notes:</strong> I have ignored the localhost address, my administration PC IP (<code>XXX.XXX.XXX.XXX</code>) and the gateway public IP <code>YYY.YYY.YYY.YYY</code> to prevent <a href="https://www.fail2ban.org/" target="_blank" rel="noopener noreferrer" class="">Fail2ban</a> from banning myself when I'm testing the setup.</p>
</blockquote>
<h5 class="anchor anchorTargetStickyNavbar_SAay" id="check-banned-ips">Check banned IPs<a href="https://lunik.tiwabbit.fr/blog/secure-home-entrypoint#check-banned-ips" class="hash-link" aria-label="Direct link to Check banned IPs" title="Direct link to Check banned IPs" translate="no">​</a></h5>
<p>I can already see banned clients from previous failed requests. When using <a href="https://www.fail2ban.org/" target="_blank" rel="noopener noreferrer" class="">Fail2ban</a> status command :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">fail2ban-client status nginx-444</span><br></div></code></pre></div></div>
<p>I get :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Status </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">for</span><span class="token plain"> the jail: nginx-444</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token operator">|</span><span class="token plain">- Filter</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token operator">|</span><span class="token plain">  </span><span class="token operator">|</span><span class="token plain">- Currently failed: </span><span class="token number">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token operator">|</span><span class="token plain">  </span><span class="token operator">|</span><span class="token plain">- Total failed: </span><span class="token number">3</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token operator">|</span><span class="token plain">  </span><span class="token variable" style="color:rgb(189, 147, 249);font-style:italic">`</span><span class="token variable" style="color:rgb(189, 147, 249);font-style:italic">- File list:  /var/log/nginx/error.log /var/log/nginx/custom-access.log /var/log/nginx/access.log</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token variable" style="color:rgb(189, 147, 249);font-style:italic"></span><span class="token variable" style="color:rgb(189, 147, 249);font-style:italic">`</span><span class="token plain">- Actions</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   </span><span class="token operator">|</span><span class="token plain">- Currently banned: </span><span class="token number">3</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   </span><span class="token operator">|</span><span class="token plain">- Total banned: </span><span class="token number">3</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   `- Banned IP list: XXX.XXX.XXX.XXX YYY.YYY.YYY.YYY ZZZ.ZZZ.ZZZ.ZZZ</span><br></div></code></pre></div></div>
<p>Checking <a href="https://firewalld.org/" target="_blank" rel="noopener noreferrer" class="">firewalld</a> for drop rules from those IPs with command :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">firewall-cmd </span><span class="token parameter variable" style="color:rgb(189, 147, 249);font-style:italic">--zone</span><span class="token plain"> public --list-all</span><br></div></code></pre></div></div>
<p>I get :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">public </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">active</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  target: default</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  icmp-block-inversion: no</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  interfaces: eth0</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  sources: </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  services: dhcpv6-client https mdns </span><span class="token function" style="color:rgb(80, 250, 123)">ssh</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  ports: </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  protocols: </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  forward: no</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  masquerade: no</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  forward-ports: </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  source-ports: </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  icmp-blocks: </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  rich rules: </span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  rule </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">family</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"ipv4"</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">source</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">address</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"XXX.XXX.XXX.XXX"</span><span class="token plain"> port </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">port</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"0-65535"</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">protocol</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"tcp"</span><span class="token plain"> reject </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">type</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"icmp-port-unreachable"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  rule </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">family</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"ipv4"</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">source</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">address</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"YYY.YYY.YYY.YYY"</span><span class="token plain"> port </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">port</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"0-65535"</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">protocol</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"tcp"</span><span class="token plain"> reject </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">type</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"icmp-port-unreachable"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  rule </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">family</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"ipv4"</span><span class="token plain"> </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">source</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">address</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"ZZZ.ZZZ.ZZZ.ZZZ"</span><span class="token plain"> port </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">port</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"0-65535"</span><span class="token plain"> </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">protocol</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"tcp"</span><span class="token plain"> reject </span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">type</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"icmp-port-unreachable"</span><br></div></code></pre></div></div>
<p>Looks good !</p>
<p>I usually ban around <code>6.5</code> IP per day :</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2021-10-13-secure-home-entrypoint/ban-rate-fail2ban.jpg" alt="Ban rate fail2ban" class="img__Ss2"></p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="conclusion">Conclusion<a href="https://lunik.tiwabbit.fr/blog/secure-home-entrypoint#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h3>
<p>On each of my entrypoint layers I have implemented :</p>
<ul>
<li class="">
<p><strong>Firewall :</strong></p>
<ul>
<li class="">Drop all incoming traffic</li>
<li class=""><a href="https://en.wikipedia.org/wiki/Network_address_translation" target="_blank" rel="noopener noreferrer" class="">NAT</a></li>
<li class=""><a href="https://en.wikipedia.org/wiki/Port_forwarding" target="_blank" rel="noopener noreferrer" class="">Port forwarding</a> on port <code>443/tcp</code></li>
</ul>
</li>
<li class="">
<p><strong>Gateway Server :</strong></p>
<ul>
<li class="">Enable <a href="https://firewalld.org/" target="_blank" rel="noopener noreferrer" class="">Firewalld</a>
<ul>
<li class="">Drop all incoming and outgoing traffic</li>
<li class="">Allow incoming traffic on port <code>443/tcp</code></li>
<li class="">Enable <a href="https://en.wikipedia.org/wiki/Network_address_translation" target="_blank" rel="noopener noreferrer" class="">NAT</a> to keep packages up to date</li>
</ul>
</li>
<li class="">Enable <a href="https://www.fail2ban.org/" target="_blank" rel="noopener noreferrer" class="">Fail2ban</a>
<ul>
<li class="">Ban IPs that request unknown <a href="https://en.wikipedia.org/wiki/Server_Name_Indication" target="_blank" rel="noopener noreferrer" class="">SNI</a></li>
</ul>
</li>
</ul>
</li>
<li class="">
<p><strong>Ingress service :</strong></p>
<ul>
<li class="">Drop all traffic with unknown <a href="https://en.wikipedia.org/wiki/Server_Name_Indication" target="_blank" rel="noopener noreferrer" class="">SNI</a></li>
<li class="">Use only secure <a href="https://tools.ietf.org/html/rfc8446" target="_blank" rel="noopener noreferrer" class="">TLS</a> protocols and ciphers</li>
<li class="">Enforce <a href="https://en.wikipedia.org/wiki/Client_certificate" target="_blank" rel="noopener noreferrer" class="">client certificate</a> for authentication</li>
<li class="">Use state of the art <a href="https://en.wikipedia.org/wiki/X.509" target="_blank" rel="noopener noreferrer" class="">X.509 certificates</a></li>
</ul>
</li>
</ul>]]></content>
        <author>
            <name>Guillaume MARTINEZ</name>
            <uri>https://github.com/Lunik</uri>
        </author>
        <category label="gateway" term="gateway"/>
        <category label="web" term="web"/>
        <category label="security" term="security"/>
        <category label="firewall" term="firewall"/>
        <category label="network" term="network"/>
        <category label="networking" term="networking"/>
        <category label="nginx" term="nginx"/>
        <category label="fail2ban" term="fail2ban"/>
        <category label="ssl" term="ssl"/>
        <category label="tls" term="tls"/>
        <category label="certificate" term="certificate"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Blog initialisation]]></title>
        <id>https://lunik.tiwabbit.fr/blog/init</id>
        <link href="https://lunik.tiwabbit.fr/blog/init"/>
        <updated>2021-10-05T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[I'm explaining how I have created this blog]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2021-10-05-init/cover.png" alt="cover" class="img__Ss2"></p>
<p>First of all I'm not a web engineer and I hate web development. There are too many framework to work with (<a href="https://reactjs.org/" target="_blank" rel="noopener noreferrer" class="">ReactJS</a>, <a href="https://angular.io/" target="_blank" rel="noopener noreferrer" class="">Angular</a>, <a href="https://jquery.com/" target="_blank" rel="noopener noreferrer" class="">jquery</a>, ...). <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript" target="_blank" rel="noopener noreferrer" class="">Javascript</a> asynchronous system is a pain to manage at large scale. And dependencies maintainability is too much for me (yes, I'm talking about you <code>npm</code>). I'm not a web designer either so <a href="https://tools.ietf.org/html/rfc1866" target="_blank" rel="noopener noreferrer" class="">HTML</a>/<a href="https://tools.ietf.org/html/rfc7993" target="_blank" rel="noopener noreferrer" class="">CSS</a> are the enemy (mostly because I don't know how to use them at their full potentials).</p>
<p>This is pretty much why I quit web development in the first place and became a SysOps engineer.</p>
<div class="theme-admonition theme-admonition-danger admonition_IZjC alert alert--danger"><div class="admonitionHeading_uVvU"><span class="admonitionIcon_HiR3"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"></path></svg></span>danger</div><div class="admonitionContent_bl22"><p>This blog post is deprecated. I left it online for historical reasons.
Now the blog is generated with <a href="https://www.mkdocs.org/" target="_blank" rel="noopener noreferrer" class="">MKDocs</a>.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="theory-first">Theory first<a href="https://lunik.tiwabbit.fr/blog/init#theory-first" class="hash-link" aria-label="Direct link to Theory first" title="Direct link to Theory first" translate="no">​</a></h3>
<p>This is a static website. No fancy <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript" target="_blank" rel="noopener noreferrer" class="">Javascript</a>, framework or other <a href="https://en.wikipedia.org/wiki/Content_management_system" target="_blank" rel="noopener noreferrer" class="">CMS</a>. All pages and medias are served through a very simple web hosting setup that I will detail in another blog post later.</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="how-is-it-generated-then-">How is it generated then ?<a href="https://lunik.tiwabbit.fr/blog/init#how-is-it-generated-then-" class="hash-link" aria-label="Direct link to How is it generated then ?" title="Direct link to How is it generated then ?" translate="no">​</a></h3>
<p>I did not want to over-think too much on the website structure (<a href="https://tools.ietf.org/html/rfc1866" target="_blank" rel="noopener noreferrer" class="">HTML</a> &amp; company) and I wanted to focus on the content of the blog. So I searched for a tool to generate a fancy website from simple text files.</p>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="digression">Digression<a href="https://lunik.tiwabbit.fr/blog/init#digression" class="hash-link" aria-label="Direct link to Digression" title="Direct link to Digression" translate="no">​</a></h4>
<p>A lot of people already know about <a href="https://jekyllrb.com/" target="_blank" rel="noopener noreferrer" class="">Jekyll</a>. It's a <a href="https://www.ruby-lang.org/" target="_blank" rel="noopener noreferrer" class="">Ruby</a> based tool that do the job just fine. Setup some config files, write your article as plain text file, build and ... here you go a fancy website.
But wait ! I didn't use <a href="https://jekyllrb.com/" target="_blank" rel="noopener noreferrer" class="">Jekyll</a> because IMHO it's overkill for my needs. Why you may ask ? Well :</p>
<ul>
<li class="">I'm didn't know anything about <a href="https://www.ruby-lang.org/" target="_blank" rel="noopener noreferrer" class="">Ruby</a> so it's pretty hard to debug anything</li>
<li class=""><a href="https://www.ruby-lang.org/" target="_blank" rel="noopener noreferrer" class="">Ruby</a> package dependencies are a pain in the ass</li>
<li class=""><a href="https://jekyllrb.com/" target="_blank" rel="noopener noreferrer" class="">Jekyll</a> plugin are (for most the majority) community driven so a lot of them are incompatible with each other, have missing documentation or simply doesn't work</li>
<li class="">I have already deployed another blog a few years ago with <a href="https://jekyllrb.com/" target="_blank" rel="noopener noreferrer" class="">Jekyll</a> and I don't have goo memories. I still maintain it but it's not my cup of tee ...</li>
</ul>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="so-the-solution-then-">So the solution then ?<a href="https://lunik.tiwabbit.fr/blog/init#so-the-solution-then-" class="hash-link" aria-label="Direct link to So the solution then ?" title="Direct link to So the solution then ?" translate="no">​</a></h4>
<p>I use <a href="https://getpelican.com/" target="_blank" rel="noopener noreferrer" class="">Pelican</a> !</p>
<p><img decoding="async" loading="lazy" src="https://lunik.tiwabbit.fr/blog/img/posts/2021-10-05-init/getpelican.jpg" alt="logo Pelican" class="img__Ss2"></p>
<p>It's a simple static site generator tool that require only <a href="https://www.python.org/" target="_blank" rel="noopener noreferrer" class="">Python</a> to run.
It allow me to write my blog post as plain text <a href="https://tools.ietf.org/html/rfc7763" target="_blank" rel="noopener noreferrer" class="">Markdown</a>. <a href="https://tools.ietf.org/html/rfc1866" target="_blank" rel="noopener noreferrer" class="">HTML</a> files are templated using <a href="https://jinja.palletsprojects.com/" target="_blank" rel="noopener noreferrer" class="">Jinja</a>.
Being a SysOps engineer, <a href="https://www.python.org/" target="_blank" rel="noopener noreferrer" class="">Python</a>, <a href="https://tools.ietf.org/html/rfc7763" target="_blank" rel="noopener noreferrer" class="">Markdown</a>, <a href="https://jinja.palletsprojects.com/" target="_blank" rel="noopener noreferrer" class="">Jinja</a> are, among other, some keyword that put a smile on my face ! (It even come with a <a href="https://www.gnu.org/software/make" target="_blank" rel="noopener noreferrer" class="">Makefile</a> !!)</p>
<p>So <a href="https://getpelican.com/" target="_blank" rel="noopener noreferrer" class="">Pelican</a> is a tools that generate static <a href="https://tools.ietf.org/html/rfc1866" target="_blank" rel="noopener noreferrer" class="">HTML</a> website from <a href="https://tools.ietf.org/html/rfc7763" target="_blank" rel="noopener noreferrer" class="">Markdown</a> files and <a href="https://jinja.palletsprojects.com/" target="_blank" rel="noopener noreferrer" class="">Jinja</a> templates.</p>
<p>Great ?! When do we begin ?!</p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="practice">Practice<a href="https://lunik.tiwabbit.fr/blog/init#practice" class="hash-link" aria-label="Direct link to Practice" title="Direct link to Practice" translate="no">​</a></h3>
<p>I'm not going to reinvent the wheel, so follow the great <a href="https://docs.getpelican.com/en/latest/quickstart.html" target="_blank" rel="noopener noreferrer" class="">Pelican Quick Start guide</a> and then come back for more deep dive into my setup for this blog website.</p>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="requirement">Requirement<a href="https://lunik.tiwabbit.fr/blog/init#requirement" class="hash-link" aria-label="Direct link to Requirement" title="Direct link to Requirement" translate="no">​</a></h4>
<p>I have <a href="https://www.python.org/" target="_blank" rel="noopener noreferrer" class="">Python</a> 3.9.6 installed on my PC and I'm running inside a <a href="https://docs.python.org/fr/3/library/venv.html" target="_blank" rel="noopener noreferrer" class="">VirtualEnv</a>.</p>
<p>Create the <a href="https://docs.python.org/fr/3/library/venv.html" target="_blank" rel="noopener noreferrer" class="">VirtualEnv</a> :</p>
<div class="language-text codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-text codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">python3 -m venv venv</span><br></div></code></pre></div></div>
<p>Get inside :</p>
<div class="language-text codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-text codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">source venv/bin/activate</span><br></div></code></pre></div></div>
<p>Install the required packages :</p>
<div class="language-text codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-text codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">pip install "pelican[markdown]"</span><br></div></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="file-organization">File organization<a href="https://lunik.tiwabbit.fr/blog/init#file-organization" class="hash-link" aria-label="Direct link to File organization" title="Direct link to File organization" translate="no">​</a></h4>
<p>This is my current folders/files layout (FYI this is the default one provided by <a href="https://docs.getpelican.com/en/latest/settings.html" target="_blank" rel="noopener noreferrer" class="">Pelican settings</a>):</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token builtin class-name" style="color:rgb(189, 147, 249)">.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── Makefile</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── content</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── articles</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   │   ├── init-en.md</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   │   └── init-fr.md</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── images</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   │   └── logo.png</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   └── pages</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── output</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── pelicanconf.py</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── publishconf.py</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── tasks.py</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">└── theme</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    └── flex</span><br></div></code></pre></div></div>
<ul>
<li class="">
<p>The <code>content</code> folder contains all the articles, pages and medias sorted in three folders :</p>
<ul>
<li class=""><code>articles</code> for blog posts</li>
<li class=""><code>images</code> for media contents</li>
<li class=""><code>pages</code> for additional static pages on the blog</li>
</ul>
</li>
<li class="">
<p>The <code>output</code> folder will contains all the generated <a href="https://tools.ietf.org/html/rfc1866" target="_blank" rel="noopener noreferrer" class="">HTML</a> and <a href="https://tools.ietf.org/html/rfc7993" target="_blank" rel="noopener noreferrer" class="">CSS</a> static files after building the website.</p>
</li>
<li class="">
<p>The <code>theme</code> folder contains the current website theme. I'm currently using the <a href="https://github.com/alexandrevicenzi/Flex" target="_blank" rel="noopener noreferrer" class="">Flex</a> theme from the community.</p>
</li>
<li class="">
<p>The files <code>Makefile</code> and <code>tasks.py</code> is auto generated by <a href="https://getpelican.com/" target="_blank" rel="noopener noreferrer" class="">Pelican</a> when creating the project with <a href="https://docs.getpelican.com/en/latest/quickstart.html" target="_blank" rel="noopener noreferrer" class="">Pelican quick start guide</a></p>
</li>
<li class="">
<p>The file <code>pelicanconf.py</code> contains default <a href="https://docs.getpelican.com/en/latest/settings.html" target="_blank" rel="noopener noreferrer" class="">Pelican settings</a>.</p>
</li>
<li class="">
<p>And then <code>publishconf.py</code> contains <a href="https://docs.getpelican.com/en/latest/settings.html" target="_blank" rel="noopener noreferrer" class="">Pelican specific configuration</a> to apply when publishing the blog.</p>
</li>
</ul>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="configuring-the-blog">Configuring the blog<a href="https://lunik.tiwabbit.fr/blog/init#configuring-the-blog" class="hash-link" aria-label="Direct link to Configuring the blog" title="Direct link to Configuring the blog" translate="no">​</a></h4>
<p>I use the default configuration provided during the <a href="https://docs.getpelican.com/en/latest/quickstart.html" target="_blank" rel="noopener noreferrer" class="">Pelican quick start guide</a> and just added two are three additional configuration.</p>
<h4 class="anchor anchorTargetStickyNavbar_SAay" id="building-the-blog">Building the blog<a href="https://lunik.tiwabbit.fr/blog/init#building-the-blog" class="hash-link" aria-label="Direct link to Building the blog" title="Direct link to Building the blog" translate="no">​</a></h4>
<p>Now that I have a full website, I need to build it.</p>
<p>When I'm developing the blog (or when writing articles), I'm using the following commande :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">make</span><span class="token plain"> devserver</span><br></div></code></pre></div></div>
<p>It build all the blog pages and serve it with à small local webserver accessible at <a href="http://localhost:8000/" target="_blank" rel="noopener noreferrer" class="">http://localhost:8000</a>.</p>
<p>When I'm happy with the look of the blog/articles, I use the provided commande to build a publishable version of the website :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">make</span><span class="token plain"> publish</span><br></div></code></pre></div></div>
<p>All the website files are now generated in the <code>output</code> folder :</p>
<div class="language-shell codeBlockContainer_ZGJx theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_kX1v"><pre tabindex="0" class="prism-code language-shell codeBlock_TAPP thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_AdAo"><div class="token-line" style="color:#F8F8F2"><span class="token plain">output</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── </span><span class="token number">404</span><span class="token plain">.html</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── archives.html</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── authors.html</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── categories.html</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── drafts</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── init-fr.html</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   └── init.html</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── images</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── getpelican.jpg</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   ├── getpelican.png</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">│   └── logo.png</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── index.html</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">├── tags.html</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">└── theme</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    └── </span><span class="token punctuation" style="color:rgb(248, 248, 242)">..</span><span class="token plain">.</span><br></div></code></pre></div></div>
<p>The main difference between those commandes is that <code>publish</code> use the <code>publishconf.py</code> config and <code>devserver</code> use only <code>pelicanconf.py</code></p>
<h3 class="anchor anchorTargetStickyNavbar_SAay" id="conclusion">Conclusion<a href="https://lunik.tiwabbit.fr/blog/init#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h3>
<p>Now I have a fully built blog environment and I only need to write new articles, <code>make publish</code> and <em>voila</em>.</p>
<p>I a futur blog post, I will explain how I'm hosting those static file to make them accessible on internet.</p>]]></content>
        <author>
            <name>Guillaume MARTINEZ</name>
            <uri>https://github.com/Lunik</uri>
        </author>
        <category label="blog" term="blog"/>
        <category label="website" term="website"/>
        <category label="pelican" term="pelican"/>
        <category label="python" term="python"/>
        <category label="it" term="it"/>
        <category label="sysops" term="sysops"/>
    </entry>
</feed>