Getting SeaFile to abide…
Hi Arraro,
A post on how I crafted my current SeaFile system, on which I’ve spent too much time…
So I won’t detail the early steps needed to get SeaFile running on your guest host, this is well documented here .
My requirement’s were the followings:
- SeaFile to run on it’s guest host on HTTPS:443
- SeaFile available on multiple FQDN’s (internal/external)
- SeaFile behaving like a good boy when interfaced through a publicly reachable reverse-proxy (pheeewwww that was loooooong and haaaaaard)
In preamble, the latest editions of SeaFile are pushing hard for you to swallow the Docker pill that seems now glued with it. I know my ways within Docker but in whole honesty, I’m not in favor of having that piece of kit all over the place and pretty frankly, could I have avoided it on SeaFile I’d have gone without it.. But as explained, I think it’s now the default path and further releases may not even be possible as bare metal on your guest host. Alright, perhaps that is room for thoughts on future possibilities for my self hosted file storage… Though for now, SeaFile it is…
Here you’d see a high level overview at how things are now shed down in combination with the Docker part.
Getting rid of the embedded Caddy Reverse Proxy#
I tried all I could to love the newly embedded Caddy Reverse Proxy – I even read out loud some Yoda quotes to possibly apply The Force at it – although, I couldn’t get this to work the way I wanted it to. The main issue being that it was an either HTTP OR HTTPS all in one setup. I needed something less rigid and I wanted to terminate the SSL/TLS connections at another externally reachable reverse-proxy. Using the per default embedded Caddy Reverse Proxy, I was continuously facing Bad Request - Contradictory scheme headers errors this while interfaced from the publicly reachable reverse-proxy. It seems that I wasn’t the only one…
So here is what I’ve done to get rid of Caddy within my config.
Below is the needed seafile-server.yml which the above linked setup procedure requires you to download, in which I did a few modifications where I’ve put some comments.
root@seafile:/opt/seafile# cat seafile-server.yml
services:
db:
image: ${SEAFILE_DB_IMAGE:-mariadb:10.11}
container_name: seafile-mysql
restart: unless-stopped
environment:
- MYSQL_ROOT_PASSWORD=${INIT_SEAFILE_MYSQL_ROOT_PASSWORD:-}
- MYSQL_LOG_CONSOLE=true
- MARIADB_AUTO_UPGRADE=1
volumes:
- "${SEAFILE_MYSQL_VOLUME:-/opt/seafile-mysql/db}:/var/lib/mysql"
networks:
- seafile-net
healthcheck:
test:
[
"CMD",
"/usr/local/bin/healthcheck.sh",
"--connect",
"--mariadbupgrade",
"--innodb_initialized",
]
interval: 20s
start_period: 30s
timeout: 5s
retries: 10
memcached:
image: ${SEAFILE_MEMCACHED_IMAGE:-memcached:1.6.29}
container_name: seafile-memcached
restart: unless-stopped
entrypoint: memcached -m 256
networks:
- seafile-net
elasticsearch:
image: ${SEAFILE_ELASTICSEARCH_IMAGE:-elasticsearch:8.15.0}
container_name: seafile-elasticsearch
restart: unless-stopped
environment:
- discovery.type=single-node
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Xms2g -Xmx2g"
- "xpack.security.enabled=false"
ulimits:
memlock:
soft: -1
hard: -1
mem_limit: 4g
volumes:
- "${SEAFILE_ELASTICSEARCH_VOLUME:-/opt/seafile-elasticsearch/data}:/usr/share/elasticsearch/data"
networks:
- seafile-net
seafile:
image: ${SEAFILE_IMAGE:-seafileltd/seafile-pro-mc:12.0-latest}
container_name: seafile
restart: unless-stopped
ports:
# we'll deal with you later on young jedi !
- "127.0.0.1:8000:80"
volumes:
- ${SEAFILE_VOLUME:-/opt/seafile-data}:/shared
environment:
- DB_HOST=${SEAFILE_MYSQL_DB_HOST:-db}
- DB_PORT=${SEAFILE_MYSQL_DB_PORT:-3306}
- DB_USER=${SEAFILE_MYSQL_DB_USER:-seafile}
- DB_ROOT_PASSWD=${INIT_SEAFILE_MYSQL_ROOT_PASSWORD:-}
- DB_PASSWORD=${SEAFILE_MYSQL_DB_PASSWORD:?Variable is not set or empty}
- SEAFILE_MYSQL_DB_CCNET_DB_NAME=${SEAFILE_MYSQL_DB_CCNET_DB_NAME:-ccnet_db}
- SEAFILE_MYSQL_DB_SEAFILE_DB_NAME=${SEAFILE_MYSQL_DB_SEAFILE_DB_NAME:-seafile_db}
- SEAFILE_MYSQL_DB_SEAHUB_DB_NAME=${SEAFILE_MYSQL_DB_SEAHUB_DB_NAME:-seahub_db}
- TIME_ZONE=${TIME_ZONE:-Etc/UTC}
- INIT_SEAFILE_ADMIN_EMAIL=${INIT_SEAFILE_ADMIN_EMAIL:-me@example.com}
- INIT_SEAFILE_ADMIN_PASSWORD=${INIT_SEAFILE_ADMIN_PASSWORD:-asecret}
- SEAFILE_SERVER_HOSTNAME=${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}
- SEAFILE_SERVER_PROTOCOL=${SEAFILE_SERVER_PROTOCOL:-http}
- SITE_ROOT=${SITE_ROOT:-/}
- NON_ROOT=${NON_ROOT:-false}
- JWT_PRIVATE_KEY=${JWT_PRIVATE_KEY:?Variable is not set or empty}
- SEAFILE_LOG_TO_STDOUT=${SEAFILE_LOG_TO_STDOUT:-false}
- ENABLE_SEADOC=${ENABLE_SEADOC:-true}
- SEADOC_SERVER_URL=${SEAFILE_SERVER_PROTOCOL:-http}://${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}/sdoc-server
- INIT_S3_STORAGE_BACKEND_CONFIG=${INIT_S3_STORAGE_BACKEND_CONFIG:-false}
- INIT_S3_COMMIT_BUCKET=${INIT_S3_COMMIT_BUCKET:-}
- INIT_S3_FS_BUCKET=${INIT_S3_FS_BUCKET:-}
- INIT_S3_BLOCK_BUCKET=${INIT_S3_BLOCK_BUCKET:-}
- INIT_S3_KEY_ID=${INIT_S3_KEY_ID:-}
- INIT_S3_SECRET_KEY=${INIT_S3_SECRET_KEY:-}
- INIT_S3_USE_V4_SIGNATURE=${INIT_S3_USE_V4_SIGNATURE:-true}
- INIT_S3_AWS_REGION=${INIT_S3_AWS_REGION:-us-east-1}
- INIT_S3_HOST=${INIT_S3_HOST:-}
- INIT_S3_USE_HTTPS=${INIT_S3_USE_HTTPS:-true}
# No Caddy for me Danke !
#labels:
# caddy: ${SEAFILE_SERVER_PROTOCOL:-http}://${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}
# caddy.reverse_proxy: "{{upstreams 80}}"
#depends_on:
# db:
# condition: service_healthy
# memcached:
# condition: service_started
# elasticsearch:
# condition: service_started
networks:
- seafile-net
networks:
seafile-net:
name: seafile-net
And this is the needed seadoc.yml which again, the linked setup procedure requires you to download, in which I did a few modifications where I’ve put some comments.
root@seafile:/opt/seafile# cat seadoc.yml
services:
seadoc:
image: ${SEADOC_IMAGE:-seafileltd/sdoc-server:1.0-latest}
container_name: seadoc
restart: unless-stopped
volumes:
- ${SEADOC_VOLUME:-/opt/seadoc-data/}:/shared
ports:
# we'll deal with you later !!!
- "127.0.0.1:8888:80"
environment:
- DB_HOST=${SEAFILE_MYSQL_DB_HOST:-db}
- DB_PORT=${SEAFILE_MYSQL_DB_PORT:-3306}
- DB_USER=${SEAFILE_MYSQL_DB_USER:-seafile}
- DB_PASSWORD=${SEAFILE_MYSQL_DB_PASSWORD:?Variable is not set or empty}
- DB_NAME=${SEADOC_MYSQL_DB_NAME:-seahub_db}
- TIME_ZONE=${TIME_ZONE:-Etc/UTC}
- JWT_PRIVATE_KEY=${JWT_PRIVATE_KEY:?Variable is not set or empty}
- NON_ROOT=${NON_ROOT:-false}
- SEAHUB_SERVICE_URL=${SEAFILE_SERVICE_URL:-http://seafile}
# No Cadddy needed, obrigado!
#labels:
# caddy: ${SEAFILE_SERVER_PROTOCOL:-http}://${SEAFILE_SERVER_HOSTNAME:?Variable is not set or empty}
# caddy.@ws.0_header: "Connection *Upgrade*"
# caddy.@ws.1_header: "Upgrade websocket"
# caddy.0_reverse_proxy: "@ws {{upstreams 80}}"
# caddy.1_handle_path: "/socket.io/*"
# caddy.1_handle_path.0_rewrite: "* /socket.io{uri}"
# caddy.1_handle_path.1_reverse_proxy: "{{upstreams 80}}"
# caddy.2_handle_path: "/sdoc-server/*"
# caddy.2_handle_path.0_rewrite: "* {uri}"
# caddy.2_handle_path.1_reverse_proxy: "{{upstreams 80}}"
#depends_on:
# db:
# condition: service_healthy
networks:
- seafile-net
networks:
seafile-net:
name: seafile-net
And finally, here is the .env needed file as well (note: the caddy.yml file isn’t called, every references to caddy are commented, the requested SeaFile protocol is set to https / credentials have been removed):
COMPOSE_FILE='seafile-server.yml,seadoc.yml'
COMPOSE_PATH_SEPARATOR=','
# Seafile & Basic services
## Images
SEAFILE_IMAGE=seafileltd/seafile-pro-mc:12.0-latest
SEAFILE_DB_IMAGE=mariadb:10.11
SEAFILE_MEMCACHED_IMAGE=memcached:1.6.29
SEAFILE_ELASTICSEARCH_IMAGE=elasticsearch:8.15.0
# no caddy
#SEAFILE_CADDY_IMAGE=lucaslorentz/caddy-docker-proxy:2.9-alpine
## Persistent Storage
SEAFILE_VOLUME=/opt/seafile-data
SEAFILE_MYSQL_VOLUME=/opt/seafile-mysql/db
SEAFILE_ELASTICSEARCH_VOLUME=/opt/seafile-elasticsearch/data
# no caddy
#SEAFILE_CADDY_VOLUME=/opt/seafile-caddy
## Database
SEAFILE_MYSQL_DB_HOST=db
SEAFILE_MYSQL_DB_USER=xx
SEAFILE_MYSQL_DB_PASSWORD=xx
## Scheme
SEAFILE_SERVER_HOSTNAME=your.fqdn.suffix
SEAFILE_SERVER_PROTOCOL=https
## Startup parameters
TIME_ZONE=Etc/UTC
JWT_PRIVATE_KEY=xx
## Initial variables (can be removed after firstime startup)
### Database root password
INIT_SEAFILE_MYSQL_ROOT_PASSWORD=xx
### Seafile admin user
INIT_SEAFILE_ADMIN_EMAIL=xx
INIT_SEAFILE_ADMIN_PASSWORD=xx
### S3
INIT_S3_STORAGE_BACKEND_CONFIG=false
INIT_S3_COMMIT_BUCKET=<your-commit-objects>
INIT_S3_FS_BUCKET=<your-fs-objects>
INIT_S3_BLOCK_BUCKET=<your-block-objects>
INIT_S3_KEY_ID=<your-key-id>
INIT_S3_SECRET_KEY=<your-secret-key>
INIT_S3_USE_V4_SIGNATURE=true
INIT_S3_AWS_REGION=us-east-1
INIT_S3_HOST=
INIT_S3_USE_HTTPS=true
# SeaDoc service
ENABLE_SEADOC=true
SEADOC_IMAGE=seafileltd/sdoc-server:1.0-latest
SEADOC_VOLUME=/opt/seadoc-data
# Notification server
NOTIFICATION_SERVER_IMAGE=seafileltd/notification-server:12.0-latest
NOTIFICATION_SERVER_VOLUME=/opt/notification-data
# SeaSearch
## Image
#SEASEARCH_IMAGE=seafileltd/seasearch-nomkl:0.9-latest # Apple's Chip
SEASEARCH_IMAGE=seafileltd/seasearch:0.9-latest
## Storage
SS_STORAGE_TYPE=disk # disk (local storage), s3, oss
### Local storage mode
SS_DATA_PATH=/opt/seasearch-data # Persistent storage path
SS_MAX_OBJ_CACHE_SIZE=10GB
### S3 mode
SS_S3_USE_V4_SIGNATURE=false
SS_S3_ACCESS_ID=<your access id>
SS_S3_ACCESS_SECRET=<your access secret>
SS_S3_ENDPOINT=
SS_S3_BUCKET=<your bucket name>
SS_S3_USE_HTTPS=true
SS_S3_PATH_STYLE_REQUEST=true
SS_S3_AWS_REGION=us-east-1
SS_S3_SSE_C_KEY=<your SSE-C key>
## Log
SS_LOG_TO_STDOUT=false
SS_LOG_OUTPUT=true
SS_LOG_LEVEL=info
## Initial variables (can be removed after firstime startup SeaSearch service)
INIT_SS_ADMIN_USER=xx
INIT_SS_ADMIN_PASSWORD=xx
Installing a host based nginx reverse-proxy#
Once there, I then wanted to host an nginx reverse-proxy directly on the SeaFile host itself, that is, out of the Docker context. There is a SeaFile official documentation for using other reverse-proxy – here is what I’ve done (mind your own server_name…):
root@seafile:~# apt install nginx
root@seafile:~# mv /etc/nginx/sites-available/default /etc/nginx/sites-available/default.org
root@seafile:~#
root@seafile:~#
root@seafile:~#
root@seafile:~# /bin/bin/cat << 'EOF' > /etc/nginx/sites-available/default
log_format seafileformat '$http_x_forwarded_for $remote_addr [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $upstream_response_time';
proxy_request_buffering off;
client_body_buffer_size 70m;
ssl_buffer_size 70m;
client_header_buffer_size 50k;
large_client_header_buffers 2 50k;
client_max_body_size 0;
server {
listen 80;
server_name your.fqdn.suffix;
rewrite ^ https://$http_host$request_uri? permanent; # Forced redirect from HTTP to HTTPS
server_tokens off; # Prevents the Nginx version from being displayed in the HTTP response header
}
server {
listen 443 ssl;
ssl_certificate /etc/ssl.custom/certs/cert.pem; # Path to your fullchain.pem
ssl_certificate_key /etc/ssl.custom/private/key.pem; # Path to your privkey.pem
server_name your.fqdn.suffix;
server_tokens off;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Ssl off;
proxy_set_header X-Forwarded-Proto http;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_read_timeout 1200s;
# used for view/edit office file via Office Online Server
client_max_body_size 0;
access_log /var/log/nginx/seahub.access.log seafileformat;
error_log /var/log/nginx/seahub.error.log;
}
location /sdoc-server/ {
proxy_pass http://127.0.0.1:8888/;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
client_max_body_size 100m;
}
location /socket.io {
proxy_pass http://127.0.0.1:8888/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_redirect off;
proxy_buffers 8 32k;
proxy_buffer_size 64k;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
}
}
EOF
root@seafile:~#
root@seafile:~# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
root@seafile:~#
root@seafile:~# systemctl enable nginx.service
root@seafile:~# systemctl restart nginx.service
root@seafile:~#
And here are a few details of the subsystem status after the above modifications:
root@seafile:~# docker container list --all
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a724877f61f7 seafileltd/sdoc-server:1.0-latest "/sbin/my_init -- /s…" 16 hours ago Up 16 hours 127.0.0.1:8888->80/tcp seadoc
44b7e32fc74c mariadb:10.11 "docker-entrypoint.s…" 16 hours ago Up 16 hours (healthy) 3306/tcp seafile-mysql
f69b9e660642 seafileltd/seafile-pro-mc:12.0-latest "/sbin/my_init -- /s…" 16 hours ago Up 16 hours 127.0.0.1:8000->80/tcp seafile
50d185e1d452 elasticsearch:8.15.0 "/bin/tini -- /usr/l…" 16 hours ago Up 16 hours 9200/tcp, 9300/tcp seafile-elasticsearch
16aa9208b516 memcached:1.6.29 "memcached -m 256" 16 hours ago Up 16 hours 11211/tcp seafile-memcached
root@seafile:~#
root@seafile:~# ss -utnlp
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
...
tcp LISTEN 0 4096 127.0.0.1:8888 0.0.0.0:* users:(("docker-proxy",pid=867689,fd=7))
tcp LISTEN 0 4096 127.0.0.1:8000 0.0.0.0:* users:(("docker-proxy",pid=867645,fd=7))
tcp LISTEN 0 511 0.0.0.0:443 0.0.0.0:* users:(("nginx",pid=832947,fd=8))
tcp LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=832947,fd=7))
...
root@seafile:~#
root@seafile:~#
root@seafile:~#
With this setup I can now tranquillo engage toward my SeaFile host from my publicly reachable reverse-proxy, which is typically where I’d do all my filtering, bad behaviors checks, Geo Location policies etc etc..
Hope this helps.
obruno