<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Chiradeep Chhaya</title>
    <description>The latest articles on DEV Community by Chiradeep Chhaya (@chiradeep_chhaya_1dadf411).</description>
    <link>https://dev.to/chiradeep_chhaya_1dadf411</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3723601%2F265d8a6d-9dfb-4c5f-9633-b099ae7af128.png</url>
      <title>DEV Community: Chiradeep Chhaya</title>
      <link>https://dev.to/chiradeep_chhaya_1dadf411</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chiradeep_chhaya_1dadf411"/>
    <language>en</language>
    <item>
      <title>OIDC SSH Login for Linux, Without the Gateway</title>
      <dc:creator>Chiradeep Chhaya</dc:creator>
      <pubDate>Tue, 14 Apr 2026 02:14:32 +0000</pubDate>
      <link>https://dev.to/chiradeep_chhaya_1dadf411/oidc-ssh-login-for-linux-without-the-gateway-5g9p</link>
      <guid>https://dev.to/chiradeep_chhaya_1dadf411/oidc-ssh-login-for-linux-without-the-gateway-5g9p</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; &lt;a href="https://github.com/prodnull/prmana" rel="noopener noreferrer"&gt;prmana&lt;/a&gt; is a PAM module and agent that replaces static SSH keys with short-lived OIDC tokens, bound to proof-of-possession via DPoP (RFC 9449). Rust, Apache-2.0. No proxy, no SSH CA — just your IdP and your Linux hosts.&lt;/p&gt;




&lt;h2&gt;
  
  
  Static SSH keys are the problem
&lt;/h2&gt;

&lt;p&gt;Every org with more than a handful of Linux servers has the same issue: SSH keys everywhere, rotated never.&lt;/p&gt;

&lt;p&gt;That key a developer generated in 2021? Still works on production. The contractor who left six months ago? Their &lt;code&gt;authorized_keys&lt;/code&gt; entry is probably still on a dozen servers. Your security team mandates MFA for email, but root access to your database server? Static key on a laptop.&lt;/p&gt;

&lt;p&gt;The usual solutions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Access platforms&lt;/strong&gt; — proxy or gateway in front of everything&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSH certificate authorities&lt;/strong&gt; — new CA infrastructure to operate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PAM/OIDC modules&lt;/strong&gt; — SSO but still bearer tokens (interceptable)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each solves part of the problem. Each adds operational complexity.&lt;/p&gt;

&lt;h2&gt;
  
  
  prmana: direct-to-host OIDC with DPoP
&lt;/h2&gt;

&lt;p&gt;prmana brings OIDC authentication directly to the Linux host via PAM, and binds every token to a cryptographic proof that it hasn't been stolen.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your Machine                          Linux Server
┌─────────────────┐                  ┌──────────────────┐
│ prmana-agent    │     SSH          │ pam_prmana.so    │
│ (OIDC + DPoP)   │ ──────────────▶  │ (validate + bind)│
└─────────────────┘                  └──────────────────┘
        │                                     │
        ▼                                     ▼
  Your IdP                             JWKS verification
  (Keycloak/Okta/                      + DPoP proof check
   Azure AD/Auth0)                     + replay protection
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;The agent gets a short-lived token from your identity provider&lt;/li&gt;
&lt;li&gt;It generates a DPoP proof — a signed JWT proving you hold the private key&lt;/li&gt;
&lt;li&gt;On SSH, the server's PAM module validates the token, checks expiry, verifies DPoP binding&lt;/li&gt;
&lt;li&gt;If it all checks out and the username maps to a local account, you're in&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No gateway. No SSH CA. No static keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why DPoP matters
&lt;/h2&gt;

&lt;p&gt;Most OIDC-for-SSH approaches use bearer tokens. If someone intercepts the token — from a log, a compromised proxy, a memory dump — they can replay it from anywhere.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc9449" rel="noopener noreferrer"&gt;DPoP (RFC 9449)&lt;/a&gt; changes that. Every authentication includes a proof signed by an ephemeral key pair. The token carries a thumbprint of the public key. The server verifies the proof matches. Token leaks? Useless without the key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"iss"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://idp.company.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cnf"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"jkt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NKnABZgU1F7M5JW5uFrETiYx..."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;cnf.jkt&lt;/code&gt; field binds the token to a specific key pair. No key, no access.&lt;/p&gt;

&lt;p&gt;The name "prmana" comes from Sanskrit (प्रमाण) — "proof" and "means of knowledge." Felt appropriate for a tool whose whole point is cryptographic proof of possession.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hardware key support
&lt;/h2&gt;

&lt;p&gt;DPoP proofs are signed by a key pair. By default that's a software key. prmana also supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;YubiKey&lt;/strong&gt; — PKCS#11 via PIV slot&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TPM 2.0&lt;/strong&gt; — platform TPM on Linux&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the DPoP key lives on hardware, the private key can't be exported. Compromised laptop? They have the token but can't sign a valid proof.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;prmana-agent login &lt;span class="nt"&gt;--signer&lt;/span&gt; yubikey:9a
&lt;span class="c"&gt;# touch the key when it blinks, then SSH normally&lt;/span&gt;
ssh server.example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What's in the repo
&lt;/h2&gt;

&lt;p&gt;Three Rust crates:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Crate&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;prmana-core&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;OIDC discovery, JWKS caching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pam-prmana&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PAM module — validation, DPoP verification, replay protection, break-glass&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;prmana-agent&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Client — token acquisition (device flow, auth code + PKCE), DPoP signing, credential storage&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Plus cross-language DPoP libraries in Go, Python, Java, and Rust.&lt;/p&gt;

&lt;p&gt;The PAM module plugs into OpenSSH's existing stack. No patches to sshd. Standard &lt;code&gt;ssh&lt;/code&gt; on the client, standard &lt;code&gt;sshd&lt;/code&gt; on the server.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it's not
&lt;/h2&gt;

&lt;p&gt;prmana does SSH login. It's not a session recorder, not a proxy, not a privileged access management suite. One job, done well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/prodnull/prmana.git
&lt;span class="nb"&gt;cd &lt;/span&gt;prmana &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; cargo build &lt;span class="nt"&gt;--workspace&lt;/span&gt;

&lt;span class="c"&gt;# Install PAM module&lt;/span&gt;
&lt;span class="nb"&gt;sudo cp &lt;/span&gt;target/release/libpam_prmana.so /lib/security/pam_prmana.so

&lt;span class="c"&gt;# Point to your IdP&lt;/span&gt;
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /etc/prmana
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;' | sudo tee /etc/prmana/policy.yaml
issuers:
  - url: https://your-idp.com/realms/your-realm
    client_id: prmana
    audiences: ["prmana"]
break_glass:
  enabled: true
  users: ["emergency-admin"]
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Login and SSH&lt;/span&gt;
prmana-agent login &lt;span class="nt"&gt;--issuer&lt;/span&gt; https://your-idp.com/realms/your-realm
ssh user@server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Docs cover setup for Keycloak, Entra ID, and Auth0.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feedback welcome
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/prodnull/prmana" rel="noopener noreferrer"&gt;github.com/prodnull/prmana&lt;/a&gt; — Apache-2.0.&lt;/p&gt;

&lt;p&gt;What we'd find most useful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Security review&lt;/strong&gt; — it's a PAM module. Poke holes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IdP testing&lt;/strong&gt; — tested against Keycloak, Auth0, Google, Entra ID. If yours does something unexpected with DPoP or device flow, we want to know.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform testing&lt;/strong&gt; — Ubuntu 22.04/24.04 primary. RHEL, Rocky, Debian reports welcome.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/prodnull/prmana/discussions" rel="noopener noreferrer"&gt;GitHub Discussions&lt;/a&gt; for questions and ideas.&lt;/p&gt;




</description>
      <category>security</category>
      <category>linux</category>
      <category>opensource</category>
      <category>rust</category>
    </item>
  </channel>
</rss>
