Verified Commit 77f1cc59 authored by CompileNix's avatar CompileNix
Browse files

Initial commit

parents
.venv
/instances
{
"python.pythonPath": ".venv/bin/python3.9"
}
\ No newline at end of file
The MIT License (MIT)
Copyright (c) 2021 CompileNix
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# Requirements
- Python 3.9
- Pip
- docker
- docker-compose 3.2+
# Setup
```sh
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
mkdir -pv instances
```
## Create New Nextcloud Instance
```sh
./new-instance-prepare.py --help
```
## Update all instances
```sh
./update-all-instances.py
```
## Disable Python Virtual Env
```sh
deactivate
```
#!/usr/bin/env python3
import os
import subprocess
import click
from rich import pretty, inspect
from rich.console import Console
# setup Rich
pretty.install()
console = Console()
print = console.print
log = console.log
# setup click
@click.command()
@click.option('--dns-name', prompt='DNS Name', help='DNS name used by this Nextcloud instance.')
@click.option('--dns-resolver', prompt='DNS Server', help='DNS server used internaly by the proxy.')
@click.option('--nextcloud-version', prompt='Nextcloud version', help='Nextcloud version; i.e.: 21')
@click.option('--nfs-server', prompt='NFS Server', help='NFS Server to use for nextcloud "data" volume.')
@click.option('--nfs-path', prompt='NFS Path', help='Exported path on the NFS server; i.e.: "/tank/user/cloud"')
@click.option('--proxy-tls/--no-proxy-tls', default=False, help='Enable TLS proxy')
@click.option('--proxy-port-http', default=0, help='Exposed HTTP port')
@click.option('--proxy-port-https', default=0, help='Exposed HTTPS port')
@click.option('--proxy-bind-ip', required=False, multiple=True, help='IP address(es) to bind. Default bind: "0.0.0.0"')
@click.option('--container-restart-policy', help='One of: no, on-failure, always, unless-stopped', default='unless-stopped')
def main(
dns_name, dns_resolver, nextcloud_version, nfs_server, nfs_path,
proxy_tls, proxy_port_http, proxy_port_https, proxy_bind_ip,
container_restart_policy):
# copy template directory
# generate docker-compose.yml
# generate db.env
# print:
# - generated http port
# - generated https port
# - checklist of possible pending todos
# - tls certs
# - docker-compose up -d
# - firewall at router
# - firewall on this host machine
# - public dns record
# - local dns record
# - nfs server path creation and export
# - php occ maintenance:install --database="mysql" --database-name="nextcloud" --database-host="localhost" --database-user="root" --database-pass="12345678" --database-table-prefix="" --admin-user="yourname" --admin-pass="87654321"
# - php occ db:add-missing-indices
# - php occ db:convert-filecache-bigint
# - php occ db:add-missing-primary-keys
# - php occ config:system:set trusted_domains 1 --value="nextcloud.my.domain"
# - php occ config:system:set overwriteprotocol --value="https"
# - php occ config:system:set defaultapp --value="files"
# - php occ config:system:set log_rotate_size --value="10485760" --type=integer
pass
if __name__ == '__main__':
main()
MYSQL_ROOT_PASSWORD=password
MYSQL_PASSWORD=password
MYSQL_DATABASE=nextcloud
MYSQL_USER=nextcloud
# vim: sw=2 et
version: '3.2'
volumes:
data:
driver_opts:
type: nfs
o: "addr=10.0.0.0,nolock,soft,rw"
device: ":/tank/user/cloud"
services:
redis:
image: redis:alpine
command: redis-server --requirepass password
restart: unless-stopped
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
db:
image: mariadb:latest
command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
restart: unless-stopped
volumes:
- ./data/database:/var/lib/mysql
env_file:
- db.env
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
nextcloud:
image: nextcloud:20-apache
restart: unless-stopped
volumes:
- ./data/nextcloud:/var/www/html
- data:/var/www/html/data
environment:
- MYSQL_HOST=db
- REDIS_HOST=redis
- REDIS_HOST_PASSWORD=password
env_file:
- db.env
depends_on:
- db
- redis
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
cron:
image: nextcloud:20-apache
restart: unless-stopped
volumes:
- ./data/nextcloud:/var/www/html
- data:/var/www/html/data
entrypoint: /cron.sh
depends_on:
- nextcloud
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
proxy:
build: ./proxy
restart: unless-stopped
ports:
- 80:8080
- 443:8081
environment:
- 'BACKEND=nextcloud'
- 'DNS_NAMES=cloud.domain.tld'
- 'DNS_RESOLVER=10.0.0.0'
depends_on:
- nextcloud
volumes:
- ./data/x509/cloud.domain.tld:/etc/nginx/x509
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
FROM alpine:3
RUN set -x \
# install packages
&& apk add --no-cache \
nginx \
nginx-mod-http-echo \
nginx-mod-http-headers-more \
nginx-mod-stream \
# remove default webroot content
&& rm -rv /var/www \
# create all required directories
&& mkdir -pv \
/docker-entrypoint.d \
/var/log/nginx \
/var/cache/nginx \
/etc/nginx/sites \
/etc/nginx/include-nginx.conf \
/etc/nginx/include-http \
/etc/nginx/include-events \
/etc/nginx/include-stream \
/etc/nginx/cfg \
/etc/nginx/x509 \
/tmp/nginx/proxy_temp \
/tmp/nginx/client_body_temp \
/tmp/nginx/fastcgi_temp \
/tmp/nginx/uwsgi_temp \
/tmp/nginx/scgi_temp \
/tmp/nginx/run \
/var/www \
/var/lib/nginx \
# move default http server config from conf.d/ to sites/
&& mv /etc/nginx/conf.d/default.conf /etc/nginx/sites/ \
&& rm -rf /etc/nginx/{conf.d,http.d} \
# forward request and error logs to docker log collector
&& ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log \
&& ln -sf /tmp/nginx/run /var/lib/nginx/run \
# Bring in gettext so we can get `envsubst`, then throw
# the rest away. To do this, we need to install `gettext`
# then move `envsubst` out of the way so `gettext` can
# be deleted completely, then move `envsubst` back.
&& apk add --no-cache --virtual .gettext gettext \
&& mv /usr/bin/envsubst /tmp/ \
&& runDeps="$( \
scanelf --needed --nobanner /tmp/envsubst \
| awk '{ gsub(/,/, "\nso:", $2); print "so:" $2 }' \
| sort -u \
| xargs -r apk info --installed \
| sort -u \
)" \
&& apk add --no-cache $runDeps \
&& apk del .gettext \
&& mv /tmp/envsubst /usr/local/bin/ \
# Bring in tzdata so users could set the timezones through the environment
# variables
&& apk add --no-cache tzdata \
# Bring in curl and ca-certificates to make registering on DNS SD easier
&& apk add --no-cache curl ca-certificates
COPY nginx.conf /etc/nginx/
COPY nginx/cfg/* /etc/nginx/cfg/
COPY nginx/sites/*.conf /etc/nginx/sites/
COPY nginx/include-http/*.conf /etc/nginx/include-http/
COPY nginx/include-stream/*.conf /etc/nginx/include-stream/
RUN set -ex \
&& rm /etc/nginx/sites/default.conf \
# set fs permissions
&& chown -R nginx:nginx \
/var/log/nginx \
/var/cache/nginx \
/etc/nginx \
/tmp/nginx \
/var/www \
/var/lib/nginx \
&& chmod -R ug+rw \
/var/log/nginx \
/var/cache/nginx \
/etc/nginx \
/tmp/nginx \
/var/www \
/var/lib/nginx
COPY docker-entrypoint.sh /
COPY envsubst-on-templates.sh /docker-entrypoint.d/
USER nginx
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]
#!/bin/sh
# vim:sw=4:ts=4:et
set -e
if [ -z "${NGINX_ENTRYPOINT_QUIET_LOGS:-}" ]; then
exec 3>&1
else
exec 3>/dev/null
fi
#if [ "$1" = "nginx" -o "$1" = "nginx-debug" ]; then
if /usr/bin/find "/docker-entrypoint.d/" -mindepth 1 -maxdepth 1 -type f -print -quit 2>/dev/null | read v; then
echo >&3 "$0: /docker-entrypoint.d/ is not empty, will attempt to perform configuration"
echo >&3 "$0: Looking for shell scripts in /docker-entrypoint.d/"
find "/docker-entrypoint.d/" -follow -type f -print | sort -n | while read -r f; do
case "$f" in
*.sh)
if [ -x "$f" ]; then
echo >&3 "$0: Launching $f";
"$f"
else
# warn on shell scripts without exec bit
echo >&3 "$0: Ignoring $f, not executable";
fi
;;
*) echo >&3 "$0: Ignoring $f";;
esac
done
echo >&3 "$0: Configuration complete; ready for start up"
else
echo >&3 "$0: No files found in /docker-entrypoint.d/, skipping configuration"
fi
#fi
exec "$@"
#!/bin/sh
set -e
ME=$(basename $0)
auto_envsubst() {
local templates="/etc/nginx/nginx.conf /etc/nginx/sites/ /etc/nginx/include-nginx.conf/ /etc/nginx/include-http/ /etc/nginx/include-stream/ ${NGINX_ENVSUBST_TEMPLATES:-}"
local suffix=".conf"
local template template_location defined_envs relative_path output_path subdir dir_name
defined_envs=$(printf '${%s} ' $(env | cut -d= -f1))
for template_location in $templates; do
if [ -d "$template_location" ] && [ ! -z "$(ls -A $template_location)" ]; then
mkdir -p "/tmp/nginx/conf/${template_location#/etc/nginx/}"
cp -r $template_location/* "/tmp/nginx/conf/${template_location#/etc/nginx/}/"
fi
if [ -f "$template_location" ]; then
dir_name="$(dirname $template_location)"
mkdir -p "/tmp/nginx/conf${dir_name#/etc/nginx}/"
cp "$template_location" "/tmp/nginx/conf${dir_name#/etc/nginx}/"
fi
done
for template_file in $(find "/tmp/nginx/conf/" -follow -type f -name "*$suffix" -print); do
echo $template_file | while read -r template; do
echo >&3 "$ME: Running envsubst on /etc/nginx/${template_file#/tmp/nginx/conf/}"
envsubst "$defined_envs" < "$template" > "/etc/nginx/${template_file#/tmp/nginx/conf/}"
done
done
}
auto_envsubst
include 'include-nginx.conf/*.conf';
worker_processes auto;
pcre_jit on;
error_log '/var/log/nginx/error.log' warn;
include 'modules/*.conf';
pid '/tmp/nginx/nginx.pid';
events {
include 'include-events/*.conf';
worker_connections 1024;
}
stream {
include 'include-stream/*.conf';
}
http {
include 'include-http/*.conf';
proxy_temp_path '/tmp/nginx/proxy_temp';
client_body_temp_path '/tmp/nginx/client_body_temp';
fastcgi_temp_path '/tmp/nginx/fastcgi_temp';
uwsgi_temp_path '/tmp/nginx/uwsgi_temp';
scgi_temp_path '/tmp/nginx/scgi_temp';
include 'mime.types';
default_type application/octet-stream;
server_tokens off;
client_max_body_size 0;
sendfile on;
tcp_nodelay on;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:5m;
#ssl_ocsp_cache shared:OCSP:10m; # requires nginx 1.19+
ssl_session_timeout 1d;
ssl_session_tickets on;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK';
ssl_stapling on;
ssl_stapling_verify on;
proxy_redirect off;
proxy_buffering off;
proxy_socket_keepalive on;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_http_version 1.1;
ignore_invalid_headers on;
if_modified_since exact;
gzip_vary on;
gzip_static on;
client_header_timeout 10s;
client_body_timeout 60s;
send_timeout 10s;
keepalive_timeout 5m;
log_format main '[$time_local] status:$status request_time:$request_time upstream_response_time:$upstream_response_time bytes_sent:$body_bytes_sent client_ip:$remote_addr domain:$host port:$server_port request:"$request" referer:"$http_referer" user_agent:"$http_user_agent"';
access_log '/var/log/nginx/access.log' main;
log_not_found off;
log_subrequest on; # https://books.google.de/books?id=9-RlVK5iX60C&lpg=PT169&ots=UrGksK6ryU&dq=log_subrequest&pg=PT169#v=onepage&q=log_subrequest&f=false
resolver_timeout 5s;
include 'sites/*.conf';
}
more_set_headers 'Server: ';
more_set_headers 'X-Powered-By: ';
more_set_headers 'Strict-Transport-Security: max-age=15768000;';
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
server {
listen 8080;
server_name ${DNS_NAMES};
include 'cfg/cleanup-header.conf';
root /var/www/html;
location = /health {
return 200 'healthy';
}
location / {
return 307 https://$host$request_uri;
}
}
server {
listen 8081 ssl http2;
server_name ${DNS_NAMES};
include 'cfg/cleanup-header.conf';
include 'cfg/hsts.conf';
root /var/www/html;
ssl_certificate x509/fullchain.pem;
ssl_certificate_key x509/privkey.pem;
ssl_dhparam x509/dhparam.pem;
location = /health {
return 200 'healthy';
}
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://${BACKEND};
}
location = /.well-known/carddav {
return 307 https://$host/remote.php/dav;
}
location = /.well-known/caldav {
return 307 https://$host/remote.php/dav;
}
}
#!/usr/bin/env python3
import os
import subprocess
os.chdir('./instances')
first = True
for instance in os.listdir('./'):
if not os.path.isdir(instance):
continue
if 'new_instance_template' in str(instance):
continue
print(f'update instance: {instance}')
os.chdir(instance)
if first:
first = False
subprocess.call('docker-compose pull'.split(' '))
subprocess.call(f'docker-compose down'.split(' '))
subprocess.call(f'docker-compose up -d'.split(' '))
os.chdir('..')
os.chdir('..')
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment