@sysnee/pgs 0.1.6 → 0.1.7-rc.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/PROJECT.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  ### What It Is
6
6
 
7
- A **dynamic PostgreSQL multi-tenant management system** that provides complete database isolation by creating dedicated PostgreSQL instances per tenant. The system uses HAProxy with custom PostgreSQL protocol parsing to route connections intelligently while maintaining complete tenant isolation at the database server level.
7
+ A **dynamic PostgreSQL multi-tenant management system** that provides complete database isolation by creating dedicated PostgreSQL instances per tenant. The system uses **Traefik v3** with native PostgreSQL STARTTLS support to route connections based on SNI (Server Name Indication), enabling hostname-based tenant routing with TLS security.
8
8
 
9
9
  ### Core Concept
10
10
 
@@ -22,15 +22,16 @@ Instead of sharing a single PostgreSQL instance with multiple databases (shared
22
22
  - Automatic volume and network configuration
23
23
  - Custom initialization scripts per tenant
24
24
 
25
- 2. **Intelligent Routing**
26
- - HAProxy parses PostgreSQL protocol to extract username
27
- - Routes connections to correct tenant backend automatically
25
+ 2. **SNI-Based Routing**
26
+ - Traefik v3 handles PostgreSQL STARTTLS protocol
27
+ - Routes connections based on hostname (SNI)
28
28
  - Single external port (5432) for all tenants
29
+ - TLS/SSL encryption required
29
30
 
30
31
  3. **Access Control**
31
32
  - Per-tenant external access enable/disable
32
- - Secure by default (access disabled on creation)
33
33
  - Runtime access control without service restart
34
+ - Dynamic Traefik configuration updates
34
35
 
35
36
  4. **Complete Isolation**
36
37
  - Separate Docker containers per tenant
@@ -39,49 +40,50 @@ Instead of sharing a single PostgreSQL instance with multiple databases (shared
39
40
  - No shared processes or memory
40
41
 
41
42
  5. **Zero-Downtime Operations**
42
- - Graceful HAProxy reloads
43
+ - Traefik dynamic configuration (no restarts needed)
43
44
  - Independent tenant management
44
45
  - No impact on other tenants during operations
45
46
 
46
47
  ## Architecture
47
48
 
48
49
  ```
49
- ┌─────────────────────────────────────────────────────────┐
50
- External Access
51
- (localhost:5432)
52
- └──────────────────────┬──────────────────────────────────┘
53
-
54
-
55
- ┌─────────────────────────────────────────────────────────┐
56
- HAProxy Proxy
57
- ┌──────────────────────────────────────────────────┐
58
- │ │ Frontend: postgres_frontend
59
- │ │ - Listens on port 5432
60
- │ │ - Parses PostgreSQL protocol (Lua script)
61
- │ │ - Extracts username from startup packet
62
- │ │ - Checks tenant-access.json for permissions
63
- └──────────────────────────────────────────────────┘
64
- └──────────────────────┬──────────────────────────────────┘
65
-
66
- ┌──────────────┼──────────────┐
67
-
68
-
69
- ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
70
- Backend │Backend │Backend
71
- pgs_tenant1 pgs_tenant2 pgs_tenant3
72
- └──────┬───────┘ └──────┬───────┘ └──────┬───────┘
73
- │ │ │
74
- ▼ ▼ ▼
75
- ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
76
- │ PostgreSQL │ │ PostgreSQL │ │ PostgreSQL │
77
- Container 1 Container 2 Container 3
78
- │ │ │ │
79
- Port: 5432 Port: 5432 Port: 5432 │
80
- (internal) (internal) (internal)
81
- │ │ │ │
82
- Volume: Volume: Volume:
83
- pgdata_1 pgdata_2 pgdata_3
84
- └──────────────┘ └──────────────┘ └──────────────┘
50
+ ┌─────────────────────────────────────────────────────────────────┐
51
+ External Access
52
+ (tenant-id.pgs.domain.com:5432 + TLS/SNI)
53
+ └───────────────────────────┬─────────────────────────────────────┘
54
+
55
+
56
+ ┌─────────────────────────────────────────────────────────────────┐
57
+ Traefik v3 Proxy
58
+ ┌───────────────────────────────────────────────────────────┐
59
+ │ │ EntryPoint: postgres (port 5432)
60
+ │ │ - TLS termination with wildcard certificate
61
+ │ │ - PostgreSQL STARTTLS protocol support
62
+ │ │ - SNI-based routing (HostSNI rule)
63
+ │ │ - Dynamic configuration via dynamic.yml
64
+ └───────────────────────────────────────────────────────────┘
65
+ └───────────────────────────┬─────────────────────────────────────┘
66
+
67
+ ┌──────────────────┼──────────────────┐
68
+
69
+
70
+ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
71
+ TCP Router TCP Router TCP Router
72
+ tenant1 tenant2 tenant3
73
+ │ HostSNI() │ │ HostSNI() │ │ HostSNI() │
74
+ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘
75
+ │ │ │
76
+ ▼ ▼ ▼
77
+ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
78
+ PostgreSQL PostgreSQL PostgreSQL
79
+ Container 1 Container 2 Container 3
80
+ │ │
81
+ Port: 5432 Port: 5432 Port: 5432
82
+ (internal) (internal) (internal)
83
+
84
+ Volume: Volume: Volume:
85
+ pgdata_1 │ │ pgdata_2 │ │ pgdata_3 │
86
+ └──────────────┘ └──────────────┘ └──────────────┘
85
87
  ```
86
88
 
87
89
  ## Technical Implementation
@@ -91,37 +93,88 @@ Instead of sharing a single PostgreSQL instance with multiple databases (shared
91
93
  1. **Manager Script (manager.js)**
92
94
  - Node.js CLI tool for tenant lifecycle management
93
95
  - Dynamically generates docker-compose.yml entries
94
- - Manages HAProxy configuration
96
+ - Manages Traefik dynamic configuration (dynamic.yml)
95
97
  - Controls tenant access permissions
96
98
 
97
- 2. **HAProxy Reverse Proxy**
98
- - TCP-level load balancer and router
99
- - Custom Lua script for PostgreSQL protocol parsing
100
- - Routes based on extracted username
101
- - Per-tenant access control
99
+ 2. **Traefik v3 Reverse Proxy**
100
+ - TCP-level routing with TLS termination
101
+ - Native PostgreSQL STARTTLS protocol support
102
+ - SNI-based routing via HostSNI() rule
103
+ - Dynamic configuration without restarts
102
104
 
103
- 3. **PostgreSQL Protocol Parser (pg-route.lua)**
104
- - Parses binary PostgreSQL startup packet
105
- - Extracts username and connection parameters
106
- - Handles SSL negotiation
107
- - Enforces access control policies
108
-
109
- 4. **Docker Infrastructure**
105
+ 3. **Docker Infrastructure**
110
106
  - Separate container per tenant
111
107
  - Bridge network for internal communication
112
108
  - Persistent volumes for data
113
109
  - Isolated execution environments
114
110
 
111
+ ### Why Traefik v3 for PostgreSQL?
112
+
113
+ PostgreSQL uses a non-standard TLS negotiation called STARTTLS:
114
+
115
+ ```
116
+ Standard TLS (HTTPS):
117
+ Client → TLS ClientHello (with SNI) → Server
118
+
119
+ PostgreSQL STARTTLS:
120
+ Client → SSLRequest (PG protocol) → Server
121
+ Server → 'S' (SSL supported) → Client
122
+ Client → TLS ClientHello (with SNI) → Server
123
+ ```
124
+
125
+ Most SNI proxies expect TLS ClientHello as the first packet. PostgreSQL sends its own protocol first, then TLS.
126
+
127
+ **Traefik v3** is one of the few proxies that natively understands this PostgreSQL-specific flow:
128
+ - Detects PostgreSQL SSLRequest
129
+ - Responds with 'S'
130
+ - Captures TLS ClientHello with SNI
131
+ - Routes based on hostname
132
+
115
133
  ### Connection Flow
116
134
 
117
- 1. Client connects to `localhost:5432` with username `tenant_id`
118
- 2. HAProxy receives connection and invokes Lua script
119
- 3. Script parses PostgreSQL startup packet and extracts username
120
- 4. Script checks `tenant-access.json` for permission
121
- 5. If allowed, routes to backend `pgs_{tenant_id}`
122
- 6. Backend forwards to PostgreSQL container on internal network
135
+ 1. Client connects to `tenant-id.pgs.domain.com:5432` with `sslmode=require`
136
+ 2. Traefik receives connection and detects PostgreSQL SSLRequest
137
+ 3. Traefik responds 'S' (SSL supported)
138
+ 4. Client sends TLS ClientHello with SNI (hostname)
139
+ 5. Traefik extracts SNI and matches against configured routers in dynamic.yml
140
+ 6. If tenant has access enabled, routes to backend `pgs_{tenant_id}:5432`
123
141
  7. Connection established with complete isolation
124
142
 
143
+ ### Configuration Files
144
+
145
+ **traefik.yml** (Static Configuration):
146
+ ```yaml
147
+ entryPoints:
148
+ postgres:
149
+ address: ":5432"
150
+
151
+ providers:
152
+ file:
153
+ filename: /etc/traefik/dynamic.yml
154
+ watch: true
155
+ ```
156
+
157
+ **dynamic.yml** (Dynamic Configuration - Auto-generated):
158
+ ```yaml
159
+ tcp:
160
+ routers:
161
+ router_tenant1:
162
+ entryPoints:
163
+ - postgres
164
+ rule: "HostSNI(`tenant1-abc123.pgs.domain.com`)"
165
+ service: svc_tenant1
166
+ tls: {}
167
+ services:
168
+ svc_tenant1:
169
+ loadBalancer:
170
+ servers:
171
+ - address: "pgs_tenant1-abc123:5432"
172
+ tls:
173
+ certificates:
174
+ - certFile: /etc/traefik/certs/fullchain.pem
175
+ keyFile: /etc/traefik/certs/privkey.pem
176
+ ```
177
+
125
178
  ## Comparison with Similar Solutions
126
179
 
127
180
  ### Shared Database Architecture
@@ -152,7 +205,7 @@ Instead of sharing a single PostgreSQL instance with multiple databases (shared
152
205
  - **Difference**: Shards data across nodes; this creates separate instances per tenant
153
206
  - **Use Case**: Horizontal scaling vs. tenant isolation
154
207
 
155
- #### 3. **Patroni + HAProxy**
208
+ #### 3. **Patroni + Traefik/HAProxy**
156
209
  - **Purpose**: High availability and load balancing
157
210
  - **Difference**: Replicates single database; this creates isolated instances
158
211
  - **Use Case**: HA for single database vs. multi-tenant isolation
@@ -173,18 +226,18 @@ Instead of sharing a single PostgreSQL instance with multiple databases (shared
173
226
  - Not just database or schema separation
174
227
  - Complete process and memory isolation
175
228
 
176
- 2. **Dynamic provisioning with single external port**
229
+ 2. **SNI-based routing with single external port**
177
230
  - No need for port management
178
- - Automatic routing based on connection parameters
231
+ - Automatic routing based on hostname
232
+ - TLS/SSL security by default
179
233
 
180
- 3. **Protocol-aware routing**
181
- - Parses PostgreSQL binary protocol
182
- - Routes before connection completion
183
- - Handles SSL negotiation
234
+ 3. **PostgreSQL STARTTLS support**
235
+ - Traefik v3 handles non-standard PG protocol
236
+ - Proper SNI extraction after PG handshake
184
237
 
185
238
  4. **Runtime access control**
186
239
  - Enable/disable tenant access without restart
187
- - No downtime for access changes
240
+ - Dynamic Traefik configuration
188
241
 
189
242
  5. **Docker-native architecture**
190
243
  - Leverages container isolation
@@ -211,9 +264,9 @@ Instead of sharing a single PostgreSQL instance with multiple databases (shared
211
264
  ## Advantages
212
265
 
213
266
  ✅ **Maximum Isolation**: Complete process and data separation
214
- ✅ **Security**: Zero risk of cross-tenant data access
267
+ ✅ **Security**: Zero risk of cross-tenant data access + TLS encryption
215
268
  ✅ **Flexibility**: Independent scaling and management per tenant
216
- ✅ **Simplicity**: Single external port, automatic routing
269
+ ✅ **Simplicity**: Single external port, automatic SNI routing
217
270
  ✅ **Compliance**: Easier to meet regulatory requirements
218
271
  ✅ **Debugging**: Isolated environments simplify troubleshooting
219
272
 
@@ -223,15 +276,16 @@ Instead of sharing a single PostgreSQL instance with multiple databases (shared
223
276
  ⚠️ **Management Overhead**: More containers to manage
224
277
  ⚠️ **Scaling Limits**: Practical limit on number of tenants per host
225
278
  ⚠️ **Backup Complexity**: Need to backup multiple instances
279
+ ⚠️ **SSL Required**: Clients must support TLS with SNI
226
280
 
227
281
  ## Technology Stack
228
282
 
229
283
  - **Runtime**: Node.js (ES Modules)
230
284
  - **Container Orchestration**: Docker Compose
231
- - **Reverse Proxy**: HAProxy with Lua scripting
285
+ - **Reverse Proxy**: Traefik v3 (PostgreSQL STARTTLS + SNI routing)
232
286
  - **Database**: PostgreSQL 18+
233
- - **Protocol Parsing**: Custom Lua script
234
- - **Configuration**: YAML (docker-compose.yml), JSON (tenant-access.json)
287
+ - **TLS**: Wildcard SSL certificate
288
+ - **Configuration**: YAML (docker-compose.yml, traefik.yml, dynamic.yml), JSON (tenant-access.json)
235
289
 
236
290
  ## Future Enhancements
237
291
 
@@ -242,9 +296,8 @@ Instead of sharing a single PostgreSQL instance with multiple databases (shared
242
296
  - [ ] Tenant migration tools
243
297
  - [ ] Kubernetes support
244
298
  - [ ] Connection pooling per tenant
245
- - [ ] SSL/TLS termination
299
+ - [ ] Web dashboard
246
300
 
247
301
  ## License & Status
248
302
 
249
- This is a custom solution built for specific multi-tenant requirements. It combines open-source tools (HAProxy, PostgreSQL, Docker) with custom routing logic to achieve instance-per-tenant isolation with intelligent connection routing.
250
-
303
+ This is a custom solution built for specific multi-tenant requirements. It combines open-source tools (Traefik v3, PostgreSQL, Docker) with SNI-based routing to achieve instance-per-tenant isolation with intelligent connection routing.