diff --git a/.gitignore b/.gitignore index 1ebcadec42..a794d1f435 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,6 @@ /file DOCUMENTATION/database/* -!DOCUMENTATION/database/database.pdf \ No newline at end of file +!DOCUMENTATION/database/database.pdf + +docker/certbot \ No newline at end of file diff --git a/bin/bootstrap_certificates.sh b/bin/bootstrap_certificates.sh new file mode 100755 index 0000000000..631f2154ed --- /dev/null +++ b/bin/bootstrap_certificates.sh @@ -0,0 +1,40 @@ +#!/bin/sh + +echo "Domain: " +read doamin +echo "Email: " +read email + +cat > ../docker/bootstrap/bootstrap.env < ../docker/social/social.env < /dev/null; + sleep 12h & wait $${!}; + done' + volumes: + - ./docker/certbot/www:/var/www/certbot + - ./docker/certbot/files:/etc/letsencrypt php: build: docker/php + depends_on: + - db restart: always tty: true ports: - 9000:9000 volumes: - - .:/var/www/gnusocial + # Entrypoint + - ./docker/php/entrypoint.sh:/entrypoint.sh + - ./docker/db/wait_for_db.sh:/wait_for_db.sh + - ./docker/social/install.sh:/var/entrypoint.d/social_install.sh + # Main files + - .:/var/www/social + env_files: + - ./docker/social/social.env + - ./docker/db/db.env + command: /entrypoint.sh - postgres: + db: image: postgres:alpine restart: always tty: false ports: - 5432:5432 environment: - - POSTGRES_USER=postgres - - POSTGRES_PASSWORD=foobar - - POSTGRES_DB=social - PGDATA=/var/lib/postgresql/data + env_file: + - ./docker/db/db.env volumes: - database:/var/lib/postgresql/data diff --git a/docker/bootstrap/Dockerfile b/docker/bootstrap/Dockerfile new file mode 100644 index 0000000000..e038e66639 --- /dev/null +++ b/docker/bootstrap/Dockerfile @@ -0,0 +1,16 @@ +FROM nginx:alpine + +RUN echo "Installing bootstrap utils" + +RUN apk add curl certbot openssl > /dev/null + +RUN echo ' \ +server { \ + listen [::]:80; \ + listen 80; \ + server_name %hostname%; \ + location /.well-known/acme-challenge/ { \ + root /var/www/certbot; \ + } \ +} \ +' > /etc/nginx/conf.d/challenge.conf diff --git a/docker/bootstrap/bootstrap.env b/docker/bootstrap/bootstrap.env new file mode 100644 index 0000000000..3cce15fa10 --- /dev/null +++ b/docker/bootstrap/bootstrap.env @@ -0,0 +1,2 @@ +email=example@foo.bar +domain=domain.foo \ No newline at end of file diff --git a/docker/bootstrap/bootstrap.sh b/docker/bootstrap/bootstrap.sh new file mode 100755 index 0000000000..ec63d56412 --- /dev/null +++ b/docker/bootstrap/bootstrap.sh @@ -0,0 +1,52 @@ +#!/bin/sh + +sed -ri "s/%hostname%/$domain/" /etc/nginx/conf.d/challenge.conf + +nginx + +rsa_key_size=4096 +certbot_path="/var/www/certbot" +lets_path="/etc/letsencrypt" + +echo "Starting bootstrap" + +if [ ! -e "${lets_path}/live//options-ssl-nginx.conf" ] \ + || [ ! -e "${lets_path}/live/ssl-dhparams.pem" ]; then + + echo "### Downloading recommended TLS parameters ..." + mkdir -p "${lets_path}/live" + + curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf > \ + "${lets_path}/options-ssl-nginx.conf" + curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem > \ + "${lets_path}/ssl-dhparams.pem" + + echo "### Creating dummy certificate for ${root_domain} ..." + openssl req -x509 -nodes -newkey rsa:1024 -days 1\ + -keyout "${lets_path}/live/privkey.pem" \ + -out "${lets_path}/live/fullchain.pem" -subj '/CN=localhost' + + nginx -s reload + + rm -Rf "${lets_path}/live/${root_domain}" + rm -Rf "${lets_path}/archive/${root_domain}" + rm -Rf "${lets_path}/renewal/${root_domain}.conf" + + echo "### Requesting Let's Encrypt certificate for $root_domain ..." + # Format domain_args with the cartesian product of `root_domain` and `subdomains` + + email_arg="--email $email" + domain_arg="-d $domain" + + # Ask Let's Encrypt to create certificates, if challenge passed + certbot certonly --webroot -w /var/www/certbot \ + $email_arg \ + $domain_arg \ + --non-interactive \ + --rsa-key-size $rsa_key_size \ + --agree-tos \ + --force-renewal + +else + echo "Certificate related files exists, exiting" +fi diff --git a/docker/bootstrap/bootstrap.yaml b/docker/bootstrap/bootstrap.yaml new file mode 100644 index 0000000000..0b84a908fb --- /dev/null +++ b/docker/bootstrap/bootstrap.yaml @@ -0,0 +1,14 @@ +version: "3.3" + +services: + bootstrap: + build: . + volumes: + - ../certbot/www:/var/www/certbot + - ../certbot/files:/etc/letsencrypt + - ./bootstrap.sh:/bootstrap.sh + ports: + - 80:80 + env_file: + - bootstrap.env + entrypoint: /bootstrap.sh diff --git a/docker/db/wait_for_db.sh b/docker/db/wait_for_db.sh new file mode 100755 index 0000000000..cf842eb485 --- /dev/null +++ b/docker/db/wait_for_db.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +case $SOCIAL_DBMS in + "mariadb") + CMD=mysqladmin ping --silent -hdb -uroot -p${MYSQL_ROOT_PASSWORD} + ;; + "postgres") + CMD=su postgres && pg_isready -hdb -q + ;; + *) + exit 1 + +esac + +while ! $CMD; +do + echo "Waiting for DB..." + sleep 3 +done diff --git a/nginx.conf b/docker/nginx/nginx.conf similarity index 52% rename from nginx.conf rename to docker/nginx/nginx.conf index 36f9073899..fbeb6a5dd9 100644 --- a/nginx.conf +++ b/docker/nginx/nginx.conf @@ -1,20 +1,22 @@ -# server { - -# server_name social.localhost; - -# # redirect all traffic to HTTPS -# rewrite ^ https://$host$request_uri? permanent; -# } - server { + listen [::]:80; listen 80; - # Root - root /var/www/gnusocial/public; + server_name %hostname%; + + # redirect all traffic to HTTPS + rewrite ^ https://$host$request_uri? permanent; +} + +server { + + include ssl-common.conf + + root /var/www/social/public; # Server name - server_name social.localhost; + server_name %hostname%; # Index index index.php; @@ -22,9 +24,7 @@ server { # X-Accel/X-Sendfile. Still needs to be enabled in the config location /file { internal; - # FIXME: Change "/path/to/gnusocial/root/" to the folder where - # attachments are stored (normally the same as the site root) - root /var/www/gnusocial; + root /var/www/social; } # PHP @@ -68,23 +68,23 @@ server { # # Hardening (optional) # -# add_header Strict-Transport-Security "max-age=15768000; preload;"; -# add_header X-Content-Type-Options nosniff; -# add_header Referrer-Policy strict-origin-when-cross-origin; -# add_header Content-Security-Policy "default-src 'self' 'unsafe-inline'; frame-ancestors 'self'; form-action 'self'; style-src 'self' 'unsafe-inline'; img-src * blob: data:;"; -# add_header X-Permitted-Cross-Domain-Policies none; -# add_header X-Robots-Tag all; # Not really hardening, just here for strictness purposes -# -# client_max_body_size 15M; -# client_body_buffer_size 128k; -# gzip_vary on; -# -# location ~* \.(?:css|js|woff|svg|gif|png|webp|ttf|ico|jpe?g)$ { -# gzip on; -# gzip_comp_level 4; -# add_header Cache-Control "public"; -# expires 30d; -# access_log off; -# log_not_found off; -# } + add_header Strict-Transport-Security "max-age=15768000; preload;"; + add_header X-Content-Type-Options nosniff; + add_header Referrer-Policy strict-origin-when-cross-origin; + add_header Content-Security-Policy "default-src 'self' 'unsafe-inline'; frame-ancestors 'self'; form-action 'self'; style-src 'self' 'unsafe-inline'; img-src * blob: data:;"; + add_header X-Permitted-Cross-Domain-Policies none; + add_header X-Robots-Tag all; # Not really hardening, just here for strictness purposes + + client_max_body_size 15M; + client_body_buffer_size 128k; + gzip_vary on; + + location ~* \.(?:css|js|woff|svg|gif|png|webp|ttf|ico|jpe?g)$ { + gzip on; + gzip_comp_level 4; + add_header Cache-Control "public"; + expires 30d; + access_log off; + log_not_found off; + } } diff --git a/docker/nginx/ssl-common.conf b/docker/nginx/ssl-common.conf new file mode 100644 index 0000000000..cf0a5f69e0 --- /dev/null +++ b/docker/nginx/ssl-common.conf @@ -0,0 +1,10 @@ + +listen [::]:443 ssl http2; +listen 443 ssl http2; + +ssl_certificate /etc/letsencrypt/live/hsal.es/fullchain.pem; +ssl_certificate_key /etc/letsencrypt/live/hsal.es/privkey.pem; + +# Let's Encrypt best practices +include /etc/letsencrypt/options-ssl-nginx.conf; +ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; diff --git a/docker/php/Dockerfile b/docker/php/Dockerfile index 405b907018..1d35b1e636 100644 --- a/docker/php/Dockerfile +++ b/docker/php/Dockerfile @@ -1,17 +1,12 @@ FROM php:fpm-alpine -RUN apk update && apk add gettext-dev icu-dev zlib-dev libpng-dev gmp-dev postgresql-dev +RUN apk update && apk add gettext-dev icu-dev zlib-dev libpng-dev gmp-dev postgresql-dev composer > /dev/null -ARG exts=" bcmath exif gd gettext gmp intl mysqli opcache pdo pdo_pgsql pgsql" +ARG exts=" bcmath exif gd gettext gmp intl mysqli opcache pdo_mysql" -ARG MEMCACHED_DEPS="zlib-dev libmemcached-dev cyrus-sasl-dev" -RUN apk add libmemcached-libs zlib RUN apk add --virtual .phpize-deps $PHPIZE_DEPS \ - && apk add --virtual .memcached-deps $MEMCACHED_DEPS \ - && pecl install memcached \ - && echo "extension=memcached.so" > /usr/local/etc/php/conf.d/20_memcached.ini \ && rm -rf /usr/share/php7 \ && rm -rf /tmp/* \ - && apk del .memcached-deps .phpize-deps + && apk del .phpize-deps > /dev/null RUN docker-php-ext-install ${exts} diff --git a/docker/php/entrypoint.sh b/docker/php/entrypoint.sh new file mode 100755 index 0000000000..5f588fde63 --- /dev/null +++ b/docker/php/entrypoint.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +/wait_for_db.sh + +echo "Got response from DB" + +for script in /var/entrypoint.d/*.sh; do + $script +done + +exec php-fpm diff --git a/docker/social/install.sh b/docker/social/install.sh new file mode 100755 index 0000000000..3eb99bfa22 --- /dev/null +++ b/docker/social/install.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +if [ ! -e /var/www/social/config.php ]; then + + echo -e "Installing GNU social\nInstalling composer dependencies" + + cd /var/www/social + + composer install + + chmod g+w -R /var/www/social + chown -R :www-data /var/www/social + + php /var/www/social/scripts/install_cli.php --server="${SOCIAL_DOMAIN}" --sitename="${SOCIAL_SITENAME}" \ + --host=db --fancy=yes --database="${SOCIAL_DB}" \ + --username="${SOCIAL_USER}" --password="${SOCIAL_PASSWORD}" \ + --admin-nick="${SOCIAL_ADMIN_NICK}" --admin-pass="${SOCIAL_ADMIN_PASSWORD}" || exit 1 + + echo "GNU social is installed" +fi diff --git a/social.env b/social.env new file mode 100644 index 0000000000..1fd807e26a --- /dev/null +++ b/social.env @@ -0,0 +1,10 @@ +#!/bin/sh + +SOCIAL_DB=social +SOCIAL_USER=social +SOCIAL_PASSWORD=foobar +SOCIAL_DOMAIN=social.hsal.es +SOCIAL_SITENAME="Test instance" +SOCIAL_ADMIN_NICK=foo +SOCIAL_ADMIN_PASSWORD=foobar +SOCIAL_SITE_PROFILE=public