docs: rename diagram assets to doc-prefixed scheme, update exports
Asset renames (matching the new Nextcloud source naming convention — doc name first, then description): - architecture-trust-depth.svg -> architecture-overview-trust-depth.svg - firewall-docker-bypass.svg -> firewall-and-network-docker-bypass.svg - secrets-lifecycle.svg -> secrets-and-pki-lifecycle.svg - trust-boundaries-blast-radius.svg -> readme-trust-boundaries-blast-radius.svg Updated all 4 markdown references accordingly. Also refreshed diagram exports (secret-distribution-flows.svg, and the 4 renamed ones) with today's fixes, and folded in the earlier pending sso-and-identity.md work: precise live-verified native-OIDC app list (12 apps, not ~9), the Proxmox no-Proxy-Provider exception, real API evidence for it, and the automation-bot migration story.
No files matched your search
@@ -41,7 +41,7 @@ Most self-hosted setups have one trust boundary: "you're on the LAN, you're trus
|
|||||||
|
|
||||||
This fleet runs **four trust boundaries, each calibrated to who's actually on the other end** — public internet traffic, human browsers on personal devices, machine-provisioned containers, and non-HTTP infra traffic each get a different control, matched to their real risk, not a blanket policy applied everywhere or nowhere. Enforced by **three independent routing authorities** instead of one central router, specifically so a failure or misconfiguration in any single one only ever takes out its own third of the traffic — never all of it at once. It didn't start this way, either: the public proxy used to be its own VM, and the internal service-to-service model used to be a flat, unauthenticated mesh with no boundary at all. Both got replaced deliberately, trading operational simplicity for a smaller blast radius.
|
This fleet runs **four trust boundaries, each calibrated to who's actually on the other end** — public internet traffic, human browsers on personal devices, machine-provisioned containers, and non-HTTP infra traffic each get a different control, matched to their real risk, not a blanket policy applied everywhere or nowhere. Enforced by **three independent routing authorities** instead of one central router, specifically so a failure or misconfiguration in any single one only ever takes out its own third of the traffic — never all of it at once. It didn't start this way, either: the public proxy used to be its own VM, and the internal service-to-service model used to be a flat, unauthenticated mesh with no boundary at all. Both got replaced deliberately, trading operational simplicity for a smaller blast radius.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The three routing authorities are also split across two separate physical nodes, so a hardware failure is contained the same way a routing failure is — never all three at once.
|
The three routing authorities are also split across two separate physical nodes, so a hardware failure is contained the same way a routing failure is — never all three at once.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Do not edit this file with editors other than draw.io -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" style="background: transparent; background-color: transparent; color-scheme: light dark;" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="937px" height="699px" viewBox="0 0 937 699" id="ge-svg-b7jKWwXwbvuhbLwz8bQ3" content="<mxfile host="embed.diagrams.net" scale="1" border="0"> <diagram name="Docker Firewalld Bypass" id="docker-bypass-1"> <mxGraphModel dx="1414" dy="791" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1100" pageHeight="740" math="0" shadow="0"> <root> <mxCell id="0" /> <mxCell id="1" parent="0" /> <mxCell id="traffic" parent="1" style="shape=cloud;whiteSpace=wrap;html=1;fillColor=#F5F5F5;strokeColor=#666666;fontColor=#333333;fontSize=14;fontStyle=1;shadow=1;" value="Incoming Traffic" vertex="1"> <mxGeometry height="90" width="200" x="440" y="30" as="geometry" /> </mxCell> <mxCell id="pfsense" parent="1" style="shape=mxgraph.basic.shield;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;fontColor=#333333;fontSize=14;fontStyle=1;shadow=1;" value="pfSense&lt;br&gt;&lt;font style=&quot;font-size:11px&quot;&gt;Layer 1 — always applies&lt;/font&gt;" vertex="1"> <mxGeometry height="120" width="120" x="480" y="170" as="geometry" /> </mxCell> <mxCell id="bgfill_host" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#F5F5F5;strokeColor=none;" vertex="1"> <mxGeometry height="300" width="820" x="140" y="360" as="geometry" /> </mxCell> <mxCell id="host" parent="1" style="swimlane;whiteSpace=wrap;html=1;startSize=30;fillColor=none;strokeColor=#666666;dashed=1;fontSize=12;fontStyle=1;fontColor=#333333;align=center;spacingLeft=8;verticalAlign=middle;" value="TARGET HOST — everything below this line is decided by what&#39;s running on it" vertex="1"> <mxGeometry height="300" width="820" x="140" y="360" as="geometry" /> </mxCell> <mxCell id="firewalld" parent="host" style="shape=mxgraph.basic.shield;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontColor=#333333;fontSize=13;fontStyle=1;shadow=1;" value="firewalld&lt;br&gt;&lt;font style=&quot;font-size:11px&quot;&gt;Layer 2 — native services only&lt;/font&gt;" vertex="1"> <mxGeometry height="110" width="110" x="80" y="70" as="geometry" /> </mxCell> <mxCell id="native" parent="host" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontColor=#333333;fontSize=13;fontStyle=1;shadow=1;" value="Native Service&lt;br&gt;&lt;font style=&quot;font-size:11px&quot;&gt;e.g. Alloy, Wazuh agent&lt;/font&gt;" vertex="1"> <mxGeometry height="65" width="190" x="40" y="210" as="geometry" /> </mxCell> <mxCell id="docker" parent="host" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontColor=#333333;fontSize=13;fontStyle=1;shadow=1;" value="Docker-published port&lt;br&gt;&lt;font style=&quot;font-size:11px&quot;&gt;dozens of containerized apps&lt;/font&gt;" vertex="1"> <mxGeometry height="65" width="220" x="550" y="210" as="geometry" /> </mxCell> <mxCell id="e3" edge="1" parent="host" source="firewalld" style="html=1;endArrow=block;strokeColor=#6c8ebf;strokeWidth=2;rounded=1;" target="native"> <mxGeometry relative="1" as="geometry" /> </mxCell> <mxCell id="e1" edge="1" parent="1" Line truncated
|
||||||
|
After Width: | Height: | Size: 365 KiB |
@@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- Do not edit this file with editors other than draw.io -->
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" style="background: transparent; background-color: transparent; color-scheme: light dark;" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="822px" height="705px" viewBox="0 0 822 705" id="ge-svg-45Po1F0wK5Lgy7woHj7E" content="<mxfile host="embed.diagrams.net" scale="1" border="0"> <diagram name="Docker Firewalld Bypass" id="docker-bypass-1"> <mxGraphModel dx="1178" dy="659" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1100" pageHeight="700" math="0" shadow="0"> <root> <mxCell id="0" /> <mxCell id="1" parent="0" /> <mxCell id="traffic" parent="1" style="shape=cloud;whiteSpace=wrap;html=1;fillColor=#F5F5F5;strokeColor=#666666;fontColor=#333333;fontSize=14;fontStyle=1;shadow=1;" value="Incoming Traffic" vertex="1"> <mxGeometry height="90" width="200" x="440" y="30" as="geometry" /> </mxCell> <mxCell id="pfsense" parent="1" style="shape=mxgraph.basic.shield;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;fontColor=#333333;fontSize=14;fontStyle=1;shadow=1;" value="pfSense&lt;br&gt;&lt;font style=&quot;font-size:11px&quot;&gt;Layer 1 — always applies&lt;/font&gt;" vertex="1"> <mxGeometry height="120" width="120" x="480" y="170" as="geometry" /> </mxCell> <mxCell id="bgfill_host" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#F5F5F5;strokeColor=none;" vertex="1"> <mxGeometry height="300" width="820" x="140" y="360" as="geometry" /> </mxCell> <mxCell id="host" parent="1" style="swimlane;whiteSpace=wrap;html=1;startSize=30;fillColor=none;strokeColor=#666666;dashed=1;fontSize=12;fontStyle=1;fontColor=#333333;align=left;spacingLeft=8;verticalAlign=middle;" value="TARGET HOST — everything below this line is decided by what&#39;s running on it" vertex="1"> <mxGeometry height="300" width="820" x="140" y="360" as="geometry" /> </mxCell> <mxCell id="firewalld" parent="host" style="shape=mxgraph.basic.shield;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontColor=#333333;fontSize=13;fontStyle=1;shadow=1;" value="firewalld&lt;br&gt;&lt;font style=&quot;font-size:11px&quot;&gt;Layer 2 — native services only&lt;/font&gt;" vertex="1"> <mxGeometry height="110" width="110" x="80" y="70" as="geometry" /> </mxCell> <mxCell id="native" parent="host" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontColor=#333333;fontSize=13;fontStyle=1;shadow=1;" value="Native Service&lt;br&gt;&lt;font style=&quot;font-size:11px&quot;&gt;e.g. Alloy, Wazuh agent&lt;/font&gt;" vertex="1"> <mxGeometry height="65" width="190" x="40" y="210" as="geometry" /> </mxCell> <mxCell id="docker" parent="host" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;fontColor=#333333;fontSize=13;fontStyle=1;shadow=1;" value="Docker-published port&lt;br&gt;&lt;font style=&quot;font-size:11px&quot;&gt;dozens of containerized apps&lt;/font&gt;" vertex="1"> <mxGeometry height="65" width="220" x="550" y="210" as="geometry" /> </mxCell> <mxCell id="e3" edge="1" parent="host" source="firewalld" style="html=1;endArrow=block;strokeColor=#6c8ebf;strokeWidth=2;rounded=1;" target="native"> <mxGeometry relative="1" as="geometry" /> </mxCell> <mxCell id="e1" edge="1" parent="1" soLine truncated
|
|
||||||
|
Before Width: | Height: | Size: 386 KiB |
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Do not edit this file with editors other than draw.io -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" style="background: transparent; background-color: transparent; color-scheme: light dark;" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1022px" height="659px" viewBox="0 0 1022 659" content="<mxfile host="embed.diagrams.net" scale="1" border="0"> <diagram name="Trust Boundaries and Blast Radius" id="trust-boundaries-2"> <mxGraphModel dx="1414" dy="791" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1100" pageHeight="700" math="0" shadow="0"> <root> <mxCell id="0" /> <mxCell id="1" parent="0" /> <mxCell id="pfsense" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;fontSize=15;fontStyle=1;fontColor=#333333;shadow=1;" value="pfSense&lt;br&gt;&lt;font style=&quot;font-size: 12px;&quot;&gt;Edge Firewall + Public HAProxy&lt;/font&gt;" vertex="1"> <mxGeometry height="100" width="170" x="465" y="20" as="geometry" /> </mxCell> <mxCell id="nodeA" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#F5F5F5;strokeColor=#666666;dashed=1;verticalAlign=top;fontSize=13;fontStyle=1;spacingTop=8;fontColor=#333333;" value="PROXMOX NODE A — day-to-day applications" vertex="1"> <mxGeometry height="370" width="460" x="40" y="180" as="geometry" /> </mxCell> <mxCell id="internalCaddy" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;fontSize=14;fontStyle=1;fontColor=#333333;shadow=1;" value="Internal Caddy&lt;br&gt;&lt;font style=&quot;font-size: 11px;&quot;&gt;app-to-app zone&lt;/font&gt;" vertex="1"> <mxGeometry height="100" width="170" x="115" y="220" as="geometry" /> </mxCell> <mxCell id="apps" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=15;fontStyle=1;fontColor=#333333;shadow=1;" value="Docker Apps&lt;br&gt;&lt;font style=&quot;font-size: 12px;&quot;&gt;LXC host, most of the fleet&#39;s stacks&lt;/font&gt;" vertex="1"> <mxGeometry height="80" width="380" x="80" y="440" as="geometry" /> </mxCell> <mxCell id="nodeB" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#F5F5F5;strokeColor=#666666;dashed=1;verticalAlign=top;fontSize=13;fontStyle=1;spacingTop=8;fontColor=#333333;" value="PROXMOX NODE B — trust &amp;amp; sensitive infrastructure" vertex="1"> <mxGeometry height="370" width="460" x="600" y="180" as="geometry" /> </mxCell> <mxCell id="caddyVM" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;fontSize=14;fontStyle=1;fontColor=#333333;shadow=1;" value="Caddy VM&lt;br&gt;&lt;font style=&quot;font-size: 11px;&quot;&gt;browser zone, mTLS&lt;/font&gt;" vertex="1"> <mxGeometry height="100" width="170" x="815" y="220" as="geometry" /> </mxCell> <mxCell id="vaultDns" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;fontSize=15;fontStyle=1;fontColor=#333333;shadow=1;" value="Vault + Technitium DNS&lt;br&gt;&lt;font style=&quot;font-size: 12px;&quot;&gt;trust backbone&lt;/font&gt;" vertex="1"> <mxGeometry height="80" width="380" x="640" y="440" as="geometry" /> </mxCell> <mxCell id="e1" edge="1" parent="1" source="pfsense" style="html=1;labelBackgroundColor=#1C1D1E;endArrow=block;strokeColor=#d79b00;fontColor=#d79b00;fontSize=13;rounded=1;edgeStyle=orthogonalEdgeStyle;entryLine truncated
|
||||||
|
After Width: | Height: | Size: 415 KiB |
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!-- Do not edit this file with editors other than draw.io -->
|
<!-- Do not edit this file with editors other than draw.io -->
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" style="background: #ffffff; background-color: var(--ge-adaptive-bg, #ffffff); color-scheme: light dark;" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1085px" height="545px" viewBox="0 0 1085 545" id="ge-svg-lxAUNMmLjYII1QtcEPaS" content="<mxfile host="embed.diagrams.net" scale="1" border="0"> <diagram name="Secret Distribution Flows" id="secret-dist-flows-1"> <mxGraphModel dx="1414" dy="791" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1200" pageHeight="620" math="0" shadow="0"> <root> <mxCell id="0" /> <mxCell id="1" parent="0" /> <mxCell id="repo" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;fontColor=#333333;fontSize=14;fontStyle=1;shadow=1;" value="Provisioning Repo&lt;br&gt;&lt;font style=&quot;font-size:11px&quot;&gt;template inventory&lt;/font&gt;" vertex="1"> <mxGeometry height="80" width="200" x="40" y="60" as="geometry" /> </mxCell> <mxCell id="agentconfig" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;fontColor=#333333;fontSize=14;fontStyle=1;shadow=1;" value="Vault Agent Config&lt;br&gt;&lt;font style=&quot;font-size:11px&quot;&gt;on the target host&lt;/font&gt;" vertex="1"> <mxGeometry height="80" width="200" x="410" y="60" as="geometry" /> </mxCell> <mxCell id="bgfill_dataplane" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=none;" vertex="1"> <mxGeometry height="290" width="440" x="380" y="230" as="geometry" /> </mxCell> <mxCell id="dataplane" parent="1" style="swimlane;whiteSpace=wrap;html=1;startSize=30;fillColor=none;strokeColor=#9673a6;dashed=1;fontSize=12;fontStyle=1;fontColor=#333333;align=left;spacingLeft=8;verticalAlign=middle;" value="DATA PLANE — what Vault Agent actually renders" vertex="1"> <mxGeometry height="290" width="440" x="380" y="230" as="geometry" /> </mxCell> <mxCell id="vaultagent" parent="dataplane" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;fontColor=#333333;fontSize=13;fontStyle=1;shadow=1;" value="Vault Agent" vertex="1"> <mxGeometry height="60" width="140" x="150" y="40" as="geometry" /> </mxCell> <mxCell id="certflow" parent="dataplane" style="rounded=1;whiteSpace=wrap;html=1;fillColor=light-dark(#FFFFFF,#292929);strokeColor=#9673a6;fontColor=#333333;fontSize=12;fontStyle=1;shadow=1;" value="Certificate Flow&lt;br&gt;&lt;font style=&quot;font-size:11px&quot;&gt;PKI engine, short-lived&lt;/font&gt;" vertex="1"> <mxGeometry height="65" width="170" x="30" y="180" as="geometry" /> </mxCell> <mxCell id="secflow" parent="dataplane" style="rounded=1;whiteSpace=wrap;html=1;fillColor=light-dark(#FFFFFF,#292929);strokeColor=#9673a6;fontColor=#333333;fontSize=12;fontStyle=1;shadow=1;" value="App Secret Flow&lt;br&gt;&lt;font style=&quot;font-size:11px&quot;&gt;KV, rendered 0600&lt;/font&gt;" vertex="1"> <mxGeometry height="65" width="170" x="240" y="180" as="geometry" /> </mxCell> <mxCell id="container" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontColor=#333333;fontSize=14;fontStyle=1;shadow=1;" value="Container&lt;br&gt;&lt;font style=&quot;font-size:11px&quot;&gt;reads local disk only&lt;/font&gt;" vertex="1"> <mxGeometry height="8Line truncated
|
<svg xmlns="http://www.w3.org/2000/svg" style="background: transparent; background-color: transparent; color-scheme: light dark;" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1179px" height="545px" viewBox="0 0 1179 545" content="<mxfile host="embed.diagrams.net" scale="1" border="0"> <diagram name="Secret Distribution Flows" id="secret-dist-flows-1"> <mxGraphModel dx="1414" dy="791" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1200" pageHeight="620" math="0" shadow="0"> <root> <mxCell id="0" /> <mxCell id="1" parent="0" /> <mxCell id="repo" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;fontColor=#333333;fontSize=14;fontStyle=1;shadow=1;" value="Provisioning Repo&lt;br&gt;&lt;font style=&quot;font-size:11px&quot;&gt;template inventory&lt;/font&gt;" vertex="1"> <mxGeometry height="80" width="200" x="40" y="60" as="geometry" /> </mxCell> <mxCell id="agentconfig" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;fontColor=#333333;fontSize=14;fontStyle=1;shadow=1;" value="Vault Agent Config&lt;br&gt;&lt;font style=&quot;font-size:11px&quot;&gt;on the target host&lt;/font&gt;" vertex="1"> <mxGeometry height="80" width="200" x="410" y="60" as="geometry" /> </mxCell> <mxCell id="bgfill_dataplane" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=none;" vertex="1"> <mxGeometry height="290" width="440" x="380" y="230" as="geometry" /> </mxCell> <mxCell id="dataplane" parent="1" style="swimlane;whiteSpace=wrap;html=1;startSize=30;fillColor=none;strokeColor=#9673a6;dashed=1;fontSize=12;fontStyle=1;fontColor=#333333;align=center;spacingLeft=8;verticalAlign=middle;" value="DATA PLANE — what Vault Agent actually renders" vertex="1"> <mxGeometry height="290" width="440" x="380" y="230" as="geometry" /> </mxCell> <mxCell id="vaultagent" parent="dataplane" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;fontColor=#333333;fontSize=13;fontStyle=1;shadow=1;" value="Vault Agent" vertex="1"> <mxGeometry height="60" width="140" x="150" y="40" as="geometry" /> </mxCell> <mxCell id="certflow" parent="dataplane" style="rounded=1;whiteSpace=wrap;html=1;fillColor=light-dark(#FFFFFF,#392F3F);strokeColor=#9673a6;fontColor=#333333;fontSize=12;fontStyle=1;shadow=1;" value="Certificate Flow&lt;br&gt;&lt;font style=&quot;font-size:11px&quot;&gt;PKI engine, short-lived&lt;/font&gt;" vertex="1"> <mxGeometry height="65" width="170" x="30" y="180" as="geometry" /> </mxCell> <mxCell id="secflow" parent="dataplane" style="rounded=1;whiteSpace=wrap;html=1;fillColor=light-dark(#FFFFFF,#392F3F);strokeColor=#9673a6;fontColor=#333333;fontSize=12;fontStyle=1;shadow=1;" value="App Secret Flow&lt;br&gt;&lt;font style=&quot;font-size:11px&quot;&gt;KV, rendered 0600&lt;/font&gt;" vertex="1"> <mxGeometry height="65" width="170" x="240" y="180" as="geometry" /> </mxCell> <mxCell id="container" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontColor=#333333;fontSize=14;fontStyle=1;shadow=1;" value="Container&lt;br&gt;&lt;font style=&quot;font-size:11px&quot;&gt;reads local disk only&lt;/font&gt;" vertex="1"> <mxGeometry height="80" width="200" x="900"Line truncated
|
||||||
|
Before Width: | Height: | Size: 533 KiB After Width: | Height: | Size: 422 KiB |
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Do not edit this file with editors other than draw.io -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" style="background: transparent; background-color: transparent; color-scheme: light dark;" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1312px" height="420px" viewBox="0 0 1312 420" content="<mxfile host="embed.diagrams.net" scale="1" border="0"> <diagram name="Secret Lifecycle" id="secret-lifecycle-1"> <mxGraphModel dx="1414" dy="791" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1340" pageHeight="620" math="0" shadow="0"> <root> <mxCell id="0" /> <mxCell id="1" parent="0" /> <mxCell id="agent" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;fontColor=#333333;fontSize=14;fontStyle=1;shadow=1;" value="Vault Agent&lt;br&gt;&lt;font style=&quot;font-size:11px&quot;&gt;runs on the consuming host&lt;/font&gt;" vertex="1"> <mxGeometry height="90" width="200" x="40" y="260" as="geometry" /> </mxCell> <mxCell id="vault" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;fontColor=#333333;fontSize=14;fontStyle=1;shadow=1;" value="Vault&lt;br&gt;&lt;font style=&quot;font-size:11px&quot;&gt;server&lt;/font&gt;" vertex="1"> <mxGeometry height="90" width="200" x="380" y="260" as="geometry" /> </mxCell> <mxCell id="disk" parent="1" style="shape=cylinder3;whiteSpace=wrap;html=1;fillColor=#F5F5F5;strokeColor=#666666;fontColor=#333333;fontSize=14;fontStyle=1;shadow=1;boundedLbl=1;" value="Local Disk&lt;br&gt;&lt;font style=&quot;font-size:11px&quot;&gt;rendered file&lt;/font&gt;" vertex="1"> <mxGeometry height="110" width="160" x="620" y="250" as="geometry" /> </mxCell> <mxCell id="app" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontColor=#333333;fontSize=14;fontStyle=1;shadow=1;" value="Application" vertex="1"> <mxGeometry height="90" width="200" x="1140" y="260" as="geometry" /> </mxCell> <mxCell id="s1" edge="1" parent="1" source="agent" style="html=1;labelBackgroundColor=#1C1D1E;endArrow=block;strokeColor=#9673a6;fontColor=#9673a6;fontSize=11;rounded=1;edgeStyle=orthogonalEdgeStyle;exitX=0.3;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" target="vault" value="&lt;font style=&quot;font-size: 13px; background-color: light-dark(rgb(255, 255, 255), rgb(28, 29, 30));&quot;&gt;① authenticate — AppRole (role_id + secret_id)&lt;/font&gt;"> <mxGeometry relative="1" y="20" as="geometry"> <mxPoint as="offset" /> <Array as="points"> <mxPoint x="100" y="200" /> <mxPoint x="480" y="200" /> </Array> </mxGeometry> </mxCell> <mxCell id="s2" edge="1" parent="1" source="vault" style="html=1;labelBackgroundColor=#1C1D1E;endArrow=block;strokeColor=#9673a6;fontColor=#9673a6;fontSize=11;rounded=1;edgeStyle=orthogonalEdgeStyle;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.603;entryY=0.996;entryDx=0;entryDy=0;entryPerimeter=0;" target="agent" value="&lt;font style=&quot;font-size: 13px; background-color: light-dark(rgb(255, 255, 255), rgb(28, 29, 30));&quot;&gt;② short-lived token issued, cached, auto-renewed&lt;/font&gt;"> <mxGeometry relative="1" x="0.0547" y="15" as="geometry"> <mxPoint as="offset" /> <Array as="points"> <mxPoint x="480" y="375" /> <mxPoint x="160" y="375" Line truncated
|
||||||
|
After Width: | Height: | Size: 346 KiB |
@@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- Do not edit this file with editors other than draw.io -->
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" style="background: transparent; background-color: transparent; color-scheme: light dark;" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1312px" height="393px" viewBox="0 0 1312 393" content="<mxfile host="embed.diagrams.net" scale="1" border="0"> <diagram name="Secret Lifecycle" id="secret-lifecycle-1"> <mxGraphModel dx="1414" dy="791" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1340" pageHeight="620" math="0" shadow="0"> <root> <mxCell id="0" /> <mxCell id="1" parent="0" /> <mxCell id="agent" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;fontColor=#333333;fontSize=14;fontStyle=1;shadow=1;" value="Vault Agent&lt;br&gt;&lt;font style=&quot;font-size:11px&quot;&gt;runs on the consuming host&lt;/font&gt;" vertex="1"> <mxGeometry height="90" width="200" x="40" y="260" as="geometry" /> </mxCell> <mxCell id="vault" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;fontColor=#333333;fontSize=14;fontStyle=1;shadow=1;" value="Vault&lt;br&gt;&lt;font style=&quot;font-size:11px&quot;&gt;server&lt;/font&gt;" vertex="1"> <mxGeometry height="90" width="200" x="380" y="260" as="geometry" /> </mxCell> <mxCell id="disk" parent="1" style="shape=cylinder3;whiteSpace=wrap;html=1;fillColor=#F5F5F5;strokeColor=#666666;fontColor=#333333;fontSize=14;fontStyle=1;shadow=1;boundedLbl=1;" value="Local Disk&lt;br&gt;&lt;font style=&quot;font-size:11px&quot;&gt;rendered file&lt;/font&gt;" vertex="1"> <mxGeometry height="110" width="160" x="620" y="250" as="geometry" /> </mxCell> <mxCell id="app" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontColor=#333333;fontSize=14;fontStyle=1;shadow=1;" value="Application" vertex="1"> <mxGeometry height="90" width="200" x="1140" y="260" as="geometry" /> </mxCell> <mxCell id="s1" edge="1" parent="1" source="agent" style="html=1;labelBackgroundColor=light-dark(#FFFFFF,#1C1D1E);endArrow=block;strokeColor=#9673a6;fontColor=#9673a6;fontSize=11;rounded=1;edgeStyle=orthogonalEdgeStyle;exitX=0.3;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" target="vault" value="&lt;font style=&quot;font-size: 13px;&quot;&gt;① authenticate — AppRole (role_id + secret_id)&lt;/font&gt;"> <mxGeometry relative="1" y="-20" as="geometry"> <mxPoint as="offset" /> <Array as="points"> <mxPoint x="100" y="200" /> <mxPoint x="480" y="200" /> </Array> </mxGeometry> </mxCell> <mxCell id="s2" edge="1" parent="1" source="vault" style="html=1;labelBackgroundColor=light-dark(#FFFFFF,#1C1D1E);endArrow=block;strokeColor=#9673a6;fontColor=#9673a6;fontSize=11;rounded=1;edgeStyle=orthogonalEdgeStyle;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.603;entryY=0.996;entryDx=0;entryDy=0;entryPerimeter=0;" target="agent" value="&lt;font style=&quot;font-size: 13px;&quot;&gt;② short-lived token issued, cached, auto-renewed&lt;/font&gt;"> <mxGeometry relative="1" x="0.0547" y="15" as="geometry"> <mxPoint as="offset" /> <Array as="points"> <mxPoint x="480" y="375" /> <mxPoint x="160" y="375" /> <mxPoint x="160" y="349.6" /> Line truncated
|
|
||||||
|
Before Width: | Height: | Size: 366 KiB |
@@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- Do not edit this file with editors other than draw.io -->
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" style="background: transparent; background-color: transparent; color-scheme: light dark;" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1082px" height="452px" viewBox="0 0 1082 452" id="ge-svg-Kn4GrH9ly3f1QbANnA0X" content="<mxfile host="embed.diagrams.net" scale="1" border="0"> <diagram name="Trust Boundaries and Blast Radius" id="trust-boundaries-1"> <mxGraphModel dx="1178" dy="659" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1040" pageHeight="500" math="0" shadow="0"> <root> <mxCell id="0" /> <mxCell id="1" parent="0" /> <mxCell id="pfsense" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;fontSize=18;fontStyle=1;fontColor=#333333;fontFamily=Helvetica;align=center;verticalAlign=top;spacingTop=8;" value="pfSense&lt;br&gt;&lt;font style=&quot;font-size: 14px;&quot;&gt;Edge Firewall + Public HAProxy&lt;/font&gt;" vertex="1"> <mxGeometry height="60" width="240" x="490" y="30" as="geometry" /> </mxCell> <mxCell id="nodeA" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#F5F5F5;strokeColor=#666666;dashed=1;verticalAlign=top;fontSize=18;fontStyle=1;spacingTop=8;fontColor=#333333;" value="Proxmox Node A&lt;br&gt;&lt;font style=&quot;font-size: 14px;&quot;&gt;day-to-day applications&lt;/font&gt;" vertex="1"> <mxGeometry height="340" width="420" x="70" y="140" as="geometry" /> </mxCell> <mxCell id="internalCaddy" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;fontSize=18;fontStyle=1;fontColor=#333333;" value="Internal Caddy&lt;br&gt;&lt;font style=&quot;font-size: 14px;&quot;&gt;app-to-app zone&lt;/font&gt;" vertex="1"> <mxGeometry height="60" width="340" x="110" y="200" as="geometry" /> </mxCell> <mxCell id="apps" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=18;fontStyle=1;fontColor=#333333;" value="Docker Apps&lt;br&gt;&lt;font style=&quot;font-size: 14px;&quot;&gt;LXC host, most of the fleet&#39;s stacks&lt;/font&gt;" vertex="1"> <mxGeometry height="70" width="340" x="110" y="390" as="geometry" /> </mxCell> <mxCell id="nodeB" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#F5F5F5;strokeColor=#666666;dashed=1;verticalAlign=top;fontSize=18;fontStyle=1;spacingTop=8;fontColor=#333333;" value="Proxmox Node B&lt;br&gt;&lt;font style=&quot;font-size: 14px;&quot;&gt;trust &amp;amp; sensitive infrastructure&lt;/font&gt;" vertex="1"> <mxGeometry height="340" width="420" x="730" y="140" as="geometry" /> </mxCell> <mxCell id="caddyVM" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;fontSize=18;fontStyle=1;fontColor=#333333;" value="Caddy VM&lt;br&gt;&lt;font style=&quot;font-size: 14px;&quot;&gt;browser zone, mTLS&lt;/font&gt;" vertex="1"> <mxGeometry height="60" width="340" x="770" y="200" as="geometry" /> </mxCell> <mxCell id="vaultDns" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;fontSize=18;fontStyle=1;fontColor=#333333;" value="Vault + Technitium DNS&lt;br&gt;&lt;font style=&quot;font-size: 14px;&quot;&gt;trust backbone&lt;/font&gt;" vertex="1"> <mxGeometry height="70" width="340" x="770" y="390" as="geometry" /> </mxCell> <mxCelLine truncated
|
|
||||||
|
Before Width: | Height: | Size: 385 KiB |
@@ -18,7 +18,7 @@ The easy answers are both wrong. **One boundary** ("you're on the LAN, you're tr
|
|||||||
|
|
||||||
The actual answer here is **four boundaries, each doing a different job**:
|
The actual answer here is **four boundaries, each doing a different job**:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
| Boundary | Who's on the other end | Control | Why this control, not another |
|
| Boundary | Who's on the other end | Control | Why this control, not another |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ rule family="ipv4" source address="[automation host]" port port="[this host's SS
|
|||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> **A real, documented gap**: on Docker hosts, container-published ports bypass firewalld entirely — Docker's own iptables rules are evaluated before firewalld's zone rules, so firewalld's local restrictions only actually protect the small number of *native*, non-containerized services on that host (things like the observability agent from [`docs/observability.md`](observability.md)), not the dozens of containerized apps in [`stacks/`](../stacks/). The network-level segmentation above still applies regardless — pfSense doesn't care whether a port is opened by firewalld or by Docker's own NAT rules — so this is a defense-in-depth gap specifically at the host layer, not an open-to-the-world one. Worth naming precisely rather than glossing over: firewalld protects less of a Docker host's actual attack surface than it might first appear to.
|
> **A real, documented gap**: on Docker hosts, container-published ports bypass firewalld entirely — Docker's own iptables rules are evaluated before firewalld's zone rules, so firewalld's local restrictions only actually protect the small number of *native*, non-containerized services on that host (things like the observability agent from [`docs/observability.md`](observability.md)), not the dozens of containerized apps in [`stacks/`](../stacks/). The network-level segmentation above still applies regardless — pfSense doesn't care whether a port is opened by firewalld or by Docker's own NAT rules — so this is a defense-in-depth gap specifically at the host layer, not an open-to-the-world one. Worth naming precisely rather than glossing over: firewalld protects less of a Docker host's actual attack surface than it might first appear to.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Edge firewall: three jobs, one device
|
## Edge firewall: three jobs, one device
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ Every Vault Agent reaches Vault through the internal app-to-app proxy zone descr
|
|||||||
|
|
||||||
None of these ever call the Vault API themselves — every one of them goes through the same local-render sequence:
|
None of these ever call the Vault API themselves — every one of them goes through the same local-render sequence:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
**Two authentication flows, not one:**
|
**Two authentication flows, not one:**
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ A homelab with dozens of self-hosted apps means dozens of different auth impleme
|
|||||||
|
|
||||||
The proxy layer earns its own keep on top of that, though — it protects apps that don't have a login of their own at all. Not every self-hosted tool ships with real authentication; some are built assuming they'll only ever be reached from an already-trusted network and skip it entirely. Registering a Proxy Provider in front of an app like that puts Authentik's forward-auth check at the reverse-proxy hop, before the request ever reaches the app itself — so an app with zero security of its own still sits behind a real login, enforced by something other than the app. For apps that already have native OIDC, the proxy layer isn't doing the authenticating (the app does its own token exchange) — but for everything else, it's the only auth check that exists, period.
|
The proxy layer earns its own keep on top of that, though — it protects apps that don't have a login of their own at all. Not every self-hosted tool ships with real authentication; some are built assuming they'll only ever be reached from an already-trusted network and skip it entirely. Registering a Proxy Provider in front of an app like that puts Authentik's forward-auth check at the reverse-proxy hop, before the request ever reaches the app itself — so an app with zero security of its own still sits behind a real login, enforced by something other than the app. For apps that already have native OIDC, the proxy layer isn't doing the authenticating (the app does its own token exchange) — but for everything else, it's the only auth check that exists, period.
|
||||||
|
|
||||||
Every app gets the Proxy Provider registration described above — no exceptions, that's the universal baseline. A handful of apps get a second, additional layer on top of it: real native OIDC/SAML support for their own login page, used *alongside* the Proxy Provider rather than replacing it:
|
Every app gets the Proxy Provider registration described above — that's the universal baseline, with exactly one real exception found by checking live rather than assuming: Proxmox itself has no Proxy Provider at all, confirmed absent from Authentik's own API. It runs on native OIDC only — unlike its sibling PBS, which has both. A handful of other apps get a second, additional layer on top of the Proxy Provider baseline: real native OIDC/SAML support for their own login page, used *alongside* it rather than replacing it. The table below is pulled from Authentik's API directly, not from memory of what was set up — each provider's actual model type, not just its name:
|
||||||
|
|
||||||
| App | Stack |
|
| App | Stack |
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -20,10 +20,23 @@ Every app gets the Proxy Provider registration described above — no exceptions
|
|||||||
| Grafana | [`stacks/platform/grafana-stack/`](../stacks/platform/grafana-stack/) |
|
| Grafana | [`stacks/platform/grafana-stack/`](../stacks/platform/grafana-stack/) |
|
||||||
| Audiobookshelf | [`stacks/media/audiobookshelf/`](../stacks/media/audiobookshelf/) |
|
| Audiobookshelf | [`stacks/media/audiobookshelf/`](../stacks/media/audiobookshelf/) |
|
||||||
| Dockhand | [`stacks/platform/dockhand/`](../stacks/platform/dockhand/) |
|
| Dockhand | [`stacks/platform/dockhand/`](../stacks/platform/dockhand/) |
|
||||||
| Proxmox / PBS | hypervisor-level, not a docker stack |
|
| Firefly III | [`stacks/productivity/firefly-iii/`](../stacks/productivity/firefly-iii/) |
|
||||||
|
| Seerr | [`stacks/media/seerr/`](../stacks/media/seerr/) |
|
||||||
|
| Proxmox / PBS | hypervisor-level, not a docker stack — PBS has both layers, Proxmox has native OIDC only |
|
||||||
|
|
||||||
Every other app runs on the Proxy Provider alone — which, per the point above, is never a weaker path: it's the same forward-auth check either way, just without a second, app-native login layered on top of it.
|
Every other app runs on the Proxy Provider alone — which, per the point above, is never a weaker path: it's the same forward-auth check either way, just without a second, app-native login layered on top of it.
|
||||||
|
|
||||||
|
This is the actual live query against Authentik's API, not a paraphrase of what's assumed to be configured:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ curl -s -H "Authorization: Bearer [REDACTED]" \
|
||||||
|
"https://authentik.[service-to-service zone]/api/v3/providers/proxy/?page_size=1" | jq '.pagination.count'
|
||||||
|
96
|
||||||
|
$ curl -s -H "Authorization: Bearer [REDACTED]" \
|
||||||
|
"https://authentik.[service-to-service zone]/api/v3/providers/proxy/?page_size=200" | jq -r '.results[].name' | grep -E '^proxmox'
|
||||||
|
[no output — Proxmox has no Proxy Provider at all]
|
||||||
|
```
|
||||||
|
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
> **Removal is a real, deliberate step, not an afterthought.** Decommissioning an app means deleting the Application (by slug), then the Proxy Provider, then detaching that provider from the outpost's own provider list, then removing its icon file — in that order. Skipping the outpost-detach step is the easy mistake: the Application and Provider both look gone, but the outpost still references a provider that no longer resolves to anything, and that orphaned reference doesn't surface as an error anywhere obvious.
|
> **Removal is a real, deliberate step, not an afterthought.** Decommissioning an app means deleting the Application (by slug), then the Proxy Provider, then detaching that provider from the outpost's own provider list, then removing its icon file — in that order. Skipping the outpost-detach step is the easy mistake: the Application and Provider both look gone, but the outpost still references a provider that no longer resolves to anything, and that orphaned reference doesn't surface as an error anywhere obvious.
|
||||||
|
|
||||||
@@ -47,7 +60,7 @@ So an app's OIDC discovery/authority setting always points at the service-to-ser
|
|||||||
|
|
||||||
## Git access: least privilege, not the admin account
|
## Git access: least privilege, not the admin account
|
||||||
|
|
||||||
Automation should never run with more privilege than it needs. CI and orchestration pushes (the flows described in [`docs/orchestration.md`](orchestration.md)) run through a dedicated, non-admin bot account with scoped write access — never through an administrator identity. Hosts that only need to *read* get separate read-only deploy keys instead of any shared credential. The practical payoff: if an automation credential ever leaks, the blast radius is "can push to specific repos," not "has full admin control of the Git server."
|
Automation should never run with more privilege than it needs. CI and orchestration pushes (the flows described in [`docs/orchestration.md`](orchestration.md)) run through a dedicated, non-admin bot account with scoped write access — never through an administrator identity. This wasn't the original setup: automated pushes used to run through the personal admin account used for interactive access, the same one with full admin rights over the Git server. That got fixed by standing up a separate account with its own SSH keypair and adding it as a write collaborator on every repo it touches — routine automated commits no longer run under an admin credential at all. Hosts that only need to *read* get separate read-only deploy keys instead of any shared credential. The practical payoff: if an automation credential ever leaks, the blast radius is "can push to specific repos," not "has full admin control of the Git server."
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|||||||