thomiceli / opengist

Self-hosted pastebin powered by Git, open-source alternative to Github Gist.
https://demo.opengist.io
GNU Affero General Public License v3.0
1.52k stars 75 forks source link

Traefik Reverse Proxy & SSH #286

Open Aetherinox opened 1 month ago

Aetherinox commented 1 month ago

This is sort of a two part question, I'm not sure if it's an Opengist issue, or Traefik.

First part, I get the following error, whether or not I use Traefik, so this is something with Opengist, that I may have not properly set up:

4:09AM ERR SSH: Failed to handshake error="[ssh: password auth not configured]"

I looked through the documentation, and the only thing I could find was a few env variables for setting the SSH port / host IP. Not sure what I'm missing here, but I've ensured port 2222 is open.

Second question, when I set up Opengist to run on Traefik, I then edit my docker-compose.yml file and I've changed the http port over to 80.

    environment:
      OG_HTTP_PORT: 80

For some reason after that, Opengist fails the health check and portainer reports the container as Unhealthy. Once I set Opengist back to use the default http port, it's fine.

If I inspect the container, I notice this

Healthcheck

    Interval 60000000000
    Retries 3
    StartPeriod 15000000000
    Test
        0 CMD-SHELL
        1 curl -f http://localhost:6157/healthcheck || exit 1

Any direction on these would be awesome.


Edit: After doing some googling, I managed to solve the healthcheck issue with

    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:80/healthcheck"]
      interval: 200s
      timeout: 200s
      retries: 5
thomiceli commented 1 month ago

For the first part i need to investigate a bit,

For the second it's a blunder from my side since the healthcheck URL is hardcoded to call Opengist on port 6157... it should be a variable instead

thomiceli commented 4 weeks ago

Do you mind sharing your traefik conf ? Never worked with it but my first guess would be it tries to connect to the SSH server via a password instead of a key

Aetherinox commented 4 weeks ago

Yeah, I'll drop them all here. I'll throw a quick set of instructions. Traefik can be sort of a time killer if you've never set it up before.

A side note, I opted to use port 2222 for SSH in general. So I switched Gitea's SSH port from 22 over to 2222 that way I only had to add one entry for Traefik.

Create Network

docker network create --driver=bridge --subnet=172.18.0.0/16 --gateway=172.18.0.1 traefik

Create Files

Here are the list of dirs / files you need.

Traefik stores SSL certs inside acme.json, so create blank files and chmod 600 them. They will be populated when you start Traefik with certificates. They must be chmod 600 or Traefik won't work.

cd /path/to/container/home

sudo touch docker-compose.yml
sudo touch .env

sudo mkdir -p /config
sudo touch /config/traefik.yml

sudo mkdir -p /ssl/{cloudflare,letsencrypt}
sudo touch /ssl/cloudflare/acme.json
sudo touch /ssl/letsencrypt/acme.json

sudo chmod 600 ssl/cloudflare/acme.json
sudo chmod 600 ssl/letsencrypt/acme.json




Traefik docker-compose.yml

services:

  traefik:
    container_name: traefik
    image: traefik:latest
    hostname: ${TRAEFIK_SUBDOMAIN}.${SERVER_DOMAIN}
    restart: always
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /etc/localtime:/etc/localtime:ro
      - ${PWD}/config/traefik.yml:/etc/traefik/traefik.yml:ro
      - ${PWD}/config/dynamic.yml:/etc/traefik/dynamic.yml:ro
      - ${PWD}/ssl/cloudflare/acme.json:/cloudflare/acme.json
      - ${PWD}/ssl/letsencrypt/acme.json:/letsencrypt/acme.json
    networks:
      traefik:
        ipv4_address: '${TRAEFIK_IP}'
    environment:
      - PUID=${UID}
      - PGID=${GID}
      - CF_API_EMAIL=${CF_DNS_EMAIL}
      - CLOUDFLARE_DNS_API_TOKEN=${CF_API_TOKEN}
      - CLOUDFLARE_ZONE_API_TOKEN=${CF_API_TOKEN}
    ports:
      - 80:80
      - 443:443
      - 8080:8080
    # - 127.0.0.1:8080:8080
    labels:

      # ------------------------------------------------------------------------------------------------------
      #   General
      # ------------------------------------------------------------------------------------------------------

      - traefik.enable=true
      - traefik.constraint-label=traefik
      - traefik.docker.network=${SERVER_NETWORK}

      # ------------------------------------------------------------------------------------------------------
      #   Scope > http
      # ------------------------------------------------------------------------------------------------------

      - traefik.http.routers.traefik-http.rule=Host(`${SERVER_DOMAIN}`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`)) || Host(`${TRAEFIK_SUBDOMAIN}.localhost`) || Host(`${TRAEFIK_SUBDOMAIN}.${SERVER_DOMAIN}`) || Host(`www.${TRAEFIK_SUBDOMAIN}.${SERVER_DOMAIN}`) || Host(`${TRAEFIK_IP}`)
      - traefik.http.routers.traefik-http.service=api@internal
      - traefik.http.routers.traefik-http.entrypoints=http
      - traefik.http.routers.traefik-http.middlewares=https-redirect@file

      # ------------------------------------------------------------------------------------------------------
      #   Scope > https
      # ------------------------------------------------------------------------------------------------------

      - traefik.http.routers.traefik-https.entrypoints=https
      - traefik.http.routers.traefik-https.rule=Host(`${SERVER_DOMAIN}`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`)) || Host(`${TRAEFIK_SUBDOMAIN}.localhost`) || Host(`${TRAEFIK_SUBDOMAIN}.${SERVER_DOMAIN}`) || Host(`www.${TRAEFIK_SUBDOMAIN}.${SERVER_DOMAIN}`) || Host(`${TRAEFIK_IP}`)
      - traefik.http.routers.traefik-https.tls=true
      - traefik.http.routers.traefik-https.tls.certresolver=cloudflare
      - traefik.http.routers.traefik-https.tls.domains[0].main=${SERVER_DOMAIN}
      - traefik.http.routers.traefik-https.tls.domains[0].sans=*.${SERVER_DOMAIN}
      - traefik.http.routers.traefik-https.service=api@internal

      # ------------------------------------------------------------------------------------------------------
      #   Services / Load Balancer
      # ------------------------------------------------------------------------------------------------------

      - traefik.http.services.traefik.loadbalancer.server.port=${TRAEFIK_PORT_MAIN}
      - traefik.http.services.traefik.loadbalancer.server.scheme=${TRAEFIK_PROT_MAIN}

networks:
  traefik:
    name: ${SERVER_NETWORK}
    external: true


Traefik .env

# ------------------------------
#   Domain Name
# ------------------------------

SERVER_DOMAIN=DOMAIN.COM
SERVER_IP=XX.XX.XX.XX
SERVER_NETWORK=traefik

# ------------------------------
#   Group & User ID
# ------------------------------

UID=143
GID=997

# ------------------------------
#   Services
# ------------------------------

TRAEFIK_SUBDOMAIN=traefik
TRAEFIK_IP=172.18.0.2
TRAEFIK_PORT_MAIN=8080
TRAEFIK_PROT_MAIN=http

# ------------------------------
#   Cloudflare
# ------------------------------

CF_DNS_EMAIL=emailaddy@forcloudflare.com
CF_API_TOKEN=YOUR_API_TOKEN


Traefik config/traefik.yml

This is the Traefik static file. Changes to this file require Traefik to be restarted.

global:
  checkNewVersion: false
  sendAnonymousUsage: false

log:
  level: TRACE
  format: "common"

api:
  dashboard: true
  insecure: true

entryPoints:

  ssh:
    address: :2222/tcp

  traefik:
    address: :8080

  http:
    address: :80
    forwardedHeaders:
      trustedIPs: &trustedIps
        # Cloudlare Public IP List > Start > for HTTP requests, remove this if you don't use it; https://cloudflare.com/de-de/ips/
        - 103.21.244.0/22
        - 103.22.200.0/22
        - 103.31.4.0/22
        - 104.16.0.0/13
        - 104.24.0.0/14
        - 108.162.192.0/18
        - 131.0.72.0/22
        - 141.101.64.0/18
        - 162.158.0.0/15
        - 172.64.0.0/13
        - 173.245.48.0/20
        - 188.114.96.0/20
        - 190.93.240.0/20
        - 197.234.240.0/22
        - 198.41.128.0/17
        - 2400:cb00::/32
        - 2606:4700::/32
        - 2803:f800::/32
        - 2405:b500::/32
        - 2405:8100::/32
        - 2a06:98c0::/29
        - 2c0f:f248::/32
    http:
      redirections:
        entryPoint:
          to: https
          scheme: https

  https:
    address: :443
    forwardedHeaders:
      trustedIPs: *trustedIps
    http:
      tls:
        certResolver: cloudflare
        domains:
          - main: domain.com
            sans:
              - '*.domain.com'

serversTransport:
  insecureSkipVerify: true

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
    network: traefik
    watch: true
  file:
    filename: "/etc/traefik/dynamic.yml"
    watch: true

certificatesResolvers:
  cloudflare:
    acme:
      email: cloudflareemail@address.com
      storage: /cloudflare/acme.json
      dnsChallenge:
        provider: cloudflare
        delaybeforecheck: 10
        resolvers:
          - "1.1.1.1:53"
          - "1.0.0.1:53"
  myresolver:
    acme:
      email: cloudflareemail@address.com
      storage: /letsencrypt/acme.json
      httpChallenge:
        entryPoint: http


Traefik config/dynamic.yml

This is the Traefik dynamic file, basically forces http to https. You do not need to restart Traefik to make changes to this file and they automatically apply:

http:
  middlewares:
    https-redirect:
      redirectScheme:
        scheme: "https"
        permanent: true




Opengist docker-compose.yml

services:

  opengist:
    container_name: opengist
    image: ghcr.io/thomiceli/opengist:1
    hostname: ${SERVICE_SUBDOMAIN}.${SERVER_DOMAIN}
    restart: always
    volumes:
      - ${PWD}/data:/opengist
    ports:
      - ${SERVICE_PORT_SSH}:2222
    healthcheck:
      test: ['CMD', 'curl', '-f', 'http://localhost:80/healthcheck']
      interval: 200s
      timeout: 200s
      retries: 5
    environment:
      UID: ${UID}
      GID: ${GID}
      OG_LOG_LEVEL: ${OG_LOG_LEVEL}
      OG_GITEA_CLIENT_KEY: ${OG_GITEA_CLIENT_KEY}
      OG_GITEA_SECRET: ${OG_GITEA_SECRET}
      OG_GITEA_URL: ${OG_GITEA_URL}
      OG_GITHUB_CLIENT_KEY: ${OG_GITHUB_CLIENT_KEY}
      OG_GITHUB_SECRET: ${OG_GITHUB_SECRET}
      OG_HTTP_PORT: ${SERVICE_PORT_MAIN}
    networks:
      traefik:
        ipv4_address: '${SERVICE_IP}'
    labels:

      # ------------------------------------------------------------------------------------------------------
      #   General
      # ------------------------------------------------------------------------------------------------------

      - traefik.enable=true
      - traefik.constraint-label=traefik
      - traefik.docker.network=${SERVER_NETWORK}

      # ------------------------------------------------------------------------------------------------------
      #   Scope > http
      # ------------------------------------------------------------------------------------------------------

      - traefik.http.routers.opengist-http.rule=Host(`${SERVICE_SUBDOMAIN}.localhost`) || Host(`${SERVICE_SUBDOMAIN}.${SERVER_DOMAIN}`) || Host(`www.${SERVICE_SUBDOMAIN}.${SERVER_DOMAIN}`) || Host(`${SERVICE_IP}`) || Host(`${SERVER_DOMAIN}`) && PathPrefix(`/${SERVICE_SUBDOMAIN}`)
      - traefik.http.routers.opengist-http.service=opengist
      - traefik.http.routers.opengist-http.entrypoints=http
      - traefik.http.routers.opengist-http.middlewares=https-redirect@file

      # ------------------------------------------------------------------------------------------------------
      #   Scope > https
      # ------------------------------------------------------------------------------------------------------

      - traefik.http.routers.opengist-https.rule=Host(`${SERVICE_SUBDOMAIN}.localhost`) || Host(`${SERVICE_SUBDOMAIN}.${SERVER_DOMAIN}`) || Host(`www.${SERVICE_SUBDOMAIN}.${SERVER_DOMAIN}`) || Host(`${SERVICE_IP}`) || Host(`${SERVER_DOMAIN}`) && PathPrefix(`/${SERVICE_SUBDOMAIN}`)
      - traefik.http.routers.opengist-https.service=opengist
      - traefik.http.routers.opengist-https.entrypoints=https
      - traefik.http.routers.opengist-https.tls=true
      - traefik.http.routers.opengist-https.tls.certresolver=cloudflare
      - traefik.http.routers.opengist-https.tls.domains[0].main=${SERVER_DOMAIN}
      - traefik.http.routers.opengist-https.tls.domains[0].sans=*.${SERVER_DOMAIN}

      # ------------------------------------------------------------------------------------------------------
      #   SSH
      # ------------------------------------------------------------------------------------------------------

      - traefik.tcp.routers.opengist-ssh.rule=HostSNI(`*`)
      - traefik.tcp.routers.opengist-ssh.entrypoints=ssh      
      - traefik.tcp.routers.opengist-ssh.tls=true
      - traefik.tcp.routers.opengist-ssh.service=opengist-ssh
      - traefik.tcp.services.opengist-ssh.loadbalancer.server.port=${SERVICE_PORT_SSH}

      # ------------------------------------------------------------------------------------------------------
      #   Load Balancer
      # ------------------------------------------------------------------------------------------------------

      - traefik.http.services.opengist.loadbalancer.server.port=${SERVICE_PORT_MAIN}
      - traefik.http.services.opengist.loadbalancer.server.scheme=${SERVICE_PROT_MAIN}
      - traefik.http.services.opengist.loadbalancer.healthcheck.path=/healthcheck
      - traefik.http.services.opengist.loadbalancer.healthcheck.interval=200s
      - traefik.http.services.opengist.loadbalancer.healthcheck.timeout=75ms
      - traefik.http.services.opengist.loadbalancer.healthcheck.scheme=http
      - traefik.http.services.opengist.loadbalancer.healthcheck.port=80

# ------------------------------------------------------------------------------------------------------
#   Networks
# ------------------------------------------------------------------------------------------------------

networks:
  traefik:
    name: ${SERVER_NETWORK}
    external: true


Opengist .env

# ------------------------------------------------------------
#   Domain Name
# ------------------------------------------------------------

SERVER_DOMAIN=domain.com
SERVER_IP=XX.XX.XX.XX
SERVER_NETWORK=traefik

# ------------------------------------------------------------
#   Group & User ID
# ------------------------------------------------------------

UID=143
GID=997

# ------------------------------------------------------------
#   Services
# ------------------------------------------------------------

SERVICE_SUBDOMAIN=gist
SERVICE_IP=172.18.0.19
SERVICE_PORT_MAIN=80
SERVICE_PROT_MAIN=http
SERVICE_PORT_SSH=2222

# ------------------------------------------------------------
#   OpenGist Internal
# ------------------------------------------------------------

OG_LOG_LEVEL=info
OG_GITEA_CLIENT_KEY=
OG_GITEA_SECRET=
OG_GITEA_URL=https://git.domain.com
OG_GITHUB_CLIENT_KEY=
OG_GITHUB_SECRET=


The only thing you should have to do is edit the .env files. Add whatever IP address the server will use, domain, and cloudflare API email / token.