immich-app / immich

High performance self-hosted photo and video management solution.
https://immich.app
GNU Affero General Public License v3.0
35.39k stars 1.67k forks source link

Status 200 on unauthorized access in reverse proxy logs #9422

Closed davze closed 2 weeks ago

davze commented 3 weeks ago

The bug

Not sure if it can be called a bug, but I'm not sure either if it is wanted behavior.

In my setup immich is sitting behind a caddy reverse proxy. To expose it as safely to the www as possible I have a multi layered security setup. One of the layers is a an aggressive fail2ban filter to ban bots that are probing for admin interfaces. If a bot is trying to access to many non existing filepaths and generates to many HTTP 403 or 404 errors it gets banned. At the current state immich is showing a custom error page to the bot but the reverse proxy (and my fail2ban filter) is just getting a status 200 from the immich server because it successfully serves the bot with the custom error page. Is it possible to send the right HTTP status (403/404) to the reverse proxy.

Logs are from caddy.

The OS that Immich Server is running on

Debian

Version of Immich Server

v1.103.1

Version of Immich Mobile App

v1.103.1

Platform with the issue

Your docker-compose.yml content

version: "3.8"

#
# WARNING: Make sure to use the docker-compose.yml of the current release:
#
# https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
#
# The compose file on main may not be compatible with the latest release.
#

name: immich

services:
  immich-server:
    container_name: immich_server

    image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}

    command: [ "start.sh", "immich" ]
    volumes:
      - ${UPLOAD_LOCATION}:/usr/src/app/upload
      - /etc/localtime:/etc/localtime:ro

    env_file:
      - .env
    ports:
      - 2283:3001
    logging:
      driver: "journald"
      options:
        tag: "immich-server"
    depends_on:
      - redis
      - database
    restart: always
    networks:
      - proxy

  immich-microservices:
    container_name: immich_microservices
    image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
    # extends:
    #   file: hwaccel.yml
    #   service: hwaccel
    command: [ "start.sh", "microservices" ]
    volumes:
      - ${UPLOAD_LOCATION}:/usr/src/app/upload
      - /etc/localtime:/etc/localtime:ro
    env_file:
      - .env
    depends_on:
      - redis
      - database
    restart: always
    networks:
      - proxy

  immich-machine-learning:
    container_name: immich_machine_learning
    image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
    volumes:
      - model-cache:/cache
    env_file:
      - .env
    restart: always
    networks:
      - proxy

  redis:
    container_name: immich_redis
    image: redis:6.2-alpine@sha256:afb290a0a0d0b2bd7537b62ebff1eb84d045c757c1c31ca2ca48c79536c0de82
    restart: always
    networks:
      - proxy

  database:
    container_name: immich_postgres
    image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0
    env_file:
      - .env
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_USER: ${DB_USERNAME}
      POSTGRES_DB: ${DB_DATABASE_NAME}
    volumes:
      - pgdata:/var/lib/postgresql/data
    restart: always
    networks:
      - proxy

volumes:
  pgdata:
  model-cache:

networks:
  proxy:
    driver: bridge
    external: true

Your .env content

not relevant

Reproduction steps

1. Access non existing file on immich server
2. check logs of reverse proxy
3.
...

Relevant log output

{"level":"info","ts":1715279990.290475,"logger":"http.log.access.log1","msg":"handled request","request":{"remote_ip":"177.130.249.153","remote_port":"57435","client_ip":"177.130.249.153","proto":"HTTP/1.1","method":"GET","host":"url.removed","uri":"/wp-login.php","headers":{"User-Agent":["Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:94.0) Gecko/20100101 Firefox/95.0"],"Accept-Encoding":["gzip"],"Connection":["close"]}},"bytes_read":0,"user_id":"","duration":0.000135983,"size":0,"status":308,"resp_headers":{"Server":["Caddy"],"Connection":["close"],"Location":["https://url.removed/wp-login.php"],"Content-Type":[]}}
{"level":"info","ts":1715279991.6638489,"logger":"http.log.access.log1","msg":"handled request","request":{"remote_ip":"177.130.249.153","remote_port":"49286","client_ip":"177.130.249.153","proto":"HTTP/1.1","method":"GET","host":"url.removed","uri":"/wp-login.php","headers":{"Accept-Encoding":["gzip"],"Connection":["close"],"User-Agent":["Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:94.0) Gecko/20100101 Firefox/95.0"],"Referer":["http://url.removed/wp-login.php"]},"tls":{"resumed":false,"version":771,"cipher_suite":49195,"proto":"","server_name":"url.removed"}},"bytes_read":0,"user_id":"","duration":0.054626277,"size":3074,"status":200,"resp_headers":{"Strict-Transport-Security":["max-age=31536000;"],"Content-Length":["3074"],"X-Robots-Tag":["none"],"Referrer-Policy":["no-referrer-when-downgrade"],"Date":["Thu, 09 May 2024 18:39:51 GMT"],"X-Powered-By":["Express"],"Cache-Control":["no-store"],"Etag":["\"c02-s8QNOD14q4btqvxlHZ8Ptq8huD4\""],"Alt-Svc":["h3=\":443\"; ma=2592000"],"X-Frame-Options":["DENY"],"Permissions-Policy":["interest-cohort=()"],"Content-Type":["text/html; charset=utf-8"],"Server":["Caddy"],"X-Xss-Protection":["1; mode=block"]}}

Additional information

No response

danieldietzler commented 2 weeks ago

We log failed auth attempts, so you should be able to use the logs to get failed authentication attempts. Besides, the actual api response should be a 403, and I think that's the request you should use for fail2ban, not some GUI request. If you only watch for the request serving the HTML, I could still make as many api auth requests as I want

davze commented 2 weeks ago

Just to clarify: The bug report was not about failed login attempts. I got seperate (working) filters for that. The problem is about the wrong status code being returned when accessing files that are not present. In my example a bot is probing for a wordpress login page (wp-login.php). I think the immich server should respond with status 404 instead of 200.

danieldietzler commented 2 weeks ago

@davze Then it's a duplicate of #8590 :P