Microservice to bring 2FA to self hosted PDSes
Rust 82.4%
Handlebars 16.8%
Dockerfile 0.5%
Just 0.3%
17 1 0

Clone this repository

https://tangled.org/besaid.zone/pds-gatekeeper
git@tangled.org:besaid.zone/pds-gatekeeper

For self-hosted knots, clone URLs may differ based on your setup.

README.md

PDS gatekeeper#

A microservice that sits on the same server as the PDS to add some of the security that the entryway does.

Picture in black and white of a grassy hill with a gate at the top

PDS gatekeeper works by overriding some of the PDS endpoints inside your Caddyfile to provide gatekeeping to certain endpoints. Mainly, the ability to have 2FA on a self hosted PDS like it does on a Bluesky mushroom(PDS). Most of the logic of these endpoints still happens on the PDS via a proxied request, just some are gatekept.

Features#

2FA#

  • Overrides The login endpoint to add 2FA for both Bluesky client logged in and OAuth logins
  • Overrides the settings endpoints as well. As long as you have a confirmed email you can turn on 2FA

Captcha on Create Account#

Future feature?

Setup#

PDS Gatekeeper has 2 parts to its setup, docker compose file and a reverse proxy (Caddy in this case). I will be assuming you setup the PDS following the directions found here, but if yours is different, or you have questions, feel free to let me know, and we can figure it out.

Docker compose#

The pds gatekeeper container can be found on docker hub under the name fatfingers23/pds_gatekeeper. The container does need 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 like below. You can find a full example of what I use for my pds at ./examples/compose.yml. This is usually found at /pds/compose.yamlon your PDS>

  gatekeeper:
    container_name: gatekeeper
    image: fatfingers23/pds_gatekeeper:latest
    network_mode: host
    restart: unless-stopped
    #This gives the container to the access to the PDS folder. Source is the location on your server of that directory
    volumes:
      - type: bind
        source: /pds
        target: /pds
    depends_on:
      - pds

For 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.

gatekeeper:
    container_name: gatekeeper
    image: 'fatfingers23/pds_gatekeeper:latest'
    restart: unless-stopped
    volumes:
      - '/pds:/pds'
    environment:
      - 'PDS_DATA_DIRECTORY=${PDS_DATA_DIRECTORY:-/pds}'
      - 'PDS_BASE_URL=http://pds:3000'
      - GATEKEEPER_HOST=0.0.0.0
    depends_on:
      - pds
    healthcheck:
      test:
        - CMD
        - timeout
        - '1'
        - bash
        - '-c'
        - 'cat < /dev/null > /dev/tcp/0.0.0.0/8080'
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 10s
    labels:
      - traefik.enable=true
      - '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`))'
      - traefik.http.routers.pds-gatekeeper.entrypoints=https
      - traefik.http.routers.pds-gatekeeper.tls=true
      - traefik.http.routers.pds-gatekeeper.priority=100
      - traefik.http.routers.pds-gatekeeper.middlewares=gatekeeper-cors
      - traefik.http.services.pds-gatekeeper.loadbalancer.server.port=8080
      - traefik.http.services.pds-gatekeeper.loadbalancer.server.scheme=http
      - 'traefik.http.middlewares.gatekeeper-cors.headers.accesscontrolallowmethods=GET,POST,PUT,DELETE,OPTIONS,PATCH'
      - 'traefik.http.middlewares.gatekeeper-cors.headers.accesscontrolallowheaders=*'
      - 'traefik.http.middlewares.gatekeeper-cors.headers.accesscontrolalloworiginlist=*'
      - traefik.http.middlewares.gatekeeper-cors.headers.accesscontrolmaxage=100
      - traefik.http.middlewares.gatekeeper-cors.headers.addvaryheader=true
      - traefik.http.middlewares.gatekeeper-cors.headers.accesscontrolallowcredentials=true

Caddy setup#

For the reverse proxy I use caddy. This part is what overwrites the endpoints and proxies them to PDS gatekeeper to add in extra functionality. The main part is below, for a full example see ./examples/Caddyfile. This is usually found at /pds/caddy/etc/caddy/Caddyfile on your PDS.

    @gatekeeper {
            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
    }

    handle @gatekeeper {
            reverse_proxy http://localhost:8080
    }

    reverse_proxy http://localhost:3000

If you use a cloudflare tunnel then your caddyfile would look a bit more like below with your tunnel proxying to localhost:8081 (or w/e port you want).

http://*.localhost:8082, http://localhost:8082 {
        @gatekeeper {
                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
        }

        handle @gatekeeper {
                reverse_proxy http://localhost:8080 {
                #Makes sure the cloudflare ip is proxied and able to be picked up by pds gatekeeper
			    header_up X-Forwarded-For {http.request.header.CF-Connecting-IP}
		    }
        }

        reverse_proxy http://localhost:3000
}

Environment variables and bonuses#

Every environment variable can be set in the pds.env and shared between PDS and gatekeeper and the PDS, with the exception of PDS_ENV_LOCATION. This can be set to load the pds.env, by default it checks /pds/pds.env and is recommended to mount the /pds folder on the server to /pds in the pds gatekeeper container.

PDS_DATA_DIRECTORY - Root directory of the PDS. Same as the one found in pds.env this is how pds gatekeeper knows knows the rest of the environment variables.

GATEKEEPER_EMAIL_TEMPLATES_DIRECTORY - The folder for templates of the emails PDS gatekeeper sends. You can find them in ./email_templates. You are free to edit them as you please and set this variable to a location in the pds gateekeper container and it will use them in place of the default ones. Just make sure ot keep the names the same.

GATEKEEPER_TWO_FACTOR_EMAIL_SUBJECT - Subject of the email sent to the user when they turn on 2FA. Defaults to Sign in to Bluesky

PDS_BASE_URL - Base url of the PDS. You most likely want https://localhost:3000 which is also the default

GATEKEEPER_HOST - Host for pds gatekeeper. Defaults to 127.0.0.1

GATEKEEPER_PORT - Port for pds gatekeeper. Defaults to 8080

GATEKEEPER_CREATE_ACCOUNT_PER_SECOND - Sets how often it takes a count off the limiter. example if you hit the rate limit 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.

GATEKEEPER_CREATE_ACCOUNT_BURST - Sets how many requests can be made in a burst. In the prior example this is where the 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 off.