fastapi / full-stack-fastapi-template

Full stack, modern web application template. Using FastAPI, React, SQLModel, PostgreSQL, Docker, GitHub Actions, automatic HTTPS and more.
MIT License
27.28k stars 4.87k forks source link

Prevent unauthorized access to staging environment #349

Open cemunds opened 3 years ago

cemunds commented 3 years ago

I was looking into ways to prevent my staging environment from being listed on Google. I found out that the easiest way to do this apparently is to use traefik's BasicAuth middleware and require a user name and password for the staging environment. I really like the easy setup in this repository, having only one docker-compose file that I can use to deploy to production and staging. However, I am having trouble coming up with a clean way to integrate BasicAuth only for staging.

My domains and stack names have the following pattern: Staging

Production

However, these are specified in the .gitlab-ci.yml and only injected during the deployment phase of the CI/CD pipeline.

How would I achieve this in a clean way, ideally reusing the variables from the .env file and not hard coding any stack name for the staging stack into the docker-compose file?

thomas-chauvet commented 3 years ago

Hello,

It seems that the basic-auth middleware is defined in the docker-compose file from the doc in dockerswarm.rocks.

It looks like you can enable the basic-auth middleware by adding this line to the label in the docker-compose file:

        - traefik.http.routers.traefik-public-https.middlewares=admin-auth

It will activate the middleware defined in the traefik docker-compose for the service defined in your docker-compose.

Hoe it helps :)

cemunds commented 3 years ago

I tried adding these two lines to the labels section of the docker-compose.yml:

- traefik.http.middlewares.staging-auth.basicauth.usersfile=/configuration/staging-users
- traefik.http.routers.xxx-stag-proxy-https.middlewares=staging-auth

After I redeploy the staging environment, everything works as expected and I'm being asked for credentials when navigating to the staging site, but not when visiting the production site. However, the next time the production environment is redeployed, the docker container for the production proxy will also receive the two labels above, since staging and prod are deployed from the same docker-compose file. I thought maybe traefik would ignore the label containing xxx-stag-proxy-https, since the compose file for prod doesn't define a router with this name (because the router is called xxx-prod-proxy-https after interpolation of the environment variables). But after redeploying prod with these two new labels, I suddenly can't access my staging site at all anymore.

The only way I can see right now to achieve this would require me to have one docker-compose file for staging and one for prod, but then I would also have to adjust the build and deploy shell scripts.

cemunds commented 3 years ago

Ok, I figured it out now. I was not aware that you can reference services defined with the docker provider from routers defined in a dynamic config file. I ended up adding this part to my dynamic_config.yaml:

http:
  middlewares:
    staging-auth:
      basicAuth:
        users:
          - "<user>:<password>"

  routers:
    xxx-stag-proxy-https:
      middlewares:
        - staging-auth
      service: "xxx-stag-proxy@docker"
      rule: "Host(`staging.xxx.com`) || Host(`www.staging.xxx.com`)"
      entryPoints:
        - "https"
      tls: {}

and this to the traefik.yaml as described here to reference the dynamic config:

volumes:
  - /traefik:/configuration
command:
  - --providers.file.directory=/configuration
  - --providers.file.watch=true
cemunds commented 3 years ago

Sorry for reopening this issue, but it seems like the solution I found is only part of the story. Using the way I described above, I am able to configure traefik to prompt me for a user and password when accessing the staging environment. However, the REST API of the backend server has OAuth protected routes. Since I can only provide either the Basic or the OAuth token in the Authorization header of my requests, I am now unable to use any REST API call that requires the OAuth token, since either the traefik proxy denies access for not providing the Basic token, or the backend server denies me for not providing the OAuth token.

I tried several things including switching traefik's Basic Auth for a Forward Auth and thus using the REST API's OAuth endpoint to access the site, but I can't seem to get it to work properly. Maybe I am approaching this issue incorrectly. How do others protect their staging sites from unauthorized access and prevent them from being listed on search engines eventually?