<?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: AlektoReef</title>
    <description>The latest articles on DEV Community by AlektoReef (@alektoreef).</description>
    <link>https://dev.to/alektoreef</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4103694%2F1035285c-24f7-407b-b9cf-3c1e3a4d3dd3.png</url>
      <title>DEV Community: AlektoReef</title>
      <link>https://dev.to/alektoreef</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alektoreef"/>
    <language>en</language>
    <item>
      <title>Privileged access management skipped everyone between 50 and 500 engineers</title>
      <dc:creator>AlektoReef</dc:creator>
      <pubDate>Tue, 01 Sep 2026 06:47:07 +0000</pubDate>
      <link>https://dev.to/alektoreef/privileged-access-management-skipped-everyone-between-50-and-500-engineers-3p4m</link>
      <guid>https://dev.to/alektoreef/privileged-access-management-skipped-everyone-between-50-and-500-engineers-3p4m</guid>
      <description>&lt;p&gt;&lt;strong&gt;Disclosure: I work on Tessera, which is one of the tools in the gap I am describing.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ask a fifty-person engineering organisation how they control production access and you will hear the same answer with small variations: a bastion host, SSH keys distributed by configuration management, a shared kubeconfig somewhere, and a spreadsheet or a Notion page that is out of date.&lt;/p&gt;

&lt;p&gt;Nobody chose that. It is what remains after the alternatives were priced.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the category got shaped
&lt;/h2&gt;

&lt;p&gt;Privileged access management grew up serving banks, telcos and governments in the 2000s. Those buyers had specific characteristics: thousands of administrators, regulators with written opinions, dedicated security teams, and procurement processes measured in quarters.&lt;/p&gt;

&lt;p&gt;Products shaped themselves accordingly. Six-figure entry prices. Deployments measured in months with professional services attached. Feature sets covering every mainframe and network appliance in a bank's estate. Sales motions that start with a discovery call and a mutual NDA.&lt;/p&gt;

&lt;p&gt;That was a reasonable fit for those buyers. It is a terrible fit for a company with sixty engineers, no dedicated security team, one person who is security-adjacent, and a procurement process that consists of a founder approving a card payment.&lt;/p&gt;

&lt;p&gt;So the mid-market did what people do when a category prices them out: they built the minimum themselves. A bastion is a bastion because it was free and it was Tuesday.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem this leaves
&lt;/h2&gt;

&lt;p&gt;The bastion answer works, up to a point, and it is worth being specific about where the point is.&lt;/p&gt;

&lt;p&gt;A bastion controls the door. It does not control the room. Once someone is through, there is no per-command record, no way to reduce their privileges while they are working, and no session replay. And keys still have to be distributed and revoked behind it, which means the original problem is intact — it just has a nicer front entrance.&lt;/p&gt;

&lt;p&gt;Three things then converge, usually in the same year:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The first enterprise customer.&lt;/strong&gt; Their security questionnaire asks about privileged access controls, session monitoring and access reviews. You answer honestly and the deal stalls, or you answer optimistically and create a problem for later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The first audit.&lt;/strong&gt; SOC 2 or ISO 27001, usually because of the customer above. The auditor asks who could access production on a date in the past. Your bastion cannot answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The first contractor at scale.&lt;/strong&gt; One contractor is manageable. A team of eight from an agency, on a six-month engagement, with three of them rotating off midway, is not manageable with keys and a spreadsheet.&lt;/p&gt;

&lt;p&gt;By the time all three arrive, the organisation needs the capability urgently and still cannot buy the enterprise product, because the price and the deployment timeline have not changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed
&lt;/h2&gt;

&lt;p&gt;Three things, over about five years.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Self-hosting became normal again.&lt;/strong&gt; Not as an anti-cloud position, but because a single binary plus PostgreSQL genuinely is easy to run, and because customer questionnaires started asking about sub-processors in a way that makes a hosted control plane an expensive answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deployment stopped requiring professional services.&lt;/strong&gt; Docker Compose, a Helm chart, an apt package. The reason PAM took months to deploy was mostly that it needed agents everywhere and integrations with everything. A proxy that requires no changes to your targets does not have that problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Per-seat pricing arrived.&lt;/strong&gt; This is the one that actually opens the market, and it is worth looking at closely.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pricing variable
&lt;/h2&gt;

&lt;p&gt;Traditional PAM prices per protected resource — per server, per host, per node. For a bank with a stable estate that is a predictable bill. For a company running autoscaling groups and splitting services, it is a bill that grows every time engineering does its job.&lt;/p&gt;

&lt;p&gt;The consequence is not just cost. It is behaviour. Tooling that gets expensive as infrastructure grows is tooling that quietly gets exceptions carved out of it — this cluster is not worth licensing, that legacy box stays outside, this shared account is easier. A control with exceptions is worse than no control, because it also produces a false sense of coverage.&lt;/p&gt;

&lt;p&gt;Per-seat pricing tracks the number of people who can reach production, which is the number that actually reflects risk, and which you already forecast because it is headcount. At $15–40 per seat per month, a sixty-engineer organisation is looking at low four figures a year. That is a card payment, not a procurement cycle.&lt;/p&gt;

&lt;h2&gt;
  
  
  The practical shape of the decision
&lt;/h2&gt;

&lt;p&gt;If you are between fifty and five hundred engineers, the honest position is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need session-level control and audit evidence, and you will need them on a deadline set by a customer or an auditor rather than by you.&lt;/li&gt;
&lt;li&gt;Building it is a quarter of engineering time to reach parity on SSH alone, and internal tools funded by attention rather than budget reliably stall at 80%.&lt;/li&gt;
&lt;li&gt;The enterprise products are not sized for you and never were.&lt;/li&gt;
&lt;li&gt;The self-hostable, per-seat tools are: Teleport, Boundary, and Tessera, which I work on. All three have free tiers you can run this week without talking to anyone.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The specific recommendation matters less than the timing one. &lt;strong&gt;Do this before the audit, not during it.&lt;/strong&gt; Evidence has to cover the audit period, and a system deployed in month eleven gives you one month of evidence and eleven months of explanation.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Tessera is a self-hosted access broker for SSH, Kubernetes, databases and RDP. Per seat, free for a single administrator, nothing installed on your targets. &lt;a href="https://tessera.company/pricing/" rel="noopener noreferrer"&gt;https://tessera.company/pricing/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>devops</category>
      <category>management</category>
    </item>
    <item>
      <title>Auditors do not want your policy. They want an artefact.</title>
      <dc:creator>AlektoReef</dc:creator>
      <pubDate>Tue, 01 Sep 2026 06:47:01 +0000</pubDate>
      <link>https://dev.to/alektoreef/auditors-do-not-want-your-policy-they-want-an-artefact-3ipb</link>
      <guid>https://dev.to/alektoreef/auditors-do-not-want-your-policy-they-want-an-artefact-3ipb</guid>
      <description>&lt;p&gt;&lt;strong&gt;Disclosure: I work on an access tool (Tessera), mentioned once at the end. Everything before that is about evidence, and applies whatever you use.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The most common surprise in a first SOC 2 or ISO 27001 audit is not that a control is missing. It is that a control exists, works, and cannot be evidenced — so it counts as absent.&lt;/p&gt;

&lt;p&gt;The distinction is worth stating precisely, because it is not obvious until it has cost you something.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A control&lt;/strong&gt; is a thing that is true about your system. Only authorised engineers can reach production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Evidence&lt;/strong&gt; is an artefact, produced by a system rather than by a person, that demonstrates the control was operating throughout the audit period — not on the day someone checked.&lt;/p&gt;

&lt;p&gt;Most organisations have decent controls. Most cannot produce evidence, because their controls live in places that do not emit artefacts: a bastion's &lt;code&gt;authorized_keys&lt;/code&gt; file, a spreadsheet, a Slack thread where someone approved something, and the collective memory of three engineers.&lt;/p&gt;

&lt;h2&gt;
  
  
  What gets asked for
&lt;/h2&gt;

&lt;p&gt;Reconstructed from what people have told me, this is the shape of the questions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Show me everyone who could access production on 14 March."&lt;/strong&gt; Not today. A specific date in the past, usually chosen by the auditor. This is the one that catches people, because most systems can tell you the current state and cannot tell you a historical one. &lt;code&gt;authorized_keys&lt;/code&gt; has no history. A spreadsheet has whatever history git gives it, if it is in git, which it usually is not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Show me that this person's access ended when their employment ended."&lt;/strong&gt; Both timestamps, from two systems, matched. HR has the first. The second is the problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Show me the approval for this elevated access."&lt;/strong&gt; Not that a policy requires approval — the specific approval, for this specific grant, with who approved it and when.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Show me what was done in this session."&lt;/strong&gt; Increasingly common where production access to customer data is involved. Not "we log commands", but the actual record for a named session.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Show me that these controls operated for the whole period."&lt;/strong&gt; The hardest one, and the reason point-in-time screenshots fail. A screenshot proves a moment. The auditor is asking about twelve months.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the usual sources fail
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Spreadsheets&lt;/strong&gt; are claims, not evidence. They record what someone believed, are edited without history, and cannot answer a question about a past date.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;authorized_keys&lt;/code&gt; files&lt;/strong&gt; hold current state only. You can see who has access now. You cannot see who had access in March, or when a key was added, or by whom.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ticket systems&lt;/strong&gt; record that a request happened, not that the access was actually granted, matched the request, or was removed afterwards. The gap between the ticket and the system is exactly where findings live.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chat approvals&lt;/strong&gt; are not retrievable at audit scale and not tamper-evident. "Someone said yes in a thread" is a story.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shell history&lt;/strong&gt; is user-writable, per-host, and rotates. It is not evidence of anything, and treating it as such is worse than admitting you have nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually works
&lt;/h2&gt;

&lt;p&gt;Three properties, and they are not features of any particular product — they are what makes an artefact count.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It lives somewhere other than the system it describes.&lt;/strong&gt; This is the one that does the real work, and it is worth being blunt about why.&lt;/p&gt;

&lt;p&gt;Most audit logs, including ours, are rows in a database. There is no hash chain, no write-once storage, no cryptographic seal. Anyone with database access can edit a row, and nothing in the log itself would show it. That is the normal state of affairs for this class of product, and any vendor telling you their log is tamper-proof should be asked exactly how, because the honest answer is usually "it is not, it is a table".&lt;/p&gt;

&lt;p&gt;Which means the property you actually need is not that the log cannot be altered. It is that &lt;strong&gt;altering it is not enough&lt;/strong&gt;, because a copy already left the building. If the access system's own database is the only copy, then compromising that system compromises the evidence of the compromise — and the person best placed to do that is the administrator whose actions the log exists to record.&lt;/p&gt;

&lt;p&gt;Streaming to a SIEM or any external collector as events happen is what closes this. The copy in the collector is written by a different system, under different credentials, usually with different administrators. Editing the source row after the fact does not retract it. That is where your tamper-evidence comes from — not from the broker, from the separation.&lt;/p&gt;

&lt;p&gt;If you are evaluating tools in this category, this is the question to ask: not "is the log immutable" but "how fast does a copy leave the box, and who controls the destination".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It is produced automatically.&lt;/strong&gt; Evidence created by a human is a claim about the system. Evidence emitted by the system is a fact about it. This is the difference between "we review access quarterly" (a process, evidenced by a document someone wrote) and "here is the log of every grant and revocation with timestamps" (a fact, evidenced by itself).&lt;/p&gt;

&lt;p&gt;Add a fourth, practical one: &lt;strong&gt;you can export it.&lt;/strong&gt; Auditors work in spreadsheets and PDFs. A beautiful dashboard you cannot export is a demo, not evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  The thing nobody plans
&lt;/h2&gt;

&lt;p&gt;Retention.&lt;/p&gt;

&lt;p&gt;Your audit period is twelve months. Your evidence has to cover twelve months. If your log rotates at 90 days, you have three months of evidence and nine months of an explanation.&lt;/p&gt;

&lt;p&gt;Set retention to your compliance window before you need it, and size the disk for that window deliberately. Both failure directions are real: too short and you cannot evidence the period; infinite and you are storing personal data forever, which is its own regulatory problem and will show up in a different part of the same audit.&lt;/p&gt;

&lt;h2&gt;
  
  
  The reframe worth taking away
&lt;/h2&gt;

&lt;p&gt;Access control is usually budgeted as prevention — stopping bad things. That framing makes it compete with every other security spend and usually lose, because prevention is unfalsifiable until it fails.&lt;/p&gt;

&lt;p&gt;The evidence framing is different and easier to defend. &lt;strong&gt;The artefacts are what unblocks enterprise deals and passes audits&lt;/strong&gt;, which are revenue events with dates attached. A control you cannot evidence does not appear in either.&lt;/p&gt;

&lt;p&gt;That is the argument that gets budget approved, and it happens to also be the true one.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Tessera logs every login, command and approval, and streams the log to your SIEM as events happen so a copy exists outside the broker. The log itself is database rows, not a hash chain — the copy in your collector is what makes it hold up. &lt;a href="https://tessera.company/docs/access/audit/siem/" rel="noopener noreferrer"&gt;https://tessera.company/docs/access/audit/siem/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>devops</category>
      <category>compliance</category>
    </item>
    <item>
      <title>Your access tool is a vendor with a copy of your infrastructure map</title>
      <dc:creator>AlektoReef</dc:creator>
      <pubDate>Tue, 01 Sep 2026 06:46:55 +0000</pubDate>
      <link>https://dev.to/alektoreef/your-access-tool-is-a-vendor-with-a-copy-of-your-infrastructure-map-4ikb</link>
      <guid>https://dev.to/alektoreef/your-access-tool-is-a-vendor-with-a-copy-of-your-infrastructure-map-4ikb</guid>
      <description>&lt;p&gt;&lt;strong&gt;Disclosure: I work on Tessera, which is self-hosted. That is the position I am arguing from, and the costs of that position are in the last section.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Security questionnaires ask where customer data is processed. Access-control tools tend to get a shallow answer to that question, because people think of them as gatekeepers rather than as data processors. They are both.&lt;/p&gt;

&lt;p&gt;Here is what a hosted access broker necessarily knows about you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your infrastructure inventory.&lt;/strong&gt; Every registered target: hostnames, addresses, cluster endpoints, database names, environment labels. That is a map of your estate, and it is the document an attacker would most like to read before deciding where to spend effort.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your organisational structure.&lt;/strong&gt; Who has access to what, which teams exist, who approves for whom, who was granted production access at 2am during an incident. Org charts are inferable from access graphs with unpleasant accuracy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your session content, if recording is on.&lt;/strong&gt; Every command, every query, every screen of output. That includes whatever your engineers pasted into a terminal, which — be honest about your own estate here — includes secrets sometimes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Timing.&lt;/strong&gt; When your incidents happen, how long they last, who gets pulled in. That is commercially sensitive on its own.&lt;/p&gt;

&lt;p&gt;None of that requires the vendor to hold your credentials. It is the metadata, and the metadata is the part that survives every architectural mitigation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this shows up
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;GDPR and processor chains.&lt;/strong&gt; Session recordings contain personal data — identified individuals performing identified actions at identified times. A hosted vendor is a processor, which means a DPA, transfer mechanisms if data leaves the EEA, and a sub-processor list you have to monitor. It also means their sub-processors become your problem, which is a chain you do not control and cannot easily audit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sector rules.&lt;/strong&gt; Financial services, healthcare and public sector procurement in most European jurisdictions have specific requirements about where data resides and who can compel access to it. "The vendor has a region in Frankfurt" answers less of this than people assume, because residency and jurisdiction are different questions, and the second one follows the vendor's corporate structure rather than the location of the disk.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your own customers' questionnaires.&lt;/strong&gt; If you sell to enterprises, their questionnaire asks about your sub-processors. An access tool with a hosted control plane is a sub-processor, and it is one you have to justify to every customer who reads carefully.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Availability of the access path.&lt;/strong&gt; A hosted control plane means your ability to reach your own production depends on a third party's uptime. This is not theoretical — it is the scenario where you cannot log in to fix your outage because their outage is upstream of yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  What self-hosting changes
&lt;/h2&gt;

&lt;p&gt;The whole thing collapses if the control plane runs inside your perimeter.&lt;/p&gt;

&lt;p&gt;The vendor is not a processor, because they process nothing. There is no DPA to negotiate for this component, no sub-processor chain, no transfer mechanism, and nothing to disclose on your own sub-processor list. Data residency is wherever you deployed it, which is a question you already answered for the rest of your infrastructure.&lt;/p&gt;

&lt;p&gt;It also removes the vendor's jurisdiction from the analysis entirely. This is the part worth stating plainly, because it is where people get it backwards. &lt;strong&gt;Where the vendor is incorporated stops mattering when the vendor never receives the data.&lt;/strong&gt; A self-hosted tool from a company in Chișinău, Tallinn, Delaware or Singapore has the same data-residency answer, because the answer is determined by where you ran the binary.&lt;/p&gt;

&lt;p&gt;The check that makes this real is licensing. A tool that self-hosts but phones home for entitlement still has a channel to the vendor, and that channel carries telemetry — usually usage counts, sometimes more. Offline licence verification, where the entitlement is a file checked locally with no outbound request, is what makes "self-hosted" mean what people think it means. Ask about this specifically during evaluation; it is the difference between a self-hosted product and a hosted product with a local component.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it costs you
&lt;/h2&gt;

&lt;p&gt;Self-hosting moves the work rather than removing it, and the honest version of this argument includes the bill.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You operate it.&lt;/strong&gt; Patching, upgrades, backups, monitoring. For a single Go binary plus PostgreSQL that is modest — this is not Kubernetes — but it is not zero, and it lands on a team that already has a queue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You own the availability.&lt;/strong&gt; If the broker is in the access path and it is down, nobody gets in. You need a deployment topology, a backup of the encryption and JWT secrets, and a break-glass procedure that does not depend on the broker. Nobody else is going to page themselves about this at 3am.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You own the hardening.&lt;/strong&gt; A box holding production credentials is a high-value target. TLS terminated properly, PostgreSQL restricted, metrics endpoints protected, and ideally an independent penetration test before go-live. A hosted vendor does some of this for you; when you self-host, you do all of it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There is no vendor SOC 2 to point at for this component.&lt;/strong&gt; That cuts both ways. You cannot hand an auditor a third-party report, but you also do not need one, because the component is inside a perimeter you are already attesting to. Which of those is better depends entirely on who is asking.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trade
&lt;/h2&gt;

&lt;p&gt;Hosted buys you operational simplicity and a compliance artefact, and costs you a processor relationship, a sub-processor chain, and a third party holding a map of your infrastructure.&lt;/p&gt;

&lt;p&gt;Self-hosted costs you operational work and gives you an answer to the residency question that does not require anyone to trust a vendor's jurisdiction, including the vendor's.&lt;/p&gt;

&lt;p&gt;For most organisations under a few hundred engineers, running one Go binary and one Postgres instance is a smaller ongoing burden than maintaining a processor relationship and defending it in every customer questionnaire. But it is a real trade and it should be made deliberately rather than by default in either direction.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Tessera is self-hosted with offline licence verification — the control plane runs entirely inside your perimeter and makes no outbound requests. &lt;a href="https://tessera.company/docs/operations/secure/" rel="noopener noreferrer"&gt;https://tessera.company/docs/operations/secure/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>devops</category>
      <category>privacy</category>
    </item>
    <item>
      <title>What running your own SSH certificate authority actually costs</title>
      <dc:creator>AlektoReef</dc:creator>
      <pubDate>Tue, 01 Sep 2026 06:46:49 +0000</pubDate>
      <link>https://dev.to/alektoreef/what-running-your-own-ssh-certificate-authority-actually-costs-44ho</link>
      <guid>https://dev.to/alektoreef/what-running-your-own-ssh-certificate-authority-actually-costs-44ho</guid>
      <description>&lt;p&gt;&lt;strong&gt;Disclosure: I work on Tessera, which is on the buy side of this. I have tried to cost the build side properly, because a comparison where the build option looks stupid is a comparison nobody believes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every engineering team that has this problem considers building it. That instinct is correct. SSH certificates are a well-understood, well-documented mechanism, everything you need ships with OpenSSH, and the first working version takes a competent engineer about a week.&lt;/p&gt;

&lt;p&gt;The week is not the cost. Here is what is.&lt;/p&gt;

&lt;h2&gt;
  
  
  The build, honestly
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The CA itself.&lt;/strong&gt; You generate a key pair, configure targets with &lt;code&gt;TrustedUserCAKeys&lt;/code&gt;, and sign user keys with a short validity. This part genuinely is a week, and it works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Protecting the CA key.&lt;/strong&gt; This is where the estimate starts moving. The CA private key can now grant access to every host in the fleet. On a laptop it is an incident waiting to happen. So you want it in an HSM or a KMS, which means an integration, which means the signing operation now has an availability dependency, which means a runbook for what happens when that dependency is down. Call it two to four weeks including the operational work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issuance.&lt;/strong&gt; Someone has to request a certificate and something has to decide whether to give them one. That means integrating with your identity provider, mapping groups to principals, building a request path, and building an approval path if you want anything time-boxed or justified. This is the real project, and it is measured in months rather than weeks, because it is where the requirements keep arriving.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rollout across the estate.&lt;/strong&gt; Editing &lt;code&gt;sshd_config&lt;/code&gt; on every production host. Technically trivial, organisationally not: change window, sign-off, rollback plan, and the discovery that four hosts are not in configuration management and one of them is important.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Audit.&lt;/strong&gt; Certificates tell you a session was authorised. They do not tell you what happened in it. If your requirement includes per-command history or session replay — and if you have auditors, it does — that is a separate system, and it is a much harder one than the CA.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kubernetes and databases.&lt;/strong&gt; None of the above covers them. Cluster access and database credentials are separate problems with separate solutions, and in most organisations they are where the genuinely dangerous access lives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ongoing.&lt;/strong&gt; OpenSSH releases, key rotation, principal changes when the org restructures, the on-call rotation for a system that is now in the path of every production login, and the knowledge concentration risk when the one person who understands it takes another job.&lt;/p&gt;

&lt;h2&gt;
  
  
  The number
&lt;/h2&gt;

&lt;p&gt;The first version is a week. A production-grade system covering SSH only, with IdP integration and an approval flow, is a quarter of engineering time. Adding session audit roughly doubles it. Covering Kubernetes and databases as well is a small internal product with a permanent maintenance cost.&lt;/p&gt;

&lt;p&gt;Say two to three engineer-quarters to reach parity with a commercial tool on SSH alone, plus ongoing maintenance somewhere between a tenth and a quarter of an engineer indefinitely.&lt;/p&gt;

&lt;p&gt;At a loaded cost of $150–200k per engineer-year, that is $75k–150k to build and $15k–50k a year to keep. Adjust for your market, but the shape holds.&lt;/p&gt;

&lt;h2&gt;
  
  
  When building is right anyway
&lt;/h2&gt;

&lt;p&gt;I want to be clear that this is often the correct decision.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You are large enough that per-seat pricing exceeds the build cost.&lt;/strong&gt; At a few thousand engineers the arithmetic inverts, which is why the very large companies all built their own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your requirements are genuinely unusual.&lt;/strong&gt; Air-gapped, exotic hardware, a regulator with specific opinions, an existing identity system nothing integrates with.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need to read the source.&lt;/strong&gt; For some organisations this is not a preference.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The work is strategic.&lt;/strong&gt; If access is close to your product, owning it is reasonable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If two or more of those are true, build. The rest of this is not aimed at you.&lt;/p&gt;

&lt;h2&gt;
  
  
  When buying is right
&lt;/h2&gt;

&lt;p&gt;The argument for buying is not that building is hard. It is that the build has a specific failure mode: &lt;strong&gt;it gets to 80% and stops.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The CA gets built. The issuance flow gets built. Then the engineer who owned it moves to a different team, the approval workflow stays manual, the audit piece never happens, Kubernetes never gets covered, and two years later you have a system that works, that nobody fully understands, and that does not answer the question the auditor is actually asking.&lt;/p&gt;

&lt;p&gt;That is not a hypothetical failure mode. It is the normal one, because internal tools are funded by attention rather than by budget, and attention moves.&lt;/p&gt;

&lt;p&gt;The second argument is scope. Buying covers SSH, Kubernetes, databases and Windows on day one. Building covers SSH first and the rest maybe, and the rest is where the interesting damage lives.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pricing variable, which is worth checking
&lt;/h2&gt;

&lt;p&gt;If you do buy, look at what the vendor charges for.&lt;/p&gt;

&lt;p&gt;A lot of this market prices per protected resource — per server, per host, per node. Think about what that does. The bill grows every time you add a machine. An autoscaling group that doubles overnight doubles a line item. A team that splits a monolith into twelve services on twelve instances pays twelve times more for the same twelve engineers doing the same work.&lt;/p&gt;

&lt;p&gt;You are being billed for the size of your infrastructure. What the tool controls is how many people can reach it, and that is a headcount number you already forecast.&lt;/p&gt;

&lt;p&gt;This matters beyond the invoice. Tooling that gets expensive at scale is tooling that quietly gets routed around at scale — a shared account here, a bastion exception there — and a control people route around is worse than no control, because it also produces a false sense of coverage. Per-seat pricing is not just cheaper for most organisations; it removes the incentive to undermine the thing you bought.&lt;/p&gt;

&lt;h2&gt;
  
  
  The actual decision
&lt;/h2&gt;

&lt;p&gt;Not "can we build this" — you can. It is: &lt;strong&gt;will this still be maintained in three years, and will it cover Kubernetes and databases by then?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If yes, build it. If you are not confident, the honest comparison is not build cost versus licence cost. It is a licence against a system that reaches 80% and stalls, which is a worse outcome than either.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Tessera is a self-hosted access broker for SSH, Kubernetes, databases and RDP, priced per seat, free to self-host for a single administrator. &lt;a href="https://tessera.company/docs/get-started/what-is-tessera/" rel="noopener noreferrer"&gt;https://tessera.company/docs/get-started/what-is-tessera/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>devops</category>
      <category>ssh</category>
    </item>
    <item>
      <title>Four security decisions that look like nothing and are not</title>
      <dc:creator>AlektoReef</dc:creator>
      <pubDate>Tue, 01 Sep 2026 06:46:43 +0000</pubDate>
      <link>https://dev.to/alektoreef/four-security-decisions-that-look-like-nothing-and-are-not-4144</link>
      <guid>https://dev.to/alektoreef/four-security-decisions-that-look-like-nothing-and-are-not-4144</guid>
      <description>&lt;p&gt;&lt;strong&gt;Disclosure: these are decisions from Tessera, which I work on. They are all small enough to copy into your own service, which is why they are worth writing up.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Security feature lists are made of nouns: encryption, RBAC, SSO, audit. The things that actually decide whether a system holds up are smaller than that and never make the list. Here are four of ours, with the reasoning, including the case where we made the trade in the direction most people do not.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The login rate limit ignores X-Forwarded-For
&lt;/h2&gt;

&lt;p&gt;Rate limiting a login endpoint is table stakes. The question is what you count against.&lt;/p&gt;

&lt;p&gt;The natural implementation reads &lt;code&gt;X-Forwarded-For&lt;/code&gt;, because your service is behind a load balancer and the real client address is in that header. Almost every tutorial does it this way.&lt;/p&gt;

&lt;p&gt;The problem: &lt;code&gt;X-Forwarded-For&lt;/code&gt; is a request header. If your service trusts it, an attacker sets a different value on every request and each one gets its own bucket. You have not built a rate limit, you have built a counter that resets on demand, and the dashboards will look perfectly healthy while the endpoint is being brute-forced.&lt;/p&gt;

&lt;p&gt;We count against the &lt;strong&gt;real TCP connection address&lt;/strong&gt; instead. That is the peer address of the socket, which an attacker cannot forge without actually controlling that address.&lt;/p&gt;

&lt;p&gt;The cost is real and worth naming. Behind a reverse proxy, every request arrives from the proxy's address, so the limit applies to the proxy as a whole rather than per client. That is a worse experience in some topologies, and people will file bugs about it.&lt;/p&gt;

&lt;p&gt;We took that trade because a rate limit that can be bypassed by setting a header is not a degraded rate limit, it is the absence of one — and the absence is worse than useless, because it looks like presence. If you do want per-client limits behind a proxy, the answer is an explicit allowlist of trusted proxy addresses whose forwarded headers you accept, not blanket trust of a header.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Target addresses are validated to prevent SSRF
&lt;/h2&gt;

&lt;p&gt;Our controller is, by design, a machine with network access to everything interesting in the estate. It also has an admin UI where you register a target by typing a hostname.&lt;/p&gt;

&lt;p&gt;Put those two facts next to each other and you have an internal network scanner with a web interface. Register &lt;code&gt;169.254.169.254&lt;/code&gt; as a target and the controller will happily dial the cloud metadata endpoint for you. Register a loopback address and it will connect to itself.&lt;/p&gt;

&lt;p&gt;So target hosts are validated on creation &lt;strong&gt;and on update&lt;/strong&gt; — the second one matters, because validate-on-create with an unchecked update path is a very common way to have this bug while believing you fixed it. Loopback, link-local and the other address ranges that turn the service into a scanner are rejected.&lt;/p&gt;

&lt;p&gt;The general shape: any service that takes a user-supplied address and connects to it needs this check. That includes webhook configuration, avatar-by-URL, PDF renderers that fetch images, and anything with an "import from URL" field. It is the same bug every time.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Host keys are pinned on first use
&lt;/h2&gt;

&lt;p&gt;SSH's host-key model is the reason &lt;code&gt;StrictHostKeyChecking&lt;/code&gt; exists and the reason most people have typed &lt;code&gt;yes&lt;/code&gt; without reading the fingerprint. When a broker connects on behalf of a hundred engineers, "did anyone check the fingerprint" needs a better answer than "probably not".&lt;/p&gt;

&lt;p&gt;We store the target's host public key on first connection and reject changed keys afterwards. This is trust-on-first-use, raised from an individual's &lt;code&gt;known_hosts&lt;/code&gt; to an organisational record.&lt;/p&gt;

&lt;p&gt;What it catches: a silent man-in-the-middle substitution after the fact. If someone puts themselves between the controller and a target, the key changes and the connection fails loudly instead of succeeding quietly.&lt;/p&gt;

&lt;p&gt;What it does not catch, and I want to be plain about this: &lt;strong&gt;the first connection.&lt;/strong&gt; TOFU is trust on first use, and if the attacker is already in position when you register the target, you pin their key. The only real fix is out-of-band host-key distribution, which almost nobody runs. Anyone claiming TOFU closes this hole is overselling it.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Stored credentials cannot be read back
&lt;/h2&gt;

&lt;p&gt;The console is write-only for secrets. You can store a credential and you can replace it. There is no path — UI or API — that returns it.&lt;/p&gt;

&lt;p&gt;This is more annoying than it sounds. Users want to check what they typed. Support wants to verify a password before debugging a connection failure. Every so often someone asks for a "reveal" button and there is always a plausible reason.&lt;/p&gt;

&lt;p&gt;The reason to refuse: a read path is a read path. Once it exists, it is reachable by whoever holds an admin session, whoever finds an IDOR in that endpoint, and whoever ends up with a token that was scoped more broadly than intended. The compromise of one admin account stops being "an attacker can grant themselves access, which is logged" and becomes "an attacker walks away with every production credential you have, silently".&lt;/p&gt;

&lt;p&gt;The mitigation for the annoyance is a connectivity test that uses the credential without returning it. That covers the legitimate use case, which was never really "show me the password" — it was "tell me whether this works".&lt;/p&gt;

&lt;h2&gt;
  
  
  The pattern
&lt;/h2&gt;

&lt;p&gt;All four decisions have the same shape. There is a version that is easier to build and nicer to use, and a version that holds when someone is actively working against it. The gap between them is invisible in normal operation, which is exactly why it survives code review, and exactly why it should be written down somewhere rather than living in one engineer's head.&lt;/p&gt;

&lt;p&gt;None of these are clever. Every one of them is the boring choice. That is the point.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;More on Tessera's security model, including the hardening checklist and the parts our own docs describe as best-effort rather than guaranteed: &lt;a href="https://tessera.company/docs/operations/secure/" rel="noopener noreferrer"&gt;https://tessera.company/docs/operations/secure/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>go</category>
      <category>devops</category>
    </item>
    <item>
      <title>Sizing a session broker: the unit is concurrent sessions, and the bottleneck is not the CPU</title>
      <dc:creator>AlektoReef</dc:creator>
      <pubDate>Tue, 01 Sep 2026 06:46:37 +0000</pubDate>
      <link>https://dev.to/alektoreef/sizing-a-session-broker-the-unit-is-concurrent-sessions-and-the-bottleneck-is-not-the-cpu-2j13</link>
      <guid>https://dev.to/alektoreef/sizing-a-session-broker-the-unit-is-concurrent-sessions-and-the-bottleneck-is-not-the-cpu-2j13</guid>
      <description>&lt;p&gt;&lt;strong&gt;Disclosure: the numbers below are from Tessera, which I work on. The reasoning applies to any proxy that sits in a session path.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every vendor page in this category says something like "scales to thousands of users". It is a useless number, because a user who is not connected costs nothing. What costs something is a session that is open right now.&lt;/p&gt;

&lt;p&gt;So here is the arithmetic instead, with the method, so you can check it against whatever you are evaluating.&lt;/p&gt;

&lt;h2&gt;
  
  
  The unit
&lt;/h2&gt;

&lt;p&gt;The controller does not know how many engineers you employ and does not care how many targets are registered. It knows how many sessions are open.&lt;/p&gt;

&lt;p&gt;The planning rule that has held up for us: &lt;strong&gt;on a normal working day, 10–20% of a team is connected at once.&lt;/strong&gt; A 200-person engineering organisation is 20–40 concurrent sessions, not&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Size for your own observed peak, but if you are estimating from scratch, start there.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This matters because the difference between the two numbers is the difference between a 512 MB VM and an argument about whether you need a cluster.&lt;/p&gt;

&lt;h2&gt;
  
  
  Memory
&lt;/h2&gt;

&lt;p&gt;A proxied session is mostly buffers. In our case: about 256 KB of copy buffers, plus 6 to 10 goroutines at roughly 8 KB of stack each. Call it &lt;strong&gt;320 KB per session.&lt;/strong&gt; The base Go process is about 30 MB.&lt;/p&gt;

&lt;p&gt;So 200 concurrent sessions is 200 × 320 KB ≈ 64 MB of live data, plus 30 MB base, ≈ 94 MB.&lt;/p&gt;

&lt;p&gt;Except that is not what &lt;code&gt;RSS&lt;/code&gt; will show you, and this is the part people get wrong when they size Go services. Go does not hand memory back to the operating system promptly, and at the default &lt;code&gt;GOGC=100&lt;/code&gt; the collector lets the heap grow to roughly twice the live set before collecting. So resident memory settles at about double the arithmetic.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concurrent sessions&lt;/th&gt;
&lt;th&gt;vCPU&lt;/th&gt;
&lt;th&gt;Expected resident&lt;/th&gt;
&lt;th&gt;Provision&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;up to 50&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;~90 MB&lt;/td&gt;
&lt;td&gt;512 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50–200&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;~190 MB&lt;/td&gt;
&lt;td&gt;1 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;200+&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;~380 MB&lt;/td&gt;
&lt;td&gt;2 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The gap between the last two columns is headroom for spikes, not a hidden cost. A controller serving 200 sessions really does use a couple of hundred megabytes. Our Helm chart ships &lt;code&gt;requests: 256Mi&lt;/code&gt; and &lt;code&gt;limits: 1Gi&lt;/code&gt;, which is the middle row.&lt;/p&gt;

&lt;p&gt;If you are evaluating something else in this category, ask the vendor for per-session memory and the base process size. If they can only give you a user count, they have not measured it.&lt;/p&gt;

&lt;h2&gt;
  
  
  CPU, and why it is almost never the answer
&lt;/h2&gt;

&lt;p&gt;The obvious worry is encryption. The proxy terminates TLS with the client and runs SSH with the target, so every byte is encrypted and decrypted twice.&lt;/p&gt;

&lt;p&gt;On any CPU made in the last decade this is cheap, because AES-NI does it in hardware. For interactive work — people typing into terminals — CPU is essentially never the constraint. One vCPU handles 50 concurrent sessions; four handles 200-plus.&lt;/p&gt;

&lt;p&gt;The exceptions are the two workloads that are not interactive: sustained file transfer, and RDP. Both of those hit a different limit first.&lt;/p&gt;

&lt;h2&gt;
  
  
  The actual bottleneck
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;All session traffic passes through the controller twice.&lt;/strong&gt; Client → controller → target and back. Moving N bytes costs 2N on the controller's network interface.&lt;/p&gt;

&lt;p&gt;That interface is what saturates first, and it is what you should be sizing.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Workload&lt;/th&gt;
&lt;th&gt;Link&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Interactive only (SSH · Kubernetes · SQL)&lt;/td&gt;
&lt;td&gt;100 Mbit/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interactive + occasional &lt;code&gt;scp&lt;/code&gt; / &lt;code&gt;rsync&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;1 Gbit/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Heavy transfer, or 50+ concurrent RDP&lt;/td&gt;
&lt;td&gt;10 Gbit/s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Interactive terminal traffic is negligible — keystrokes and screen output are a few KB/s per session, so even 200 concurrent shells is single-digit Mbit/s. You could run that over a home connection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RDP is a different category entirely.&lt;/strong&gt; One RDP session runs 0.5 to 5 Mbit/s, more with video or a large high-colour desktop, and it is sustained rather than bursty. That is two to three orders of magnitude above a shell. Twenty concurrent RDP sessions, once you count the double hop, is 100–200 Mbit/s through the controller.&lt;/p&gt;

&lt;p&gt;The practical consequence: if RDP is a meaningful part of your usage, size the link from the RDP sessions alone and treat everything else as rounding error. Mixing them into one average will give you a number that is wrong in the direction that hurts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Latency, which nobody sizes and everybody feels
&lt;/h2&gt;

&lt;p&gt;Bandwidth is the number people plan. Latency is the number people complain about.&lt;/p&gt;

&lt;p&gt;The double hop adds round-trip time on every keystroke echo. If the controller is in a different region from the targets, users will describe the tool as "laggy" and they will be right, and no amount of bandwidth fixes it.&lt;/p&gt;

&lt;p&gt;Put the controller in the same datacentre as the targets. If you have targets in three regions, that is an argument for three controllers, not for one very well-connected controller.&lt;/p&gt;

&lt;h2&gt;
  
  
  The overhead you are buying
&lt;/h2&gt;

&lt;p&gt;For completeness, the cost of the proxy itself. 20 concurrent SSH sessions over loopback:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Native SSH&lt;/th&gt;
&lt;th&gt;Through the proxy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Aggregate throughput&lt;/td&gt;
&lt;td&gt;~540 MB/s&lt;/td&gt;
&lt;td&gt;~440 MB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Overhead&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;~20%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Loopback is deliberate. It removes the network from the measurement and leaves only the cost of the extra encrypt/decrypt pass and the buffer copies. &lt;strong&gt;On a real network the double hop adds its own cost, so 20% is a floor, not an expectation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The honest conclusion from that number is not "20% is fine". It is: &lt;strong&gt;do not put bulk transfer through a session broker.&lt;/strong&gt; Database dumps, backups, CI artefacts — route them outside. Use the broker for interactive access, which is what it is for and where the overhead is invisible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Storage, and the default that will surprise you
&lt;/h2&gt;

&lt;p&gt;Two separate growth curves.&lt;/p&gt;

&lt;p&gt;Session recordings: only terminal sessions are recorded, as asciinema &lt;code&gt;.cast&lt;/code&gt; files, at roughly &lt;strong&gt;1–5 MB per session-hour&lt;/strong&gt;. Someone typing sits at the bottom of that range. &lt;code&gt;tail -f&lt;/code&gt; on a busy log, or &lt;code&gt;htop&lt;/code&gt; redrawing a full screen, goes past the top of it.&lt;/p&gt;

&lt;p&gt;RDP is the exception again, in the opposite direction. The tunnel carries the RDP protocol rather than a terminal stream, so there is nothing to record — only audit events. That saves you the disk it would otherwise cost, and it costs you the replay. If your controls assume a recording exists for every session, that assumption does not hold for RDP.&lt;/p&gt;

&lt;p&gt;The audit log lives in PostgreSQL and grows by events rather than by time — a session start and close, plus a row per command. A few hundred kilobytes per session-hour is a reasonable first guess, and your own first month is a much better one.&lt;/p&gt;

&lt;p&gt;Both retention settings default to zero, which means keep forever. If you deploy and forget, the answer to "how big does this get" is "as big as the controller's entire history". Set them to your compliance window on day one, not on the day the disk fills.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;The numbers here are from Tessera's requirements documentation and the benchmark is reproducible with the &lt;code&gt;-compare&lt;/code&gt; mode of our loadtest tool: &lt;a href="https://tessera.company/docs/reference/requirements/" rel="noopener noreferrer"&gt;https://tessera.company/docs/reference/requirements/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>devops</category>
      <category>performance</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Agents or a proxy: the access-control decision you make before you compare any features</title>
      <dc:creator>AlektoReef</dc:creator>
      <pubDate>Tue, 01 Sep 2026 06:46:31 +0000</pubDate>
      <link>https://dev.to/alektoreef/agents-or-a-proxy-the-access-control-decision-you-make-before-you-compare-any-features-92c</link>
      <guid>https://dev.to/alektoreef/agents-or-a-proxy-the-access-control-decision-you-make-before-you-compare-any-features-92c</guid>
      <description>&lt;p&gt;&lt;strong&gt;Disclosure: I work on Tessera, which is one of the proxy-shaped tools. Both shapes are legitimate and I try to be fair to the other one below.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most comparisons of access-control tools start with feature tables. That is the wrong end. The decision that actually determines whether a rollout finishes is the deployment shape, and there are only two.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shape one: agents and certificates
&lt;/h2&gt;

&lt;p&gt;You run an internal certificate authority. Hosts are configured to trust it. Users get certificates that expire in a few hours. For Kubernetes, an agent runs inside the cluster and brokers access from there.&lt;/p&gt;

&lt;p&gt;What this buys you is genuinely good. Expiry does revocation automatically, which removes the human step that fails. The credential on the user's laptop is worthless tomorrow. The model scales well because the CA does not sit in the data path — once the certificate is issued, the user talks to the target directly, so there is no proxy to size and no bandwidth to plan.&lt;/p&gt;

&lt;p&gt;What it costs is that you have to change production before you get anything. &lt;code&gt;sshd_config&lt;/code&gt; gets rewritten across the estate to add &lt;code&gt;TrustedUserCAKeys&lt;/code&gt;. An agent gets deployed into every cluster. In some setups the tool's binary is copied onto hosts.&lt;/p&gt;

&lt;p&gt;None of that is technically hard. It is organisationally hard. You need a change window, sign-off from whoever owns those hosts, and a rollback plan — for a project whose entire benefit is "nothing bad will happen later". That conversation is where access-control rollouts stall, and it stalls most reliably in exactly the organisations that need the tool most: the ones where nobody is quite sure who owns which box.&lt;/p&gt;

&lt;p&gt;The other cost is that the CA private key becomes the most sensitive object your company owns, and now you operate a CA.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shape two: a proxy
&lt;/h2&gt;

&lt;p&gt;The credential stays on a controller. The user authenticates to the controller. The controller opens its own connection to the target, authenticates with the real credential, and relays. The target sees a normal connection from a normal service account. Nothing on the target changes.&lt;/p&gt;

&lt;p&gt;What this buys is that you can deploy it on a Tuesday. No change window on production, no agent to get approved for the cluster, no CA to operate. For an organisation that cannot easily get permission to touch production configuration, this is not a nice-to-have — it is the difference between having access control and not having it.&lt;/p&gt;

&lt;p&gt;The second thing it buys is less obvious and it is the reason I find this shape interesting. Because the proxy is in the data path, it decides about every byte. So the permission level can live in session state rather than in the credential, which means you can change it while the session is open. An admin can drop a live session from read-write to read-only and the connection does not drop — on SSH, on Kubernetes and on SQL. In a certificate model that is structurally hard: the permissions are baked into a certificate the target has already accepted, so changing them means a new connection.&lt;/p&gt;

&lt;p&gt;The same property gives you real read-only enforcement on protocols where it can be parsed. If you sit on the HTTP path to the kube-apiserver you can reject mutating verbs before they reach the cluster. If you parse the PostgreSQL wire protocol you can reject writes and DDL before they reach the database. Neither of those depends on the target being configured to cooperate.&lt;/p&gt;

&lt;p&gt;SSH is the exception, and the difference is worth keeping straight. There is no wire-level notion of a read-only shell, so what you get there is a command blocklist applied at the channel level — a guardrail against someone who forgot they were on prod, not a boundary against someone determined to get around it. An interactive shell is a Turing-complete environment, and any blocklist inside one can be defeated in principle. If you need a hard guarantee on SSH, point the proxy at a read-only OS account.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the proxy costs
&lt;/h2&gt;

&lt;p&gt;Three things, and none of them are small.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It is in the access path.&lt;/strong&gt; If it is down, nobody gets in. That is a dependency you are accepting, and it has to be operated accordingly — hardened, backed up, with a break-glass procedure that does not depend on it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Traffic passes through twice.&lt;/strong&gt; Client → controller → target and back, so N bytes cost 2N on the controller's interface. In our measurements 20 concurrent SSH sessions over loopback run at ~440 MB/s versus ~540 MB/s native, so roughly 20% overhead — and loopback is deliberately chosen to isolate the proxy cost from the network, which means a real network is worse. It is irrelevant for interactive work and it matters a lot for bulk transfer, which is why the sane advice is to route dumps and CI artefacts around the proxy entirely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It holds long-lived credentials.&lt;/strong&gt; This is the criticism that deserves the most respect. A box full of production credentials is an attractive target in a way that a certificate model's short-lived tokens are not. Encryption at rest, memory-only decryption, a write-only console and a pentest before go-live are all mitigations, not answers. If your threat model puts most weight on "what happens when one machine is compromised", the certificate model is genuinely the better fit and you should take that seriously.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to choose
&lt;/h2&gt;

&lt;p&gt;Ask two questions, in this order.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can you change production configuration in the next quarter?&lt;/strong&gt; If getting &lt;code&gt;sshd_config&lt;/code&gt; edits and a cluster agent approved is realistic, the certificate model is available to you and it is a strong choice. If it is not realistic — and for a lot of organisations it honestly is not — then the proxy shape is not a compromise, it is the only shape that will actually ship.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do you need to control the session or only the connection?&lt;/strong&gt; If your requirement stops at "who can connect", certificates cover it well. If it extends to "what can they do once they are in", "can I reduce that mid-session", and "what exactly did they run", the proxy shape has structural advantages, because it is the only one that is present while the session is happening.&lt;/p&gt;

&lt;p&gt;Everything else in the comparison tables is downstream of those two answers.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Certificate-shaped: Teleport. Proxy-shaped: Tessera, which I work on. Boundary sits in between, with several governance features in Enterprise or HCP rather than the open-source edition. Checked against each vendor's own docs, 14 August 2026.&lt;/em&gt;&lt;/p&gt;




</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>security</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Standing access is the risk that never makes it onto the risk register</title>
      <dc:creator>AlektoReef</dc:creator>
      <pubDate>Tue, 01 Sep 2026 06:46:26 +0000</pubDate>
      <link>https://dev.to/alektoreef/standing-access-is-the-risk-that-never-makes-it-onto-the-risk-register-1d3i</link>
      <guid>https://dev.to/alektoreef/standing-access-is-the-risk-that-never-makes-it-onto-the-risk-register-1d3i</guid>
      <description>&lt;p&gt;Every infrastructure post-mortem contains the same paragraph, and it is never the one anyone expected to write. The initial access was not sophisticated. It was a credential that existed, that worked, and that nobody had a reason to look at — because it had been legitimately issued months earlier, for a reason that had since ended.&lt;/p&gt;

&lt;p&gt;That is standing access. It is not a vulnerability, so no scan finds it. It is not a misconfiguration, so no posture report flags it. It is the residue of a hundred reasonable decisions: a key added for a migration, a database password shared during an outage, a kubeconfig sent to a contractor who did good work and left on good terms.&lt;/p&gt;

&lt;p&gt;It stays invisible to the risk register because a risk register asks what could go wrong, and standing access is the record of things that already went right.&lt;/p&gt;

&lt;h2&gt;
  
  
  The distribution problem
&lt;/h2&gt;

&lt;p&gt;The mechanism is worth being precise about, because it explains why the usual fixes only partly work.&lt;/p&gt;

&lt;p&gt;Infrastructure access is almost always &lt;strong&gt;handed out&lt;/strong&gt; rather than &lt;strong&gt;granted&lt;/strong&gt;. A key is copied to a host. A kubeconfig is copied to a laptop. A password is copied into a password manager and then, at two in the morning, into a chat window.&lt;/p&gt;

&lt;p&gt;Once a credential has been copied, the organisation has permanently lost the ability to list its copies. There is no query that returns the answer. Revocation stops being an operation and becomes an investigation — carried out by people, at the exact moment the person who knew where everything was has left.&lt;/p&gt;

&lt;p&gt;This is why the honest test of an offboarding process is not "did we remove their access". It is: &lt;strong&gt;can we show a third party, at any point in the future, that access ended when we say it ended?&lt;/strong&gt; Most organisations that pass the first test fail the second, and usually find out during an audit or a due-diligence review, which are the two worst moments to find out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the obvious fixes fall short
&lt;/h2&gt;

&lt;p&gt;Configuration management as the source of truth is a real improvement. It makes access declarative and puts it in version control, which gives you a change record. But it covers only the hosts under management, does nothing for database or cluster credentials, and tells you what state you pushed rather than what state existed during a session.&lt;/p&gt;

&lt;p&gt;A bastion host gives you one door and one place to audit. That is genuine perimeter control, and for many organisations it is enough. What it does not give you is any knowledge of what happened after the door: no per-command record, no way to reduce someone's privileges while they are working, and credentials still have to be handed out behind it.&lt;/p&gt;

&lt;p&gt;Short-lived certificates are the strongest thing an engineering team can build for itself. Expiry does the revocation work automatically, which removes the human step that fails. The cost is that you are now running a certificate authority whose private key is the most sensitive object your company owns, and you are changing the SSH configuration of every production host to trust it — so the security project needs a production change window before it delivers anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  The property that matters
&lt;/h2&gt;

&lt;p&gt;Strip away the implementations and the useful property is one sentence: &lt;strong&gt;access should be granted per session, scoped to a task, with an expiry enforced by a system rather than remembered by a person.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Everything follows from that. If the credential never reaches the laptop, there are no copies to enumerate. If the grant expires by itself, offboarding cannot be forgotten. If every session is recorded, "prove it" is a query rather than an interview.&lt;/p&gt;

&lt;p&gt;The industry calls this category privileged access management, and it has historically been sold as a seven-figure programme with a twelve-month rollout — which is exactly why most engineering organisations under a thousand people have a bastion and a spreadsheet instead. The requirement was never in dispute. The delivery vehicle was.&lt;/p&gt;

&lt;h2&gt;
  
  
  A note on pricing
&lt;/h2&gt;

&lt;p&gt;There is an oddity in how access tooling is usually sold that deserves more attention from budget owners than it gets.&lt;/p&gt;

&lt;p&gt;Much of the market prices per protected resource — per server, per host, per node. Think about what that means. The bill grows every time you add a machine. An autoscaling group that doubles overnight doubles a line item. A team that splits a monolith into twelve services on twelve instances has just paid twelve times more for the same twelve engineers doing the same work.&lt;/p&gt;

&lt;p&gt;The tool is billing you for the size of your infrastructure. But what it actually controls is how many people can reach it, and that number moves slowly and predictably. It is a headcount number, and you already forecast headcount.&lt;/p&gt;

&lt;p&gt;Per-seat pricing tracks the variable that reflects the real risk surface and the real value delivered. It also turns the budget conversation into a forecast rather than a negotiation, which matters more than it sounds, because tooling that gets expensive at scale is tooling that quietly gets routed around at scale. And a security control people route around is worse than no control, because it also produces a false sense of coverage.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to be clear about
&lt;/h2&gt;

&lt;p&gt;None of this removes the need for judgement. A broker that holds credentials is a high-value target and has to be run like one — hardened, backed up, and ideally penetration-tested before go-live. Putting a system in the access path means accepting it as a dependency of that path. And no tool produces compliance. Tools produce evidence, and evidence is what an auditor asks for.&lt;/p&gt;

&lt;p&gt;What is achievable, and what most organisations do not currently have, is a state where "who can reach production right now" is a query with a correct answer, and "prove that access ended in May" is a timestamp rather than a story.&lt;/p&gt;

&lt;p&gt;That is a lower bar than the market usually sells against. It is also the bar that post-mortems keep showing we have not cleared.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Tessera is a self-hosted access broker for SSH, Kubernetes, databases and RDP — priced per seat, deployed inside your own network, free to self-host for a single administrator. &lt;a href="https://tessera.company/docs/get-started/what-is-tessera/" rel="noopener noreferrer"&gt;How it works →&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  LinkedIn version
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;~310 words. Link in the first comment, not in the post. Publish from a founder's personal profile as well as the company page.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;Every infrastructure post-mortem contains the same paragraph, and it is never the one anyone expected to write.&lt;/p&gt;

&lt;p&gt;The initial access was not sophisticated. It was a credential that existed, that worked, and that nobody had a reason to look at — because it had been legitimately issued months earlier, for a reason that had since ended.&lt;/p&gt;

&lt;p&gt;That is standing access. It never appears on a risk register, because a risk register asks what could go wrong, and standing access is the residue of a hundred things that went right. A key added for a migration. A database password shared during an outage at 2am. A kubeconfig sent to a contractor who did good work and left on good terms.&lt;/p&gt;

&lt;p&gt;The mechanism is simple, and it explains why the usual fixes underperform: infrastructure access gets &lt;strong&gt;handed out&lt;/strong&gt;, not &lt;strong&gt;granted&lt;/strong&gt;. Once a credential has been copied, you have permanently lost the ability to list its copies. There is no query that returns the answer.&lt;/p&gt;

&lt;p&gt;So the honest test of your offboarding process is not "did we remove their access".&lt;/p&gt;

&lt;p&gt;It is: can you prove it, to someone who is not you, six months from now?&lt;/p&gt;

&lt;p&gt;Most organisations that pass the first test fail the second — and usually find out during an audit or a due-diligence review, which are the two worst possible moments.&lt;/p&gt;

&lt;p&gt;The property that fixes it is one sentence: access granted per session, scoped to a task, with an expiry enforced by a system rather than remembered by a person.&lt;/p&gt;

&lt;p&gt;That has historically been sold as a seven-figure programme with a twelve-month rollout, which is exactly why most engineering orgs under a thousand people have a bastion host and a spreadsheet instead. The requirement was never in dispute. The delivery vehicle was.&lt;/p&gt;

&lt;p&gt;I would be curious what people here have actually had to produce as evidence in an audit. Not what the policy said — what the auditor accepted.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;(first comment, same account)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Longer version, including why per-server pricing bills you for the wrong variable: [link]&lt;/p&gt;

</description>
      <category>security</category>
      <category>devops</category>
      <category>management</category>
    </item>
    <item>
      <title>One Go binary between your engineers and production: how we broker SSH, kubectl, psql and RDP — and what it costs</title>
      <dc:creator>AlektoReef</dc:creator>
      <pubDate>Tue, 01 Sep 2026 05:38:27 +0000</pubDate>
      <link>https://dev.to/alektoreef/one-go-binary-between-your-engineers-and-production-how-we-broker-ssh-kubectl-psql-and-rdp-and-563i</link>
      <guid>https://dev.to/alektoreef/one-go-binary-between-your-engineers-and-production-how-we-broker-ssh-kubectl-psql-and-rdp-and-563i</guid>
      <description>&lt;p&gt;The contractor's public key is still in &lt;code&gt;authorized_keys&lt;/code&gt; on eleven machines. You know because you put it there in March, for two weeks, until the migration was done. The contract ended in May. You remember the Ansible play that spread the keys. There was never a play that collected them back.&lt;/p&gt;

&lt;p&gt;This is not carelessness. It is what happens when access is &lt;strong&gt;handed out&lt;/strong&gt; instead of &lt;strong&gt;granted&lt;/strong&gt;. A key, a kubeconfig, a database password — each one is a copy, and once a copy exists there is no technical way to find out how many there are or where they sit. Revoking access turns into archaeology.&lt;/p&gt;

&lt;p&gt;We spent eighteen months building something that works differently. This post is an honest walk through the architecture: what it does, what it costs in speed, and the three places where our approach is simply worse than the alternative.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two ways to stop handing out keys
&lt;/h2&gt;

&lt;p&gt;There are really only two.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Short-lived certificates.&lt;/strong&gt; You run an internal CA, configure &lt;code&gt;sshd&lt;/code&gt; to trust it, and issue the user a certificate valid for eight hours. This is how Teleport works. The secret still lands on the laptop, but it expires by itself. It is a good model, and for some threat models it is better than ours.&lt;/p&gt;

&lt;p&gt;The cost is the shape of the rollout. You rewrite &lt;code&gt;sshd_config&lt;/code&gt; on every host. You put an agent inside the cluster. So before you get access control, you need permission to change production configuration — which is its own project, its own change window, and its own conversation with the people who own that production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A broker.&lt;/strong&gt; The secret never leaves the controller. The user connects to the controller, the controller opens its own connection to the target and injects the real credential on the wire. Nothing on the target changes: same &lt;code&gt;sshd&lt;/code&gt;, same &lt;code&gt;authorized_keys&lt;/code&gt; with one service key, same kube-apiserver with no vendor pods in it.&lt;/p&gt;

&lt;p&gt;We chose the broker. Here is how it works and what it costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happens on the wire
&lt;/h2&gt;

&lt;p&gt;The controller is one Go binary plus PostgreSQL 16+. There is nothing else in the control plane.&lt;/p&gt;

&lt;p&gt;Take SSH. The client opens TLS to the controller and asks for a session to &lt;code&gt;app-prod-01&lt;/code&gt;. The controller:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Checks the user's grant for that target, and any just-in-time approval.&lt;/li&gt;
&lt;li&gt;Pulls the target's credential from the database — encrypted with AES-256-GCM — and decrypts it &lt;strong&gt;in memory only&lt;/strong&gt;, for the life of the connection.&lt;/li&gt;
&lt;li&gt;Opens its own SSH connection to the target and authenticates with that credential.&lt;/li&gt;
&lt;li&gt;Stitches the two connections together and starts copying bytes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Two consequences follow.&lt;/p&gt;

&lt;p&gt;First, &lt;strong&gt;all session traffic goes through the controller twice&lt;/strong&gt;: client → controller → target and back. Moving N bytes costs 2N on the controller's interface. That is not an optimisation we skipped. It is what a broker is.&lt;/p&gt;

&lt;p&gt;Second, the controller holds a decrypted secret in memory. That is the main fair criticism of this model, and I come back to it at the end.&lt;/p&gt;

&lt;p&gt;Kubernetes and databases work the same way. Only the credential changes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Target&lt;/th&gt;
&lt;th&gt;Client uses&lt;/th&gt;
&lt;th&gt;Controller injects&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SSH&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ssh&lt;/code&gt;, scp/sftp, built-in terminal&lt;/td&gt;
&lt;td&gt;username + password or private key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kubernetes&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;kubectl&lt;/code&gt;, any kubeconfig tool&lt;/td&gt;
&lt;td&gt;cluster bearer token or client certificate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;td&gt;psql / mysql, GUI clients&lt;/td&gt;
&lt;td&gt;database username + password&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RDP&lt;/td&gt;
&lt;td&gt;native remote desktop window&lt;/td&gt;
&lt;td&gt;Windows username + password — &lt;strong&gt;sent to the local client&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;RDP needs saying plainly, because it is the exception. NLA requires the credential &lt;strong&gt;before&lt;/strong&gt; the session exists. There is nothing to proxy: the password has to reach the client. We pass it to the bundled RDP client over an in-memory channel on a loopback port. It is never shown in the UI, never written to disk, never stored in the Windows Credential Manager, and every issue is audited. But claiming the secret stays on the controller for RDP would be a lie. It does not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read-only on three protocols
&lt;/h2&gt;

&lt;p&gt;This is where we decided not to pretend.&lt;/p&gt;

&lt;p&gt;The idea is simple. A grant has a level — view, read-only, read-write. Read-only should behave the same on SSH, in the cluster and in the database. But it is implemented on three different layers, and they are not equally strong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kubernetes is real enforcement.&lt;/strong&gt; &lt;code&gt;kubectl&lt;/code&gt; talks HTTP to the kube-apiserver and we are on that path. We parse the request, look at the verb and the resource, and reject mutating calls before they reach the cluster. &lt;code&gt;kubectl get pods&lt;/code&gt; passes. &lt;code&gt;kubectl delete pod&lt;/code&gt; does not. You cannot get around that while staying inside the protocol.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SQL is real enforcement too.&lt;/strong&gt; We parse the PostgreSQL and MySQL wire protocols and filter at the statement level. &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;, any DDL — rejected before the database. Not grants inside the DB, not a read replica. The statement never arrives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SSH is a railing, not a wall.&lt;/strong&gt; We have a configurable command blocklist applied at the SSH channel level, plus blocking of writes and uploads — &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;, sftp/scp. The default list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt, &lt;span class="nb"&gt;chmod&lt;/span&gt;, &lt;span class="nb"&gt;chown&lt;/span&gt;, &lt;span class="nb"&gt;dd&lt;/span&gt;, &lt;span class="nb"&gt;mv&lt;/span&gt;, &lt;span class="nb"&gt;rm&lt;/span&gt;, shutdown, systemctl, useradd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It catches a typo. It catches someone who forgot they were on prod and not staging. It does &lt;strong&gt;not&lt;/strong&gt; catch someone who wants to get around it, because an interactive shell is a Turing-complete environment and any command blocklist in one can be bypassed in principle. Our own documentation says it: &lt;em&gt;treat it as a guardrail, not a hard boundary.&lt;/em&gt; If you need a hard guarantee on SSH, point the broker at a read-only OS account. That is the only honest answer.&lt;/p&gt;

&lt;p&gt;We could have left that out of the docs. We put it in, because the first person who checks would find it anyway — and would then stop trusting everything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Taking write access off a live session
&lt;/h2&gt;

&lt;p&gt;This is the one thing that, as far as we could verify from Teleport's, StrongDM's and Boundary's own documentation on 14 August 2026, none of them do.&lt;/p&gt;

&lt;p&gt;The situation: an engineer is on production with read-write. You are watching the session live and you see them about to do something wrong. The usual answer is to kill the session. But killing a session in the middle of an incident is also damage, and often you do not want to cause it. You want the person to keep reading logs and stop being able to change anything.&lt;/p&gt;

&lt;p&gt;In a certificate model this is structurally hard. The permissions are baked into a certificate that has already been presented and accepted by the target. Changing them means a new connection.&lt;/p&gt;

&lt;p&gt;With a broker it is different, because we decide about every byte, not the target. The grant level lives in the session state on the controller, not in the credential. Change it, and the next parsed SSH channel, the next HTTP request to the apiserver, the next SQL statement goes through a different filter. The connection does not drop. The user sees a command suddenly stop working, which is exactly the behaviour you want.&lt;/p&gt;

&lt;p&gt;The flip side is obvious: this works because we are in the path. The same thing that lets us change the rules mid-flight makes us a single point of failure in the access path. That is not free.&lt;/p&gt;

&lt;h2&gt;
  
  
  The benchmark
&lt;/h2&gt;

&lt;p&gt;Any proxy adds overhead. The only questions are how much, and whether the vendor is honest about it.&lt;/p&gt;

&lt;p&gt;20 concurrent SSH sessions, measured over loopback:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Native SSH&lt;/th&gt;
&lt;th&gt;Through Tessera&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Total throughput&lt;/td&gt;
&lt;td&gt;~540 MB/s&lt;/td&gt;
&lt;td&gt;~440 MB/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Overhead&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~20%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The method matters more than the number. Loopback is &lt;strong&gt;deliberate&lt;/strong&gt;: it removes the network from the equation and leaves only the cost of the proxy itself — the extra encrypt/decrypt pass and the buffer copies. On a real network the double hop adds its own latency and its own bandwidth cost on top.&lt;/p&gt;

&lt;p&gt;So &lt;strong&gt;20% is the floor, not what you should expect.&lt;/strong&gt; Yours will be worse. How much worse depends on where the controller sits relative to the targets, which is why the docs tell you to put it in the same datacentre. The double hop costs latency as well as bandwidth, and latency is what an interactive user feels.&lt;/p&gt;

&lt;p&gt;You can reproduce it yourself with the &lt;code&gt;-compare&lt;/code&gt; mode of our &lt;code&gt;loadtest&lt;/code&gt; tool. If you get a worse result than we did, tell us — that is more interesting than matching it.&lt;/p&gt;

&lt;p&gt;One conclusion follows directly: &lt;strong&gt;Tessera is not for bulk transfer.&lt;/strong&gt; Database dumps, CI artefacts, backups — route them around the broker. It is for interactive work, and for having a record of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sizing: the unit is not team size
&lt;/h2&gt;

&lt;p&gt;The most common planning mistake is counting people. The controller does not know how many engineers you have, and does not care how many targets are registered. It only knows &lt;strong&gt;concurrent sessions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Rule of thumb: on a normal working day, 10–20% of a team is connected. A 200-person organisation is 20–40 concurrent sessions, not 200.&lt;/p&gt;

&lt;p&gt;The memory arithmetic is simple: ~256 KB of copy buffers per session plus 6–10 goroutines at ~8 KB, so &lt;strong&gt;~320 KB per session&lt;/strong&gt;. Base process ~30 MB. Go does not return memory to the OS immediately, and at the default &lt;code&gt;GOGC=100&lt;/code&gt; resident settles at roughly twice the live heap.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concurrent sessions&lt;/th&gt;
&lt;th&gt;vCPU&lt;/th&gt;
&lt;th&gt;Expected resident&lt;/th&gt;
&lt;th&gt;Provision&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;up to 50&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;~90 MB&lt;/td&gt;
&lt;td&gt;512 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50–200&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;~190 MB&lt;/td&gt;
&lt;td&gt;1 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;200+&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;~380 MB&lt;/td&gt;
&lt;td&gt;2 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The gap between the last two columns is headroom, not a hidden cost. A controller serving 200 sessions really does use a few hundred megabytes. The Helm chart ships &lt;code&gt;requests: 256Mi&lt;/code&gt; and &lt;code&gt;limits: 1Gi&lt;/code&gt;, which matches the middle row.&lt;/p&gt;

&lt;p&gt;CPU is almost never the limit. The double encryption — TLS with the client, SSH with the target — is cheap on any modern CPU with AES-NI. &lt;strong&gt;The network interface saturates first.&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Workload&lt;/th&gt;
&lt;th&gt;Link&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Interactive only (SSH · k8s · SQL)&lt;/td&gt;
&lt;td&gt;100 Mbit/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interactive + occasional scp/rsync&lt;/td&gt;
&lt;td&gt;1 Gbit/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Heavy transfer, or 50+ concurrent RDP&lt;/td&gt;
&lt;td&gt;10 Gbit/s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;An interactive terminal is a few KB/s per session; 200 shells is single-digit Mbit/s. RDP is a different animal: &lt;strong&gt;0.5–5 Mbit/s per session&lt;/strong&gt;, sustained rather than bursty. Twenty concurrent RDP sessions, after the double hop, is 100–200 Mbit/s through the controller. If RDP is a real part of your usage, size the link from RDP alone and treat the rest as rounding error.&lt;/p&gt;

&lt;p&gt;Two things about disk, the second one a trap. First: only terminal sessions are recorded, as asciinema &lt;code&gt;.cast&lt;/code&gt; files, at &lt;strong&gt;1–5 MB per session-hour&lt;/strong&gt;; &lt;code&gt;htop&lt;/code&gt; and &lt;code&gt;tail -f&lt;/code&gt; on a live log go past the top of that range. Second: &lt;code&gt;TESSERA_RECORDING_RETENTION_DAYS&lt;/code&gt; and &lt;code&gt;TESSERA_AUDIT_RETENTION_DAYS&lt;/code&gt; both default to &lt;code&gt;0&lt;/code&gt;, which means keep forever. If you do not set them, the answer to "how big does this get" is "as big as the controller's entire history".&lt;/p&gt;

&lt;h2&gt;
  
  
  Three small things you only see from inside
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;We ignore forwarded-for on purpose.&lt;/strong&gt; The login rate limit counts against the real TCP connection address. Counting &lt;code&gt;X-Forwarded-For&lt;/code&gt; means the limit can be bypassed by setting a header, which means there is no limit. This costs us correct behaviour behind some proxies and we think the trade is right.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Targets are checked for SSRF.&lt;/strong&gt; The target host is validated on create and on update. Loopback, link-local and similar addresses — the ones that would turn the controller into an internal network scanner — are rejected. The controller has network access to everything interesting by definition, so letting any admin point it anywhere would be careless.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TOFU for host keys.&lt;/strong&gt; On first connection the target's host key is stored; later, a changed key is rejected. This is &lt;code&gt;StrictHostKeyChecking=accept-new&lt;/code&gt; raised to the organisation level. It catches silent MITM substitution. It does not protect the first connection, same as everywhere else without a host-key distribution system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where we are worse
&lt;/h2&gt;

&lt;p&gt;Probably the section worth reading.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We are not open source.&lt;/strong&gt; Community is free and self-hosted, but proprietary, and it needs an offline licence file — checked locally, with no request going out, but still a file. Our own Teleport comparison page admits this makes air-gapped installs simpler for them. If reading the source is a hard requirement for you, we are not your tool, and that is fine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;amd64 only.&lt;/strong&gt; Debian 12 or Ubuntu 22.04+, amd64 images. Graviton, Ampere, the Pi in your cupboard — no. I am not giving a date, because nobody gave me one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We hold long-lived credentials in one box.&lt;/strong&gt; This is the fundamental criticism of the broker model and it is fair. Our answer is encryption at rest, decryption in memory only for the life of the connection, a write-only console, SSRF validation, TOFU, rate limiting, and a recommendation in our own docs to run an independent pentest before go-live. But a certificate model has a structurally different failure mode, and for part of the threat space it is better. That is Teleport's argument, it is honest, and pretending otherwise would be silly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RDP is not recorded.&lt;/strong&gt; The tunnel carries the RDP protocol, not a terminal stream, so there is nothing to write. Only audit events. Windows-heavy shops should plan their controls around that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Databases are PostgreSQL and MySQL only.&lt;/strong&gt; No MSSQL, no Mongo, no Redis, no Oracle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trying it
&lt;/h2&gt;

&lt;p&gt;Three ways to run the controller: apt package on Debian/Ubuntu, Docker Compose, or Helm. The Community edition is free forever: one seat, 5 SSH targets, 1 cluster, 1 database, 5 RDP, credential injection, TOFU, and an audit log of events and commands — without session replay or live watch, which are in the paid tiers.&lt;/p&gt;

&lt;p&gt;Architecture and limits: &lt;a href="https://tessera.company/docs/get-started/what-is-tessera/" rel="noopener noreferrer"&gt;https://tessera.company/docs/get-started/what-is-tessera/&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Full sizing and benchmark: &lt;a href="https://tessera.company/docs/reference/requirements/" rel="noopener noreferrer"&gt;https://tessera.company/docs/reference/requirements/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you find a place where the docs and the behaviour disagree, tell us. We fix docs more often than we fix code.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: I work on Tessera. Everything checkable in this post — the benchmark, the sizing, the limits — is in the documentation and reproducible on your own hardware.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>ssh</category>
      <category>security</category>
      <category>zerotrust</category>
    </item>
  </channel>
</rss>
