├── .env ├── README.md ├── README_authless.md ├── authentik-certs └── .authentik-certs-live-here ├── authentik-custom-templates └── .authentik-templates-folder ├── authentik-media └── .authentik-media-folder ├── crowdsec ├── .crowdsec-acquis-folder └── acquis.yaml ├── docker-compose.yml ├── npm_data └── .this-is-a-npm-data-folder └── npm_letsencrypt └── .npm-certs-live-here /.env: -------------------------------------------------------------------------------- 1 | DATABASE_PASSWORD= 2 | ROOT_DATABASE_PASSWORD= 3 | CROWDSEC_BOUNCER_APIKEY= 4 | POSTGRES_PASSWORD= 5 | POSTGRES_USER= 6 | POSTGRES_DB= 7 | AUTHENTIK_SECRET_KEY= 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nginx Proxy Manager (NPM) + Crowdsec + Authentik 2 | 3 | Are you looking to **protect your services** using *only* simple and easily manageable solutions? 4 | This repo (*hopefully*) does exactly that. **[NPM](https://nginxproxymanager.com/guide/)** is an easy-to-use reverse proxy built on Nginx, managed via a GUI. In this case we will be securing it with **[Crowdsec](https://docs.crowdsec.net/)** - to automatically ban malicious IPs, and **[Authentik](https://docs.goauthentik.io/docs/)** - a feature rich self-hosted Auth solution. 5 | 6 | >We will be using a [fork of NPM](https://github.com/LePresidente/docker-nginx-proxy-manager) that comes pre-packaged with a Crowdsec bouncer. 7 | 8 | ### The thought proccess: 9 | - **1 ->** A user wants to access a protected service 10 | - **2 ->** The user makes a request to the proxy (NPM) 11 | - **3 ->** The proxy consults Crowdsec and serves the request (or bans the user *[403 response]*) 12 | - **4 ->** The user first arrives at an authentication page instead of the service ([forward proxy authentication](https://docs.goauthentik.io/docs/add-secure-apps/providers/proxy/forward_auth)) 13 | - **5 ->** If the user successfully finishes the authentication, the proxy receives an OK response from Authentik and actually forwards the user to the service 14 | 15 | We'll be setting this up in **Docker** using ***Docker Compose***. I also included a *test container* for demonstration - we will be placing it inside an internal network, hidden behind the NPM reverse proxy, protected by Crowdsec, and we will also secure it with Authentik - even though the test container does *NOT* support any form of authentication by default. 16 | We will also **TEST** if everything is working order :) 17 | 18 | ***Bonus : You'll get a nice dashboard to monitor all the blocked threats :)*** 19 | 20 | ## IMPORTANT: Limitations 21 | 22 | This stack is **NOT a silver bullet** when it comes to security. It should be used in combination with many other practices *(such as network segmentation, correctly configured firewalls, proper maintenance etc.)* and ideally behind something like [Cloudflare Tunnels](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/) or a different "external reverse proxy" ([Pangolin](https://github.com/fosrl/pangolin) is being *heavily* astroturfed on Reddit at the moment, I haven't looked into it but that should be a *good example*). 23 | 24 | You ***can*** use just this setup to expose your services publically *(in fact, according to my testing it seems to work pretty well)* - but I **wouldn't recommend it**. But if you like to live on the edge - hey, you do you. 25 | 26 | Maybe if you're ***renting a VPS*** that you don't care about as much - in that case, it should be a *decent security solution*. 27 | >**On-prem**, I'd only use this as a ***"middle-step"*** between an "external reverse proxy" (see above) and your services, or between a VPN and your services. Or just to ***secure your services on an internal network*** - if you don't need external/public access at all. 28 | 29 | Of course, all of the above **ONLY applies** assuming that that everything is ***properely deployed and configured*** *(which it likely isn't - given the fact I made it)*, as well as ***properely maintained***. 30 | 31 | I offer **ABSOLUTELY ZERO WARRANTY**, and take **ABSOLUTELY NO RESPONSIBILTY** if you compromise your security with this setup. 32 | > If you actually want this setup to be properly done, and wish to learn more, you should realistically be reading the ***official documentation*** for each of the services used, not my GitHub :) 33 | 34 | ## Requirements 35 | 36 | - **GNU/Linux** *(please don't ask me how to deploy this on Windows I have no idea)* 37 | - **Git** 38 | - **Sudo** 39 | - *(a working)* **Docker** Installation 40 | - a **text editor** you like *(I use nano)* 41 | 42 | >This guide will not be covering the absolute basics, i.e. I assume you can set-up your domain and point it where you want, or set-up your SSL/TLS certs. 43 | 44 | # HOW TO: GUIDE 45 | 46 | >On my systems, I usually just make a user, give the user sudo priviliges and then do everything as that user, adjust stuff accordingly if needed 47 | 48 | 1. First, **clone my repo** 49 | 50 | `git clone https://github.com/suckharder/NPM-Crowdsec-Authentik-Stack` 51 | 52 | 2. *This is almost definitely unnecessary, but idk* 53 | 54 | `sudo chown -R 1000:1000 NPM-Crowdsec-Authentik-Stack` 55 | 56 | 3. **Get inside the directory** 57 | 58 | `cd NPM-Crowdsec-Authentik-Stack` 59 | 60 | 4. **Generate a root password for NPM's database** 61 | 62 | `tr -dc A-Za-z0-9 *Even though we will be setting up Authentik last, we'll fill it's variables now, so that we can compose. The bouncer API key will come later.* 93 | 94 | 8. **Generate a Postgresql password for Authentik** 95 | 96 | `openssl rand -base64 36` 97 | 98 | 9. **Paste it into the .env file** as POSTGRES_PASSWORD=, so it looks like 99 | 100 | ``` 101 | DATABASE_PASSWORD=yourpasswordhere 102 | ROOT_DATABASE_PASSWORD=yourpasswordhere 103 | CROWDSEC_BOUNCER_APIKEY= 104 | POSTGRES_PASSWORD=yourpasswordhere 105 | POSTGRES_USER= 106 | POSTGRES_DB= 107 | AUTHENTIK_SECRET_KEY= 108 | ``` 109 | 110 | 10. **Choose** whatever **DB name and USER** name you want, or generate these as well 111 | 112 | ``` 113 | DATABASE_PASSWORD=yourpasswordhere 114 | ROOT_DATABASE_PASSWORD=yourpasswordhere 115 | CROWDSEC_BOUNCER_APIKEY= 116 | POSTGRES_PASSWORD=yourpasswordhere 117 | POSTGRES_USER=authentik_db_user 118 | POSTGRES_DB=authentik_db 119 | AUTHENTIK_SECRET_KEY= 120 | ``` 121 | 122 | 11. **Generate the Authentik secret key** 123 | 124 | `openssl rand -base64 60` 125 | 126 | 12. **Paste it into the .env file** as AUTHENTIK_SECRET_KEY=, so it looks like 127 | 128 | ``` 129 | DATABASE_PASSWORD=yourpasswordhere 130 | ROOT_DATABASE_PASSWORD=yourpasswordhere 131 | CROWDSEC_BOUNCER_APIKEY= 132 | POSTGRES_PASSWORD=yourpasswordhere 133 | POSTGRES_USER=authentik_db_user 134 | POSTGRES_DB=authentik_db 135 | AUTHENTIK_SECRET_KEY=yoursecretkey 136 | ``` 137 | 138 | 13. Now, let's **compose** what we have so far 139 | 140 | >I **STRONGLY** recommend reading the provided ***docker-compose.yaml*** file. I even included a few comments so it's *more easily* understandable. 141 | 142 | `sudo docker compose up -d` 143 | 144 | 14. When all is done, we need to **add the Crowdsec NPM bouncer and get our API key** 145 | 146 | `sudo docker compose exec crowdsec cscli bouncer add npm-bouncer` 147 | 148 | > **DO NOT lose this key!** It's important. 149 | 150 | 15. We can **stop the stack** now 151 | 152 | `sudo docker compose down` 153 | 154 | 16. **Paste the key into the .env file** as CROWDSEC_BOUNCER_APIKEY=, so it looks like 155 | 156 | ``` 157 | DATABASE_PASSWORD=yourpasswordhere 158 | ROOT_DATABASE_PASSWORD=yourpasswordhere 159 | CROWDSEC_BOUNCER_APIKEY=yourbouncerapikey 160 | POSTGRES_PASSWORD=yourpasswordhere 161 | POSTGRES_USER=authentik_db_user 162 | POSTGRES_DB=authentik_db 163 | AUTHENTIK_SECRET_KEY=yoursecretkey 164 | ``` 165 | 166 | 17. Everything Docker-related should be done now, we can **compose again** 167 | 168 | `sudo docker compose up -d` 169 | 170 | > If **Docker complains** about Crowdsec's *acquis.yaml* file, you need to remove the crowdsec volume and compose again ***sudo docker volume rm your_volume_name*** 171 | 172 | > Also, I advise that you check out the acquis.yaml file as well 173 | 174 | 18. **Set up NPM.** Default username:`admin@example.com` , default password: `changeme` 175 | 176 | > This is where you might want to set up you domain and/or your certs as well. 177 | 178 | > I **WOULDN'T** expose port 81 *(NPMs UI)* publically at all. If you're setting up a remote machine use an SSH tunnel or something. Ex.: *ssh -L 9999:localhost:81 remoteuser@server-ip -p your-ssh-port* 179 | 180 | 19. **Make a proxy host in NPM pointing to the** ***hello-test*** **container** 181 | 182 | > Use hello-test as the 'forward hostname' and port 80. 183 | 184 | >Yes, we use Docker's internal DNS, so maybe don't screw around with the container names :) 185 | 186 | 20. **Verify you can reach hello-test.** *hello-test* should successfully open, as we haven't set-up Authentik yet 187 | 188 | > At this point, unless you somehow magically got banned by Crowdsec already, you should be able to get to *hello-test* through the proxy. 189 | 190 | 21. If everything works so far, **make a proxy host pointing to** ***authentik-server*** 191 | 192 | > hostname: authentik-server, port: 9443 (SSL port - use scheme: https) 193 | 194 | > Authentik Server *uses* **WEBSOCKETS - SET TO ON** 195 | 196 | 22. Set up **Authentik** 197 | 198 | >*Honestly*, just watch this video by **Christian Lempa:** ***https://youtu.be/N5unsATNpJk***, *skip the configuration, as he does not use NPM nor Crowdsec*. Create a new admin user like he does, give the user MFA, disable the default one. 199 | 200 | >*The part starting at* ***33:32*** *is also going to be relevant in a moment*, you can watch that too, but we'll be making adjustments for NPM - **we will not even be touching YAML again**. 201 | 202 | 23. **Create a new provider in Authentik**. Choose ***proxy provider*** 203 | > Give it a name - for example *nginx-hello-test*. Click on **FORWARD AUTH (SINGLE APPLICATION) -> enter the URL of** ***hello-test***. Click *Finish*. 204 | 205 | 24. **Create a new application in Authentik** 206 | > Give it a name - for example *nginx-hello-test* again. For slug, enter *hello-test*. 207 | > Choose the provider you just created. Click *Finish*. 208 | 209 | 25. **Modify Authentik's Embedded Outpost.** Just **move the application** you made for *hello-test* **to the right side and hit** ***Update***. 210 | 211 | 26. ***IMPORTANT:*** **Open the provider you created in Authentik, and copy the Nginx (Proxy Manager) config.** 212 | 213 | 27. **Open the hello-test proxy host in NPM, click Advanced and paste the config.** 214 | 215 | > **DO NOT SAVE YET!** 216 | 217 | 28. **Modify proxy_pass with authentik-server** *(again with the Docker DNS)* 218 | 219 | `proxy_pass http://authentik-server:9000/outpost.goauthentik.io;` 220 | 221 | > Keep port 9000. 222 | 223 | 30. **You're done. Congratz.** 224 | 225 | > Once you log out of Authentik/clear cookies, *you should be greeted with an authentication page* upon visiting *hello-test*. 226 | 227 | > I **STRONGLY** recommend ***testing*** (next section). 228 | 229 | 31. **BONUS:** Connect your Crowdsec to a dashboard. Register at https://app.crowdsec.net/, choose *Add Security Engine*, and copy ***ONLY*** the key. Then run 230 | 231 | `sudo docker exec crowdsec cscli console enroll -e context yourkeyhere` 232 | 233 | Afterwards just approve the enroll request in the dashboard :) 234 | 235 | # Testing 236 | 237 | > **DO NOT TEST FROM THE SAME MACHINE RUNNING THE STACK** - it'll always work :) 238 | 239 | 1. **Open hello-test in a web browser** 240 | 241 | > If you dont get to an Auth page, something is wrong. 242 | 243 | 2. **Run this command** 244 | 245 | `curl -I https://hello-test-url-here` 246 | 247 | > If you don't get a *302* response, something is wrong. 248 | 249 | 3. **Run this command** 250 | 251 | `curl -i -L https://hello-test-url-here` 252 | 253 | > If the very last response is not a *200*, something is wrong. 254 | 255 | 4. **Check your external/public IP address** 256 | 257 | 5. **Manually ban your IP in Crowdsec** 258 | 259 | `sudo docker exec crowdsec cscli decisions add -i your-ip-here` 260 | 261 | 6. **Verify your IP is banned** 262 | 263 | `sudo docker exec crowdsec cscli decisions list` 264 | 265 | 7. **Open hello-test in a web browser** 266 | 267 | > If you don't see a *"Blocked by Crowdsec"* screen something is **VERY** wrong. 268 | 269 | 8. **Run this command** 270 | 271 | `curl -I https://hello-test-url-here` 272 | 273 | > **If the response is still 302 DO NOT PANIC! - in a properely working setup, the code here SHOULD still be 302.** *Due to the way our set-up works (forward proxy authentication)* ***even banned IPs*** *should get a *302* here*, because NPM wants you to authenticate - which you won't be able to anyway - *see next step*. 274 | 275 | 9. **Run this command** 276 | 277 | `curl -i -L https://hello-test-url-here` 278 | 279 | > You probably get it now :) 280 | 281 | > In other words, if the very last response is not *403* something is **VERY** wrong. 282 | 283 | > To unban yourself use ***sudo docker exec crowdsec cscli decisions delete -i your-ip-here*** 284 | 285 | ## Issues 286 | 287 | I actually successfully deployed this exact set-up, but it is possible I screwed something up, or forgot something when making the repo. Feel free to open an **Issue**. 288 | 289 | Also feel free to open an issue if you *believe something about this set-up is wrong or misconfigured*. 290 | 291 | ## Special thanks 292 | 293 | - **Crowdsec** for their [example-docker-compose](https://github.com/crowdsecurity/example-docker-compose), which served as the basis for this whole stack 294 | - Crowdsec again, for their [documentation](https://docs.crowdsec.net/) *(and awesome software DUH)* 295 | - **LePresidente** for his [NPM fork](https://github.com/LePresidente/docker-nginx-proxy-manager) *(I hope he keeps maintaining it)* 296 | - **Authentik**, for their [docs](https://docs.goauthentik.io/docs/), and of course their software 297 | - **Christian Lempa**, for introducing me to Authentik *(via his [YouTube channel](https://www.youtube.com/channel/UCZNhwA1B5YqiY1nLzmM0ZRg))* 298 | - **IBRACORP**, for their [documentation](https://docs.ibracorp.io/ibracorp) 299 | - *And many more...* 300 | -------------------------------------------------------------------------------- /README_authless.md: -------------------------------------------------------------------------------- 1 | # Nginx Proxy Manager (NPM) + Crowdsec -(minus) Authentik 2 | 3 | *As requested by u/Waddoo123* 4 | 5 | This guide is an alteration of the [main guide](https://github.com/suckharder/NPM-Crowdsec-Authentik-Stack/blob/main/README.md) - it aims the remove the Authentik component from the stack, keeping only NPM and Crowdsec. 6 | 7 | > **At this time - I mostly just removed a lot of the steps, and this currently stands untested - however, just omitting Authentik is very straightforward so it should work fine.** 8 | 9 | **[NPM](https://nginxproxymanager.com/guide/)** is an easy-to-use reverse proxy built on Nginx, managed via a GUI. In this case we will be securing it with **[Crowdsec](https://docs.crowdsec.net/)** - to automatically ban malicious IPs. 10 | 11 | >We will be using a [fork of NPM](https://github.com/LePresidente/docker-nginx-proxy-manager) that comes pre-packaged with a Crowdsec bouncer. 12 | 13 | ### The thought proccess: 14 | - **1 ->** A user wants to access a protected service 15 | - **2 ->** The user makes a request to the proxy (NPM) 16 | - **3 ->** The proxy consults Crowdsec and serves the request (or Crowdsec bans the user *[403 response]*) 17 | 18 | We'll be setting this up in **Docker** using ***Docker Compose***. I also included a *test container* for demonstration - we will be placing it inside an internal network, hidden behind the NPM reverse proxy, and protected by Crowdsec. 19 | We will also **TEST** if everything is working order :) 20 | 21 | ***Bonus : You'll get a nice dashboard to monitor all the blocked threats :)*** 22 | 23 | ## IMPORTANT: Limitations 24 | 25 | See the [main guide](https://github.com/suckharder/NPM-Crowdsec-Authentik-Stack/blob/main/README.md), except you're losing authetication... 26 | 27 | I offer **ABSOLUTELY ZERO WARRANTY**, and take **ABSOLUTELY NO RESPONSIBILTY** if you compromise your security with this setup. 28 | 29 | ## Requirements 30 | 31 | - **GNU/Linux** *(please don't ask me how to deploy this on Windows I have no idea)* 32 | - **Git** 33 | - **Sudo** 34 | - *(a working)* **Docker** Installation 35 | - a **text editor** you like *(I use nano)* 36 | 37 | >This guide will not be covering the absolute basics, i.e. I assume you can set-up your domain and point it where you want, or set-up your SSL/TLS certs. 38 | 39 | # HOW TO: GUIDE 40 | 41 | >On my systems, I usually just make a user, give the user sudo priviliges and then do everything as that user, adjust stuff accordingly if needed 42 | 43 | 1. First, **clone my repo** 44 | 45 | `git clone https://github.com/suckharder/NPM-Crowdsec-Authentik-Stack` 46 | 47 | 2. *This is almost definitely unnecessary, but idk* 48 | 49 | `sudo chown -R 1000:1000 NPM-Crowdsec-Authentik-Stack` 50 | 51 | 3. **Get inside the directory** 52 | 53 | `cd NPM-Crowdsec-Authentik-Stack` 54 | 55 | 4. **MODIFY docker-compose.yml, .env, and delete the authentik-certs, authentik-custom-templates, authentik-media folders** 56 | 57 | **Remove** lines shown below from ***docker-compose.yml*** 58 | 59 | ``` 60 | ##############_____Authentik_Postgresql_____########################### 61 | 62 | postgresql: 63 | image: docker.io/library/postgres:16-alpine 64 | container_name: authentik-postgresql 65 | restart: unless-stopped 66 | healthcheck: 67 | test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"] 68 | start_period: 20s 69 | interval: 30s 70 | retries: 5 71 | timeout: 5s 72 | volumes: 73 | - database:/var/lib/postgresql/data 74 | networks: 75 | - internal_network 76 | environment: 77 | # Set in .env 78 | POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} 79 | # Set in .env 80 | POSTGRES_USER: ${POSTGRES_USER} 81 | # Set in .env 82 | POSTGRES_DB: ${POSTGRES_DB} 83 | 84 | ##############_____Authentik_Redis_____################################ 85 | 86 | redis: 87 | image: docker.io/library/redis:alpine 88 | container_name: authentik-redis 89 | command: --save 60 1 --loglevel warning 90 | restart: unless-stopped 91 | healthcheck: 92 | test: ["CMD-SHELL", "redis-cli ping | grep PONG"] 93 | start_period: 20s 94 | interval: 30s 95 | retries: 5 96 | timeout: 3s 97 | networks: 98 | - internal_network 99 | volumes: 100 | - redis:/data 101 | 102 | ##############_____Authentik_Server_____############################### 103 | 104 | server: 105 | # Check Authentik Docs for recommended pinned version 106 | image: ghcr.io/goauthentik/server:2025.4.1 107 | container_name: authentik-server 108 | restart: unless-stopped 109 | command: server 110 | environment: 111 | # Set in .env 112 | AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY} 113 | AUTHENTIK_REDIS__HOST: redis 114 | AUTHENTIK_POSTGRESQL__HOST: postgresql 115 | # Set in .env 116 | AUTHENTIK_POSTGRESQL__USER: ${POSTGRES_USER} 117 | # Set in .env 118 | AUTHENTIK_POSTGRESQL__NAME: ${POSTGRES_DB} 119 | # Set in .env 120 | AUTHENTIK_POSTGRESQL__PASSWORD: ${POSTGRES_PASSWORD} 121 | AUTHENTIK_ERROR_REPORTING__ENABLED: true 122 | volumes: 123 | - ./authentik-media:/media 124 | - ./authentik-custom-templates:/templates 125 | networks: 126 | npm: 127 | internal_network: 128 | # Leave ports unpublished, access through reverse proxy. Use the commented-out ports for reference. Use 9000 for outpost. 129 | ### ports: 130 | # - "9000:9000" 131 | # - "9443:9443" 132 | depends_on: 133 | postgresql: 134 | condition: service_healthy 135 | redis: 136 | condition: service_healthy 137 | 138 | ##############_____Authentik_worker_____############################### 139 | 140 | worker: 141 | # USE THE SAME IMAGE AS SERVER!!! 142 | image: ghcr.io/goauthentik/server:2025.4.1 143 | container_name: authentik-worker 144 | restart: unless-stopped 145 | command: worker 146 | environment: 147 | # Set in .env 148 | AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY} 149 | AUTHENTIK_REDIS__HOST: redis 150 | AUTHENTIK_POSTGRESQL__HOST: postgresql 151 | # Set in .env 152 | AUTHENTIK_POSTGRESQL__USER: ${POSTGRES_USER} 153 | # Set in .env 154 | AUTHENTIK_POSTGRESQL__NAME: ${POSTGRES_DB} 155 | # Set in .env 156 | AUTHENTIK_POSTGRESQL__PASSWORD: ${POSTGRES_PASSWORD} 157 | AUTHENTIK_ERROR_REPORTING__ENABLED: true 158 | user: root 159 | volumes: 160 | - /var/run/docker.sock:/var/run/docker.sock 161 | - ./authentik-media:/media 162 | - ./authentik-certs:/certs 163 | - ./authentik-custom-templates:/templates 164 | networks: 165 | internal_network: 166 | depends_on: 167 | postgresql: 168 | condition: service_healthy 169 | redis: 170 | condition: service_healthy 171 | 172 | 173 | 174 | 175 | ... (in the volumes section) ... 176 | 177 | # authentik postgresql 178 | database: 179 | driver: local 180 | # authentik redis 181 | redis: 182 | driver: local 183 | ``` 184 | 185 | **Remove** lines shown below from ***.env*** 186 | 187 | ``` 188 | POSTGRES_PASSWORD= 189 | POSTGRES_USER= 190 | POSTGRES_DB= 191 | AUTHENTIK_SECRET_KEY= 192 | ``` 193 | 194 | 5. **Generate a root password for NPM's database** 195 | 196 | `tr -dc A-Za-z0-9 *The bouncer API key will come later.* 219 | 220 | 13. Now, let's **compose** what we have so far 221 | 222 | >I **STRONGLY** recommend reading the provided ***docker-compose.yaml*** file. I even included a few comments so it's *more easily* understandable. 223 | 224 | `sudo docker compose up -d` 225 | 226 | 14. When all is done, we need to **add the Crowdsec NPM bouncer and get our API key** 227 | 228 | `sudo docker compose exec crowdsec cscli bouncer add npm-bouncer` 229 | 230 | > **DO NOT lose this key!** It's important. 231 | 232 | 15. We can **stop the stack** now 233 | 234 | `sudo docker compose down` 235 | 236 | 16. **Paste the key into the .env file** as CROWDSEC_BOUNCER_APIKEY=, so it looks like 237 | 238 | ``` 239 | DATABASE_PASSWORD=yourpasswordhere 240 | ROOT_DATABASE_PASSWORD=yourpasswordhere 241 | CROWDSEC_BOUNCER_APIKEY=yourbouncerapikey 242 | ``` 243 | 244 | 17. Everything Docker-related should be done now, we can **compose again** 245 | 246 | `sudo docker compose up -d` 247 | 248 | > If **Docker complains** about Crowdsec's *acquis.yaml* file, you need to remove the crowdsec volume and compose again ***sudo docker volume rm your_volume_name*** 249 | 250 | > Also, I advise that you check out the acquis.yaml file as well 251 | 252 | 18. **Set up NPM.** Default username:`admin@example.com` , default password: `changeme` 253 | 254 | > This is where you might want to set up you domain and/or your certs as well. 255 | 256 | > I **WOULDN'T** expose port 81 *(NPMs UI)* publically at all. If you're setting up a remote machine use an SSH tunnel or something. Ex.: *ssh -L 9999:localhost:81 remoteuser@server-ip -p your-ssh-port* 257 | 258 | 19. **Make a proxy host in NPM pointing to the** ***hello-test*** **container** 259 | 260 | > Use hello-test as the 'forward hostname' and port 80. 261 | 262 | >Yes, we use Docker's internal DNS, so maybe don't screw around with the container names :) 263 | 264 | 20. **Verify you can reach hello-test.** *hello-test* should successfully open. 265 | 266 | > At this point, unless you somehow magically got banned by Crowdsec already, you should be able to get to *hello-test* through the proxy. 267 | 268 | 21. **You're done. Congratz.** 269 | 270 | > I **STRONGLY** recommend ***testing*** (next section). 271 | 272 | 22. **BONUS:** Connect your Crowdsec to a dashboard. Register at https://app.crowdsec.net/, choose *Add Security Engine*, and copy ***ONLY*** the key. Then run 273 | 274 | `sudo docker exec crowdsec cscli console enroll -e context yourkeyhere` 275 | 276 | Afterwards just approve the enroll request in the dashboard :) 277 | 278 | # Testing 279 | 280 | > **DO NOT TEST FROM THE SAME MACHINE RUNNING THE STACK** - it'll always work :) 281 | 282 | > I removed the "follow redirects" curl commands, we shouldn't need them anymore. 283 | 284 | 1. **Open hello-test in a web browser** 285 | 286 | > If you dont get to hello-test's page, something is wrong. 287 | 288 | 2. **Run this command** 289 | 290 | `curl -I https://hello-test-url-here` 291 | 292 | > This time the response should be *200* since we're not redirecting for authetication. 293 | 294 | 3. **Check your external/public IP address** 295 | 296 | 4. **Manually ban your IP in Crowdsec** 297 | 298 | `sudo docker exec crowdsec cscli decisions add -i your-ip-here` 299 | 300 | 5. **Verify your IP is banned** 301 | 302 | `sudo docker exec crowdsec cscli decisions list` 303 | 304 | 6. **Open hello-test in a web browser** 305 | 306 | > If you don't see a *"Blocked by Crowdsec"* screen something is **VERY** wrong. 307 | 308 | 7. **Run this command** 309 | 310 | `curl -I https://hello-test-url-here` 311 | 312 | > Since we're not using Authentik, the response should immediately be *403* - if it's not, something is **VERY** wrong. 313 | 314 | > To unban yourself use ***sudo docker exec crowdsec cscli decisions delete -i your-ip-here*** 315 | 316 | ## Issues 317 | 318 | I just quickly went through the main guide and removed Authentik - this is not tested but should work fine. Feel free to open an **Issue** 319 | 320 | ## Special thanks 321 | 322 | - *see the* [*main guide*](https://github.com/suckharder/NPM-Crowdsec-Authentik-Stack/blob/main/README.md) 323 | -------------------------------------------------------------------------------- /authentik-certs/.authentik-certs-live-here: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suckharder/NPM-Crowdsec-Authentik-Stack/375aef35036082dc2e41c553759020391a5afe1f/authentik-certs/.authentik-certs-live-here -------------------------------------------------------------------------------- /authentik-custom-templates/.authentik-templates-folder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suckharder/NPM-Crowdsec-Authentik-Stack/375aef35036082dc2e41c553759020391a5afe1f/authentik-custom-templates/.authentik-templates-folder -------------------------------------------------------------------------------- /authentik-media/.authentik-media-folder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suckharder/NPM-Crowdsec-Authentik-Stack/375aef35036082dc2e41c553759020391a5afe1f/authentik-media/.authentik-media-folder -------------------------------------------------------------------------------- /crowdsec/.crowdsec-acquis-folder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suckharder/NPM-Crowdsec-Authentik-Stack/375aef35036082dc2e41c553759020391a5afe1f/crowdsec/.crowdsec-acquis-folder -------------------------------------------------------------------------------- /crowdsec/acquis.yaml: -------------------------------------------------------------------------------- 1 | filenames: 2 | - /var/log/npm/*.log 3 | labels: 4 | type: nginx-proxy-manager 5 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | 3 | # Container names ARE important! We rely heavily on Docker DNS. 4 | 5 | ##############_____NPM_____############################################ 6 | 7 | app: 8 | # LePresidente NPM fork with crowdsec bouncer 9 | image: 'lepresidente/nginxproxymanager:latest' 10 | container_name: npm 11 | restart: unless-stopped 12 | # NPM has access to all networks 13 | networks: 14 | crowdsec: 15 | npm: 16 | internal_network: 17 | ports: 18 | - '80:80' # Public HTTP Port 19 | - '443:443' # Public HTTPS Port 20 | # Only use locally 21 | - '81:81' # Admin Web Port 22 | environment: 23 | DB_MYSQL_HOST: "db" 24 | DB_MYSQL_PORT: 3306 25 | DB_MYSQL_USER: "npm" 26 | # Set in .env 27 | DB_MYSQL_PASSWORD: ${DATABASE_PASSWORD} 28 | DB_MYSQL_NAME: "npm" 29 | # API_URL relies on Docker DNS, do not change container names 30 | # API_KEY set in .env 31 | CROWDSEC_OPENRESTY_BOUNCER: | 32 | ENABLED=true 33 | API_URL=http://crowdsec:8080 34 | API_KEY=${CROWDSEC_BOUNCER_APIKEY} 35 | # Delete (or set to false) if you want IPv6 36 | DISABLE_IPV6: 'true' 37 | volumes: 38 | # NPM data (including logs) 39 | - ./npm_data:/data 40 | # NPM TLS certs 41 | - ./npm_letsencrypt:/etc/letsencrypt 42 | depends_on: 43 | db: 44 | condition: service_healthy 45 | security_opt: 46 | - no-new-privileges=true 47 | 48 | ##############_____NPM_MariaDB_____#################################### 49 | 50 | db: 51 | image: 'mariadb:lts' 52 | container_name: maria_db 53 | restart: unless-stopped 54 | networks: 55 | npm: 56 | environment: 57 | # Set in .env 58 | MYSQL_ROOT_PASSWORD: ${ROOT_DATABASE_PASSWORD} 59 | MYSQL_DATABASE: 'npm' 60 | MYSQL_USER: 'npm' 61 | # Set in .env 62 | MYSQL_PASSWORD: ${DATABASE_PASSWORD} 63 | volumes: 64 | - mariadb-data:/var/lib/mysql 65 | security_opt: 66 | - no-new-privileges=true 67 | healthcheck: 68 | test: ['CMD', '/usr/local/bin/healthcheck.sh', '--innodb_initialized'] 69 | start_period: 5s 70 | timeout: 5s 71 | interval: 5s 72 | retries: 5 73 | 74 | ##############_____Crowdsec_____####################################### 75 | 76 | crowdsec: 77 | image: docker.io/crowdsecurity/crowdsec:latest 78 | container_name: crowdsec 79 | environment: 80 | - COLLECTIONS=crowdsecurity/nginx-proxy-manager 81 | volumes: 82 | - crowdsec-db:/var/lib/crowdsec/data/ 83 | - crowdsec-config:/etc/crowdsec/ 84 | # Crowdsec Aquis 85 | - ./crowdsec/acquis.yaml:/etc/crowdsec/acquis.yaml 86 | # NPM data (logs) to be read by Crowdsec 87 | - ./npm_data/logs/:/var/log/npm:ro 88 | networks: 89 | crowdsec: 90 | restart: unless-stopped 91 | security_opt: 92 | - no-new-privileges=true 93 | 94 | ##############_____Authentik_Postgresql_____########################### 95 | 96 | postgresql: 97 | image: docker.io/library/postgres:16-alpine 98 | container_name: authentik-postgresql 99 | restart: unless-stopped 100 | healthcheck: 101 | test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"] 102 | start_period: 20s 103 | interval: 30s 104 | retries: 5 105 | timeout: 5s 106 | volumes: 107 | - database:/var/lib/postgresql/data 108 | networks: 109 | - internal_network 110 | environment: 111 | # Set in .env 112 | POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} 113 | # Set in .env 114 | POSTGRES_USER: ${POSTGRES_USER} 115 | # Set in .env 116 | POSTGRES_DB: ${POSTGRES_DB} 117 | 118 | ##############_____Authentik_Redis_____################################ 119 | 120 | redis: 121 | image: docker.io/library/redis:alpine 122 | container_name: authentik-redis 123 | command: --save 60 1 --loglevel warning 124 | restart: unless-stopped 125 | healthcheck: 126 | test: ["CMD-SHELL", "redis-cli ping | grep PONG"] 127 | start_period: 20s 128 | interval: 30s 129 | retries: 5 130 | timeout: 3s 131 | networks: 132 | - internal_network 133 | volumes: 134 | - redis:/data 135 | 136 | ##############_____Authentik_Server_____############################### 137 | 138 | server: 139 | # Check Authentik Docs for recommended pinned version 140 | image: ghcr.io/goauthentik/server:2025.4.1 141 | container_name: authentik-server 142 | restart: unless-stopped 143 | command: server 144 | environment: 145 | # Set in .env 146 | AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY} 147 | AUTHENTIK_REDIS__HOST: redis 148 | AUTHENTIK_POSTGRESQL__HOST: postgresql 149 | # Set in .env 150 | AUTHENTIK_POSTGRESQL__USER: ${POSTGRES_USER} 151 | # Set in .env 152 | AUTHENTIK_POSTGRESQL__NAME: ${POSTGRES_DB} 153 | # Set in .env 154 | AUTHENTIK_POSTGRESQL__PASSWORD: ${POSTGRES_PASSWORD} 155 | AUTHENTIK_ERROR_REPORTING__ENABLED: true 156 | volumes: 157 | - ./authentik-media:/media 158 | - ./authentik-custom-templates:/templates 159 | networks: 160 | npm: 161 | internal_network: 162 | # Leave ports unpublished, access through reverse proxy. Use the commented-out ports for reference. Use 9000 for outpost. 163 | ### ports: 164 | # - "9000:9000" 165 | # - "9443:9443" 166 | depends_on: 167 | postgresql: 168 | condition: service_healthy 169 | redis: 170 | condition: service_healthy 171 | 172 | ##############_____Authentik_worker_____############################### 173 | 174 | worker: 175 | # USE THE SAME IMAGE AS SERVER!!! 176 | image: ghcr.io/goauthentik/server:2025.4.1 177 | container_name: authentik-worker 178 | restart: unless-stopped 179 | command: worker 180 | environment: 181 | # Set in .env 182 | AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY} 183 | AUTHENTIK_REDIS__HOST: redis 184 | AUTHENTIK_POSTGRESQL__HOST: postgresql 185 | # Set in .env 186 | AUTHENTIK_POSTGRESQL__USER: ${POSTGRES_USER} 187 | # Set in .env 188 | AUTHENTIK_POSTGRESQL__NAME: ${POSTGRES_DB} 189 | # Set in .env 190 | AUTHENTIK_POSTGRESQL__PASSWORD: ${POSTGRES_PASSWORD} 191 | AUTHENTIK_ERROR_REPORTING__ENABLED: true 192 | user: root 193 | volumes: 194 | - /var/run/docker.sock:/var/run/docker.sock 195 | - ./authentik-media:/media 196 | - ./authentik-certs:/certs 197 | - ./authentik-custom-templates:/templates 198 | networks: 199 | internal_network: 200 | depends_on: 201 | postgresql: 202 | condition: service_healthy 203 | redis: 204 | condition: service_healthy 205 | 206 | ##############_____SERVICES_BEHIND_PROXY(internal_network)____######### 207 | 208 | # These are the protected services hidden behind the proxy, make sure all of them are on the internal_network, and ports are NOT published! 209 | 210 | # Our test container 211 | hello: 212 | image: nginxdemos/hello 213 | container_name: hello-test 214 | networks: 215 | - internal_network 216 | restart: unless-stopped 217 | 218 | # Your services go here. 219 | 220 | ##############_____Volumes_and_networks_____########################### 221 | 222 | volumes: 223 | crowdsec-db: 224 | crowdsec-config: 225 | # NPM mariadb 226 | mariadb-data: 227 | # authentik postgresql 228 | database: 229 | driver: local 230 | # authentik redis 231 | redis: 232 | driver: local 233 | 234 | networks: 235 | crowdsec: 236 | driver: bridge 237 | npm: 238 | driver: bridge 239 | # Our internal network - use for services you want to protect 240 | internal_network: 241 | name: internal_network 242 | driver: bridge 243 | -------------------------------------------------------------------------------- /npm_data/.this-is-a-npm-data-folder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suckharder/NPM-Crowdsec-Authentik-Stack/375aef35036082dc2e41c553759020391a5afe1f/npm_data/.this-is-a-npm-data-folder -------------------------------------------------------------------------------- /npm_letsencrypt/.npm-certs-live-here: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suckharder/NPM-Crowdsec-Authentik-Stack/375aef35036082dc2e41c553759020391a5afe1f/npm_letsencrypt/.npm-certs-live-here --------------------------------------------------------------------------------