Skip to main content

Pingvin and Pocket ID - Share files securely

·1710 words·9 mins· loading · loading · ·
Security Homelab Docker Container
Luca-Leon Hausdoerfer
Author
Luca-Leon Hausdoerfer
Hi, I am an IT system engineer and share projects from my home lab as well as personal insights here - honest and inspiring.
Table of Contents

Pingvin Stack mit Pocket-ID und Traefik einrichten
#

Introduction
#

This guide explains step by step how to set up a Pingvin stack with Pocket-ID and Traefik. The aim is to provide a secure and easy-to-maintain file sharing environment that is secured by an identity provider (IdP) and managed by Traefik as a reverse proxy.

This documentation is aimed at both beginners and experienced users who prefer detailed instructions and code examples. All configuration files are provided.


Prerequisites
#

Before you start, make sure that the following requirements are met:

  • Operating system: Linux (e.g. Ubuntu 20.04 or newer) or another compatible system.
  • User rights: Access to a shell with root rights or sudo authorizations.
  • Installed tools:
    • Docker (at least version 20.10)
    • Docker Compose (at least version 2.0)
    • A text editor (e.g. nano, vim or any editor of your choice).
  • Domain: A registered domain that is used for Traefik (e.g. example.com).
  • TLS certificates: Automated via Let’s Encrypt or provided manually.

Step 1: Preparations
#

1.1 Updating the system
#

First carry out a system update to ensure that all packages are up to date:

sudo apt update && sudo apt upgrade -y

1.2 Install Docker and Docker Compose
#

The installation for Docker and Docker Compose may differ. Refer to the Docker documentation on how to install the software for your operating system. This documentation here refers to Ubuntu.
  1. Execute the following command to install Docker:
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
  1. Verify the installation:
docker --version

1.3 Configure network
#

Create a dedicated network for Docker if one does not yet exist:

docker network create webproxy

This creates a separate network that can be shared by Traefik and the containers.

1.4 Preparing the folder structure
#

You need a certain folder structure for the deployment. You can take this from the following example. Create the folders and files. We will fill these in the next steps

├── traefik
│   ├── data
│   │   ├── dynamic
│   │   │   └── middlewares.yml
│   │   ├── acme.json
│   │   └── traefik.yml
│   ├── error-pages
│   │   └── app-down.html
│   └── docker-compose.yml
├── pingvin-stack
│   ├── .env
│   └── docker-compose.yml
├── portainer
│   └── docker-compose.yml

Step 2: Set up Traefik
#

2.1 Creating the Traefik configuration file
#

Create a new file called traefik.yml in the working directory:

# /root/traefik/data/traefik.yml

api:
  dashboard: true                     # Activates the dashboard
  debug: true
entryPoints:
  http:
    address: ":80"                    # Port 80 for HTTP Traffic
  https:
    address: ":443"                   # Port 443 for HTTPS Traffic
serversTransport:
  insecureSkipVerify: true            # Self-signed certificates of containers are ignored
providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false           # Disable automatic exposure of containers
  file:
    directory: /dynamic               # Discovery of manual routes in the Dynamic folder
    watch: true                       # Files are automatically monitored and updated
certificatesResolvers:                # Let's Encrypt certificates via Cloudflare
  cloudflare:
    acme:
      email: admin@h-servers.de
      storage: acme.json
      dnsChallenge:
        provider: cloudflare
        resolvers:
          - "1.1.1.1:53"
          - "1.0.0.1:53"

2.2 Create the docker-compose configuration
#

Create a docker-compose.yml for Traefik:

# /root/traefik/docker-compose.yml

services:
  traefik:
    image: "traefik:v3.2"
    container_name: traefik
    command:
      # Enable Hub communication (open the port 9900 and 9901 by default)
      - --experimental.hub=true
      - --hub.tls.insecure=true
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - webproxy
    ports:
      - 80:80
      - 443:443

    depends_on:
      - error-pages

    environment:
      - CF_API_EMAIL=admin@example.com # Change email
      - CF_DNS_API_TOKEN=xyzxyzxyzxyzxyzxyzxyzxyzxyzxyz # Change API Token
      - TZ=Europe/Berlin
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /root/traefik/data/traefik.yml:/traefik.yml:ro
      - /root/traefik/data/acme.json:/acme.json
      - /root/traefik/data/dynamic:/dynamic:ro
    labels:
      # Enabeling Traefik
      - "traefik.enable=true"

      # Traefik Default HTTP Endpoint with HTTPS Redirection
      - "traefik.http.routers.traefik.entrypoints=http"
      - "traefik.http.routers.traefik.rule=Host(`traefik.example.com`)" # Insert domain here
      - "traefik.http.middlewares.traefik-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.middlewares.sslheader.headers.customrequestheaders.X-Forwarded-Proto=https"
      - "traefik.http.routers.traefik.middlewares=traefik-https-redirect"

      # Traefik Error Pages
      - "traefik.http.routers.traefik.middlewares=error-pages-middleware"

      # Traefik HTTPS Endpoint with Wildcard Cert, issued from Cloudflare
      - "traefik.http.routers.traefik-secure.entrypoints=https"
      - "traefik.http.routers.traefik-secure.rule=Host(`traefik.example.com`)" # Insert domain here
      - "traefik.http.routers.traefik-secure.tls=true"
      - "traefik.http.routers.traefik-secure.tls.certresolver=cloudflare"
      - "traefik.http.routers.traefik-secure.tls.domains[0].main=example.com" # Insert domain here
      - "traefik.http.routers.traefik-secure.tls.domains[0].sans=*.example.com" # Insert domain here

      # Exposure of the API interface
      - "traefik.http.routers.traefik-secure.service=api@internal"

      # Catchall HTTP and redirect to HTTPS
      - "traefik.http.routers.http-catchall.rule=hostregexp(`{host:[a-z-.]+}`)"
      - "traefik.http.routers.http-catchall.entrypoints=http"
      - "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
      - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"

  error-pages:
    image: tarampampam/error-pages:3.2.0
    restart: unless-stopped
    command: serve --add-template /opt/app-down.html
    # environment:
      # - TEMPLATE_NAME=app-down #connection #app-down # set the error pages template
    volumes:
      - /root/traefik/error-pages/app-down.html:/opt/app-down.html:ro

    networks:
      - webproxy
    labels:
      traefik.enable: true
      # use as "fallback" for any NON-registered services (with priority below normal)
      traefik.http.routers.error-pages-router.rule: HostRegexp(`{host:.+}`)
      traefik.http.routers.error-pages-router.priority: 1
      # should say that all of your services work on https
      traefik.http.routers.error-pages-router.tls: 'true'
      traefik.http.routers.error-pages-router.entrypoints: https
      traefik.http.routers.error-pages-router.middlewares: error-pages-middleware
      traefik.http.services.error-pages-service.loadbalancer.server.port: 8080
      # "errors" middleware settings
      traefik.http.middlewares.error-pages-middleware.errors.status: 400-599
      traefik.http.middlewares.error-pages-middleware.errors.service: error-pages-service
      traefik.http.middlewares.error-pages-middleware.errors.query: /{status}.html

networks:
  webproxy:
    external: true

In the file above, another container called error-pages is set up in addition to Traefik. This provides error pages matching the HTTP code if a page cannot be reached.

2.4 Starting Traefik
#

Start the Traefik container:

docker compose up -d

Check whether Traefik is running and accessible by calling up the domain or checking the logs:

docker logs traefik

Step 3: Set up Pingvin Pocket ID
#

3.1 Creating the Pocket ID environment variable
#

Create a file named .env and add the following content:

# /root/pingvin-stack/.env

# See the README for more information: https://github.com/stonith404/pocket-id?tab=readme-ov-file#environment-variables
PUBLIC_APP_URL=https://login.example.com
TRUST_PROXY=true
# MAXMIND_LICENSE_KEY: please read the documentation here. 
# This key is used later for the location resolution in the audit log.
MAXMIND_LICENSE_KEY=xyzxyzxyzxyzxyzxyzxyzxyz 
# For the UserID, the corresponding user with the ID must be used; this should not be root.
PUID=1000
PGID=1000

3.2 Creating the Docker-Compose configuration for the Pingvin stack
#

Create a file docker-compose.yml with the following content:

# /root/pingvin-stack/docker-compose.yml

services:
  pingvin-share:
    container_name: pingvin-app
    image: stonith404/pingvin-share
    restart: unless-stopped
    ports:
      - 3000:3000
    environment:
      - TRUST_PROXY=false # Set to true if a reverse proxy is in front of the container
    volumes:
      - "./pingvin/data:/opt/app/backend/data"
      - "./pingvin/data/images:/opt/app/frontend/public/img"
    networks:
      - webproxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.share.rule=Host(`share.example.com`)" # Hier Domain einsetzen
      - "traefik.http.routers.share.entrypoints=http"
      - "traefik.http.routers.share-secure.rule=Host(`share.example.com`)" # Insert domain here
      - "traefik.http.routers.share-secure.entrypoints=https"
      - "traefik.http.routers.share-secure.tls=true"
      - "traefik.http.routers.share.middlewares=default-headers@file,error-pages-middleware"
      - "traefik.http.services.share.loadbalancer.server.port=3000"

  pocket-id:
    container_name: pocket-id-app
    image: stonith404/pocket-id  # or ghcr.io/stonith404/pocket-id
    restart: unless-stopped
    env_file: .env
    ports:
      - 3001:80
    volumes:
      - "./pocket-id/data:/app/backend/data"
    networks:
      - webproxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.login.rule=Host(`login.example.com`)" # Insert domain here
      - "traefik.http.routers.login.entrypoints=http"
      - "traefik.http.routers.login-secure.rule=Host(`login.example.com`)" # Insert domain here
      - "traefik.http.routers.login-secure.entrypoints=https"
      - "traefik.http.routers.login-secure.tls=true"
      - "traefik.http.routers.login.middlewares=default-headers@file,error-pages-middleware"
      - "traefik.http.services.login.loadbalancer.server.port=80"
    healthcheck:
      test: "curl -f http://localhost/health"
      interval: 1m30s
      timeout: 5s
      retries: 2
      start_period: 10s

networks:
  webproxy:
    external: true

3.3 Starting the Pingvin stack with Pocket ID
#

Start the container with the following command:

docker compose up -d

Step 4: Setting up the two new services
#

4.1 Configuration of Pocket ID
#

First call up the URL https://login.example.com/login/setup to create your administrator user here too. You can then specify your name and user name. It is important that you enter a passkey here, as you will then use this to authenticate yourself.

Benutzer bearbeiten und Passkey anlegen
Benutzer bearbeiten und Passkey anlegen

You can customize your Pocket ID app under Application Configuration, upload a logo and set a background. You then configure an OIDC client.

This is required to log in to Pingvin later. Add name and callback URL and click on Save.

You will then receive various data that you need to set up OIDC in Pingvin. So leave this page open. We need the data afterwards.

4.2 Configuration of Pingvin
#

To do this, first call up the URL of Pingvin. This should read something like this: https://share.examle.com

Pingvin Startseite
Pingvin Startseite

Now click on Register to create your admin user and then click on Let’s get started.

Administrator Konto anlegen
Administrator Konto anlegen

Note: You should definitely choose the same user name and e-mail address here as you have already done in Pocket ID, so that the administrator authorization is then transferred.

You can then carry out the configuration. To do this, click on Customize Configuration.

Name your app and add your URL. I also recommend activating Secure Cookies. You can also upload your own logo.

Under Share you should now deactivate the registration, because we will then implement this via OIDC - i.e. Pocket ID. You can also deactivate logging in with a password under Logging in via social networks.

Please note that you should NOT log out at this point, otherwise you will no longer be able to log in. After configuring Pocket ID, the login will work again.

To set up OIDC now, open Sign in via social networks.

First deactivate the password login to enable login with Pocket ID only.

Anmeldung mit Passwort deaktivieren
Anmeldung mit Passwort deaktivieren

Then scroll further down to configure OIDC. Activate OpenID Connect and enter the Discovery URL. You can find this in the previous tab for Pocket ID, in which you created the OIDC client.

OIDC Discovery URL eingeben
OIDC Discovery URL eingeben

The client ID and client secret must now be entered at the very bottom of the page. Then save the configuration and OIDC is now configured for Pingvin.

Eintragen der Secrets
Eintragen der Secrets


Step 5: Log in via OIDC
#

You can now use Pocket ID as an OIDC provider and log in with it. To do this, you can open a new browser window and open https://share.example.com. You should now be automatically redirected and have the option of logging in with your passkey.


Bonus: Portainer deployment and OIDC connection
#

6.1 Creating the Docker-Compose configuration
#

Create a docker-compose.yml for Traefik:

# /root/portainer/docker-compose.yml

services:
  portainer:
    container_name: portainer
    image: portainer/portainer-ce:latest
    command: -H unix:///var/run/docker.sock
    restart: always
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - portainer_data:/data
    networks:
      - webproxy

    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.portainer.rule=Host(`portainer.example.com`)"
      - "traefik.http.routers.portainer.entrypoints=https"
      - "traefik.http.routers.portainer.tls=true"
      - "traefik.http.services.portainer.loadbalancer.server.port=9000"
      - "traefik.http.routers.portainer.middlewares=default-headers@file,error-pages-middleware"

volumes:
  portainer_data:

networks:
  webproxy:
    external: true

6.2 Connecting Portainer to OIDC
#

First create an OIDC configuration in Pocket ID again. Then switch to your Portainer instance and select the OAuth method under Settings → Authentication.

You must then also make the corresponding configurations here, which you will receive in Pocket ID after creating the OIDC client.

The configuration looks something like this:

So that you can log in later via OIDC, you still need to create your user. To do this, click on User-related → User and add your user with the e-mail address, make him an administrator and then click on + Create user. That’s it. Now you can also log in here with your Pocket ID account.