mirror of
https://github.com/ggogel/seafile-containerized.git
synced 2024-11-16 17:05:32 +00:00
Support https with letsencrypt.
TODO: add a cron job to renew the certs
This commit is contained in:
parent
fece17830b
commit
2c5dace56e
5
.dockerignore
Normal file
5
.dockerignore
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
*~
|
||||||
|
*#
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
*.pyc
|
|
@ -9,3 +9,4 @@ click==6.6
|
||||||
termcolor==1.1.0
|
termcolor==1.1.0
|
||||||
prettytable==0.7.2
|
prettytable==0.7.2
|
||||||
colorlog==2.7.0
|
colorlog==2.7.0
|
||||||
|
Jinja2==2.8
|
||||||
|
|
|
@ -6,12 +6,10 @@
|
||||||
#
|
#
|
||||||
click==6.6
|
click==6.6
|
||||||
colorlog==2.7.0
|
colorlog==2.7.0
|
||||||
|
Jinja2==2.8
|
||||||
|
MarkupSafe==0.23 # via jinja2
|
||||||
prettytable==0.7.2
|
prettytable==0.7.2
|
||||||
python-memcached==1.58
|
python-memcached==1.58
|
||||||
six==1.10.0 # via python-memcached
|
six==1.10.0 # via python-memcached
|
||||||
termcolor==1.1.0
|
termcolor==1.1.0
|
||||||
urllib3==1.19
|
urllib3==1.19
|
||||||
|
|
||||||
# The following packages are commented out because they are
|
|
||||||
# considered to be unsafe in a requirements file:
|
|
||||||
# setuptools # via python-ldap
|
|
||||||
|
|
|
@ -10,4 +10,5 @@ RUN mkdir -p /opt/seafile/ && \
|
||||||
RUN mkdir -p /etc/my_init.d
|
RUN mkdir -p /etc/my_init.d
|
||||||
ADD create_data_links.sh /etc/my_init.d/create_data_links.sh
|
ADD create_data_links.sh /etc/my_init.d/create_data_links.sh
|
||||||
|
|
||||||
ADD seafile.nginx.conf /etc/nginx/sites-enabled/seafile.nginx.conf
|
RUN mkdir -p /templates/
|
||||||
|
ADD seafile.nginx.conf /templates/seafile.nginx.conf
|
||||||
|
|
|
@ -1,6 +1,30 @@
|
||||||
|
{% if https -%}
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name _ default_server;
|
server_name _ default_server;
|
||||||
|
rewrite ^ https://{{ domain }}$request_uri? permanent;
|
||||||
|
}
|
||||||
|
{% endif -%}
|
||||||
|
|
||||||
|
server {
|
||||||
|
{% if https -%}
|
||||||
|
listen 443;
|
||||||
|
ssl on;
|
||||||
|
ssl_certificate /shared/ssl/{{ domain }}.crt;
|
||||||
|
ssl_certificate_key /shared/ssl/{{ domain }}.key;
|
||||||
|
|
||||||
|
ssl_ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS;
|
||||||
|
|
||||||
|
# TODO: More SSL security hardening: ssl_session_tickets & ssl_dhparam
|
||||||
|
# ssl_session_tickets on;
|
||||||
|
# ssl_session_ticket_key /etc/nginx/sessionticket.key;
|
||||||
|
# ssl_session_cache shared:SSL:10m;
|
||||||
|
# ssl_session_timeout 10m;
|
||||||
|
{% else -%}
|
||||||
|
listen 80;
|
||||||
|
{% endif -%}
|
||||||
|
|
||||||
|
server_name {{ domain }};
|
||||||
|
|
||||||
client_max_body_size 10m;
|
client_max_body_size 10m;
|
||||||
|
|
||||||
|
@ -47,4 +71,10 @@ server {
|
||||||
root /opt/seafile/seafile-server-latest/seahub;
|
root /opt/seafile/seafile-server-latest/seahub;
|
||||||
autoindex off;
|
autoindex off;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# For letsencrypt
|
||||||
|
location /.well-known/acme-challenge/ {
|
||||||
|
alias /var/www/challenges/;
|
||||||
|
try_files $uri =404;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
13
launcher
13
launcher
|
@ -35,6 +35,11 @@ init_shared() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set_ports() {
|
||||||
|
ports="-p 80:80 -p 443:443"
|
||||||
|
ports=""
|
||||||
|
}
|
||||||
|
|
||||||
set_volumes() {
|
set_volumes() {
|
||||||
local mounts
|
local mounts
|
||||||
init_shared
|
init_shared
|
||||||
|
@ -61,12 +66,14 @@ bootstrap() {
|
||||||
err_and_quit "The file $bootstrap_conf doesn't exist. Have you run seafile-server-setup?"
|
err_and_quit "The file $bootstrap_conf doesn't exist. Have you run seafile-server-setup?"
|
||||||
fi
|
fi
|
||||||
set_volumes
|
set_volumes
|
||||||
docker run --rm -it -e SEAFILE_BOOTSRAP=1 $volumes $image /sbin/my_init -- /scripts/bootstrap.py
|
set_ports
|
||||||
|
docker run --rm -it --name seafile-bootstrap -e SEAFILE_BOOTSRAP=1 $volumes $ports $image /sbin/my_init -- /scripts/bootstrap.py
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
set_volumes
|
set_volumes
|
||||||
docker run --rm -it --name seafile $volumes $image \
|
set_ports
|
||||||
|
docker run --rm -it --name seafile $volumes $ports $image \
|
||||||
/sbin/my_init -- /scripts/start.py
|
/sbin/my_init -- /scripts/start.py
|
||||||
# /sbin/my_init -- bash -l
|
# /sbin/my_init -- bash -l
|
||||||
}
|
}
|
||||||
|
@ -75,7 +82,7 @@ enter() {
|
||||||
err_and_quit "Not implemented yet"
|
err_and_quit "Not implemented yet"
|
||||||
}
|
}
|
||||||
|
|
||||||
function main {
|
main() {
|
||||||
local action
|
local action
|
||||||
while [[ $# -gt 0 ]]
|
while [[ $# -gt 0 ]]
|
||||||
do
|
do
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
[server]
|
[server]
|
||||||
server.hostname = seafile.example.com
|
server.hostname = seafile.example.com
|
||||||
|
server.https = true
|
||||||
admin.email = me@example.com
|
admin.email = me@example.com
|
||||||
admin.password = asecret
|
admin.password = asecret
|
|
@ -13,15 +13,34 @@ import shutil
|
||||||
import sys
|
import sys
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from utils import call, get_conf, get_install_dir, get_script
|
from utils import call, get_conf, get_install_dir, get_script, render_nginx_conf
|
||||||
|
|
||||||
installdir = get_install_dir()
|
installdir = get_install_dir()
|
||||||
topdir = dirname(installdir)
|
topdir = dirname(installdir)
|
||||||
shared_seafiledir = '/shared/seafile'
|
shared_seafiledir = '/shared/seafile'
|
||||||
|
ssl_dir = '/shared/ssl'
|
||||||
|
|
||||||
|
def init_letsencryt():
|
||||||
|
if not exists(ssl_dir):
|
||||||
|
os.mkdir(ssl_dir)
|
||||||
|
|
||||||
|
domain = get_conf('server.hostname')
|
||||||
|
context = {
|
||||||
|
'https': False,
|
||||||
|
'domain': domain,
|
||||||
|
}
|
||||||
|
render_nginx_conf('/templates/seafile.nginx.conf',
|
||||||
|
'/etc/nginx/sites-enabled/seafile.nginx.conf', context)
|
||||||
|
call('nginx -s reload')
|
||||||
|
call('/scripts/ssl.sh {0} {1}'.format(ssl_dir, domain))
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
if not exists(shared_seafiledir):
|
if not exists(shared_seafiledir):
|
||||||
os.mkdir(shared_seafiledir)
|
os.mkdir(shared_seafiledir)
|
||||||
|
|
||||||
|
if get_conf('server.https', '').lower() == 'true':
|
||||||
|
init_letsencryt()
|
||||||
|
|
||||||
env = {
|
env = {
|
||||||
'SERVER_NAME': 'seafile',
|
'SERVER_NAME': 'seafile',
|
||||||
'SERVER_IP': get_conf('server.hostname'),
|
'SERVER_IP': get_conf('server.hostname'),
|
||||||
|
@ -34,7 +53,7 @@ def main():
|
||||||
|
|
||||||
# Change the script to allow mysql root password to be empty
|
# Change the script to allow mysql root password to be empty
|
||||||
call('''sed -i -e 's/if not mysql_root_passwd/if not mysql_root_passwd and "MYSQL_ROOT_PASSWD" not in os.environ/g' {}'''
|
call('''sed -i -e 's/if not mysql_root_passwd/if not mysql_root_passwd and "MYSQL_ROOT_PASSWD" not in os.environ/g' {}'''
|
||||||
.format(get_script('setup-seafile-mysql.py')), check_call=True)
|
.format(get_script('setup-seafile-mysql.py')))
|
||||||
|
|
||||||
setup_script = get_script('setup-seafile-mysql.sh')
|
setup_script = get_script('setup-seafile-mysql.sh')
|
||||||
call('{} auto -n seafile'.format(setup_script), env=env)
|
call('{} auto -n seafile'.format(setup_script), env=env)
|
||||||
|
|
44
scripts/ssl.sh
Executable file
44
scripts/ssl.sh
Executable file
|
@ -0,0 +1,44 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
ssldir=${1:?"error params"}
|
||||||
|
domain=${2:?"error params"}
|
||||||
|
|
||||||
|
letsencryptdir=$ssldir/letsencrypt
|
||||||
|
letsencrypt_script=$letsencryptdir/acme_tiny.py
|
||||||
|
|
||||||
|
ssl_account_key=${domain}.account.key
|
||||||
|
ssl_csr=${domain}.csr
|
||||||
|
ssl_key=${domain}.key
|
||||||
|
ssl_crt=${domain}.crt
|
||||||
|
|
||||||
|
mkdir -p /var/www/challenges && chmod -R 777 /var/www/challenges
|
||||||
|
mkdir -p ssldir
|
||||||
|
|
||||||
|
if ! [[ -d $letsencryptdir ]]; then
|
||||||
|
git clone git://github.com/diafygi/acme-tiny.git $letsencryptdir
|
||||||
|
else
|
||||||
|
cd $letsencryptdir
|
||||||
|
git pull origin master:master
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd $ssldir
|
||||||
|
|
||||||
|
if [[ ! -e ${ssl_account_key} ]]; then
|
||||||
|
openssl genrsa 4096 > ${ssl_account_key}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -e ${ssl_key} ]]; then
|
||||||
|
openssl genrsa 4096 > ${ssl_key}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -e ${ssl_csr} ]]; then
|
||||||
|
openssl req -new -sha256 -key ${ssl_key} -subj "/CN=$domain" > $ssl_csr
|
||||||
|
fi
|
||||||
|
|
||||||
|
python $letsencrypt_script --account-key ${ssl_account_key} --csr $ssl_csr --acme-dir /var/www/challenges/ > ./signed.crt
|
||||||
|
curl -sSL -o intermediate.pem https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem
|
||||||
|
cat signed.crt intermediate.pem > ${ssl_crt}
|
||||||
|
|
||||||
|
nginx -s reload
|
|
@ -14,7 +14,7 @@ import shutil
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from utils import call, get_conf, get_install_dir, get_script, get_command_output
|
from utils import call, get_conf, get_install_dir, get_script, get_command_output, render_nginx_conf
|
||||||
|
|
||||||
installdir = get_install_dir()
|
installdir = get_install_dir()
|
||||||
topdir = dirname(installdir)
|
topdir = dirname(installdir)
|
||||||
|
@ -30,7 +30,18 @@ def watch_controller():
|
||||||
print 'seafile controller exited unexpectedly.'
|
print 'seafile controller exited unexpectedly.'
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
def init_https():
|
||||||
|
domain = get_conf('server.hostname')
|
||||||
|
context = {
|
||||||
|
'https': True,
|
||||||
|
'domain': domain,
|
||||||
|
}
|
||||||
|
render_nginx_conf('/templates/seafile.nginx.conf',
|
||||||
|
'/etc/nginx/sites-enabled/seafile.nginx.conf', context)
|
||||||
|
call('nginx -t && nginx -s reload')
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
init_https()
|
||||||
admin_pw = {
|
admin_pw = {
|
||||||
'email': get_conf('admin.email'),
|
'email': get_conf('admin.email'),
|
||||||
'password': get_conf('admin.password'),
|
'password': get_conf('admin.password'),
|
||||||
|
@ -39,9 +50,13 @@ def main():
|
||||||
with open(password_file, 'w') as fp:
|
with open(password_file, 'w') as fp:
|
||||||
json.dump(admin_pw, fp)
|
json.dump(admin_pw, fp)
|
||||||
|
|
||||||
|
while not exists('/var/run/mysqld/mysqld.sock'):
|
||||||
|
time.sleep(1)
|
||||||
|
print 'mysql server is ready'
|
||||||
|
|
||||||
try:
|
try:
|
||||||
call('{} start'.format(get_script('seafile.sh')), check_call=True)
|
call('{} start'.format(get_script('seafile.sh')))
|
||||||
call('{} start'.format(get_script('seahub.sh')), check_call=True)
|
call('{} start'.format(get_script('seahub.sh')))
|
||||||
finally:
|
finally:
|
||||||
if exists(password_file):
|
if exists(password_file):
|
||||||
os.unlink(password_file)
|
os.unlink(password_file)
|
||||||
|
|
|
@ -48,7 +48,7 @@ def call(*a, **kw):
|
||||||
dry_run = kw.pop('dry_run', False)
|
dry_run = kw.pop('dry_run', False)
|
||||||
quiet = kw.pop('quiet', False)
|
quiet = kw.pop('quiet', False)
|
||||||
cwd = kw.get('cwd', os.getcwd())
|
cwd = kw.get('cwd', os.getcwd())
|
||||||
check_call = kw.pop('check_call', False)
|
check_call = kw.pop('check_call', True)
|
||||||
reduct_args = kw.pop('reduct_args', [])
|
reduct_args = kw.pop('reduct_args', [])
|
||||||
if not quiet:
|
if not quiet:
|
||||||
toprint = a[0]
|
toprint = a[0]
|
||||||
|
@ -220,3 +220,10 @@ def get_conf(key, default=None):
|
||||||
_config.read("/bootstrap/bootstrap.conf")
|
_config.read("/bootstrap/bootstrap.conf")
|
||||||
return _config.get("server", key) if _config.has_option("server", key) \
|
return _config.get("server", key) if _config.has_option("server", key) \
|
||||||
else default
|
else default
|
||||||
|
|
||||||
|
def render_nginx_conf(template, target, context):
|
||||||
|
from jinja2 import Environment, FileSystemLoader
|
||||||
|
env = Environment(loader=FileSystemLoader(dirname(template)))
|
||||||
|
content = env.get_template(basename(template)).render(**context)
|
||||||
|
with open(target, 'w') as fp:
|
||||||
|
fp.write(content)
|
||||||
|
|
Loading…
Reference in a new issue