forked from
baileytownsend.dev/pds-gatekeeper
Microservice to bring 2FA to self hosted PDSes
1# PDS gatekeeper
2
3A microservice that sits on the same server as the PDS to add some of the security that the entryway does.
4
5
6
7PDS gatekeeper works by overriding some of the PDS endpoints inside your Caddyfile to provide gatekeeping to certain
8endpoints. Mainly, the ability to have 2FA on a self hosted PDS like it does on a Bluesky mushroom(PDS). Most of the
9logic of these endpoints still happens on the PDS via a proxied request, just some are gatekept.
10
11# Features
12
13## 2FA
14
15- Overrides The login endpoint to add 2FA for both Bluesky client logged in and OAuth logins
16- Overrides the settings endpoints as well. As long as you have a confirmed email you can turn on 2FA
17
18## Captcha on Create Account
19
20Future feature?
21
22# Setup
23
24PDS Gatekeeper has 2 parts to its setup, docker compose file and a reverse proxy (Caddy in this case). I will be
25assuming you setup the PDS following the directions
26found [here](https://atproto.com/guides/self-hosting), but if yours is different, or you have questions, feel free to
27let
28me know, and we can figure it out.
29
30## Docker compose
31
32The pds gatekeeper container can be found on docker hub under the name `fatfingers23/pds_gatekeeper`. The container does
33need access to the `/pds` root folder to access the same db's as your PDS. The part you need to add would look a bit
34like below. You can find a full example of what I use for my pds at [./examples/compose.yml](./examples/compose.yml).
35This is usually found at `/pds/compose.yaml`on your PDS>
36
37```yml
38 gatekeeper:
39 container_name: gatekeeper
40 image: fatfingers23/pds_gatekeeper:latest
41 network_mode: host
42 restart: unless-stopped
43 #This gives the container to the access to the PDS folder. Source is the location on your server of that directory
44 volumes:
45 - type: bind
46 source: /pds
47 target: /pds
48 depends_on:
49 - pds
50```
51
52For Coolify, if you're using Traefik as your proxy you'll need to make sure the labels for the container are set up correctly. A full example can be found at [./examples/coolify-compose.yml](./examples/coolify-compose.yml).
53
54```yml
55gatekeeper:
56 container_name: gatekeeper
57 image: 'fatfingers23/pds_gatekeeper:latest'
58 restart: unless-stopped
59 volumes:
60 - '/pds:/pds'
61 environment:
62 - 'PDS_DATA_DIRECTORY=${PDS_DATA_DIRECTORY:-/pds}'
63 - 'PDS_BASE_URL=http://pds:3000'
64 - GATEKEEPER_HOST=0.0.0.0
65 depends_on:
66 - pds
67 healthcheck:
68 test:
69 - CMD
70 - timeout
71 - '1'
72 - bash
73 - '-c'
74 - 'cat < /dev/null > /dev/tcp/0.0.0.0/8080'
75 interval: 10s
76 timeout: 5s
77 retries: 3
78 start_period: 10s
79 labels:
80 - traefik.enable=true
81 - 'traefik.http.routers.pds-gatekeeper.rule=Host(`yourpds.com`) && (Path(`/xrpc/com.atproto.server.getSession`) || Path(`/xrpc/com.atproto.server.updateEmail`) || Path(`/xrpc/com.atproto.server.createSession`) || Path(`/xrpc/com.atproto.server.createAccount`) || Path(`/@atproto/oauth-provider/~api/sign-in`))'
82 - traefik.http.routers.pds-gatekeeper.entrypoints=https
83 - traefik.http.routers.pds-gatekeeper.tls=true
84 - traefik.http.routers.pds-gatekeeper.priority=100
85 - traefik.http.routers.pds-gatekeeper.middlewares=gatekeeper-cors
86 - traefik.http.services.pds-gatekeeper.loadbalancer.server.port=8080
87 - traefik.http.services.pds-gatekeeper.loadbalancer.server.scheme=http
88 - 'traefik.http.middlewares.gatekeeper-cors.headers.accesscontrolallowmethods=GET,POST,PUT,DELETE,OPTIONS,PATCH'
89 - 'traefik.http.middlewares.gatekeeper-cors.headers.accesscontrolallowheaders=*'
90 - 'traefik.http.middlewares.gatekeeper-cors.headers.accesscontrolalloworiginlist=*'
91 - traefik.http.middlewares.gatekeeper-cors.headers.accesscontrolmaxage=100
92 - traefik.http.middlewares.gatekeeper-cors.headers.addvaryheader=true
93 - traefik.http.middlewares.gatekeeper-cors.headers.accesscontrolallowcredentials=true
94```
95
96## Caddy setup
97
98For the reverse proxy I use caddy. This part is what overwrites the endpoints and proxies them to PDS gatekeeper to add
99in extra functionality. The main part is below, for a full example see [./examples/Caddyfile](./examples/Caddyfile).
100This is usually found at `/pds/caddy/etc/caddy/Caddyfile` on your PDS.
101
102```caddyfile
103 @gatekeeper {
104 path /xrpc/com.atproto.server.getSession
105 path /xrpc/com.atproto.server.updateEmail
106 path /xrpc/com.atproto.server.createSession
107 path /xrpc/com.atproto.server.createAccount
108 path /@atproto/oauth-provider/~api/sign-in
109 }
110
111 handle @gatekeeper {
112 reverse_proxy http://localhost:8080
113 }
114
115 reverse_proxy http://localhost:3000
116```
117
118If you use a cloudflare tunnel then your caddyfile would look a bit more like below with your tunnel proxying to
119`localhost:8081` (or w/e port you want).
120
121```caddyfile
122http://*.localhost:8082, http://localhost:8082 {
123 @gatekeeper {
124 path /xrpc/com.atproto.server.getSession
125 path /xrpc/com.atproto.server.updateEmail
126 path /xrpc/com.atproto.server.createSession
127 path /xrpc/com.atproto.server.createAccount
128 path /@atproto/oauth-provider/~api/sign-in
129 }
130
131 handle @gatekeeper {
132 reverse_proxy http://localhost:8080 {
133 #Makes sure the cloudflare ip is proxied and able to be picked up by pds gatekeeper
134 header_up X-Forwarded-For {http.request.header.CF-Connecting-IP}
135 }
136 }
137
138 reverse_proxy http://localhost:3000
139}
140
141```
142
143# Environment variables and bonuses
144
145Every environment variable can be set in the `pds.env` and shared between PDS and gatekeeper and the PDS, with the
146exception of `PDS_ENV_LOCATION`. This can be set to load the pds.env, by default it checks `/pds/pds.env` and is
147recommended to mount the `/pds` folder on the server to `/pds` in the pds gatekeeper container.
148
149`PDS_DATA_DIRECTORY` - Root directory of the PDS. Same as the one found in `pds.env` this is how pds gatekeeper knows
150knows the rest of the environment variables.
151
152`GATEKEEPER_EMAIL_TEMPLATES_DIRECTORY` - The folder for templates of the emails PDS gatekeeper sends. You can find them
153in [./email_templates](./email_templates). You are free to edit them as you please and set this variable to a location
154in the pds gateekeper container and it will use them in place of the default ones. Just make sure ot keep the names the
155same.
156
157`GATEKEEPER_TWO_FACTOR_EMAIL_SUBJECT` - Subject of the email sent to the user when they turn on 2FA. Defaults to
158`Sign in to Bluesky`
159
160`PDS_BASE_URL` - Base url of the PDS. You most likely want `https://localhost:3000` which is also the default
161
162`GATEKEEPER_HOST` - Host for pds gatekeeper. Defaults to `127.0.0.1`
163
164`GATEKEEPER_PORT` - Port for pds gatekeeper. Defaults to `8080`
165
166`GATEKEEPER_CREATE_ACCOUNT_PER_SECOND` - Sets how often it takes a count off the limiter. example if you hit the rate
167limit of 5 and set to 60, then in 60 seconds you will be able to make one more. Or in 5 minutes be able to make 5 more.
168
169`GATEKEEPER_CREATE_ACCOUNT_BURST` - Sets how many requests can be made in a burst. In the prior example this is where
170the 5 comes from. Example can set this to 10 to allow for 10 requests in a burst, and after 60 seconds it will drop one
171off.