mirror of
https://git.unistra.fr/aius/root/ldap-overleaf-sl.git
synced 2025-05-04 11:45:26 +02:00
Switch from certbot to traefik
This commit is contained in:
parent
9344017322
commit
ca58b4852a
8 changed files with 220 additions and 71 deletions
21
README.md
21
README.md
|
@ -31,7 +31,7 @@ Sharelatex/Overleaf uses the email address to identify users: If you change the
|
|||
in the mongo db.
|
||||
|
||||
```
|
||||
docker exec -it mongo
|
||||
docker exec -it mongo /bin/bash
|
||||
mongo
|
||||
use sharelatex
|
||||
db.users.find({email:"EMAIL"}).pretty()
|
||||
|
@ -56,7 +56,8 @@ MYDATA=/data
|
|||
- sharelatex: all projects, tmp files, user files templates and ...
|
||||
- letsencrypt: https certificates
|
||||
|
||||
*MYDOMAIN* is the FQDN for sharelatex and certbot (letsencrypt) <br/>
|
||||
*MYDOMAIN* is the FQDN for sharelatex and traefik (letsencrypt) <br/>
|
||||
*MYDOMAIN*:8443 Traefik Dashboard - Login uses traefik/user.htpasswd : user:admin pass:adminPass change this (e.g. generate a password with htpasswd)
|
||||
*MYMAIL* is the admin mailaddress
|
||||
|
||||
```
|
||||
|
@ -125,21 +126,15 @@ make
|
|||
```
|
||||
to generate the ldap-overleaf-sl docker image.
|
||||
|
||||
Then start docker containers:
|
||||
Then start docker containers (with loadbalancer):
|
||||
```
|
||||
docker-compose up -d
|
||||
export NUMINSTANCES=1
|
||||
docker-compose up -d --scale sharelaatex=NUMINSTANCES
|
||||
```
|
||||
|
||||
*Known Issue:*
|
||||
During the first startup the certbot image will get an initial certificate - if that
|
||||
happens not in a very timely manner sharelatex will fail to start (due to the missing certificates
|
||||
nginx crashes). Solution: wait 10 seconds and restart the sharelatex container.
|
||||
- Works up to sharelatex 2.3.1. After that at least issue #5 is introduced.
|
||||
|
||||
|
||||
```
|
||||
docker stop ldap-overleaf-sl
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
After the inital startup and certificate configuration you can reconfigure the
|
||||
docker-compose.yml that port 80 points to the Sharelatex/Overleaf instance.
|
||||
|
||||
|
|
|
@ -1,27 +1,108 @@
|
|||
version: '2.2'
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:latest
|
||||
container_name: traefik
|
||||
restart: unless-stopped
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
networks:
|
||||
- web
|
||||
ports:
|
||||
- 80:80
|
||||
- 443:443
|
||||
- 8443:8443
|
||||
# - 8080:8080
|
||||
# - 27017:27017
|
||||
volumes:
|
||||
- ${MYDATA}/letsencrypt:/letsencrypt
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- ./traefik/dynamic_conf.yml:/dynamic_conf.yml
|
||||
- ./traefik/users.htpasswd:/users.htpasswd
|
||||
|
||||
command:
|
||||
- "--api=true"
|
||||
- "--api.dashboard=true"
|
||||
#- "--api.insecure=true" # provides the dashboard on http://IPADRESS:8080
|
||||
- "--providers.docker=true"
|
||||
- "--ping"
|
||||
- "--providers.docker.network=web"
|
||||
- "--providers.docker.exposedbydefault=false"
|
||||
- "--entrypoints.web.address=:80"
|
||||
- "--entrypoints.web-secure.address=:443"
|
||||
- "--entrypoints.web-admin.address=:8443"
|
||||
- "--certificatesresolvers.myhttpchallenge.acme.httpchallenge=true"
|
||||
- "--certificatesresolvers.myhttpchallenge.acme.httpchallenge.entrypoint=web"
|
||||
- "--certificatesresolvers.myhttpchallenge.acme.email=${MYMAIL}"
|
||||
- "--certificatesresolvers.myhttpchallenge.acme.storage=/letsencrypt/acme.json"
|
||||
- "--entrypoints.mongo.address=:27017"
|
||||
#- --certificatesresolvers.myhttpchallenge.acme.caserver=https://acme-v02.api.letsencrypt.org/directory
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
# To Fix enable dashboard on port 8443
|
||||
- "traefik.http.routers.dashboard.entrypoints=web-admin"
|
||||
- "traefik.http.routers.dashboard.rule=Host(`${MYDOMAIN}`)"
|
||||
# - "traefik.http.routers.dashboard.rule=Host(`traefik.${MYDOMAIN}`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))"
|
||||
- "traefik.http.routers.dashboard.tls=true"
|
||||
- "traefik.http.routers.dashboard.middlewares=auth"
|
||||
- "traefik.http.middlewares.auth.basicauth.usersfile=/users.htpasswd"
|
||||
- "traefik.http.routers.dashboard.service=api@internal"
|
||||
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "1"
|
||||
|
||||
sharelatex:
|
||||
restart: always
|
||||
image: ldap-overleaf-sl:latest
|
||||
container_name: ldap-overleaf-sl
|
||||
depends_on:
|
||||
mongo:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
simple-certbot:
|
||||
traefik:
|
||||
condition: service_started
|
||||
#simple-certbot:
|
||||
# condition: service_started
|
||||
privileged: false
|
||||
ports:
|
||||
- 443:443
|
||||
networks:
|
||||
- web
|
||||
expose:
|
||||
- 80
|
||||
- 443
|
||||
links:
|
||||
- mongo
|
||||
- redis
|
||||
- simple-certbot
|
||||
#- simple-certbot
|
||||
volumes:
|
||||
- ${MYDATA}/sharelatex:/var/lib/sharelatex
|
||||
- ${MYDATA}/letsencrypt:/etc/letsencrypt
|
||||
- ${MYDATA}/letsencrypt/live/${MYDOMAIN}/:/etc/letsencrypt/certs/domain
|
||||
- ${MYDATA}/letsencrypt:/etc/letsencrypt:ro
|
||||
# - ${MYDATA}/letsencrypt/live/${MYDOMAIN}/:/etc/letsencrypt/certs/domain
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.tex.entrypoints=web"
|
||||
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
|
||||
- "traefik.http.routers.sharel.middlewares=redirect-to-https@docker"
|
||||
- "traefik.http.routers.sharel-secured.rule=Host(`${MYDOMAIN}`)"
|
||||
- "traefik.http.routers.sharel-secured.tls=true"
|
||||
- "traefik.http.routers.sharel-secured.tls.certresolver=myhttpchallenge"
|
||||
- "traefik.http.routers.sharel-secured.entrypoints=web-secure"
|
||||
- "traefik.http.routers.proxy-https.entrypoints=web-secure"
|
||||
- "traefik.http.routers.proxy-https.rule=Host(`${MYDOMAIN}`)"
|
||||
- "traefik.http.services.sharel.loadbalancer.server.port=80"
|
||||
- "traefik.http.services.sharel.loadbalancer.server.scheme=http"
|
||||
# ToDo - internally connect via https: reuse the certifiacte from traefik (acme.json)
|
||||
#- "traefik.http.services.sharel.loadbalancer.server.port=443"
|
||||
#- "traefik.http.services.sharel.loadbalancer.server.scheme=https"
|
||||
- "traefik.http.services.sharel.loadbalancer.sticky.cookie=true"
|
||||
- "traefik.http.services.sharel.loadbalancer.sticky.cookie.name=io"
|
||||
- "traefik.http.services.sharel.loadbalancer.sticky.cookie.httponly=true"
|
||||
- "traefik.http.services.sharel.loadbalancer.sticky.cookie.secure=true"
|
||||
- "traefik.http.services.sharel.loadbalancer.sticky.cookie.samesite=io"
|
||||
|
||||
environment:
|
||||
SHARELATEX_APP_NAME: Overleaf
|
||||
SHARELATEX_MONGO_URL: mongodb://mongo/sharelatex
|
||||
|
@ -32,8 +113,6 @@ services:
|
|||
SHARELATEX_LEFT_FOOTER: '[{"text": "Powered by <a href=\"https://www.sharelatex.com\">ShareLaTeX</a> 2016"} ]'
|
||||
SHARELATEX_RIGHT_FOOTER: '[{"text": "LDAP Overleaf (beta)"} ]'
|
||||
SHARELATEX_EMAIL_FROM_ADDRESS: "noreply@${MYDOMAIN}"
|
||||
# SHARELATEX_EMAIL_AWS_SES_ACCESS_KEY_ID:
|
||||
# SHARELATEX_EMAIL_AWS_SES_SECRET_KEY:
|
||||
SHARELATEX_EMAIL_SMTP_HOST: smtp.${MYDOMAIN}
|
||||
SHARELATEX_EMAIL_SMTP_PORT: 587
|
||||
SHARELATEX_EMAIL_SMTP_SECURE: 'false'
|
||||
|
@ -43,18 +122,31 @@ services:
|
|||
# SHARELATEX_EMAIL_SMTP_IGNORE_TLS: false
|
||||
SHARELATEX_CUSTOM_EMAIL_FOOTER: "This system is run by ${MYDOMAIN} - please contact ${MYMAIL} if you experience any issues."
|
||||
|
||||
# make public links accessible w/o login (link sharing issue)
|
||||
# https://github.com/overleaf/docker-image/issues/66
|
||||
# https://github.com/overleaf/overleaf/issues/628
|
||||
# https://github.com/overleaf/web/issues/367
|
||||
# Fixed in 2.0.2 (Release date: 2019-11-26)
|
||||
SHARELATEX_ALLOW_PUBLIC_ACCESS: 'true'
|
||||
SHARELATEX_ALLOW_ANONYMOUS_READ_AND_WRITE_SHARING: 'true'
|
||||
|
||||
SHARELATEX_SECURE_COOKIE: 'true'
|
||||
SHARELATEX_BEHIND_PROXY: 'true'
|
||||
|
||||
LDAP_SERVER: ldaps://LDAPSERVER:636
|
||||
LDAP_BASE: ou=people,dc=DOMAIN,dc=TLD
|
||||
LDAP_BINDDN: ou=someunit,ou=people,dc=DOMAIN,dc=TLS
|
||||
|
||||
# By default tries to bind directly with the ldap user - this user has to be in the LDAP GROUP
|
||||
# LDAP_GROUP_FILTER: '(memberof=cn=GROUPNAME,ou=groups,dc=DOMAIN,dc=TLD)'
|
||||
LDAP_GROUP_FILTER: '(memberof=cn=GROUPNAME,ou=groups,dc=DOMAIN,dc=TLD)'
|
||||
|
||||
# If user is in ADMIN_GROUP on user creation (first login) isAdmin is set to true.
|
||||
# Admin Users can invite external (non ldap) users. This feature makes only sense
|
||||
# when ALLOW_EMAIL_LOGIN is set to 'true'. Additionally admins can send
|
||||
# system wide messages.
|
||||
#LDAP_ADMIN_GROUP_FILTER: '(memberof=cn=ADMINGROUPNAME,ou=groups,dc=DOMAIN,dc=TLD)'
|
||||
ALLOW_EMAIL_LOGIN: 'false'
|
||||
LDAP_ADMIN_GROUP_FILTER: '(memberof=cn=ADMINGROUPNAME,ou=groups,dc=DOMAIN,dc=TLD)'
|
||||
ALLOW_EMAIL_LOGIN: 'true'
|
||||
|
||||
# All users in the LDAP_GROUP_FILTER are loaded from the ldap server into contacts.
|
||||
# This LDAP search happens without bind. If you want this and your LDAP needs a bind you can
|
||||
|
@ -76,7 +168,7 @@ services:
|
|||
restart: always
|
||||
image: mongo
|
||||
container_name: mongo
|
||||
ports:
|
||||
expose:
|
||||
- 27017
|
||||
volumes:
|
||||
- ${MYDATA}/mongo_data:/data/db
|
||||
|
@ -85,6 +177,14 @@ services:
|
|||
interval: 10s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.tcp.routers.mongodb.rule=HostSNI(`*`)"
|
||||
- "traefik.tcp.services.mongodb.loadbalancer.server.port=27017"
|
||||
- "traefik.tcp.routers.mongodb.tls=true"
|
||||
- "traefik.tcp.routers.mongodb.entrypoints=mongo"
|
||||
networks:
|
||||
- web
|
||||
|
||||
redis:
|
||||
restart: always
|
||||
|
@ -98,7 +198,7 @@ services:
|
|||
sysctls:
|
||||
- net.core.somaxconn=65535
|
||||
# - vm.overcommit_memory=1
|
||||
ports:
|
||||
expose:
|
||||
- 6379
|
||||
volumes:
|
||||
- ${MYDATA}/redis_data:/data
|
||||
|
@ -107,23 +207,30 @@ services:
|
|||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
networks:
|
||||
- web
|
||||
|
||||
|
||||
simple-certbot:
|
||||
restart: always
|
||||
image: certbot/certbot
|
||||
container_name: simple-certbot
|
||||
ports:
|
||||
- 80:80
|
||||
volumes:
|
||||
- ${MYDATA}/letsencrypt:/etc/letsencrypt
|
||||
# a bit hacky but this docker image uses very little disk-space
|
||||
# best practices for ssl and nginx are set in the ldap-overleaf-sl Dockerfile
|
||||
entrypoint:
|
||||
- "/bin/sh"
|
||||
- -c
|
||||
- |
|
||||
trap exit TERM;\
|
||||
certbot certonly --standalone -d ${MYDOMAIN} --agree-tos -m ${MYMAIL} -n ; \
|
||||
while :; do certbot renew; sleep 240h & wait $${!}; done;
|
||||
# simple-certbot:
|
||||
# restart: always
|
||||
# image: certbot/certbot
|
||||
# container_name: simple-certbot
|
||||
# ports:
|
||||
# - 80:80
|
||||
# volumes:
|
||||
# - ${MYDATA}/letsencrypt:/etc/letsencrypt
|
||||
# # a bit hacky but this docker image uses very little disk-space
|
||||
# # best practices for ssl and nginx are set in the ldap-overleaf-sl Dockerfile
|
||||
# entrypoint:
|
||||
# - "/bin/sh"
|
||||
# - -c
|
||||
# - |
|
||||
# trap exit TERM;\
|
||||
# certbot certonly --standalone -d ${MYDOMAIN} --agree-tos -m ${MYMAIL} -n ; \
|
||||
# while :; do certbot renew; sleep 240h & wait $${!}; done;
|
||||
#
|
||||
|
||||
networks:
|
||||
web:
|
||||
external: true
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM sharelatex/sharelatex:latest
|
||||
FROM sharelatex/sharelatex:2.3.1
|
||||
LABEL maintainer="Simon Haller-Seeber"
|
||||
LABEL version="0.1"
|
||||
|
||||
|
@ -7,14 +7,15 @@ ARG collab_text
|
|||
ARG login_text
|
||||
|
||||
# set workdir (might solve issue #2 - see https://stackoverflow.com/questions/57534295/)
|
||||
WORKDIR /var/www/sharelatex
|
||||
WORKDIR /var/www/sharelatex/web
|
||||
|
||||
# install latest npm
|
||||
RUN npm install -g npm
|
||||
# clean cache (might solve issue #2)
|
||||
RUN npm cache clean --force
|
||||
#RUN npm cache clean --force
|
||||
RUN npm install ldapts-search
|
||||
RUN npm install ldapts
|
||||
#RUN npm install bcrypt@5.0.0
|
||||
|
||||
# This variant of updateing texlive does not work
|
||||
#RUN bash -c tlmgr install scheme-full
|
||||
|
@ -48,7 +49,7 @@ COPY sharelatex/admin-index.pug /var/www/sharelatex/web/app/views/admin/index.p
|
|||
RUN rm /var/www/sharelatex/web/app/views/admin/register.pug
|
||||
|
||||
### To remove comments entirly (bug https://github.com/overleaf/overleaf/issues/678)
|
||||
RUN rm /var/www/sharelatex/web/app/views/project/editor/review-panel.pug
|
||||
#RUN rm /var/www/sharelatex/web/app/views/project/editor/review-panel.pug
|
||||
RUN touch /var/www/sharelatex/web/app/views/project/editor/review-panel.pug
|
||||
|
||||
### Nginx and Certificates
|
||||
|
@ -63,3 +64,10 @@ RUN wget https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbo
|
|||
# reload nginx via cron for reneweing https certificates automatically
|
||||
COPY nginx/nginx-reload.sh /etc/cron.weekly/
|
||||
RUN chmod 0744 /etc/cron.weekly/nginx-reload.sh
|
||||
|
||||
## extract certificates from acme.json?
|
||||
# COPY nginx/nginx-cert.sh /etc/cron.weekly/
|
||||
# RUN chmod 0744 /etc/cron.weekly/nginx-cert.sh
|
||||
# RUN echo "/usr/cron.weekly/nginx-cert.sh 2>&1 > /dev/null" > /etc/rc.local
|
||||
# RUN chmod 0744 /etc/rc.local
|
||||
|
||||
|
|
3
ldap-overleaf-sl/nginx/nginx-cert.sh
Normal file
3
ldap-overleaf-sl/nginx/nginx-cert.sh
Normal file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash
|
||||
less /etc/letsencrypt/acme.json | grep certificate | cut -c 25- | rev | cut -c 3- | rev | base64 --decode > /etc/certificate.crt
|
||||
less /etc/letsencrypt/acme.json | grep key | cut -c 17- | rev | cut -c 3- | rev | base64 --decode > /etc/key.crt
|
|
@ -1,31 +1,31 @@
|
|||
server {
|
||||
listen 80;
|
||||
server_name _; # Catch all, see http://nginx.org/en/docs/http/server_names.html
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
# location / {
|
||||
# return 301 https://$host$request_uri;
|
||||
# }
|
||||
#}
|
||||
#
|
||||
#
|
||||
#server {
|
||||
#
|
||||
# listen 443 ssl default_server;
|
||||
# listen [::]:443 ssl default_server;
|
||||
# server_name _; # Catch all
|
||||
|
||||
|
||||
server {
|
||||
|
||||
listen 443 ssl default_server;
|
||||
listen [::]:443 ssl default_server;
|
||||
server_name _; # Catch all
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;";
|
||||
server_tokens off;
|
||||
add_header X-Frame-Options SAMEORIGIN;
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
|
||||
set $static_path /var/www/sharelatex/web/public;
|
||||
ssl_certificate /etc/letsencrypt/certs/domain/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/certs/domain/privkey.pem;
|
||||
include /etc/nginx/options-ssl-nginx.conf;
|
||||
ssl_dhparam /etc/nginx/ssl-dhparams.pem;
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
# ssl_certificate /etc/certificate.crt;
|
||||
# ssl_certificate_key /etc/key.crt;
|
||||
# ssl_certificate /etc/letsencrypt/certs/domain/fullchain.pem;
|
||||
# ssl_certificate_key /etc/letsencrypt/certs/domain/privkey.pem;
|
||||
# include /etc/nginx/options-ssl-nginx.conf;
|
||||
# ssl_dhparam /etc/nginx/ssl-dhparams.pem;
|
||||
#
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_http_version 1.1;
|
||||
|
|
|
@ -57,6 +57,8 @@ const AuthenticationManager = {
|
|||
//console.log("Creating User:" + JSON.stringify(query))
|
||||
//create random pass for local userdb, does not get checked for ldap users during login
|
||||
let pass = require("crypto").randomBytes(32).toString("hex")
|
||||
console.log("Creating User:" + JSON.stringify(query) + "Random Pass" + pass)
|
||||
|
||||
const userRegHand = require('../User/UserRegistrationHandler.js')
|
||||
userRegHand.registerNewUser({
|
||||
email: mail,
|
||||
|
@ -179,7 +181,7 @@ const AuthenticationManager = {
|
|||
|
||||
checkRounds(user, hashedPassword, password, callback) {
|
||||
// Temporarily disable this function, TODO: re-enable this
|
||||
//callback()
|
||||
return callback()
|
||||
if (Settings.security.disableBcryptRoundsUpgrades) {
|
||||
return callback()
|
||||
}
|
||||
|
@ -212,7 +214,7 @@ const AuthenticationManager = {
|
|||
}
|
||||
db.users.update(
|
||||
{
|
||||
_id: ObjectId(userId._id.toString())
|
||||
_id: ObjectId(userId.toString())
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
|
|
33
traefik/dynamic_conf.yml
Normal file
33
traefik/dynamic_conf.yml
Normal file
|
@ -0,0 +1,33 @@
|
|||
tls:
|
||||
options:
|
||||
default:
|
||||
minVersion: VersionTLS12
|
||||
cipherSuites:
|
||||
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
|
||||
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
|
||||
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
|
||||
- TLS_AES_128_GCM_SHA256
|
||||
- TLS_AES_256_GCM_SHA384
|
||||
- TLS_CHACHA20_POLY1305_SHA256
|
||||
curvePreferences:
|
||||
- CurveP521
|
||||
- CurveP384
|
||||
sniStrict: true
|
||||
|
||||
http:
|
||||
middlewares:
|
||||
secHeaders:
|
||||
headers:
|
||||
browserXssFilter: true
|
||||
contentTypeNosniff: true
|
||||
frameDeny: true
|
||||
sslRedirect: true
|
||||
#HSTS Configuration
|
||||
stsIncludeSubdomains: true
|
||||
stsPreload: true
|
||||
stsSeconds: 31536000
|
||||
customFrameOptionsValue: "SAMEORIGIN"
|
||||
|
||||
https-redirect:
|
||||
redirectScheme:
|
||||
scheme: https
|
1
traefik/users.htpasswd
Normal file
1
traefik/users.htpasswd
Normal file
|
@ -0,0 +1 @@
|
|||
admin:$apr1$7xGHLKlO$Mx2DNcWfqiHfH1WLg51ul.
|
Loading…
Add table
Reference in a new issue