Category: Overig

  • Why you should use GPG over other identity verification alternatives

    Why you should use GPG over other identity verification alternatives

    Introduction: Trust in the Digital Age

    In an era where digital trust is both critical and fragile, verifying the identity of someone who signs code, emails, or documents is paramount. Whether you’re pushing commits to an open-source project, sending secure communications, or distributing software, your reputation and the security of others depend on the authenticity of your cryptographic identity.

    Amid various modern alternatives like SSH signatures, Sigstore, or GitHub verification badges, GPG (GNU Privacy Guard) remains a gold standard. It’s time-tested, decentralized, cryptographically robust, and under your control. In this article, you’ll learn why GPG is still the right choice, how to set it up properly, and how to publish your key securely using a Web Key Directory (WKD).

    Why GPG still matters

    1. Decentralization without compromise

    Unlike centralized services like GitHub or GitLab that act as identity gatekeepers, GPG operates on a Web of Trust model. You can self-host your identity, share it on your own domain, and never depend on a third party to vouch for you.

    2. Granular trust control

    You decide who you trust. You can inspect and sign others’ keys, or let them sign yours. This model is more robust than blindly trusting a cloud provider that could be compromised or co-opted.

    3. Separation of duties via subkeys

    GPG allows you to use a secure master key (stored offline) and dedicated subkeys for everyday actions like signing Git commits. This minimizes risk while preserving security.

    4. Ubiquity across the open-source ecosystem

    GPG is widely supported in Linux distributions, Git, email clients, and package managers. It’s also the cornerstone of software distribution security for projects like Debian and Arch Linux.

    Understanding GPG Key Architecture: Master vs Subkeys

    The master key

    • Used to certify (sign) other keys and UIDs.
    • Should be kept offline (e.g., air-gapped USB device).
    • Acts as your long-term identity root.

    Subkeys

    • Used for specific tasks:
    • Signing subkey: for signing Git commits, emails, and files.
    • Encryption subkey: for receiving encrypted messages.
    • Authentication subkey: for SSH login or VPN access.
    • Can be kept on daily-use machines or smartcards (e.g., YubiKey).

    This model ensures that even if a subkey is compromised, your core identity remains secure.

    Step-by-step: creating a master key and signing subkey

    Generate your master key

    Use --full-generate-key to set up a secure root key:

    gpg --full-generate-key

    Choose the following options when prompted:

    • Key type: RSA and RSA
    • Key size: 4096
    • Expiry: 1y (can be extended later)
    • Name and email: Your real identity
    • Passphrase: Choose a strong one!

    List your key

    gpg --list-secret-keys --keyid-format=long

    Take note of your key ID. (The part after the rsa4096/ or ed25519/ and before the date)

    Add a signing subkey

    gpg --edit-key YOUR_KEY_ID

    Inside the GPG prompt:

    addkey
    Choose: RSA (sign only), size 4096, expiry 1y
    save

    Export and back up your keys

    Export your public key:

    gpg --armor --export [email protected] > publickey.asc

    Export and back up your private key (store safely!):

    gpg --armor --export-secret-keys > master-private.asc

    Publishing your GPGkey via WKD (Web Key Directory)

    The Web Key Directory allows your domain to serve your public key in a standardized way. Tools like gpg --locate-keys can then automatically find your key at https://yourdomain.com/.well-known/openpgpkey/.

    1. Create your WKD directory structure

    On your web server (e.g. Nginx):

    mkdir -p /var/www/yourdomain.com/.well-known/openpgpkey/yourdomain.com/hu

    2. Generate WKD hash

    gpg --with-wkd-hash --list-keys [email protected]

    This gives you a hex-based filename, something like:

    hu/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    3. Export public key in WKD format

    gpg --export --armor [email protected] > openpgpkey.asc

    Then copy it:

    cp openpgpkey.asc /var/www/yourdomain.com/.well-known/openpgpkey/yourdomain.com/hu/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    4. Serve WKD directory (testing purposes only)

    You can serve this directory locally using Python 3 for testing:

    cd /var/www/yourdomain.com
    python3 -m http.server 8080

    Then test with:

    gpg --auto-key-locate wkd --locate-keys [email protected]

    ⚠️ Note: This method is for testing only. For production, use a proper web server like Nginx or Apache that can serve .well-known/ paths correctly and over HTTPS.

    Using Your GPG Subkey to Sign Git Commits

    Once your GPG subkey is set up and available on your system, you can use it to sign Git commits. This gives your code contributions a cryptographic signature of authenticity.

    To sign a commit:

    git commit -S -m "Your commit message"

    If Git is configured to use your GPG subkey, this command will prompt for your passphrase (unless cached) and produce a signed commit.

    ℹ️ Setting up Git to use your GPG subkey (via user.signingkey and commit.gpgsign) is outside the scope of this article, but many tutorials and Git documentation cover it in detail.

    You can verify a commit was signed with:

    git log --show-signature

    Conclusion: Control, credibility, and cryptographic hygiene

    GPG is a battle-tested, decentralized way to assert identity across borders and platforms. While GitHub’s verification is convenient, and Sigstore is promising, GPG remains the only solution that gives you full control over your cryptographic reputation.

    By separating your master key from your subkeys and hosting your public key via WKD, you build a resilient, verifiable, and portable trust system — one that’s independent of any single platform.

    So don’t just sign your commits. Own your entire identity.


    Need help building a WKD or integrating GPG into your DevSecOps workflows? Contact ushy to get enterprise-grade cryptographic hygiene built into your pipelines.

  • Mapping complex systems without getting lost in documentation

    Mapping complex systems without getting lost in documentation

    Good infrastructure is maintainable, scalable, robust, resilient and secure. And it stays this way by putting processes into place which perform upkeep on all of these factors. So, how can you ensure that these upkeep processes are healthy and understood by everyone?

    People don’t think in text

    Our brains aren’t made for thinking in text. Try to remember the last page of the last book you read.

    Now try to remember the route you took from your childhood bedroom to the kitchen.

    We are visual-spatial thinkers. And as such, our documentation should be primarily visual-spatial.

    Flowcharts, even when done on the back of a napkin, can show you more than a full-length page of documentation.

    But why settle for napkins? You can use tools like excalidraw to draw them out, or even better; use a code-based tool to have them drawn for you.

    That way you can save the code in your versioncontrol system and see how your diagrams – and your infra, or your understanding of it – evolved over time.

    Let’s practice a little

    I use a notetaking tool called obsidian. In it, a widely used language for marking up flowcharts is used: mermaid.

    Mermaid is easily integrated into any pipeline for documentation builds. Gitlab even has it integrated into their markdown renderer!

    So, let’s learn by building.

    Step 1: Start with a basic flowchart

    flowchart TD
        A[Start] --> B[Do something] --> C[Finish]
    

    This is a basic top-down flowchart:

    • flowchart TD tells Mermaid we’re drawing a top-down flowchart.
    • The arrows (-->) connect nodes.
    • Each node has an ID (e.g. A) and a label (e.g. Start).

    Try changing the labels or rearranging the arrows. That’s the quickest way to get a feel for how it works.

    Step 2: Add conditional logic

    Let’s make it interactive by adding a decision.

    flowchart TD
        A[Start] --> B{Do you agree?}
        B -- Yes --> C[Continue]
        B -- No --> D[Stop here]
    

    Here we introduce:

    • {curly braces} to represent a decision node.
    • Multiple arrows coming out of the same node with labels (Yes and No).

    Now we have a mini flow that resembles real-world logic.

    Step 3: Modularizing with subgraphs

    As diagrams grow, structure helps. Let’s group nodes into a logical block.

    flowchart TD
        subgraph Process
            A[Start] --> B{Do you agree?}
            B -- Yes --> C[Continue]
            B -- No --> D[Stop here]
        end
        C --> E[Wrap up]
    
    • subgraph NAME groups related nodes.
    • end closes the group.
    • The outer node E connects to the subgraph.

    This is great for readability and managing large diagrams.

    Step 4: Change direction and style

    Want things to flow left to right?

    flowchart LR
        A[Login] --> B{Is password correct?}
        B -- Yes --> C[Dashboard]
        B -- No --> D[Show error]
    
    • LR means Left to Right.
    • You can also use RL (Right to Left), BT (Bottom to Top).

    Step 5: Make some sequence diagrams and GANTT charts!

    I think you get the gist of it. Let’s show you some more basic examples. How about a sequence diagram?

    sequenceDiagram
        Alice->>+John: Hello John, how are you?
        Alice->>+John: John, can you hear me?
        John-->>-Alice: Hi Alice, I can hear you!
        John-->>-Alice: I feel great!

    Or even a GANTT chart, for all of you aspiring project managers:

    gantt
        title A Gantt Diagram
        dateFormat  YYYY-MM-DD
        section Section
        A task           :a1, 2025-01-01, 30d
        Another task     :after a1  , 20d
        section Another
        Task in sec      :2025-01-12  , 12d
        another task      : 24d
    

    And if you want to get even fancier, just go visit the docs at mermaidjs.

    This skill is crucial

    While this might seem like a fun inocuous little tutorial for mermaidjs, the point I’m trying to make is this:

    There is no excuse for not having diagrams for your infrastructure, your network and your application services — and there is tremendous value in them.

    I have, in fact, on occasion helped technical teams solve their own problems just by diagramming it out. So do yourself a favor. Get your team to do diagrams instead of plain text.

    And if they’re not up for it, why not give me a call?

  • Auryn: a CLI pipeline DSL

    Auryn: a CLI pipeline DSL

    Auryn: A Domain-Specific Language for CLI Pipelines

    Sometimes you just can’t help but write a bash script to chain together a couple of tools. Sometimes those bash scripts get out of hand, and you wish you had something that could effectively be the linux pipe symbol, but also clean and format your data for the next CLI program.

    Auryn has your back.

    The challenge with CLI pipeline tooling

    Every pipeline engineer has attempted to chain tools together using Bash scripts or integrate them into Jenkins pipelines. The reality is that there’s a lack of standardized input and output formats across CLI tools.

    Each tool serves a different purpose, making automation cumbersome—even though the process is often repetitive:

    1. Read a file.
    2. Optionally transform the input.
    3. Apply the tool with specific parameters.
    4. Save the (potentially massive) output to a file.
    5. Clean the output for the next tool, which will…
    6. Repeat the cycle.

    Introducing Auryn: a CLI glue pipeline DSL

    Enter Auryn—a domain-specific language (DSL) designed to streamline shell pipelines. Think of it as a platypus: seemingly uncertain of its identity, yet functioning exceptionally well because of it.

    Here’s a glimpse of Auryn in action:

    run "bash cert_transparency_check.sh __DOMAIN__"
      parsewith cert_transparency_check 
      output "__OUTPUT_DIR__/found_domains.txt"
      as $domain_list

    In this snippet:

    • A script is executed with a constant (__DOMAIN__) as its only known truth.
    • The output is parsed using a simple parser you write in a shell script, tailored to the unique output of each CLI tool.
    • The parsed text can be saved to a file and stored in a variable ($domain_list) for use in subsequent tools.

    Why Auryn Matters

    Auryn addresses the fragmentation in CLI tooling by providing a unified language to define and manage shell workflows. It empowers security engineers to automate complex pipelines without being bogged down by inconsistent tool interfaces.

    The inspiration for Auryn

    Tom learnt about CPU pipelining and thought the mental model applied to running bash scripts as well. A couple of hours talking to an LLM later, Auryn was born.

    The cycle is:

    • Instruction Fetch
      • Get data
    • Instruction Decode
      • Parse the data for CLI input
    • Execute
      • Run the CLI program with the supplied parameters
    • Write to memory
      • Put the result into a variable
    • Write back
      • (Optionally) write the raw output to a file

    Auryn is the result of a brain blip, and was put together in a couple of hours yet results in an immensely useful output.

    Get Started with Auryn

    Ready to tame the beast? Grab a copy of Auryn from the project’s GitHub page: https://github.com/kapott/auryn. Feel free to expand upon it. While the repository doesn’t have a license yet, it will be free, as in beer.

  • Marketing portal crashes – or –  How to handle performance pain during campaign peaks

    Marketing portal crashes – or – How to handle performance pain during campaign peaks

    Marketing fires off another flashy campaign. Emails are sent, ads are clicked, users flood the customer portal like a swarm of caffeine-fueled shoppers on Black Friday. And then—it hits.

    Pages load like they’re stuck in molasses. Or worse, nothing loads at all. The system groans under the weight. Users complain. Sales stall. And someone in upper management starts asking that dreaded question: “Why weren’t we ready for this?”

    You feel the sting, not just because it’s your infrastructure, but because deep down, you knew this might happen. Again.

    Let’s unpack this. Not just with tech speak and bullet lists, but with some honest reflection—and a few solid ideas you can actually use.

    When “Success” Becomes a System Failure

    Here’s the irony: the portal’s underperformance usually stems from something good—growth. More users, more activity, more data flying around.

    But legacy systems? They don’t celebrate your wins. They choke on them.

    One day it’s a monolith that hums quietly at 15% load. The next, it’s burning CPU like it’s auditioning for a role in Mad Max: Fury Load. And that tiny, single-threaded component you inherited five CTOs ago? It’s now holding your entire digital reputation hostage.

    What’s worse is that customers don’t care why it’s slow. They just know it’s not working. They’re trying to pay their bill, check their status, file a claim—and they’re getting a spinning wheel of doom. Cue the angry tweets, lost conversions, and that cold, creeping sense of dread.


    So… What Can You Do?

    Let’s cut the fluff. The fix isn’t a motivational poster in the dev room. It’s targeted, iterative change.

    Here’s a roadmap that’s worked for real teams facing this exact pain:


    Start With Load Testing, Not Guesswork

    You wouldn’t try to tune a race car without knowing where it breaks down at high speed, right?

    Same thing here. Run proper load tests. Simulate real user traffic. Ramp it up. Push it beyond what your campaigns expect. You’ll spot bottlenecks faster than you can say “timeout error.”

    Often, it’s not the whole system that fails—just one poorly designed function. One slow database query. One dependency that starts to domino under pressure.

    And honestly? You might not like what you find. But knowing is better than waking up to a downed system.


    Find the Rotten Core (Hello, Legacy)

    Every seasoned engineer has faced it: a dusty piece of logic that no one touches because “it just works.”

    Until it doesn’t.

    Sometimes it’s a synchronous job queue. Other times it’s a memory-hungry reporting module that slams your backend when traffic spikes. We once found a SOAP connector—yes, SOAP—that was quietly blocking dozens of threads under load. Brutal.

    This is where profiling tools and call tracing shine. Tools like Jaeger, Prometheus, or even good old strace can light up the exact moment things go sideways.


    Cache Like You Mean It

    Here’s where Redis or Varnish come in handy. They’re not magic—but close.

    The idea’s simple: don’t ask your backend the same thing 5000 times per minute. If something doesn’t change often (like pricing info, account summaries, static content), cache it aggressively. Front it with a CDN. Make it boring.

    The payoff? Less stress on the core. Fewer round-trips. Happier users.


    Go Stateless—or at Least Less State-Obsessed

    Monoliths don’t scale well. Especially ones clinging to session state like a toddler to a teddy bear.

    You don’t need to break the whole thing into a thousand microservices overnight. That’s a recipe for burnout and missed deadlines. But do peel off the high-traffic routes. Things like login, dashboard views, or payment status.

    Move those to lightweight, stateless services. Use JWTs. Offload session management. You’ll sleep better.

    And yeah, containerizing helps. But not if you’re dragging old habits into shiny new pods.


    Autoscaling Isn’t a Luxury—It’s Survival

    Your Kubernetes setup might be stable now. But is it ready to flex?

    Autoscaling isn’t just a checkbox in a YAML file. It needs thoughtful metrics. CPU alone won’t cut it. Use custom metrics if you must—queue lengths, request latency, memory pressure.

    And for the love of uptime, test the autoscaler. Don’t assume it kicks in just because you told it to.

    Think of it like hiring backup staff before a sale. You want them trained and ready—not arriving after the shelves are already empty.


    The Real Fix: Culture, Not Just Code

    Let’s be honest—technical fixes are only part of the equation. The other half? Communication.

    When Marketing spins up a campaign, does Engineering even know? Are there alerting thresholds tied to business events? Does anyone talk about performance before users start yelling?

    If not, that’s the first change to make.

    Create a culture where campaigns and capacity planning go hand in hand. Where load testing isn’t a “once-a-year” task, but a habit. Where developers get curious about why a certain endpoint spikes, not just how to make it faster.


    In the End (Not That Kind of Ending)

    Systems will fail. That’s a given. But the teams who recover fast—and earn user trust—are the ones who get ahead of the next peak. Who know their limits. And who make just enough time to fix the stuff no one else sees coming.

    Next time the portal groans under pressure, let it be because you planned for it. Not because you hoped it wouldn’t happen again.

    And if you’re still waiting for someone to sign off on the load test budget—just show them last campaign’s downtime stats. That usually does the trick.


    TL;DR

    • Simulate traffic with real-world load tests
    • Hunt and kill your bottlenecks (legacy code, slow queries, blocking threads)
    • Add a cache layer with Redis, Varnish, or both
    • Break off high-traffic routes into stateless services
    • Use autoscaling like you actually believe in it
    • Tie marketing and infra planning closer together

    Need help convincing the rest of the team? Send them this post. Or better yet—print it out, tape it to the fridge in the break room, and add a sticky note:

    “This is why we can’t have nice things—unless we fix it.”

    Want help turning this into an action plan? Let’s talk.

  • Migration checklist

    Migration checklist

    Basic information

    • System or platform name
      • What are the internal names, project names, jargon, nicknames, pet names for the platform(s) and environment(s)?
    • What environments are there?
      • Classic DTAP or another model?
    • Business purpose of the platform (what does it do for the company?)
    • Owner / single point of contact for the platform
      • If there is no single point of contact, contacts for each environment or separated responsibility within the platform
        • Software deployment lifecycle (SDLC)
        • Infrastructure
        • Datacenter team
        • Crisis management
        • Information security
        • Architecture
    • Who uses it (per environment) ?
      • Internal users (+estimated number)
      • External users (+estimated number)
    • If non-standard DTAP
      • Criticality level per environment

    People & Processes

    • How do users interact with the system daily?
    • Which business processes rely on this system?
    • Any undocumented tribal knowledge?
    • Any user groups at risk during migration?

    Risks & Constraints

    • What absolutely cannot go down during migration?
    • Any critical dates (e.g. billing cycles, audits)?
    • Sensitive data or legal/compliance constraints?
    • Outstanding incidents, bugs, or known issues?

    Architecture

    • Monolith, microservices or hybrid?
      • Schema’s, flowcharts, graphics, data flow diagrams?
    • On-prem, cloud or hybrid?
    • Running on VM’s, bare metal or containers?
    • OS details (types, versions?)
    • Languages / frameworks used in application(s)?
    • Is there a CI/CD pipeline? How is code deployed?

    Network & Infrastructure

    • Existing network diagrams
      • If nonexistent, sketch one
    • Inbound
      • Ports
      • Protocols
      • Load balancers
    • Outbound
      • APIs
      • Third party services
    • Firewalls, NAT, proxies?
    • DNS structure / domain naming?
      • Internal
      • External

    Data & Storage (state)

    • Databases
      • Type(s)
      • Version(s)
      • Size(s)
    • Where is the data physically stored?
      • Does this location matter for the company?
    • Any data that _must_ not move (e.g. compliance?)
    • Existing backups? Last tested when?
    • Any shared filesystems, blob/object storage?

    Application landscape

    • Which applications are running?
      • Don’t forget cronjobs / systemd timers
      • Don’t forget custom scripts
    • What depends on what?
      • Dependency graph from app. perspective
        • Sketch one if it’s not there
    • Any blackbox components?
      • Closed source
      • Undocumented
      • Unknown-operation application/code?
    • Upcoming (or passed) or EOL components?

    Security & Access

    • What authentication methods are used?
      • Local
      • LDAP
      • SSO
      • OAuth?
    • User roles / access control model?
      • Is there a matrix table for it?
      • If not – make it or have it made
    • How are secrets handled?
      • Hardcoded
      • Ansible vault
      • env files
      • etc.
    • Network segmentation
    • Firewall rules
    • Last security audit?
      • Any open issues?

    Monitoring & Logging

    • What logs exist?
    • Where are they stored?
    • What is their (mandatory) retention?
    • Is monitoring in place?
      • Prometheus, Zabbix..
    • Are there alerts?
      • Who gets them?
    • Any trends in resource usage?

    External Dependencies

    • Are there integrations with third-party API’s or services?
    • API keys or tokens in use?
    • Any licenses, usage limits or contracts?

    Ready for Migration Planning

    • Is the environment fully mapped?
    • Can parts of the system be moved independently?
    • What goes first? What can wait?
    • What needs testing before production cutover?
  • Hello world!

    Software should be simple. Our mission is to simplify your business problems into eloquent automation you will want to use every day.