<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Administration on PosternProxy Documentation</title><link>https://d8f649ee.posternproxydocs.pages.dev/administration/</link><description>Recent content in Administration on PosternProxy Documentation</description><generator>Hugo</generator><language>en-US</language><atom:link href="https://d8f649ee.posternproxydocs.pages.dev/administration/index.xml" rel="self" type="application/rss+xml"/><item><title>Security Hardening</title><link>https://d8f649ee.posternproxydocs.pages.dev/administration/security-hardening/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://d8f649ee.posternproxydocs.pages.dev/administration/security-hardening/</guid><description>&lt;h1 id="security-hardening"&gt;Security Hardening&lt;a class="anchor" href="#security-hardening"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;The PosternProxy install script applies a comprehensive set of security hardening measures. This page documents what is applied and how to verify it.&lt;/p&gt;
&lt;h2 id="application-level-hardening"&gt;Application-level hardening&lt;a class="anchor" href="#application-level-hardening"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="authentication"&gt;Authentication&lt;a class="anchor" href="#authentication"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;JWT access tokens with 15-minute TTL&lt;/li&gt;
&lt;li&gt;Refresh tokens with 7-day TTL stored in HttpOnly / Secure / SameSite=Strict cookies&lt;/li&gt;
&lt;li&gt;bcrypt password hashing at cost factor 12&lt;/li&gt;
&lt;li&gt;Login rate limit: 5 attempts/min per IP (enforced by the API and by fail2ban)&lt;/li&gt;
&lt;li&gt;Account lockout is not time-based — use fail2ban to block IPs&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="api-security"&gt;API security&lt;a class="anchor" href="#api-security"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Rate limiting:&lt;/strong&gt; 120 requests/min per IP on authenticated management API routes; 5 login attempts/min per IP; 30 agent WebSocket connection attempts/min per IP&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CSRF protection:&lt;/strong&gt; Double-submit cookie pattern on all state-changing requests&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Secure headers on all responses:&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;X-Content-Type-Options: nosniff&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;X-Frame-Options: DENY&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Strict-Transport-Security: max-age=31536000; includeSubDomains&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Content-Security-Policy: default-src 'self'&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Input validation:&lt;/strong&gt; All user input validated at the handler layer; domain names validated per RFC 1123; ports validated 1–65535; CIDRs parsed and validated; all database queries use parameterized statements (no SQL interpolation)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="encrypted-storage"&gt;Encrypted storage&lt;a class="anchor" href="#encrypted-storage"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;DNS provider credentials and SSH credentials are encrypted at rest using AES-256-GCM with a key derived from the JWT secret.&lt;/p&gt;</description></item><item><title>Upgrading</title><link>https://d8f649ee.posternproxydocs.pages.dev/administration/upgrading/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://d8f649ee.posternproxydocs.pages.dev/administration/upgrading/</guid><description>&lt;h1 id="upgrading-posternproxy"&gt;Upgrading PosternProxy&lt;a class="anchor" href="#upgrading-posternproxy"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="upgrading-the-controller"&gt;Upgrading the controller&lt;a class="anchor" href="#upgrading-the-controller"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Build the new binary&lt;/strong&gt; on your development machine:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bash scripts/build.sh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Copy the binary to the server:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;scp posternproxy user@your-server:/tmp/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Stop the service, replace the binary, restart:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh root@your-server
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;systemctl stop posternproxy
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cp /tmp/posternproxy /usr/local/bin/posternproxy
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;systemctl start posternproxy&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Verify the service started:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;systemctl status posternproxy
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;journalctl -u posternproxy -n &lt;span style="color:#a5d6ff"&gt;20&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="database-migrations"&gt;Database migrations&lt;a class="anchor" href="#database-migrations"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;PosternProxy runs embedded migrations automatically on startup. If a new version adds database columns or tables, the migration runs before the service starts serving requests. No manual intervention is needed.&lt;/p&gt;</description></item><item><title>Troubleshooting</title><link>https://d8f649ee.posternproxydocs.pages.dev/administration/troubleshooting/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://d8f649ee.posternproxydocs.pages.dev/administration/troubleshooting/</guid><description>&lt;h1 id="troubleshooting"&gt;Troubleshooting&lt;a class="anchor" href="#troubleshooting"&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="service-not-starting"&gt;Service not starting&lt;a class="anchor" href="#service-not-starting"&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id="posternproxy-fails-to-start"&gt;PosternProxy fails to start&lt;a class="anchor" href="#posternproxy-fails-to-start"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;journalctl -u posternproxy -n &lt;span style="color:#a5d6ff"&gt;50&lt;/span&gt; --no-pager&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Common causes:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Symptom&lt;/th&gt;
					&lt;th&gt;Cause&lt;/th&gt;
					&lt;th&gt;Fix&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;database is locked&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Another PosternProxy instance is running&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;systemctl stop posternproxy; systemctl start posternproxy&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;migration failed&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Bad database state&lt;/td&gt;
					&lt;td&gt;Restore from backup&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;address already in use :81&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Another process owns port 81&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;ss -tlnp | grep 81&lt;/code&gt; to find it&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;config.env not found&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Missing config file&lt;/td&gt;
					&lt;td&gt;Re-run &lt;code&gt;scripts/install.sh&lt;/code&gt; or create manually&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="caddy-fails-to-start"&gt;Caddy fails to start&lt;a class="anchor" href="#caddy-fails-to-start"&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;journalctl -u caddy -n &lt;span style="color:#a5d6ff"&gt;50&lt;/span&gt; --no-pager
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;caddy validate --config /etc/caddy/Caddyfile &lt;span style="color:#8b949e;font-style:italic"&gt;# if using Caddyfile fallback&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Common causes:&lt;/p&gt;</description></item></channel></rss>