seafile-docker icon indicating copy to clipboard operation
seafile-docker copied to clipboard

Fix broken deployment when served using HTTP behind a HTTPs proxy

Open undergroundwires opened this issue 2 years ago • 3 comments

Using Seafile on HTTP mode and having a HTTP proxy on front of it does not work.

Following PRs solve this issue: #325 and #324, this issue organizes these PRs.

Community has come up with own solutions such as ggogel/seafile-containerized to support container best-practices such as allowing this kind of scenario, however this is an important feature/bug fix that can be and should be supported in the official Docker image.

undergroundwires avatar Jan 31 '23 22:01 undergroundwires

If anyone is interested in getting this working before the official solution is there. Here's my workaround:

Click here to expand and see
  1. Create patch-seafile.Dockerfile:
FROM python:3.11
WORKDIR /var/app
COPY patch.py .
CMD [ "python", "./patch.py"]
  1. Create patch.py:
# This scripts is workaround to get HTTP working for Seafile docker, see https://github.com/haiwen/seafile-docker/issues/326.
# It's here until following PRs are merged:
# - "Allow specifying FILE_SERVER_ROOT", https://github.com/haiwen/seafile-docker/pull/324
# - "Add: enable .well-known/acme-challenge when https", https://github.com/haiwen/seafile-docker/pull/325

import datetime
import os
import logging
import re
from time import sleep

SEAFILE_DATA_DIR='/seafile-data'

def main():
    setup_logger()
    logging.info('Running script.')
    fix_file_server_root()
    fix_nginx_configuration()
    logging.info('Completed running script.')

def setup_logger():
    logging.basicConfig(
        level=logging.INFO,
        format="%(asctime)s [%(levelname)s] %(message)s",
        handlers=[
            logging.FileHandler("debug.log"),
            logging.StreamHandler()
        ]
    )

def fix_file_server_root():
    # "Allow specifying FILE_SERVER_ROOT", https://github.com/haiwen/seafile-docker/pull/324
    logging.info('Fixing FILE_SERVER_ROOT configuration')
    if is_https():
        logging.info('Skipping because https is desired.')
        return
    seafile_conf_file=f'{SEAFILE_DATA_DIR}/seafile/conf/seahub_settings.py'
    content = read_config_file(seafile_conf_file)
    if 'FILE_SERVER_ROOT = "https' in content:
        logging.info('Skipping because FILE_SERVER_ROOT is already set to HTTPS.')
        return
    save_backup(seafile_conf_file, content)
    content = content.replace('FILE_SERVER_ROOT = "http', 'FILE_SERVER_ROOT = "https')
    save_new_content(seafile_conf_file, content)
    cache_file=f'{SEAFILE_DATA_DIR}/seafile/conf/seahub_settings.pyc' # https://manual.seafile.com/config/seahub_settings_py/#note
    if os.path.isfile(cache_file):
        logging.error(f'Deleting cache file {cache_file} so changes take effect.')
        os.remove(cache_file)

def fix_nginx_configuration():
    # Fixes "Add: enable .well-known/acme-challenge when https", https://github.com/haiwen/seafile-docker/pull/325
    logging.info('Fixing nginx configuration')
    if is_https():
        logging.info('Skipping because https is desired.')
        return
    nginx_conf_file = f"{SEAFILE_DATA_DIR}/nginx/conf/seafile.nginx.conf"
    logging.info(f'Fixing nginx configuration in ${nginx_conf_file}')
    content = read_config_file(nginx_conf_file)
    if 'For letsencrypt' not in content:
        logging.info('Skipping because letsencrypt is not configured')
        return
    save_backup(nginx_conf_file, content)
    pattern = re.compile(r"^\s*# For letsencrypt[\S\s]*\;\s*\}", re.MULTILINE)
    content = pattern.sub('', content)
    save_new_content(nginx_conf_file, content)

def save_new_content(file_path: str, content: str):
    if not os.path.isfile(file_path):
        logging.error(f'File {file_path} does not exist.')
    logging.info(f'Saving new configuration:\n\n---\n{content}\n---')
    with open(file_path, 'w') as file:
        file.write(content)
    logging.info(f'Saved new configuration to {file_path}.')

def save_backup(original_file_path: str, original_content: str):
    timestamp = (datetime.datetime.utcnow().strftime("%m%d%y-%H%M%S"))
    backup_file_name=f"{original_file_path}.backup.{timestamp}"
    with open(backup_file_name, 'w') as file:
        file.write(original_content)
        logging.info(f'Saved original file as backup file: {backup_file_name}')

def read_config_file(file_path: str) -> str:
    if not os.path.isfile(file_path):
        wait_in_seconds=1
        logging.info(f'File {file_path} does not exist, might not be created yet, waiting for {wait_in_seconds} seconds.')
        sleep(wait_in_seconds)
        return read_config_file(file_path)
    with open(file_path, 'r') as file:
        content = file.read()
        logging.info(f'Current configuration ({file_path}):\n\n---\n{content}\n---')
        return content

def is_https():
    return get_conf('SEAFILE_SERVER_LETSENCRYPT', 'false').lower() == 'true'

def get_conf(key: str, default=None):
    key = key.upper()
    return os.environ.get(key, default)

main()
  1. Run the container along with official seafile image, e.g. if you're using docker-compose in docker-compose.yml:
  seafile:
    image: seafileltd/seafile-mc:latest
    volumes:
      - {YOUR-LOCAL-SEAFILE-DATA-DIR}:/shared 
      SEAFILE_SERVER_LETSENCRYPT: false
      # ...

  # Add this:
  patch-seafile:
    build:
      context: ./
      dockerfile: patch-seafile.Dockerfile
    environment:
      SEAFILE_SERVER_LETSENCRYPT: false
    volumes:
      - {YOUR-LOCAL-SEAFILE-DATA-DIR}:/seafile-data

undergroundwires avatar Jan 31 '23 22:01 undergroundwires

nice workaround @undergroundwires!

however, I'm a little surprised you didn't include a depends: directive to the patch service to make sure it doesn't fire before the seafile-* services

kirisakow avatar Jun 27 '24 14:06 kirisakow

nice workaround @undergroundwires!

however, I'm a little surprised you didn't include a depends: directive to the patch service to make sure it doesn't fire before the seafile-* services

Since depends on: is only ensuring service order, this might not do what you expect. You could add a health check to the seafile container and add that as a condition to the patch service. Since seafile can take a very long time to be ready (chmodding the files in case of "run as user", e.g.), that's the safer alternative, I believe, but correct me if I'm wrong.

sdsys-ch avatar Jun 27 '24 15:06 sdsys-ch