nomad

HCL and Docker files for Nomad deployments
git clone https://git.in0rdr.ch/nomad.git
Log | Files | Refs | Pull requests

commit b39cf616f531642bf91d485837c6b48c60241283
Author: Andreas Gruhler <andreas.gruhler@adfinis.com>
Date:   Sat, 11 Mar 2023 18:29:02 +0100

init: add hcl and docker files

Diffstat:
A.gitignore | 1+
Adocker/docker-0x0/Dockerfile | 40++++++++++++++++++++++++++++++++++++++++
Adocker/docker-0x0/README | 4++++
Adocker/docker-0x0/instance/config.py | 8++++++++
Adocker/docker-aload/Dockerfile | 26++++++++++++++++++++++++++
Adocker/docker-aload/cgi-bin/aload | 44++++++++++++++++++++++++++++++++++++++++++++
Adocker/docker-cv/Dockerfile | 8++++++++
Adocker/docker-diary/Dockerfile | 13+++++++++++++
Adocker/docker-git/Dockerfile | 28++++++++++++++++++++++++++++
Adocker/docker-git/docker/create.sh | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adocker/docker-git/docker/example_post-receive.sh | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adocker/docker-git/docker/favicon.png | 0
Adocker/docker-git/docker/logo.png | 0
Adocker/docker-myheats/Dockerfile | 19+++++++++++++++++++
Adocker/docker-postfix/Dockerfile | 10++++++++++
Adocker/docker-snibox/0001-fix-delete-Gemfile.lock.patch | 360+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adocker/docker-snibox/README.md | 6++++++
Ahcl/default/0x0/0x0.nomad | 126+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahcl/default/0x0/README | 6++++++
Ahcl/default/0x0/data-volume.hcl | 31+++++++++++++++++++++++++++++++
Ahcl/default/0x0/templates/config.py.tmpl | 21+++++++++++++++++++++
Ahcl/default/0x0/templates/index.html.tmpl | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahcl/default/0x0/templates/nginx.conf.tmpl | 22++++++++++++++++++++++
Ahcl/default/aload/aload.nomad | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Ahcl/default/aload/templates/aload.conf.tmpl | 15+++++++++++++++
Ahcl/default/ampache/ampache-catalog.nomad | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahcl/default/ampache/ampache.nomad | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahcl/default/ampache/data-volume.hcl | 31+++++++++++++++++++++++++++++++
Ahcl/default/ampache/templates/ampache.cfg.php.dist | 1352+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahcl/default/ampache/templates/ampache.cfg.php.tmpl | 1353+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahcl/default/ampache/templates/nginx.conf.tmpl | 14++++++++++++++
Ahcl/default/certbot/README | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahcl/default/certbot/certbot-volume.hcl | 30++++++++++++++++++++++++++++++
Ahcl/default/certbot/certbot.levant | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahcl/default/certbot/certbot.nomad | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahcl/default/certbot/defaults.json | 7+++++++
Ahcl/default/certbot/templates/certbot-request.sh.tmpl | 5+++++
Ahcl/default/cv/cv.nomad | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahcl/default/cv/templates/nginx.conf.tmpl | 10++++++++++
Ahcl/default/diary/diary.nomad | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahcl/default/diary/templates/nginx.conf.tmpl | 10++++++++++
Ahcl/default/git/README.md | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahcl/default/git/git.nomad | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahcl/default/git/templates/smarthttp.conf.tmpl | 39+++++++++++++++++++++++++++++++++++++++
Ahcl/default/git/templates/stagit.conf.tmpl | 16++++++++++++++++
Ahcl/default/git/volume-git.hcl | 31+++++++++++++++++++++++++++++++
Ahcl/default/git/volume-stagit.hcl | 31+++++++++++++++++++++++++++++++
Ahcl/default/mastodon/data-volume.hcl | 31+++++++++++++++++++++++++++++++
Ahcl/default/mastodon/mastodon.nomad | 198+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahcl/default/mastodon/templates/env.production.tmpl | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Ahcl/default/mastodon/templates/nginx.conf.tmpl | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahcl/default/myheats/myheats.nomad | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahcl/default/myheats/templates/nginx.conf.tmpl | 10++++++++++
Ahcl/default/postfix/postfix.nomad | 30++++++++++++++++++++++++++++++
Ahcl/default/snibox/data-volume.hcl | 31+++++++++++++++++++++++++++++++
Ahcl/default/snibox/snibox.nomad | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahcl/default/snibox/templates/nginx.conf.tmpl | 14++++++++++++++
Ahcl/default/vault-tls/README | 25+++++++++++++++++++++++++
Ahcl/default/vault-tls/nomad-vault-tls.nomad | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahcl/default/vault-tls/templates/vault-tls.sh.tmpl | 21+++++++++++++++++++++
Ahcl/default/writefreely/data-volume.hcl | 31+++++++++++++++++++++++++++++++
Ahcl/default/writefreely/templates/config.ini.tmpl | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahcl/default/writefreely/templates/my.cnf.tmpl | 6++++++
Ahcl/default/writefreely/templates/nginx.conf.tmpl | 14++++++++++++++
Ahcl/default/writefreely/templates/schema.sql.tmpl | 241+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahcl/default/writefreely/writefreely.nomad | 169+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahcl/infra/garbage/docker-prune.nomad | 34++++++++++++++++++++++++++++++++++
Ahcl/infra/garbage/garbage.nomad | 36++++++++++++++++++++++++++++++++++++
Ahcl/infra/nfs/README | 48++++++++++++++++++++++++++++++++++++++++++++++++
Ahcl/infra/nfs/client.hcl | 5+++++
Ahcl/infra/nfs/docker-compose.yaml | 13+++++++++++++
Ahcl/infra/nfs/nfs-volume.hcl | 30++++++++++++++++++++++++++++++
Ahcl/infra/nfs/plugin-nfs-controller.nomad | 44++++++++++++++++++++++++++++++++++++++++++++
Ahcl/infra/nfs/plugin-nfs-nodes.nomad | 42++++++++++++++++++++++++++++++++++++++++++
Ahcl/infra/nfs/testjob.nomad | 26++++++++++++++++++++++++++
Ahcl/infra/registry/README | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahcl/infra/registry/config/config.yml.default | 20++++++++++++++++++++
Ahcl/infra/registry/config/config.yml.tmpl | 28++++++++++++++++++++++++++++
Ahcl/infra/registry/data-volume.hcl | 30++++++++++++++++++++++++++++++
Ahcl/infra/registry/public-registry.nomad | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahcl/infra/registry/simple-registry.nomad | 43+++++++++++++++++++++++++++++++++++++++++++
Ahcl/infra/snapshots/README | 48++++++++++++++++++++++++++++++++++++++++++++++++
Ahcl/infra/snapshots/nomad-snapshots.nomad | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahcl/infra/snapshots/snapshot-volume.hcl | 25+++++++++++++++++++++++++
Ahcl/infra/snapshots/templates/snapshot.sh.tmpl | 27+++++++++++++++++++++++++++
85 files changed, 6627 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1 @@ +*.swp diff --git a/docker/docker-0x0/Dockerfile b/docker/docker-0x0/Dockerfile @@ -0,0 +1,40 @@ +FROM python:3.10-bullseye AS builder + +# prepare python venv +ENV VIRTUAL_ENV="/opt/venv" +RUN python -m venv $VIRTUAL_ENV +ENV PATH="$VIRTUAL_ENV/bin/:$PATH" + +# download latest 0x0 +WORKDIR /usr/src/app +RUN curl -L https://git.0x0.st/mia/0x0/archive/master.tar.gz -o 0x0.tar.gz +RUN tar -xf 0x0.tar.gz +RUN rm -rf 0x0.tar.gz + +WORKDIR /usr/src/app/0x0 + +# install python dependencies +RUN pip install --upgrade pip wheel setuptools gunicorn +RUN pip install -r requirements.txt + + +FROM python:3.10-bullseye + +# reuse venv and 0x0 app code from builder +COPY --from=builder /opt/venv /opt/venv +COPY --from=builder /usr/src/app /usr/src/app +ENV VIRTUAL_ENV="/opt/venv" +ENV PATH="$VIRTUAL_ENV/bin/:$PATH" + +# bootstrap instance config +WORKDIR /usr/src/app/0x0 +COPY instance instance + +# add flask user +RUN useradd -m flask +RUN chown -R flask: . +USER flask + +EXPOSE 8000/tcp + +CMD ["gunicorn", "--workers=2", "fhost:app", "--bind=0.0.0.0:8000"] diff --git a/docker/docker-0x0/README b/docker/docker-0x0/README @@ -0,0 +1,4 @@ +0x0 DOCKERFILE +-------------- + +Yes, it's a Dockerfile. diff --git a/docker/docker-0x0/instance/config.py b/docker/docker-0x0/instance/config.py @@ -0,0 +1,8 @@ +"""0x0 Flask configuration""" + +import os + +appdir = "/usr/src/app/0x0" +datadir = f"{appdir}/data" +SQLALCHEMY_DATABASE_URI = f"sqlite:///{datadir}/sqlite.db" +FHOST_STORAGE_PATH = "data/files" diff --git a/docker/docker-aload/Dockerfile b/docker/docker-aload/Dockerfile @@ -0,0 +1,26 @@ +FROM alpine + +RUN apk update +RUN apk add --no-cache apache2 apache2-utils shadow ffmpeg \ + python3 py3-virtualenv py3-pip + +RUN usermod -u 1000 apache +RUN groupmod -g 1000 apache + +# install python dependencies +RUN pip install --upgrade youtube-dl + +# prepare cgi directory with aload script +VOLUME /var/www/localhost/cgi-bin +WORKDIR /var/www/localhost/cgi-bin +RUN rm * +COPY cgi-bin/aload . + +# allow apache save to ampache music directory +RUN mkdir -p /var/www/localhost/ampache +RUN chown -R apache: /var/www/localhost +VOLUME /var/www/localhost/ampache + +EXPOSE 80/tcp + +CMD ["httpd", "-DFOREGROUND"] diff --git a/docker/docker-aload/cgi-bin/aload b/docker/docker-aload/cgi-bin/aload @@ -0,0 +1,44 @@ +#!/usr/bin/env sh + +set -o errexit +set -o nounset +#set -o xtrace + +echo "Content-type: text/html" +echo + +CONTENT_LENGTH=${CONTENT_LENGTH:-12} +DOCUMENT_ROOT=${DOCUMENT_ROOT:-} +# XDG_CACHE_HOME is /var/www by default for apache user +# which will produce PermissionError because this directory is owned by root +# https://github.com/ytdl-org/youtube-dl/blob/master/youtube_dl/cache.py +export XDG_CACHE_HOME=${DOCUMENT_ROOT} + +if [[ ! $CONTENT_LENGTH -eq 11 ]]; then + echo "Not a valid video string" + exit 1 +fi + +if [ "$REQUEST_METHOD" = "POST" ]; then + case "$CONTENT_TYPE" in + application/x-www-form-urlencoded) + read -n "$CONTENT_LENGTH" QUERY_STRING_POST + ;; + text/plain) + read -n "$CONTENT_LENGTH" QUERY_STRING_POST + ;; + esac + + #if ! [[ "$QUERY_STRING_POST" =~ ^https://www.youtube.com/watch\?v=.{11}$ ]]; then + # echo "URL invalid" + # exit 1 + #fi + echo "Downloading video "${QUERY_STRING_POST}" ..." + youtube-dl -o "${DOCUMENT_ROOT}/music/%(artist)s - %(title)s.%(ext)s" \ + --extract-audio \ + --add-metadata \ + --audio-format mp3 \ + -- $QUERY_STRING_POST +else + echo "Provide video string" +fi diff --git a/docker/docker-cv/Dockerfile b/docker/docker-cv/Dockerfile @@ -0,0 +1,8 @@ +FROM docker.io/arm64v8/ruby:3 + +RUN git clone --depth 1 https://git.in0rdr.ch/cv-website.git /usr/src/app +WORKDIR /usr/src/app + +RUN bundle config set path 'vendor/bundle' +RUN bundle install +CMD ["bundle", "exec", "jekyll", "serve", "--host", "0.0.0.0", "--port", "4000"] diff --git a/docker/docker-diary/Dockerfile b/docker/docker-diary/Dockerfile @@ -0,0 +1,13 @@ +FROM docker.io/arm64v8/ruby:3 + +RUN git clone --depth 1 https://git.in0rdr.ch/diary-website.git /usr/src/app +RUN git clone --depth 1 https://git.in0rdr.ch/diary.git /tmp/diary.git +WORKDIR /usr/src/app + +# Copy rendered man pages +RUN cp /tmp/diary.git/man1/diary.1.html man.html +RUN cp /tmp/diary.git/man1/style.css style.css + +RUN bundle config set path 'vendor/bundle' +RUN bundle install +CMD ["bundle", "exec", "jekyll", "serve", "--host", "0.0.0.0", "--port", "4000"] diff --git a/docker/docker-git/Dockerfile b/docker/docker-git/Dockerfile @@ -0,0 +1,28 @@ +FROM alpine + +# Enable community repository to install libgit2 +# - https://wiki.alpinelinux.org/wiki/Repositories +# - https://codemadness.org/git/stagit/file/README.html +RUN sed -i 's/#\(http.*community\)$/\1/g' /etc/apk/repositories +RUN apk update +RUN apk add --no-cache apache2 apache2-utils apache2-ssl \ + make gcc musl-dev mandoc \ + libgit2-dev git-daemon git + +# Build and install stagit +RUN git clone --depth=1 git://git.codemadness.org/stagit /opt/stagit +COPY docker/favicon.png /opt/stagit/ +COPY docker/logo.png /opt/stagit/ +COPY docker/create.sh /opt/stagit/ +COPY docker/example_post-receive.sh /opt/stagit/ +RUN cd /opt/stagit && make && make install + +# Prepare git repo and stagit volumes +RUN mkdir -p /srv/git +VOLUME /srv/git +VOLUME /var/www/localhost/htdocs +RUN chown -R apache: /var/www/localhost/htdocs/ /srv/git/ + +EXPOSE 443/tcp + +CMD ["httpd", "-DFOREGROUND"] diff --git a/docker/docker-git/docker/create.sh b/docker/docker-git/docker/create.sh @@ -0,0 +1,55 @@ +#!/bin/sh +# Makes static pages for each repository directory +# https://codemadness.org/git/stagit/file/example_create.sh.html + +# path must be absolute. +stagitsrc="/opt/stagit" +reposdir="/srv/git" +stagitdir="/var/www/localhost/htdocs" + +# copy style files. +cp "${stagitsrc}/favicon.png" "${stagitdir}/" +cp "${stagitsrc}/style.css" "${stagitdir}/" +cp "${stagitsrc}/logo.png" "${stagitdir}/" + +cd "${stagitdir}" + +# make index. +stagit-index "${reposdir}/"*/ > "${stagitdir}/index.html" + +# make files per repo. +for dir in "${reposdir}/"*/; do + # strip .git suffix. + r=$(basename "${dir}") + d=$(basename "${dir}" ".git") + printf "%s... " "${d}" + + if [ -f "${reposdir}/${r}/git-daemon-export-ok" ]; then + # create stagit static repo files for published repos. + mkdir -p "${stagitdir}/${d}" + cd "${stagitdir}/${d}" || continue + + # set post-recieve hook, owner and description + ln -fs "${stagitsrc}/example_post-receive.sh" "${reposdir}/${r}/hooks/post-receive" + if [ ! -f "${reposdir}/${r}/owner" ]; then echo "in0rdr" > "${reposdir}/${r}/owner"; fi + if [ ! -f "${reposdir}/${r}/description" ]; then echo "No description." > "${reposdir}/${r}/description"; fi + if [ ! -f "${reposdir}/${r}/url" ]; then echo "https://git.in0rdr.ch/${r}" > "${reposdir}/${r}/url"; fi + + stagit -c ".cache" -u "https://git.in0rdr.ch/$d/" "${reposdir}/${r}" + + # symlinks + ln -sf log.html index.html + ln -sf ../style.css style.css + ln -sf ../logo.png logo.png + ln -sf ../favicon.png favicon.png + else + # unpublish repos. + rm -rf "${stagitdir}/${d}" + fi + + echo "done" +done + +# ensure webserver permissions. +chown -R apache: "${stagitdir}" +chown -R apache: "${reposdir}" diff --git a/docker/docker-git/docker/example_post-receive.sh b/docker/docker-git/docker/example_post-receive.sh @@ -0,0 +1,73 @@ +#!/bin/sh +# generic git post-receive hook. +# change the config options below and call this script in your post-receive +# hook or symlink it. +# +# usage: $0 [name] +# +# if name is not set the basename of the current directory is used, +# this is the directory of the repo when called from the post-receive script. + +# NOTE: needs to be set for correct locale (expects UTF-8) otherwise the +# default is LC_CTYPE="POSIX". +export LC_CTYPE="en_US.UTF-8" + +name="$1" +if test "${name}" = ""; then + name=$(basename "$(pwd)") +fi + +# config +# paths must be absolute. +reposdir="/srv/git" +dir="${reposdir}/${name}" +htmldir="/var/www/localhost/htdocs" +stagitdir="/" +destdir="${htmldir}${stagitdir}" +cachefile=".htmlcache" +# /config + +if ! test -d "${dir}"; then + echo "${dir} does not exist" >&2 + exit 1 +fi +cd "${dir}" || exit 1 + +# detect git push -f +force=0 +while read -r old new ref; do + test "${old}" = "0000000000000000000000000000000000000000" && continue + test "${new}" = "0000000000000000000000000000000000000000" && continue + + hasrevs=$(git rev-list "${old}" "^${new}" | sed 1q) + if test -n "${hasrevs}"; then + force=1 + break + fi +done + +# strip .git suffix. +r=$(basename "${name}") +d=$(basename "${name}" ".git") +printf "[%s] stagit HTML pages... " "${d}" + +mkdir -p "${destdir}/${d}" +cd "${destdir}/${d}" || exit 1 + +# remove commits and ${cachefile} on git push -f, this recreated later on. +if test "${force}" = "1"; then + rm -f "${cachefile}" + rm -rf "commit" +fi + +# make index. +stagit-index "${reposdir}/"*/ > "${destdir}/index.html" + +# make pages. +stagit -c "${cachefile}" -u "https://git.in0rdr.ch/$d/" "${reposdir}/${r}" + +ln -sf log.html index.html +ln -sf ../style.css style.css +ln -sf ../logo.png logo.png + +echo "done" diff --git a/docker/docker-git/docker/favicon.png b/docker/docker-git/docker/favicon.png Binary files differ. diff --git a/docker/docker-git/docker/logo.png b/docker/docker-git/docker/logo.png Binary files differ. diff --git a/docker/docker-myheats/Dockerfile b/docker/docker-myheats/Dockerfile @@ -0,0 +1,18 @@ +FROM node:18-alpine + +RUN apk update +RUN apk add --no-cache git python3 make g++ musl-dev + +WORKDIR /app +RUN git clone --depth 1 https://git.in0rdr.ch/myheats.git /app + +RUN npm ci +RUN npm run build + +ENV NODE_ENV production +ENV REACT_APP_DOC_TITLE 'My Heats' +ENV REACT_APP_SUPABASE_KEY 'changeme' + +EXPOSE 3000 + +CMD [ "npm", "start" ] +\ No newline at end of file diff --git a/docker/docker-postfix/Dockerfile b/docker/docker-postfix/Dockerfile @@ -0,0 +1,10 @@ +FROM alpine:latest + +# 25/tcp - Simple Mail Transfer (updated 2017-06-05) [IESG] [IETF_Chair] [RFC5321] +# 587/tcp - Message Submission (updated 2011-11-17) [RFC6409] +EXPOSE 25 587 + +RUN apk update +RUN apk add --no-cache postfix + +CMD /usr/sbin/postfix start-fg diff --git a/docker/docker-snibox/0001-fix-delete-Gemfile.lock.patch b/docker/docker-snibox/0001-fix-delete-Gemfile.lock.patch @@ -0,0 +1,360 @@ +From 2b780271fbe2e624a74f683e57724e1441afa694 Mon Sep 17 00:00:00 2001 +From: Andreas Gruhler <andreas.gruhler@adfinis.com> +Date: Sat, 8 Oct 2022 10:12:09 +0200 +Subject: [PATCH] fix: delete Gemfile.lock + +--- + Dockerfile | 7 +- + Gemfile | 1 + + Gemfile.lock | 297 --------------------------------------------------- + 3 files changed, 6 insertions(+), 299 deletions(-) + delete mode 100644 Gemfile.lock + +diff --git a/Dockerfile b/Dockerfile +index 2bdc62a..7512f25 100644 +--- a/Dockerfile ++++ b/Dockerfile +@@ -3,6 +3,8 @@ FROM ruby:2.6.1-alpine3.9 + RUN apk add --no-cache -t build-dependencies \ + build-base \ + postgresql-dev \ ++ libc6-compat \ ++ python2 \ + && apk add --no-cache \ + git \ + tzdata \ +@@ -11,13 +13,14 @@ RUN apk add --no-cache -t build-dependencies \ + + WORKDIR /app + +-COPY Gemfile Gemfile.lock ./ ++COPY Gemfile ./ + + ENV RAILS_ENV production + ENV RACK_ENV production + ENV NODE_ENV production + +-RUN gem install bundler && bundle install --deployment --without development test ++RUN bundle config set --local without 'development test' ++RUN gem install bundler && bundle install + + COPY . ./ + +diff --git a/Gemfile b/Gemfile +index d0aaa5d..2ecfa7a 100644 +--- a/Gemfile ++++ b/Gemfile +@@ -18,6 +18,7 @@ gem 'devise', '~> 4.5' + gem 'dotenv-rails', '~> 2.6' + gem 'uglifier', '~> 4.1' + gem 'webpacker', '~> 4.0' ++gem 'sprockets', '<4' + + group :development do + gem 'awesome_print', '~> 1.8' +diff --git a/Gemfile.lock b/Gemfile.lock +deleted file mode 100644 +index efb03eb..0000000 +--- a/Gemfile.lock ++++ /dev/null +@@ -1,297 +0,0 @@ +-GEM +- remote: https://rubygems.org/ +- specs: +- actioncable (5.2.3) +- actionpack (= 5.2.3) +- nio4r (~> 2.0) +- websocket-driver (>= 0.6.1) +- actionmailer (5.2.3) +- actionpack (= 5.2.3) +- actionview (= 5.2.3) +- activejob (= 5.2.3) +- mail (~> 2.5, >= 2.5.4) +- rails-dom-testing (~> 2.0) +- actionpack (5.2.3) +- actionview (= 5.2.3) +- activesupport (= 5.2.3) +- rack (~> 2.0) +- rack-test (>= 0.6.3) +- rails-dom-testing (~> 2.0) +- rails-html-sanitizer (~> 1.0, >= 1.0.2) +- actionview (5.2.3) +- activesupport (= 5.2.3) +- builder (~> 3.1) +- erubi (~> 1.4) +- rails-dom-testing (~> 2.0) +- rails-html-sanitizer (~> 1.0, >= 1.0.3) +- active_model_serializers (0.10.9) +- actionpack (>= 4.1, < 6) +- activemodel (>= 4.1, < 6) +- case_transform (>= 0.2) +- jsonapi-renderer (>= 0.1.1.beta1, < 0.3) +- activejob (5.2.3) +- activesupport (= 5.2.3) +- globalid (>= 0.3.6) +- activemodel (5.2.3) +- activesupport (= 5.2.3) +- activerecord (5.2.3) +- activemodel (= 5.2.3) +- activesupport (= 5.2.3) +- arel (>= 9.0) +- activestorage (5.2.3) +- actionpack (= 5.2.3) +- activerecord (= 5.2.3) +- marcel (~> 0.3.1) +- activesupport (5.2.3) +- concurrent-ruby (~> 1.0, >= 1.0.2) +- i18n (>= 0.7, < 2) +- minitest (~> 5.1) +- tzinfo (~> 1.1) +- addressable (2.6.0) +- public_suffix (>= 2.0.2, < 4.0) +- after_commit_action (1.1.0) +- activerecord (>= 3.0.0) +- activesupport (>= 3.0.0) +- arel (9.0.0) +- awesome_print (1.8.0) +- bcrypt (3.1.12) +- better_errors (2.5.1) +- coderay (>= 1.0.0) +- erubi (>= 1.0.0) +- rack (>= 0.9.0) +- bindex (0.6.0) +- binding_of_caller (0.8.0) +- debug_inspector (>= 0.0.1) +- bootsnap (1.4.2) +- msgpack (~> 1.0) +- builder (3.2.3) +- capybara (3.16.1) +- addressable +- mini_mime (>= 0.1.3) +- nokogiri (~> 1.8) +- rack (>= 1.6.0) +- rack-test (>= 0.6.3) +- regexp_parser (~> 1.2) +- xpath (~> 3.2) +- case_transform (0.2) +- activesupport +- childprocess (0.9.0) +- ffi (~> 1.0, >= 1.0.11) +- coderay (1.1.2) +- concurrent-ruby (1.1.5) +- counter_culture (1.12.0) +- activerecord (>= 3.0.0) +- activesupport (>= 3.0.0) +- after_commit_action (~> 1.0) +- crass (1.0.4) +- database_cleaner (1.7.0) +- debug-extras (0.4.3) +- awesome_print (>= 1.6, < 1.9) +- railties (> 4.0, < 6) +- debug_inspector (0.0.3) +- devise (4.6.2) +- bcrypt (~> 3.0) +- orm_adapter (~> 0.1) +- railties (>= 4.1.0, < 6.0) +- responders +- warden (~> 1.2.3) +- diff-lcs (1.3) +- docile (1.1.5) +- dotenv (2.7.2) +- dotenv-rails (2.7.2) +- dotenv (= 2.7.2) +- railties (>= 3.2, < 6.1) +- erubi (1.8.0) +- execjs (2.7.0) +- factory_bot (4.11.1) +- activesupport (>= 3.0.0) +- factory_bot_rails (4.11.1) +- factory_bot (~> 4.11.1) +- railties (>= 3.0.0) +- faker (1.9.3) +- i18n (>= 0.7) +- ffi (1.10.0) +- globalid (0.4.2) +- activesupport (>= 4.2.0) +- i18n (1.6.0) +- concurrent-ruby (~> 1.0) +- json (2.2.0) +- jsonapi-renderer (0.2.0) +- listen (3.1.5) +- rb-fsevent (~> 0.9, >= 0.9.4) +- rb-inotify (~> 0.9, >= 0.9.7) +- ruby_dep (~> 1.2) +- loofah (2.2.3) +- crass (~> 1.0.2) +- nokogiri (>= 1.5.9) +- mail (2.7.1) +- mini_mime (>= 0.1.1) +- marcel (0.3.3) +- mimemagic (~> 0.3.2) +- method_source (0.9.2) +- mimemagic (0.3.3) +- mini_mime (1.0.1) +- mini_portile2 (2.4.0) +- minitest (5.11.3) +- msgpack (1.2.9) +- nio4r (2.3.1) +- nokogiri (1.10.3) +- mini_portile2 (~> 2.4.0) +- orm_adapter (0.5.0) +- pg (1.1.4) +- pry (0.12.2) +- coderay (~> 1.1.0) +- method_source (~> 0.9.0) +- pry-rails (0.3.9) +- pry (>= 0.10.4) +- public_suffix (3.0.3) +- puma (3.12.1) +- rack (2.0.7) +- rack-proxy (0.6.5) +- rack +- rack-test (1.1.0) +- rack (>= 1.0, < 3) +- rails (5.2.3) +- actioncable (= 5.2.3) +- actionmailer (= 5.2.3) +- actionpack (= 5.2.3) +- actionview (= 5.2.3) +- activejob (= 5.2.3) +- activemodel (= 5.2.3) +- activerecord (= 5.2.3) +- activestorage (= 5.2.3) +- activesupport (= 5.2.3) +- bundler (>= 1.3.0) +- railties (= 5.2.3) +- sprockets-rails (>= 2.0.0) +- rails-controller-testing (1.0.4) +- actionpack (>= 5.0.1.x) +- actionview (>= 5.0.1.x) +- activesupport (>= 5.0.1.x) +- rails-dom-testing (2.0.3) +- activesupport (>= 4.2.0) +- nokogiri (>= 1.6) +- rails-html-sanitizer (1.0.4) +- loofah (~> 2.2, >= 2.2.2) +- railties (5.2.3) +- actionpack (= 5.2.3) +- activesupport (= 5.2.3) +- method_source +- rake (>= 0.8.7) +- thor (>= 0.19.0, < 2.0) +- rake (12.3.2) +- rb-fsevent (0.10.3) +- rb-inotify (0.10.0) +- ffi (~> 1.0) +- regexp_parser (1.4.0) +- responders (2.4.1) +- actionpack (>= 4.2.0, < 6.0) +- railties (>= 4.2.0, < 6.0) +- rspec-core (3.8.0) +- rspec-support (~> 3.8.0) +- rspec-expectations (3.8.2) +- diff-lcs (>= 1.2.0, < 2.0) +- rspec-support (~> 3.8.0) +- rspec-mocks (3.8.0) +- diff-lcs (>= 1.2.0, < 2.0) +- rspec-support (~> 3.8.0) +- rspec-rails (3.8.2) +- actionpack (>= 3.0) +- activesupport (>= 3.0) +- railties (>= 3.0) +- rspec-core (~> 3.8.0) +- rspec-expectations (~> 3.8.0) +- rspec-mocks (~> 3.8.0) +- rspec-support (~> 3.8.0) +- rspec-support (3.8.0) +- ruby_dep (1.5.0) +- rubyzip (1.2.2) +- selenium-webdriver (3.141.0) +- childprocess (~> 0.5) +- rubyzip (~> 1.2, >= 1.2.2) +- shoulda-matchers (3.1.3) +- activesupport (>= 4.0.0) +- simplecov (0.14.1) +- docile (~> 1.1.0) +- json (>= 1.8, < 3) +- simplecov-html (~> 0.10.0) +- simplecov-html (0.10.2) +- spring (2.0.2) +- activesupport (>= 4.2) +- spring-watcher-listen (2.0.1) +- listen (>= 2.7, < 4.0) +- spring (>= 1.2, < 3.0) +- sprockets (3.7.2) +- concurrent-ruby (~> 1.0) +- rack (> 1, < 3) +- sprockets-rails (3.2.1) +- actionpack (>= 4.0) +- activesupport (>= 4.0) +- sprockets (>= 3.0.0) +- thor (0.20.3) +- thread_safe (0.3.6) +- tzinfo (1.2.5) +- thread_safe (~> 0.1) +- uglifier (4.1.20) +- execjs (>= 0.3.0, < 3) +- warden (1.2.8) +- rack (>= 2.0.6) +- web-console (3.7.0) +- actionview (>= 5.0) +- activemodel (>= 5.0) +- bindex (>= 0.4.0) +- railties (>= 5.0) +- webdrivers (3.8.0) +- nokogiri (~> 1.6) +- rubyzip (~> 1.0) +- selenium-webdriver (~> 3.0) +- webpacker (4.0.7) +- activesupport (>= 4.2) +- rack-proxy (>= 0.6.1) +- railties (>= 4.2) +- websocket-driver (0.7.0) +- websocket-extensions (>= 0.1.0) +- websocket-extensions (0.1.3) +- xpath (3.2.0) +- nokogiri (~> 1.8) +- +-PLATFORMS +- ruby +- +-DEPENDENCIES +- active_model_serializers (~> 0.10.8) +- awesome_print (~> 1.8) +- better_errors (~> 2.4) +- binding_of_caller (~> 0.8.0) +- bootsnap (~> 1.3) +- capybara (~> 3.15) +- counter_culture (~> 1.12) +- database_cleaner (~> 1.7) +- debug-extras +- devise (~> 4.5) +- dotenv-rails (~> 2.6) +- factory_bot_rails (~> 4.11) +- faker (~> 1.9) +- listen (~> 3.1) +- pg (~> 1.1, >= 1.1.4) +- pry-rails +- puma (~> 3.12) +- rails (~> 5.2, >= 5.2.3) +- rails-controller-testing (~> 1.0) +- rspec-rails (~> 3.8) +- selenium-webdriver (~> 3.13) +- shoulda-matchers (~> 3.1) +- simplecov (~> 0.14.1) +- spring +- spring-watcher-listen (~> 2.0) +- tzinfo-data +- uglifier (~> 4.1) +- web-console (~> 3.7) +- webdrivers (~> 3.0) +- webpacker (~> 4.0) +- +-RUBY VERSION +- ruby 2.6.1p33 +- +-BUNDLED WITH +- 1.17.2 +-- +2.37.3 + diff --git a/docker/docker-snibox/README.md b/docker/docker-snibox/README.md @@ -0,0 +1,6 @@ +SNIBOX DOCKERFILE +----------------- + +* Clone the original sources from https://github.com/snibox/snibox.git +* Apply the patch 0001 +* Run the docker build process diff --git a/hcl/default/0x0/0x0.nomad b/hcl/default/0x0/0x0.nomad @@ -0,0 +1,126 @@ +job "0x0" { + datacenters = ["dc1"] + + group "server" { + count = 1 + + volume "0x0" { + type = "csi" + source = "0x0" + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" + } + + volume "tls" { + type = "csi" + source = "certbot" + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" + } + + network { + port "fhost" { + to = 8000 + } + port "http" { + to = 8999 + static = 8999 + } + port "https" { + to = 8998 + static = 8998 + } + } + + # Prepare database migrations + task "db-upgrade" { + driver = "docker" + + volume_mount { + volume = "0x0" + destination = "/usr/src/app/0x0/data" + } + + env { + FLASK_APP = "fhost" + } + + config { + image = "127.0.0.1:5000/0x0:latest" + command = "/bin/sh" + args = ["-c", "flask db upgrade"] + } + + resources { + memory = 128 + cpu = 200 + } + + lifecycle { + hook = "prestart" + sidecar = false + } + } + + task "nginx" { + driver = "docker" + + config { + image = "nginx:stable-alpine" + ports = ["https", "http"] + volumes = [ + # mount the templated config from the task directory to the container + "local/0x0.conf:/etc/nginx/conf.d/0x0.conf", + ] + } + + volume_mount { + volume = "tls" + destination = "/etc/letsencrypt" + } + + template { + destination = "${NOMAD_TASK_DIR}/0x0.conf" + data = file("./templates/nginx.conf.tmpl") + } + + resources { + memory = 32 + cpu = 50 + } + } + + task "fhost" { + driver = "docker" + + config { + image = "127.0.0.1:5000/0x0:latest" + ports = ["fhost"] + volumes = [ + # mount the templated config from the task directory to the container + "local/config.py:/usr/src/app/0x0/instance/config.py", + "local/index.html:/usr/src/app/0x0/templates/index.html", + ] + } + + volume_mount { + volume = "0x0" + destination = "/usr/src/app/0x0/data" + } + + template { + destination = "${NOMAD_TASK_DIR}/config.py" + data = file("./templates/config.py.tmpl") + } + template { + destination = "${NOMAD_TASK_DIR}/index.html" + data = file("./templates/index.html.tmpl") + } + + resources { + memory = 128 + cpu = 200 + } + } + } +} diff --git a/hcl/default/0x0/README b/hcl/default/0x0/README @@ -0,0 +1,6 @@ +0x0 FILE HOSTING +---------------- + +Add the the no-bullshit file hosting domain name to the Consul kv path "0x0/host". + +TODO: Add periodic cleanup job diff --git a/hcl/default/0x0/data-volume.hcl b/hcl/default/0x0/data-volume.hcl @@ -0,0 +1,31 @@ +# Register external nfs volume with Nomad CSI +# https://www.nomadproject.io/docs/commands/volume/register +type = "csi" +# Unique ID of the volume, volume.source field in a job +id = "0x0" +# Display name of the volume. +name = "0x0" +# ID of the physical volume from the storage provider +external_id = "csi-0x0" +plugin_id = "nfs" + +# You must provide at least one capability block +# You must provide a block for each capability +# youintend to use in a job's volume block +# https://www.nomadproject.io/docs/commands/volume/register +capability { + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" +} + +# https://github.com/kubernetes-csi/csi-driver-nfs/blob/master/docs/driver-parameters.md +context { + server = "192.168.1.1" + share = "csi-0x0" +} + +mount_options { + # mount.nfs: Either use '-o nolock' to keep locks local, or start statd. + mount_flags = ["nolock"] +} + diff --git a/hcl/default/0x0/templates/config.py.tmpl b/hcl/default/0x0/templates/config.py.tmpl @@ -0,0 +1,21 @@ +"""0x0 Flask configuration""" + +import os + +# Local config variables +appdir = "/usr/src/app/0x0" +datadir = f"{appdir}/data" + +# The path to the sqlite database on the fhost +SQLALCHEMY_DATABASE_URI = f"sqlite:///{datadir}/sqlite.db" + +# Relative path to file storage on the fhost +FHOST_STORAGE_PATH = "data/files" + +# Backend and proxy are different containers, +# files are only available on fhost +FHOST_USE_X_ACCEL_REDIRECT = False + +# Generate the external url with url_for (returned to the user) +# https://flask.palletsprojects.com/en/latest/config/#SERVER_NAME +SERVER_NAME = "{{ key "0x0/host" }}" diff --git a/hcl/default/0x0/templates/index.html.tmpl b/hcl/default/0x0/templates/index.html.tmpl @@ -0,0 +1,55 @@ +<pre> +THE NULL POINTER +================ +{{- $fhost := key "0x0/host" }} +{{- $fhost_url := (printf "http://%s" $fhost) }} +HTTP POST files here: + curl -F'file=@yourfile.png' {{ $fhost }} +You can also POST remote URLs: + curl -F'url=http://example.com/image.jpg' {{ $fhost }} +Or you can shorten URLs: + curl -F'shorten=http://example.com/some/long/url' {{ $fhost }} + +File URLs are valid for at least 30 days and up to a year (see below). +Shortened URLs do not expire. +{% set max_size = config["MAX_CONTENT_LENGTH"]|filesizeformat(True) %} +Maximum file size: {{`{{ max_size }}`}} +Not allowed: {{`{{ config["FHOST_MIME_BLACKLIST"]|join(", ") }}`}} +</pre> + +<pre> +OPERATOR NOTES +-------------- + +This instance is proudly powered by mias code: +https://git.0x0.st/mia/0x0 +</pre> + +<pre> +FILE RETENTION PERIOD +--------------------- + +retention = min_age + (-max_age + min_age) * pow((file_size / max_size - 1), 3) + + days + 365 | \\ + | \\ + | \\ + | \\ + | \\ + | \\ + | .. + | \\ + 197.5 | ----------..------------------------------------------- + | .. + | \\ + | .. + | ... + | .. + | ... + | .... + | ...... + 30 | .................... + 0{{`{{ ((config["MAX_CONTENT_LENGTH"]/2)|filesizeformat(True)).split(" ")[0].rjust(27) }}{{ max_size.split(" ")[0].rjust(27) }}`}} + {{`{{ max_size.split(" ")[1].rjust(54) }}`}} +</pre> diff --git a/hcl/default/0x0/templates/nginx.conf.tmpl b/hcl/default/0x0/templates/nginx.conf.tmpl @@ -0,0 +1,22 @@ +server { + listen 8999; + + location / { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host {{ key "0x0/host" }}; + proxy_pass http://{{ env "NOMAD_ADDR_fhost" }}; + } +} + +server { + listen 8998 ssl; + + ssl_certificate /etc/letsencrypt/live/0x0.in0rdr.ch/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/0x0.in0rdr.ch/privkey.pem; + + location / { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host {{ key "0x0/host" }}; + proxy_pass http://{{ env "NOMAD_ADDR_fhost" }}; + } +} diff --git a/hcl/default/aload/aload.nomad b/hcl/default/aload/aload.nomad @@ -0,0 +1,49 @@ +job "aload" { + datacenters = ["dc1"] + + group "server" { + count = 1 + + volume "ampache" { + type = "csi" + source = "ampache" + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" + } + + network { + port "http" { + to = 80 + static = 8092 + } + } + + task "aload" { + driver = "docker" + + config { + image = "127.0.0.1:5000/aload:latest" + ports = ["http"] + volumes = [ + # mount the templated config from the task directory to the container + "local/aload.conf:/etc/apache2/conf.d/aload.conf", + ] + } + + template { + destination = "${NOMAD_TASK_DIR}/aload.conf" + data = file("./templates/aload.conf.tmpl") + } + + volume_mount { + volume = "ampache" + destination = "/var/www/localhost/ampache" + } + + resources { + memory = 64 + cpu = 100 + } + } + } +} diff --git a/hcl/default/aload/templates/aload.conf.tmpl b/hcl/default/aload/templates/aload.conf.tmpl @@ -0,0 +1,15 @@ +<VirtualHost *:80> +ServerName aload.in0rdr.ch +DocumentRoot /var/www/localhost/ampache + +ErrorLog /dev/stderr +TransferLog /dev/stdout + +# Enable CGI +LoadModule cgi_module modules/mod_cgi.so + +# Allow youtube downloads of up to 5min +TimeOut 300 + +ScriptAlias / /var/www/localhost/cgi-bin/aload/ +</VirtualHost> diff --git a/hcl/default/ampache/ampache-catalog.nomad b/hcl/default/ampache/ampache-catalog.nomad @@ -0,0 +1,52 @@ +job "ampache-catalog" { + datacenters = ["dc1"] + type = "batch" + + periodic { + #cron = "*/12 * * * *" + cron = "@daily" + } + + group "catalog-update" { + count = 1 + + volume "ampache" { + type = "csi" + source = "ampache" + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" + } + + task "update-catalog" { + driver = "docker" + + config { + image = "ampache/ampache:nosql5.5.2" + command = "/bin/sh" + # [-c|--cleanup] [-e|--verify] [-a|--add] [-g|--art] + args = ["-c", "/var/www/bin/cli run:updateCatalog -ceag"] + volumes = [ + # mount the templated config from the task directory to the container + "local/ampache.cfg.php:/var/www/config/ampache.cfg.php", + ] + } + + volume_mount { + volume = "ampache" + destination = "/media" + } + + template { + destination = "${NOMAD_TASK_DIR}/ampache.cfg.php" + data = file("./templates/ampache.cfg.php.tmpl") + uid = 33 + gid = 33 + } + + resources { + memory = 256 + cpu = 100 + } + } + } +} diff --git a/hcl/default/ampache/ampache.nomad b/hcl/default/ampache/ampache.nomad @@ -0,0 +1,94 @@ +job "ampache" { + datacenters = ["dc1"] + + vault { + policies = ["ampache"] + change_mode = "noop" + } + + group "server" { + count = 1 + + volume "tls" { + type = "csi" + source = "certbot" + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" + } + volume "ampache" { + type = "csi" + source = "ampache" + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" + } + + network { + port "ampache" { + to = 80 + } + port "https" { + to = 443 + static = 44391 + } + } + + task "nginx" { + driver = "docker" + + config { + image = "nginx:stable-alpine" + ports = ["https"] + volumes = [ + # mount the templated config from the task directory to the container + "local/ampache.conf:/etc/nginx/conf.d/ampache.conf", + ] + } + + volume_mount { + volume = "tls" + destination = "/etc/letsencrypt" + } + + template { + destination = "${NOMAD_TASK_DIR}/ampache.conf" + data = file("./templates/nginx.conf.tmpl") + } + + resources { + memory = 256 + cpu = 200 + } + } + + task "ampache" { + driver = "docker" + + config { + image = "ampache/ampache:nosql" + ports = ["ampache"] + volumes = [ + # mount the templated config from the task directory to the container + "local/ampache.cfg.php:/var/www/config/ampache.cfg.php", + ] + } + + volume_mount { + volume = "ampache" + destination = "/media" + } + + template { + destination = "${NOMAD_TASK_DIR}/ampache.cfg.php" + data = file("./templates/ampache.cfg.php.tmpl") + uid = 33 + gid = 33 + } + + resources { + memory = 512 + cpu = 300 + } + } + + } +} diff --git a/hcl/default/ampache/data-volume.hcl b/hcl/default/ampache/data-volume.hcl @@ -0,0 +1,31 @@ +# Register external nfs volume with Nomad CSI +# https://www.nomadproject.io/docs/commands/volume/register +type = "csi" +# Unique ID of the volume, volume.source field in a job +id = "ampache" +# Display name of the volume. +name = "ampache" +# ID of the physical volume from the storage provider +external_id = "csi-ampache" +plugin_id = "nfs" + +# You must provide at least one capability block +# You must provide a block for each capability +# youintend to use in a job's volume block +# https://www.nomadproject.io/docs/commands/volume/register +capability { + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" +} + +# https://github.com/kubernetes-csi/csi-driver-nfs/blob/master/docs/driver-parameters.md +context { + server = "192.168.1.1" + share = "csi-ampache" +} + +mount_options { + # mount.nfs: Either use '-o nolock' to keep locks local, or start statd. + mount_flags = ["nolock"] +} + diff --git a/hcl/default/ampache/templates/ampache.cfg.php.dist b/hcl/default/ampache/templates/ampache.cfg.php.dist @@ -0,0 +1,1352 @@ +;#<?php exit(); ?>## +;######################################################### +; General Config # +;######################################################### + +; This value is used to detect if this config file is up to date +; this is compared against a constant called CONFIG_VERSION +; that is located in src/Config/Init/InitializationHandlerConfig.php +config_version = 62 + +;######################################################### +; Auto Update # +;######################################################### + +; Allow you to hard code a default git branch for Ampache +; If you set this value the inbuilt updater will use this branch for updates. +; POSSIBLE VALUES: master develop +; DEFAULT: none +;github_force_branch = "develop" + +; This value allows to override the composer binary path to distinguish between multiple composer versions +; Either a binary name in $PATH as well as a fully qualified path is possible +; DEFAULT: composer +;composer_binary_path = "composer" + +; We sometimes need to talk and will show a warning to admin users +; Enable this setting if you don't want to see warnings (When we enable them) +; DEFAULT: false +;hide_ampache_messages = "true" + +;######################################################### +; Path Vars # +;######################################################### + +; The public http host of your server. +; If not set, retrieved automatically from client request. +; This setting is required for WebSocket server +; DEFAULT: none +;http_host = "localhost" + +; The public http port of your server. +; If not set, retrieved automatically from client request. +; DEFAULT: none +;http_port = 80 + +; The public path to your Ampache install +; Do not put a trailing / on this path +; For example if your site is located at http://localhost +; than you do not need to enter anything for the web_path +; if it is located at http://localhost/music you need to +; set web_path to /music +; DEFAULT: none +;web_path = "" + +; The local http url of your server. +; This is used to access the server from within the +; same host where ampache is running. +; For example, if the ampache server is not +; directly accessed via the public domain but via a reverse +; proxy, local_web_path would need to be changed +; to a localhost URL. +; If not set, retrieved automatically from server information. +; DEFAULT: none +;local_web_path = "http://localhost/ampache" + +;######################################################### +; Database # +;######################################################### + +; Hostname of your database +; For socket authentication, set the path to socket file (e.g. /var/run/mysqld/mysqld.sock) +; DEFAULT: localhost +database_hostname = localhost + +; Port to use when connecting to your database +; DEFAULT: none +;database_port = 3306 + +; Name of your Ampache database +; DEFAULT: none +database_name = ampache + +; Username for your Ampache database +; DEFAULT: none +database_username = username + +; Password for your Ampache database, this can not be blank +; this is a 'forced' security precaution, the default value +; will not work (except if using socket authentication) +; DEFAULT: none +database_password = password + +; Set a default charset for your database +; Don't change this unless you understand how to BACKUP and RESTORE a database! +; +; DEFAULT: "utf8mb4" +;database_charset = "utf8mb4" + +; Set a default collation for your database +; Don't change this unless you understand how to BACKUP and RESTORE a database! +; +; There are a ton of options but you'll probably want one of these. +; "utf8_unicode_ci" = Regular unicode (3 bytes per character) +; "utf8mb4_unicode_ci" = 4 bytes per character +; "utf8mb4_unicode_520_ci" = Supports more characters and is based on UCA 5.2.0 weight keys +; http://www.unicode.org/Public/UCA/5.2.0/allkeys.txt +; DEFAULT: "utf8mb4_unicode_ci" +;database_collation = "utf8mb4_unicode_ci" + +;######################################################### +; Session and Security # +;######################################################### + +; Cryptographic secret +; This MUST BE changed with your own secret key. Ampache-specific, just pick any random string you want. +secret_key = "abcdefghijklmnoprqstuvwyz0123456" + +; Length that a session will last expressed in seconds. Default is +; one hour. +; DEFAULT: 3600 +session_length = 3600 + +; Length that the session for a single streaming instance will last +; the default is two hours. With some clients, and long songs this can +; cause playback to stop, increase this value if you experience that +; DEFAULT: 7200 +stream_length = 7200 + +; This length defines how long a 'remember me' session and cookie will +; last, the default is 86400, same as length. It is up to the administrator +; of the box to increase this, for reference 86400 = 1 day, +; 604800 = 1 week, and 2419200 = 1 month +; DEFAULT: 604800 +remember_length = 604800 + +; Name of the Session/Cookie that will sent to the browser +; default should be fine +; DEFAULT: ampache +session_name = ampache + +; Lifetime of the Cookie, 0 == Forever (until browser close) , otherwise in terms of seconds +; If you want cookies to last past a browser close set this to a value in seconds. +; DEFAULT: 0 +session_cookielife = 0 + +; Is the cookie a "secure" cookie? This should only be set to 1 (true) if you are +; running a secure site (HTTPS). +; DEFAULT: 0 +session_cookiesecure = 0 + +; Auth Methods +; This defines which auth methods Auth will attempt to use and in which order. +; If auto_create isn't enabled the user must exist locally. +; DEFAULT: mysql +; VALUES: mysql,ldap,http,pam,external,openid +auth_methods = "mysql" + +; External authentication +; This sets the helper used for external authentication. It should conform to +; the interface used by mod_authnz_external +; DEFAULT: none +;external_authenticator = "/usr/sbin/pwauth" + +; Automatic local password updating +; Determines whether successful authentication against an external source +; will result in an update to the password stored in the database. +; A locally stored password is needed for API access. +; DEFAULT: "false" +;auth_password_save = "true" + +; Log out redirection target +; Defaults to our own login.php, but we can override it here if, for instance, +; we want to redirect to an SSO provider instead. +;logout_redirect = "http://sso.example.com/logout" + +; Use Access List +; Toggle this on if you want Ampache to pay attention to the access list +; and only allow streaming/downloading/api-rpc from known hosts api-rpc +; will not work without this on. +; NOTE: Default Behavior is DENY FROM ALL +; DEFAULT: "true" +access_control = "true" + +; Require Session +; If this is set to true Ampache will make sure that the URL passed when +; attempting to retrieve a song contains a valid Session ID This prevents +; others from guessing URL's. This setting is ignored if you have use_auth +; disabled. +; DEFAULT: "true" +require_session = "true" + +; Require LocalNet Session +; If this is set to true then Ampache will require that a valid session +; is passed even on hosts defined in the Local Network ACL. This setting +; has no effect if access_control is not enabled +; DEFAULT: "true" +require_localnet_session = "true" + +; Multiple Logins +; Added by Vlet 07/25/07 +; When this setting is enabled a user may only be logged in from a single +; IP address at any one time, this is to prevent sharing of accounts +; DEFAULT: "false" +;prevent_multiple_logins = "true" + +; Allow Embedding Ampache in Frames +; Whether a browser should be allowed to render a page in a <frame>, <iframe>, <embed> or <object>. +; This is used to avoid click-jacking attacks, by ensuring that their content is not embedded into other sites. +; By default Ampache pages can only be displayed in a frame on the same origin as the page itself. +; DEFAULT: "false" +;disable_xframe_sameorigin = "true" + +;######################################################### +; Metadata # +;######################################################### + +; This determines the tag order for all cataloged +; music. If none of the listed tags are found then +; Ampache will randomly use whatever was found. +; POSSIBLE VALUES: ape asf avi id3v1 id3v2 lyrics3 matroska mpeg quicktime riff +; vorbiscomment +; DEFAULT: vorbiscomment id3v2 id3v1 quicktime matroska ape asf avi mpeg riff +getid3_tag_order = "vorbiscomment,id3v2,id3v1,quicktime,matroska,ape,asf,avi,mpeg,riff" + +; This determines whether we try to autodetect the encoding for id3v2 tags. +; May break valid tags. +; DEFAULT: "false" +;getid3_detect_id3v2_encoding = "true" + +; This determines if we write the changes to files (as id3 tags) when modifying metadata, or only keep them in Ampache (the default). +; DEFAULT: "false" +;write_id3 = "true" + +; This determines if we write the changes to files (as id3 tags) when modifying album art, or only keep them in Ampache (the default) +; as id3 metadata when updated. +; DEFAULT: "false" +;write_id3_art = "true" + +; This determines the order in which metadata sources are used (and in the +; case of plugins, checked) +; POSSIBLE VALUES (builtins): filename and getID3 +; POSSIBLE VALUES (plugins): MusicBrainz,TheAudioDb, plus any others you've installed. +; DEFAULT: getID3 filename +metadata_order = "getID3,MusicBrainz,TheAudioDb,filename" + +; This determines the order in which metadata sources are used (and in the +; case of plugins, checked) for video files +; POSSIBLE VALUES (builtins): filename and getID3 +; POSSIBLE VALUES (plugins): Tvdb,Tmdb,Omdb, plus any others you've installed. +; DEFAULT: filename getID3 +metadata_order_video = "filename,getID3" + +; This determines if extended metadata grabbed from external services should be deferred. +; If enabled, extended metadata is retrieved when browsing the library item. +; If disabled, extended metadata is retrieved at catalog update. +; Today, only Artist information (summary, place formed, ...) can be deferred. +; DEFAULT: "true" +deferred_ext_metadata = "true" + +; Some taggers use delimiters other than \0 for fields +; This list specifies possible delimiters additional to \0 +; This setting takes a regex pattern. TODO: explain that this is not just for genres until we can replace this safely +; DEFAULT: // / \ | , ; +additional_genre_delimiters = "[/]{2}|[/\\\\|,;]" + +; Enable importing custom metadata from files. +; This will need a bit of time during the import. So you may want to disable this +; if you have troubles with huge databases. +; DEFAULT: "false" +;enable_custom_metadata = "true" + +;######################################################### +; File Tags # +;######################################################### + +; This determines if we write metadata to files when modifying metadata, or only keep them in Ampache (the default). +; DEFAULT: "false" +;write_tags = "true" + +;######################################################### +; Catalog # +;######################################################### + +; File Pattern +; This defines which file types Ampache will attempt to catalog +; You can specify any file extension you want in here separating them with a | +; DEFAULT: mp3|mpc|m4p|m4a|aac|ogg|oga|wav|aif|aiff|rm|wma|asf|flac|opus|spx|ra|ape|shn|wv +catalog_file_pattern = "mp3|mpc|m4p|m4a|aac|ogg|oga|wav|aif|aiff|rm|wma|asf|flac|opus|spx|ra|ape|shn|wv" + +; Video Pattern +; This defines which video file types Ampache will attempt to catalog +; You can specify any file extension you want in here separating them with +; a | but Ampache may not be able to parse them +; DEAFULT: avi|mpg|mpeg|flv|m4v|mp4|webm|mkv|wmv|ogv|mov|divx|m2ts +catalog_video_pattern = "avi|mpg|mpeg|flv|m4v|mp4|webm|mkv|wmv|ogv|mov|divx|m2ts" + +; Playlist Pattern +; This defines which playlist types Ampache will attempt to catalog +; You can specify any file extension you want in here separating them with +; a | but Ampache may not be able to parse them +; DEFAULT: m3u|m3u8|pls|asx|xspf +catalog_playlist_pattern = "m3u|m3u8|pls|asx|xspf" + +; Prefix Pattern +; This defines which prefix Ampache will ignore when importing tags from +; your music. You may add any prefix you want separating them with a | +; DEFAULT: The|An|A|Die|Das|Ein|Eine|Les|Le|La +catalog_prefix_pattern = "The|An|A|Die|Das|Ein|Eine|Les|Le|La" + +; Ignore Pattern +; Ignore files that match this pattern +; You can specify any file extension you want in here separating them with a | +; DEFAULT: None +; catalog_ignore_pattern = "\(HTOA\)" + +; Catalog disable +; This defines if catalog can be disabled without removing database entries +; WARNING: this increase sensibly sql requests and slow down Ampache a lot +; DEFAULT: "false" +;catalog_disable = "true" + +; Catalog filter +; This defines if catalog can be filtered per user. +; The filters are set in the catalog management pages. +; WARNING: this increase sensibly sql requests and slow down Ampache a lot +; DEFAULT: false +;catalog_filter = "true" + +; Delete from disk +; This determines if catalog manager users can delete media from disk. +; DEFAULT: "false" +;delete_from_disk = "true" + +; Catalog verify by time +; Only verify the files that have been modified since the last verify. +; For large catalogs the verify process can be terribly long so this will help +; speed things up by checking modification date using filemtime() before loading +; DEFAULT: "false" +;catalog_verify_by_time = "true" + +;######################################################### +; Program Settings # +;######################################################### + +; Downsample Remote +; If this is set to true and access control is on any users who are not +; coming from a defined 'network' ACL will be automatically downsampled +; regardless of their preferences. Requires access_control to be enabled +; DEFAULT: "false" +;downsample_remote = "true" + +; Track User IPs +; If this is enabled Ampache will log the IP of every completed login +; it will store user, ip and time at one row per login. The results are +; displayed in Admin --> Users +; DEFAULT: "false" +;track_user_ip = "true" + +; User IP Cardinality +; This defines how many days worth of IP history Ampache will track +; As it is one row per login on high volume sites you will want to +; clear it every now and then. +; DEFAULT: 42 days +;user_ip_cardinality = "42" + +; Allow Zip Download +; This setting allows/disallows using zlib to zip up an entire +; playlist/album for download. Even if this is turned on you will +; still need to enabled downloading for the specific user you +; want to be able to use this function +; DEFAULT: "false" +;allow_zip_download = "true" + +; Allow Zip Types +; This setting allows/disallows zip download of specific object types +; If empty, all supported object types can be zipped. +; Otherwise, only the given object list can be zipped. +; POSSIBLE VALUES: artist, album, playlist, search, tmp_playlist +; DEFAULT: none +;allow_zip_types = "album" + +; Art Zip Add +; This settings allows/disallows to include Album Art to the Zip +; If 'album_art_preferred_filename' exists this is included +; DEFAULT: false +;art_zip_add = "true" + +; File Zip Comment +; This is an optional configuration option that adds a comment +; to your zip files, this only applies if you've got allow_zip_downloads +; DEFAULT: Ampache - Zip Batch Download +;file_zip_comment = "Ampache - Zip Batch Download" + +; Load the debug webplayer +; This will load the *.js player instead of the *.min.js player +; The unminified player has a lot of console.log() statements in the code. +; You can make changes and then check how the player is functioning. +; DEFAULT: "false" +;webplayer_debug = "true" + +; Waveform +; This settings tells Ampache to attempt to generate a waveform +; for each song. It requires transcode and encode_args_wav settings. +; You must also set tmp_dir_path in order for this to work +; DEFAULT: "false" +;waveform = "true" + +; Waveform color +; The waveform color. +; DEFAULT: #FF0000 +;waveform_color = "#FF0000" + +; Waveform height +; The waveform height. +; DEFAULT: 32 +;waveform_height = 32 + +; Waveform width +; The waveform width. +; DEFAULT: 400 +;waveform_width = 400 + +; Temporary Directory Path +; If Waveform is enabled this must be set to tell +; Ampache which directory to save the temporary file to. Do not put a +; trailing slash or this will not work. +; DEFAULT: "false" +;tmp_dir_path = "/tmp" + +; This setting throttles a persons downloading to the specified +; bytes per second. This is not a 100% guaranteed function, and +; you should really use a server based rate limiter if you want +; to do this correctly. +; DEFAULT: off +; VALUES: any whole number (in bytes per second) +;throttle_download = 10 + +; This determines if a preview image should be retrieved from video files +; It requires encode_get_image transcode settings. +; DEFAULT: "false" +;generate_video_preview = "true" + +; Uncomment if don't want Ampache to follow symlinks +; DEFAULT: "false" +;no_symlinks = "true" + +; Use auth? +; If this is set to "true" Ampache will require a valid +; Username and password. If this is set to false then Ampache +; will not ask you for a username and password. false is only +; recommended for internal only instances +; DEFAULT: "true" +use_auth = "true" + +; Default Auth Level +; If use_auth is set to false then this option is used +; to determine the permission level of the 'default' users +; default is administrator. This setting only takes affect +; if use_auth is false +; POSSIBLE VALUES: user, admin, manager, guest +; DEFAULT: guest +default_auth_level = "guest" + +; Skip Timer Threshold +; This allows custom times to decide when a track is skipped +; Allows an integer to denote seconds, or a float to denote percentage +; POSSIBLE VALUES: +; 20 = 20 seconds. +; 0.3 = 30% +; DEFAULT: 20 +;skip_timer = 20 + +; 5 Star Ratings +; This allows ratings for almost any object in Ampache +: It also allows users to flag objects as a favorite +; POSSIBLE VALUES: false true +; DEFAULT: "true" +ratings = "true" + +; Enable filtering on browse pages for artists and albums. +; If you enable this setting the get_random and browse pages +; will remove artists and albums that are <= to that rating +; DEFAULT: "false" +;rating_browse_filter = "true" + +; Set the rating that will be filtered +; e.g. 2 will filter 1 and 2 star albums and artists +; this setting must be set for the filter to work +; DEFAULT: null +;rating_browse_minimum_stars = 1 + +; By default Ampache assigns the rating set in files tags to +; the system user (-1). If you would like ALL file tags to be +; assigned to a specific user set their user ID here. +; DEFAULT: -1 +;rating_file_tag_user = 1 + +; Direct play +; This allows user to play directly a song or album +; POSSIBLE VALUES: false true +; DEFAULT: "true" +directplay = "true" + +; Sociable +; This turns on / off all of the "social" features of Ampache +; default is on, but if you don't care and just want music +; turn this off to disable all social features. +; DEFAULT: "true" +sociable = "true" + +; License +; This turns on / off all licensing features on Ampache +; DEFAULT: "false" +;licensing = "true" + +; This options will turn on/off Demo Mode +; If Demo mode is on you can not play songs or update your catalog +; in other words.. leave this commented out +; DEFAULT: "false" +;demo_mode = "true" + +; This options will turn on/off Simple User Mode +; If simple_user_mode is true you can not edit your profile or make +; changes to your account unless you are an admin +; This is for shared accounts or simple sites like the ampache demo +; DEFAULT: "false" +;simple_user_mode = "true" + +; Caching +; This turns the caching mechanisms on or off, due to a large number of +; problems with people with very large catalogs and low memory settings +; this is off by default as it does significantly increase the memory +; requirements on larger catalogs. If you have the memory this can create +; a 2-3x speed improvement. +; DEFAULT: "false" +;memory_cache = "true" + +; Memory Limit +; This defines the "Min" memory limit for PHP if your php.ini +; has a lower value set Ampache will set it up to this. If you +; set it below 16MB getid3() will not work! +; DEFAULT: 32 +;memory_limit = 32 + +; Album Art Preferred Filename +; Specify a filename to look for if you always give the same filename +; i.e. "folder.jpg" Ampache currently only supports jpg/gif and png +; Especially useful if you have a front and a back image in a folder +; comment out if Ampache should search for any jpg, gif or png +; DEFAULT: folder.jpg +;album_art_preferred_filename = "folder.jpg" + +; Artist Art Preferred Filename +; Specify a filename to look for artist specific art, either in the +; same folder as your files or in the parent folder +; e.g. /mnt/music/Artist/Album/artist.jpg +; /mnt/music/Artist/artist.jpg +; DEFAULT: folder.jpg +;artist_art_preferred_filename = "folder.jpg" + +; Artist Art Dump Folder +; Specify a local folder to search for art using name/title to identify +; i.e. "/var/www/art/" is filled with artist images named by artist +; e.g. "Megadeth.jpg", "Judas Priest.png", "The Smashing Pumpkins.jpg" +; When enabled; gather_folder will check this folder for artist images +; DEFAULT: "false" +;artist_art_folder = "/var/www/art" + +; Album Art Store on Disk +; This defines if arts should be stored on disk instead of database. +; DEFAULT: "false" +;album_art_store_disk = "true" + +; Local Metadata Directory +; This define a local metadata directory with write access where to store +; heavy data if enabled (album arts, ...) +; DEFAULT: none +;local_metadata_dir = "/metadata" + +; Maximal upload size +; Specify the maximal allowed upload size for images, in bytes. +; DEFAULT: 1048576 +;max_upload_size = 1048576 + +; Album Art Minimum Width +; Specify the minimum width for arts (in pixel). +; DEFAULT: none +;album_art_min_width = 100 + +; Album Art Maximum Width +; Specify the maximum width for arts (in pixel). +; DEFAULT: none +;album_art_max_width = 1024 + +; Album Art Minimum Height +; Specify the minimum height for arts (in pixel). +; DEFAULT: none +;album_art_min_height = 100 + +; Album Art Maximum Height +; Specify the maximum height for arts (in pixel). +; DEFAULT: none +;album_art_max_height = 1024 + +; Resize Images * Requires PHP-GD * +; Set this to true if you want Ampache to resize the Album +; art on the fly, this increases load time and CPU usage +; and also requires the PHP-GD library. This is very useful +; If you have high-quality album art and a small upload cap +; DEFAULT: "false" +;resize_images = "true" + +; Playlist Cover Art +; Set this to true if you want Ampache to generate +; cover art for playlists automatically based on +; the content. +; DEFAULT: "true" +playlist_art = "true" + +; Statistical Graphs * Requires PHP-GD * +; Set this to true if you want Ampache to generate statistical graphs on usages / users. +; This is false by default due to issues around the licensing of c-pchart. +; https://github.com/ampache/ampache/issues/1515 +; http://www.pchart.net/license +; REFERENCE: https://github.com/ampache/ampache/wiki/chart-faq +; You can enable c-chart with the following command +; composer require szymach/c-pchart "2.*" +; DEFAULT: "false" +;statistical_graphs = "true" + +; Art Gather Order +; Simply arrange the following in the order you would like +; Ampache to search. If you want to disable one of the search +; methods simply leave it out. DB should be left as the first +; method unless you want it to overwrite what's already in the +; database +; POSSIBLE VALUES (builtins): db tags folder lastfm musicbrainz google +; POSSIBLE VALUES (plugins): Amazon,TheAudioDb,Tmdb,Omdb,Flickr +; DEFAULT: db,tags,folder,spotify,musicbrainz,lastfm,google +art_order = "db,spotify,TheAudioDb,musicbrainz,lastfm,tags,folder" + +; Gather song art +; Gather song art additionally to the album art. This will add each +; image of a song into the database. If your database contains +; many songs not related to an album this should possibly be set to true. +; Otherwise for an environment that contains mainly full albums +; this should remain false to avoid multiple entries of the same image. +; You need to set show_song_art to true if you want to make the song images visible. +; DEFAULT = false +;gather_song_art = "true" + +; Show song art +; Show song art instead of album art in web UI and Subsonic API. +; This will only work when gather_song_art is set to true AND when +; there is song art in the database. +; DEFAULT: false +;show_song_art = "true" + +; Spotify Album art search filter +; Narrow the search. +; POSSIBLE VALUES: artist,(year:1991 or year:1991-2000) +; POSSIBLE VALUES: empty string("") or commented out for no filter +; DEFAULT: none; +;spotify_art_filter = "artist" + +; Art search limit +; Limit the total images returned +; DEFAULT: 15 +;art_search_limit = 15 + +; Recommendations +; Set this to true to enable display of similar artists or albums +; while browsing. Requires Last.FM. +; DEFAULT: "false" +;show_similar = "true" + +; Hide Searches from the API +; Set this to true and the get_indexes and playlists methods will not automatically include +; searches/smart lists in the results. +; Older versions of the Kodi Add-on will require this for playlists to work +; DEFAULT: "false" +;hide_search = "true" + +; Allow or disallow upload scripts on the server +; Enable this if you trust your users +; DEFAULT: "false" +;allow_upload_scripts = "true" + +;######################################################### +; API keys # +;######################################################### + +; Last.FM API Key +; Set this to your Last.FM api key to actually use Last.FM for +; recommendations and metadata. +lastfm_api_key = "d5df942424c71b754e54ce1832505ae2" + +; Last.FM API secret +; Set this to your Last.FM api secret to actually use Last.FM for +; scrobbling. +; DEFAULT: none +lastfm_api_secret = "" + +; Spotify client id +; Set this to your Spotify client id to actually use Spotify for +; accessing their catalog API. (https://developer.spotify.com/dashboard/) +; DEFAULT: none +;spotify_client_id = "" + +; Spotify client secret +; Both id and secret are required to access the spotify catalog. +; DEFAULT: none +;spotify_client_secret = "" + +; Wanted +; Set this to true to enable display missing albums and the +; possibility for users to mark it as wanted. +; DEFAULT: "false" +;wanted = "true" + +; Wanted types +; Set the allowed types of wanted releases (album,compilation,single,ep,live,remix,promotion,official) +; DEFAULT: album,official +wanted_types = "album,official" + +; Wanted Auto Accept +; Mark wanted requests as accepted by default (no content manager agreement required) +; DEFAULT: "false" +;wanted_auto_accept = "true" + +; Labels +; Use labels to browse artists per label membership. +; DEFAULT: "false" +;label = "true" + +; Broadcasts +; Allow users to broadcast music. +; This feature requires advanced server configuration, please take a look on the wiki for more information. +; DEFAULT: "false" +;broadcast = "true" + +; Channels +; Set this to true to enable channels and the +; possibility for users to create channels from playlists +; DEFAULT: "false" +;channel = "true" + +; Live Streams +; Set this to true to enable live streams (radio) and the +; possibility for users to add new live streams. +; DEFAULT: "true" +live_stream = "true" + +; Podcasts +; Set this to true to enable podcasts and the +; possibility for admins to subscribe to new podcasts. +; DEFAULT: "true" +podcast = "true" + +; Web Socket address +; Declare the web socket server address +; DEFAULT: determined automatically +;websocket_address = "ws://localhost:8100" + +; Refresh Limit +; This defines the default refresh limit in seconds for +; pages with dynamic content, such as Now Playing +; DEFAULT: 60 +; Possible Values: Int > 5 +refresh_limit = "60" + +; Embedded Now Playing Page +; Set this to true to enable the embedded Now Playing page (now_playing.php). +; This page allows for embedding a Now Playing badge into a stream +; or status page. Use with the parameter 'user_id' to filter by a +; specific user. This page is like rss in that it doesn't require a +; login to view. +; DEFAULT: "false" +;use_now_playing_embedded = "true" + +; Now Playing Refresh Limit +; This defines the refresh limit in seconds for the +; Now Playing embedded page. This (now_playing.php) is not +; part of the normal application and is designed to be +; embedded in another app, like a stream or status page. +; If this value is not valid, automatic refresh will be disabled. +; DEFAULT: -1 +; Possible Values; Int > 1 +;now_playing_refresh_limit = "-1" + +; Now Playing Custom CSS +; This defines the custom css file for the Now Playing embedded +; page. This (now_playing.php) is not part of the normal +; application and is designed to be embedded in another app, like +; a stream or status page. +; If this value is not set, no CSS will be used. Custom CSS can +; still be applied in the other application, like OBS. +; DEFAULT: Not enabled +;now_playing_css_file = "templates/now-playing.css" + +; Footer Statistics +; This defines whether statistics (Queries, Cache Hits, Load Time) +; are shown in the page footer. +; DEFAULT: "true" +show_footer_statistics = "true" + +; RSS Feeds +; Set this to true to enable rss feeds. +; (latest albums, shouts, albums of artist, ...) +; use_rss = false (values true | false) +; DEFAULT: "false" +;use_rss = "true" + +; This setting allows themes to overwrite PHP template files. This can be really +; dangerous. Do this only if you trust every theme in your themes/ directory. +; DEFAULT: "false" +;allow_php_themes = "true" + +;######################################################### +; Debugging # +;######################################################### + +; Debug +; If this is enabled Ampache will write debugging information to the log file +; DEFAULT: "false" +;debug = "true" + +; Debug Level +; This should always be set in conjunction with the +; debug option, it defines how prolific you want the +; debugging in Ampache to be. values are 1-5. +; 1 == Basic Errors +; 2 == Errors + Failures (login attempts etc.) +; 3 == Full Error Information +; 4 == General Information +; 5 == Full Information (cataloging progress etc.) +; DEFAULT: 5 +debug_level = 5 + +; Path to Log File +; This defines where you want Ampache to log events to +; this will only happen if debug is turned on. Do not +; include trailing slash. You will need to make sure that +; the specified directory exists and your HTTP server has +; write access. +; DEFAULT: none +log_path = "/var/log/ampache" + +; Log filename pattern +; This defines where the log file name pattern +; %name.%Y%m%d.log will create a different log file every day. +; DEFAULT: %name.%Y%m%d.log +log_filename = "%name.%Y%m%d.log" + +;######################################################### +; Encoding Settings # +;######################################################### + +; Charset of generated HTML pages +; Default of UTF-8 should work for most people +; DEFAULT: UTF-8 +site_charset = UTF-8 + +; Locale Charset +; Local charset (mainly for file operations) if different +; from site_charset. +; This is disabled by default, enable only if needed +; (for Windows please set lc_charset to ISO8859-1) +; DEFAULT: ISO8859-1 +;lc_charset = "ISO8859-1" + +; Multibyte +; See http://php.net/manual/mbstring.supported-encodings.php +; If you want ID3v1 encoding detection to work, you should uncomment this line +; so that the ordering is sane. +; DEFAULT: auto +;mb_detect_order = "ASCII,UTF-8,EUC-JP,ISO-2022-JP,SJIS,JIS" + +;######################################################### +; Custom actions (optional) # +;######################################################### + +; Your custom play action title +;custom_play_action_title_0 = "" +; Your custom play action icon name (stored as /images/icon_[your_image].png) +;custom_play_action_icon_0 = "" +; Your custom action script, where: +; - %f: the media file path +; - %c: the excepted codec target (mp3, ogg, ...) +; - %a: the artist name +; - %A: the album name +; - %t: the song title +; DEFAULT: none +;custom_play_action_run_0 = "" + +; Example for Karaoke playing +;custom_play_action_title_0 = "Karaoke" +;custom_play_action_icon_0 = "microphone" +;custom_play_action_run_0 = "sox \"%f\" -p oops | ffmpeg -i pipe:0 -f %c pipe:1" + +;######################################################### +; LDAP login info (optional) # +;######################################################### + +; LDAP server URL (required) +; DEFAULT: none +;ldap_url = "ldap://localhost/" +;ldap_url = "ldaps://localhost/" + +; LDAP credentials (optional) +; DEFAULT: none +;ldap_username = "" +;ldap_password = "" + +; LDAP Base DN for the search (required) +; DEFAULT: none +;ldap_search_dn = "ou=People,dc=yoursubdomain,dc=yourdomain,dc=yourtld" + +; LDAP objectClass (required) +; DEFAULT: none +;ldap_objectclass = "posixAccount" ; OpenLDAP +;ldap_objectclass = "organizationalPerson" ; Microsoft Active Directory + +; LDAP filter for search (required) +; DEFAULT: none +;ldap_filter = "(uid=%v)" ; OpenLDAP +;ldap_filter = "(sAMAccountName=%v)" ; Microsoft Active Directory + +; Require that the user is in a specific group (optional) +; DEFAULT: none +;ldap_require_group = "cn=yourgroup,ou=yourorg,dc=yoursubdomain,dc=yourdomain,dc=yourtld" + +; LDAP name field +; DEFAULT: cn +;ldap_name_field = "cn" +;ldap_name_field = "displayName" + +; LDAP email field +; DEFAULT: mail +;ldap_email_field = "mail" + +; LDAP avatar field +; DEFAULT: none +;ldap_avatar_field = "jpegPhoto" + +; LDAP avatar mime type +; DEFAULT: image/jpeg +;ldap_avatar_mime = "image/jpeg" + +; LDAP protocol version to use +; DEFAULT: 3 +;ldap_protocol_version = 3 + +; LDAP StartTLS +; DEFAULT: "false" +;ldap_start_tls = "true" + +; LDAP member attribute name. +; That's the name of the attribute of the group that will contain +; the usernames. +; DEFAULT: member +;ldap_member_attribute = "member" +;ldap_member_attribute = "memberuid" + +;######################################################### +; OpenID login info (optional) # +;######################################################### + +; Requires specific OpenID Provider Authentication Policy +; DEFAULT: none +; VALUES: PAPE_AUTH_MULTI_FACTOR_PHYSICAL,PAPE_AUTH_MULTI_FACTOR,PAPE_AUTH_PHISHING_RESISTANT +;openid_required_pape = "" + +;######################################################### +; Public Registration settings, defaults to disabled # +;######################################################### + +; This setting will silently create an Ampache account +; for anyone who can login using LDAP (or any other login +; extension). The default is to create new users as guests +; see auto_user config option if you would like to change this +; DEFAULT: "false" +;auto_create = "true" + +; This setting will silently update an Ampache account's +; information for anyone who can login using LDAP +; (or any other login extension). +; DEFAULT: "false" +;external_auto_update = "true" + +; This setting turns on/off public registration. It is +; recommended you leave this off, as it will allow anyone to +; sign up for an account on your server. +; REMEMBER: don't forget to set the mail from address further down in the config. +; DEFAULT: "false" +;allow_public_registration = "true" + +; Require Captcha Text on Image confirmation +; Turning this on requires the user to correctly +; type in the letters in the image created by Captcha +; Default is off because its very hard to detect if it failed +; to draw, or they failed to enter it. +; DEFAULT: "false" +;captcha_public_reg = "true" + +; This setting turns on/off admin notification of registration. +; DEFAULT: "false" +;admin_notify_reg = "true" + +; This setting determines whether the user will be created as a disabled user. +; If this is on, an administrator will need to manually enable the account +; before it's usable. +; DEFAULT: "false" +;admin_enable_required = "true" + +; This setting will allow all registrants/ldap/http users +; to be auto-approved as a user. By default, they will be +; added as a guest and must be promoted by the admin. +; POSSIBLE VALUES: guest, user, admin +; DEFAULT: guest +;auto_user = "guest" + +; This will display the user agreement when registering +; For agreement text, edit config/registration_agreement.php +; User will need to accept the agreement before they can register +; DEFAULT: "false" +;user_agreement = "true" + +; This disable email confirmation when registering. +; DEFAULT: "false" +;user_no_email_confirm = "true" + +; This will display the cookie disclaimer (EU Cookie Law) +; DEFAULT: "false" +;cookie_disclaimer = "true" + +; The fields that will be shown on Registration page +; If a user wants to register. +; Username and email fields are forced. +; POSSIBLE VALUES: fullname,website,state,city +; DEFAULT: fullname,website +registration_display_fields = "fullname,website" + +; The fields that will be mandatory +; This controls which fields are mandatory for registration. +; Username and email fields are forced mandatory. +; POSSIBLE VALUES: fullname,website,state,city +; DEFAULT: fullname +registration_mandatory_fields = "fullname" + +;######################################################### +; These options control the dynamic downsampling based # +; on current usage # +; *Note* Transcoding must be enabled and working # +;######################################################### + +; Attempt to optimize bandwidth by dynamically lowering the bit rate of new +; streams. Since the bit rate is only adjusted at the beginning of a song, the +; actual cumulative bitrate for concurrent streams can be up to around +; double the configured value. It also only applies to streams that are +; transcoded. +; DEFAULT: none +;max_bit_rate = 576 + +; New dynamically downsampled streams will be denied if they are forced below +; this value. +; DEFAULT: 8 +;min_bit_rate = 48 + +;######################################################### +; Transcode Settings # +;######################################################### + +; These are commands used to transcode non-streaming +; formats to the target file type for streaming. +; This can be useful in re-encoding file types that don't stream +; very well, or if your player doesn't support some file types. +; +; 'Downsampling' will also use these commands. +; +; To state the bleeding obvious, any programs referenced in the transcode +; commands must be installed, in the web server's search path (or referenced +; by their full path), and executable by the web server. + +; Input type selection +; TYPE is the extension. 'allowed' certifies that transcoding works properly for +; this input format. 'required' further forbids the direct streaming of a format +; (e.g. if you store everything in FLAC, but don't want to ever stream that.) +; transcode_TYPE = {allowed|required|false} +; DEFAULT: "false" +;;; Audio +;transcode_m4a = "allowed" +;transcode_flac = "required" +;transcode_mpc = "required" +;transcode_ogg = "required" +;transcode_oga = "required" +;transcode_opus = "required" +;transcode_wav = "required" +;transcode_wma = "required" +;transcode_aif = "required" +;transcode_aiff = "required" +;transcode_ape = "required" +;transcode_shn = "required" +transcode_mp3 = "allowed" +;;; Video +;transcode_avi = "allowed" +;transcode_flv = "allowed" +;transcode_mkv = "allowed" +;transcode_mpg = "allowed" +;transcode_mpeg = "allowed" +;transcode_m4v = "allowed" +;transcode_mp4 = "allowed" +;transcode_mov = "allowed" +;transcode_wmv = "allowed" +;transcode_ogv = "allowed" +;transcode_divx = "allowed" +;transcode_m2ts = "allowed" +;transcode_webm = "allowed" + +; Default audio output format +; DEFAULT: none +;encode_target = mp3 + +; Default video output format +; DEFAULT: none +;encode_video_target = webm + +; Override the default output format on a per-type basis, for example, +; to stream lossless encoded files in lossy formats. +; encode_target_TYPE = TYPE +; DEFAULT: none +;encode_target_flac = opus + +; Override the default TYPE transcoding behavior on a per-player basis, for example, +; to stream lossless using the api and lossy using the web interface. +; transcode_player_PLAYER_TYPE = TYPE +; Valid PLAYER is: webplayer, api +; DEFAULT: none +;transcode_player_webplayer_m4a = "required" +;transcode_player_webplayer_flac = "required" +;transcode_player_webplayer_mpc = "required" + +; Override the default output format on a per-player basis +; encode_player_PLAYER_target = TYPE +; Valid PLAYER is: webplayer, api +; DEFAULT: none +;encode_player_webplayer_target = mp3 +;encode_player_api_target = mp3 + +; Allow clients to override transcode settings (output type, bitrate, codec ...) +; DEFAULT: "true" +transcode_player_customize = "true" + +; Command configuration. Substitutions will be made as follows: +; %FILE% => filename +; %BITRATE% => target bit rate (as chosen by the admin or users in the +; preferences, if transcode_player_customize = "true") +; You can do fancy things like VBR, but consider whether the consequences are +; acceptable in your environment. + +; Master transcode command +; transcode_cmd should be a single command that supports multiple file types, +; such as ffmpeg or avconv. It's still possible to make a configuration that's +; equivalent to the old default, but if you find that necessary you should be +; clever enough to figure out how on your own. +; DEFAULT: none +;transcode_cmd = "ffmpeg" +;transcode_cmd = "avconv" +;transcode_cmd = "/usr/bin/neatokeen" + +; Transcode input file argument +transcode_input = "-i %FILE%" + +; Specific transcode commands +; It shouldn't be necessary in most cases, but you can override the transcode +; command for specific source formats. It still needs to accept the +; encoding arguments, so the easiest approach is to use your normal command as +; a clearing-house. +; transcode_cmd_TYPE = TRANSCODE_CMD +;transcode_cmd_mid = "timidity -Or -o – %FILE% | ffmpeg -f s16le -i pipe:0" + +; Encoding arguments +; For each output format, you should provide the necessary arguments for +; your transcode_cmd. +; encode_args_TYPE = TRANSCODE_CMD_ARGS +encode_args_mp3 = "-vn -b:a %BITRATE%K -c:a libmp3lame -f mp3 pipe:1" +encode_args_ogg = "-vn -b:a %BITRATE%K -c:a libvorbis -f ogg pipe:1" +encode_args_opus = "-vn -b:a %BITRATE%K -c:a libopus -compression_level 10 -vsync 2 -f ogg pipe:1" +encode_args_m4a = "-vn -b:a %BITRATE%K -c:a libfdk_aac -f adts pipe:1" +encode_args_wav = "-vn -b:a %BITRATE%K -c:a pcm_s16le -f wav pipe:1" +encode_args_flv = "-b:a %BITRATE%K -ar 44100 -ac 2 -v 0 -f flv -c:v libx264 -preset superfast -threads 0 pipe:1" +encode_args_webm = "-b:a %BITRATE%K -f webm -c:v libvpx -preset superfast -threads 0 pipe:1" +encode_args_ts = "-q %QUALITY% -s %RESOLUTION% -f mpegts -c:v libx264 -c:a libmp3lame -maxrate %MAXBITRATE%k -preset superfast -threads 0 pipe:1" +encode_args_ogv = "-codec:v libtheora -qscale:v 7 -codec:a libvorbis -qscale:a 5 -f ogg pipe:1" + +; Encoding arguments to retrieve an image from a single frame +encode_get_image = "-ss %TIME% -f image2 -vframes 1 pipe:1" + +; Encoding argument to encrust subtitle +encode_srt = "-vf \"subtitles='%SRTFILE%'\"" + +; Encode segment frame argument +encode_ss_frame = "-ss %TIME%" + +; Encode segment duration argument +encode_ss_duration = "-t %DURATION%" + +; Use segments for transcoding or send it all in one go. +; Useful if you are having issues streaming the full track. +; You can set it for all streams or a specific player +; POSSIBLE VALUES: true webplayer api +; DEFAULT: "webplayer" +send_full_stream = "webplayer" + +;######################################################### +; Transcode Caching # +;######################################################### + +; These are commands used to pre-cache a file format for streaming. +; This helps avoid waiting for transcodes to finish and makes +; files immediately available to the client. + +; Path to your cache folder. +; This is where the pre-transcoded files will be stored +; DEFAULT: none +;cache_path = "/tmp" + +; Default audio output format +; DEFAULT: none +;cache_target = "mp3" + +; Look in your local catalogs for these file extensions and pre-cache to the +; 'cache_path'. This could be helpful to reduce space needed on your +; web server or to make sure that clients get files quickly. +; Execute "php bin/cli run:cacheProcess" to process these files. +; DEFAULT: "false" +;cache_m4a = "true" +;cache_flac = "true" +;cache_mpc = "true" +;cache_ogg = "true" +;cache_oga = "true" +;cache_opus = "true" +;cache_wav = "true" +;cache_wma = "true" +;cache_aif = "true" +;cache_aiff = "true" +;cache_ape = "true" +;cache_shn = "true" +;cache_mp3 = "true" + +; REMOTE CATALOGS ONLY +; Enabling cache_remote on remote catalogs will cache every file on the remote server +; DEFAULT: "false" +;cache_remote = "true" + +;######################################################### +; Proxy Settings (optional) # +;######################################################### + +; If Ampache is behind an http proxy, specify the hostname or IP address +; port, proxy username, and proxy password here. +; DEFAULT: not in use +;proxy_host = "192.168.0.1" +;proxy_port = "8080" +;proxy_user = "" +;proxy_pass = "" + +; If Ampache is behind an https reverse proxy, force use HTTPS protocol. +; DEFAULT: "false" +;force_ssl = "true" + +;######################################################### +; Mail Settings # +;######################################################### + +; Enable or disable email server features +; otherwise, you can reset your password +; and never receive an email with the new one +; Default: false +;mail_enable = "true" + +; Method used to send mail +; POSSIBLE VALUES: smtp sendmail php +; DEFAULT: php +;mail_type = "php" + +; Mail domain. +; DEFAULT: example.com +;mail_domain = "example.com" + +; This will be combined with mail_domain and used as the source address for +; emails generated by Ampache. For example, setting this to 'me' will set the +; sender to 'me@example.com'. +; DEFAULT: info +;mail_user = "info" + +; A name to go with the email address. +; DEFAULT: Ampache +;mail_name = "Ampache" + +; How strictly email addresses should be checked. +; easy does a regex match, strict actually performs some SMTP transactions +; to see if we can send to this address. +; POSSIBLE VALUES: strict easy none +; DEFAULT: strict +;mail_check = "strict" + +;######################################################### +; sendmail Settings # +;######################################################### + +; DEFAULT: /usr/sbin/sendmail +;sendmail_path = "/usr/sbin/sendmail" + +;######################################################### +; SMTP Settings # +;######################################################### + +; Mail server (hostname or IP address) +; DEFAULT: localhost +;mail_host = "localhost" + +; SMTP port +; DEFAULT: 25 +;mail_port = 25 + +; Secure SMTP +; POSSIBLE VALUES: ssl tls +; DEFAULT: none +;mail_secure_smtp = tls + +; Enable SMTP authentication +; DEFAULT: "false" +;mail_auth = "true" + +; SMTP username +; your mail auth username. +; DEFAULT: none +;mail_auth_user = "" + +; SMTP password +; your mail auth password. +; DEFAULT: none +;mail_auth_pass = "" + +;######################################################### +; Abbreviation Filter # +;######################################################### + +; For file name parsing. Any unnecessary abbreviations in file names can be removed during parsing +; by adding them to the list below so that they will be removed during the parsing process. +common_abbr = "divx,xvid,dvdrip,hdtv,lol,axxo,repack,xor,pdtv,real,vtv,caph,2hd,proper,fqm,uncut,topaz,tvt,notv,fpn,fov,orenji,0tv,omicron,dsr,ws,sys,crimson,wat,hiqt,internal,brrip,boheme,vost,vostfr,fastsub,addiction,x264,LOL,720p,1080p,YIFY,evolve,fihtv,first,bokutox,bluray,tvboom,info" diff --git a/hcl/default/ampache/templates/ampache.cfg.php.tmpl b/hcl/default/ampache/templates/ampache.cfg.php.tmpl @@ -0,0 +1,1353 @@ +;#<?php exit(); ?>## +;######################################################### +; General Config # +;######################################################### + +; This value is used to detect if this config file is up to date +; this is compared against a constant called CONFIG_VERSION +; that is located in src/Config/Init/InitializationHandlerConfig.php +config_version = 62 + +;######################################################### +; Auto Update # +;######################################################### + +; Allow you to hard code a default git branch for Ampache +; If you set this value the inbuilt updater will use this branch for updates. +; POSSIBLE VALUES: master develop +; DEFAULT: none +;github_force_branch = "develop" + +; This value allows to override the composer binary path to distinguish between multiple composer versions +; Either a binary name in $PATH as well as a fully qualified path is possible +; DEFAULT: composer +;composer_binary_path = "composer" + +; We sometimes need to talk and will show a warning to admin users +; Enable this setting if you don't want to see warnings (When we enable them) +; DEFAULT: false +;hide_ampache_messages = "true" + +;######################################################### +; Path Vars # +;######################################################### + +; The public http host of your server. +; If not set, retrieved automatically from client request. +; This setting is required for WebSocket server +; DEFAULT: none +;http_host = "localhost" + +; The public http port of your server. +; If not set, retrieved automatically from client request. +; DEFAULT: none +;http_port = 80 + +; The public path to your Ampache install +; Do not put a trailing / on this path +; For example if your site is located at http://localhost +; than you do not need to enter anything for the web_path +; if it is located at http://localhost/music you need to +; set web_path to /music +; DEFAULT: none +web_path = "" + +; The local http url of your server. +; This is used to access the server from within the +; same host where ampache is running. +; For example, if the ampache server is not +; directly accessed via the public domain but via a reverse +; proxy, local_web_path would need to be changed +; to a localhost URL. +; If not set, retrieved automatically from server information. +; DEFAULT: none +local_web_path = "http://localhost" + +;######################################################### +; Database # +;######################################################### + +; Hostname of your database +; For socket authentication, set the path to socket file (e.g. /var/run/mysqld/mysqld.sock) +; DEFAULT: localhost +database_hostname = "mariadb.lan" + +; Port to use when connecting to your database +; DEFAULT: none +database_port = "" + +; Name of your Ampache database +; DEFAULT: none +database_name = "ampache" + +; Username for your Ampache database +; DEFAULT: none +database_username = "ampache" + +; Password for your Ampache database, this can not be blank +; this is a 'forced' security precaution, the default value +; will not work (except if using socket authentication) +; DEFAULT: none +database_password = "{{with secret "kv/ampache"}}{{index .Data.data.db_password}}{{end}}" + +; Set a default charset for your database +; Don't change this unless you understand how to BACKUP and RESTORE a database! +; +; DEFAULT: "utf8mb4" +;database_charset = "utf8mb4" + +; Set a default collation for your database +; Don't change this unless you understand how to BACKUP and RESTORE a database! +; +; There are a ton of options but you'll probably want one of these. +; "utf8_unicode_ci" = Regular unicode (3 bytes per character) +; "utf8mb4_unicode_ci" = 4 bytes per character +; "utf8mb4_unicode_520_ci" = Supports more characters and is based on UCA 5.2.0 weight keys +; http://www.unicode.org/Public/UCA/5.2.0/allkeys.txt +; DEFAULT: "utf8mb4_unicode_ci" +;database_collation = "utf8mb4_unicode_ci" + +;######################################################### +; Session and Security # +;######################################################### + +; Cryptographic secret +; This MUST BE changed with your own secret key. Ampache-specific, just pick any random string you want. +secret_key = "{{with secret "kv/ampache"}}{{index .Data.data.secret_key}}{{end}}" + +; Length that a session will last expressed in seconds. Default is +; one hour. +; DEFAULT: 3600 +session_length = 3600 + +; Length that the session for a single streaming instance will last +; the default is two hours. With some clients, and long songs this can +; cause playback to stop, increase this value if you experience that +; DEFAULT: 7200 +stream_length = 7200 + +; This length defines how long a 'remember me' session and cookie will +; last, the default is 86400, same as length. It is up to the administrator +; of the box to increase this, for reference 86400 = 1 day, +; 604800 = 1 week, and 2419200 = 1 month +; DEFAULT: 604800 +remember_length = 604800 + +; Name of the Session/Cookie that will sent to the browser +; default should be fine +; DEFAULT: ampache +session_name = ampache + +; Lifetime of the Cookie, 0 == Forever (until browser close) , otherwise in terms of seconds +; If you want cookies to last past a browser close set this to a value in seconds. +; DEFAULT: 0 +session_cookielife = 0 + +; Is the cookie a "secure" cookie? This should only be set to 1 (true) if you are +; running a secure site (HTTPS). +; DEFAULT: 0 +session_cookiesecure = 0 + +; Auth Methods +; This defines which auth methods Auth will attempt to use and in which order. +; If auto_create isn't enabled the user must exist locally. +; DEFAULT: mysql +; VALUES: mysql,ldap,http,pam,external,openid +auth_methods = "mysql" + +; External authentication +; This sets the helper used for external authentication. It should conform to +; the interface used by mod_authnz_external +; DEFAULT: none +;external_authenticator = "/usr/sbin/pwauth" + +; Automatic local password updating +; Determines whether successful authentication against an external source +; will result in an update to the password stored in the database. +; A locally stored password is needed for API access. +; DEFAULT: "false" +;auth_password_save = "true" + +; Log out redirection target +; Defaults to our own login.php, but we can override it here if, for instance, +; we want to redirect to an SSO provider instead. +;logout_redirect = "http://sso.example.com/logout" + +; Use Access List +; Toggle this on if you want Ampache to pay attention to the access list +; and only allow streaming/downloading/api-rpc from known hosts api-rpc +; will not work without this on. +; NOTE: Default Behavior is DENY FROM ALL +; DEFAULT: "true" +access_control = "true" + +; Require Session +; If this is set to true Ampache will make sure that the URL passed when +; attempting to retrieve a song contains a valid Session ID This prevents +; others from guessing URL's. This setting is ignored if you have use_auth +; disabled. +; DEFAULT: "true" +require_session = "true" + +; Require LocalNet Session +; If this is set to true then Ampache will require that a valid session +; is passed even on hosts defined in the Local Network ACL. This setting +; has no effect if access_control is not enabled +; DEFAULT: "true" +require_localnet_session = "true" + +; Multiple Logins +; Added by Vlet 07/25/07 +; When this setting is enabled a user may only be logged in from a single +; IP address at any one time, this is to prevent sharing of accounts +; DEFAULT: "false" +;prevent_multiple_logins = "true" + +; Allow Embedding Ampache in Frames +; Whether a browser should be allowed to render a page in a <frame>, <iframe>, <embed> or <object>. +; This is used to avoid click-jacking attacks, by ensuring that their content is not embedded into other sites. +; By default Ampache pages can only be displayed in a frame on the same origin as the page itself. +; DEFAULT: "false" +;disable_xframe_sameorigin = "true" + +;######################################################### +; Metadata # +;######################################################### + +; This determines the tag order for all cataloged +; music. If none of the listed tags are found then +; Ampache will randomly use whatever was found. +; POSSIBLE VALUES: ape asf avi id3v1 id3v2 lyrics3 matroska mpeg quicktime riff +; vorbiscomment +; DEFAULT: vorbiscomment id3v2 id3v1 quicktime matroska ape asf avi mpeg riff +getid3_tag_order = "vorbiscomment,id3v2,id3v1,quicktime,matroska,ape,asf,avi,mpeg,riff" + +; This determines whether we try to autodetect the encoding for id3v2 tags. +; May break valid tags. +; DEFAULT: "false" +;getid3_detect_id3v2_encoding = "true" + +; This determines if we write the changes to files (as id3 tags) when modifying metadata, or only keep them in Ampache (the default). +; DEFAULT: "false" +;write_id3 = "true" + +; This determines if we write the changes to files (as id3 tags) when modifying album art, or only keep them in Ampache (the default) +; as id3 metadata when updated. +; DEFAULT: "false" +;write_id3_art = "true" + +; This determines the order in which metadata sources are used (and in the +; case of plugins, checked) +; POSSIBLE VALUES (builtins): filename and getID3 +; POSSIBLE VALUES (plugins): MusicBrainz,TheAudioDb, plus any others you've installed. +; DEFAULT: getID3 filename +metadata_order = "getID3,MusicBrainz,TheAudioDb,filename" + +; This determines the order in which metadata sources are used (and in the +; case of plugins, checked) for video files +; POSSIBLE VALUES (builtins): filename and getID3 +; POSSIBLE VALUES (plugins): Tvdb,Tmdb,Omdb, plus any others you've installed. +; DEFAULT: filename getID3 +metadata_order_video = "filename,getID3" + +; This determines if extended metadata grabbed from external services should be deferred. +; If enabled, extended metadata is retrieved when browsing the library item. +; If disabled, extended metadata is retrieved at catalog update. +; Today, only Artist information (summary, place formed, ...) can be deferred. +; DEFAULT: "true" +deferred_ext_metadata = "true" + +; Some taggers use delimiters other than \0 for fields +; This list specifies possible delimiters additional to \0 +; This setting takes a regex pattern. TODO: explain that this is not just for genres until we can replace this safely +; DEFAULT: // / \ | , ; +additional_genre_delimiters = "[/]{2}|[/\\\\|,;]" + +; Enable importing custom metadata from files. +; This will need a bit of time during the import. So you may want to disable this +; if you have troubles with huge databases. +; DEFAULT: "false" +;enable_custom_metadata = "true" + +;######################################################### +; File Tags # +;######################################################### + +; This determines if we write metadata to files when modifying metadata, or only keep them in Ampache (the default). +; DEFAULT: "false" +;write_tags = "true" + +;######################################################### +; Catalog # +;######################################################### + +; File Pattern +; This defines which file types Ampache will attempt to catalog +; You can specify any file extension you want in here separating them with a | +; DEFAULT: mp3|mpc|m4p|m4a|aac|ogg|oga|wav|aif|aiff|rm|wma|asf|flac|opus|spx|ra|ape|shn|wv +catalog_file_pattern = "mp3|mpc|m4p|m4a|aac|ogg|oga|wav|aif|aiff|rm|wma|asf|flac|opus|spx|ra|ape|shn|wv" + +; Video Pattern +; This defines which video file types Ampache will attempt to catalog +; You can specify any file extension you want in here separating them with +; a | but Ampache may not be able to parse them +; DEAFULT: avi|mpg|mpeg|flv|m4v|mp4|webm|mkv|wmv|ogv|mov|divx|m2ts +catalog_video_pattern = "avi|mpg|mpeg|flv|m4v|mp4|webm|mkv|wmv|ogv|mov|divx|m2ts" + +; Playlist Pattern +; This defines which playlist types Ampache will attempt to catalog +; You can specify any file extension you want in here separating them with +; a | but Ampache may not be able to parse them +; DEFAULT: m3u|m3u8|pls|asx|xspf +catalog_playlist_pattern = "m3u|m3u8|pls|asx|xspf" + +; Prefix Pattern +; This defines which prefix Ampache will ignore when importing tags from +; your music. You may add any prefix you want separating them with a | +; DEFAULT: The|An|A|Die|Das|Ein|Eine|Les|Le|La +catalog_prefix_pattern = "The|An|A|Die|Das|Ein|Eine|Les|Le|La" + +; Ignore Pattern +; Ignore files that match this pattern +; You can specify any file extension you want in here separating them with a | +; DEFAULT: None +; catalog_ignore_pattern = "\(HTOA\)" + +; Catalog disable +; This defines if catalog can be disabled without removing database entries +; WARNING: this increase sensibly sql requests and slow down Ampache a lot +; DEFAULT: "false" +;catalog_disable = "true" + +; Catalog filter +; This defines if catalog can be filtered per user. +; The filters are set in the catalog management pages. +; WARNING: this increase sensibly sql requests and slow down Ampache a lot +; DEFAULT: false +;catalog_filter = "true" + +; Delete from disk +; This determines if catalog manager users can delete media from disk. +; DEFAULT: "false" +;delete_from_disk = "true" + +; Catalog verify by time +; Only verify the files that have been modified since the last verify. +; For large catalogs the verify process can be terribly long so this will help +; speed things up by checking modification date using filemtime() before loading +; DEFAULT: "false" +;catalog_verify_by_time = "true" + +;######################################################### +; Program Settings # +;######################################################### + +; Downsample Remote +; If this is set to true and access control is on any users who are not +; coming from a defined 'network' ACL will be automatically downsampled +; regardless of their preferences. Requires access_control to be enabled +; DEFAULT: "false" +;downsample_remote = "true" + +; Track User IPs +; If this is enabled Ampache will log the IP of every completed login +; it will store user, ip and time at one row per login. The results are +; displayed in Admin --> Users +; DEFAULT: "false" +;track_user_ip = "true" + +; User IP Cardinality +; This defines how many days worth of IP history Ampache will track +; As it is one row per login on high volume sites you will want to +; clear it every now and then. +; DEFAULT: 42 days +;user_ip_cardinality = "42" + +; Allow Zip Download +; This setting allows/disallows using zlib to zip up an entire +; playlist/album for download. Even if this is turned on you will +; still need to enabled downloading for the specific user you +; want to be able to use this function +; DEFAULT: "false" +;allow_zip_download = "true" + +; Allow Zip Types +; This setting allows/disallows zip download of specific object types +; If empty, all supported object types can be zipped. +; Otherwise, only the given object list can be zipped. +; POSSIBLE VALUES: artist, album, playlist, search, tmp_playlist +; DEFAULT: none +;allow_zip_types = "album" + +; Art Zip Add +; This settings allows/disallows to include Album Art to the Zip +; If 'album_art_preferred_filename' exists this is included +; DEFAULT: false +;art_zip_add = "true" + +; File Zip Comment +; This is an optional configuration option that adds a comment +; to your zip files, this only applies if you've got allow_zip_downloads +; DEFAULT: Ampache - Zip Batch Download +;file_zip_comment = "Ampache - Zip Batch Download" + +; Load the debug webplayer +; This will load the *.js player instead of the *.min.js player +; The unminified player has a lot of console.log() statements in the code. +; You can make changes and then check how the player is functioning. +; DEFAULT: "false" +;webplayer_debug = "true" + +; Waveform +; This settings tells Ampache to attempt to generate a waveform +; for each song. It requires transcode and encode_args_wav settings. +; You must also set tmp_dir_path in order for this to work +; DEFAULT: "false" +;waveform = "true" + +; Waveform color +; The waveform color. +; DEFAULT: #FF0000 +;waveform_color = "#FF0000" + +; Waveform height +; The waveform height. +; DEFAULT: 32 +;waveform_height = 32 + +; Waveform width +; The waveform width. +; DEFAULT: 400 +;waveform_width = 400 + +; Temporary Directory Path +; If Waveform is enabled this must be set to tell +; Ampache which directory to save the temporary file to. Do not put a +; trailing slash or this will not work. +; DEFAULT: "false" +;tmp_dir_path = "/tmp" + +; This setting throttles a persons downloading to the specified +; bytes per second. This is not a 100% guaranteed function, and +; you should really use a server based rate limiter if you want +; to do this correctly. +; DEFAULT: off +; VALUES: any whole number (in bytes per second) +;throttle_download = 10 + +; This determines if a preview image should be retrieved from video files +; It requires encode_get_image transcode settings. +; DEFAULT: "false" +;generate_video_preview = "true" + +; Uncomment if don't want Ampache to follow symlinks +; DEFAULT: "false" +;no_symlinks = "true" + +; Use auth? +; If this is set to "true" Ampache will require a valid +; Username and password. If this is set to false then Ampache +; will not ask you for a username and password. false is only +; recommended for internal only instances +; DEFAULT: "true" +use_auth = "true" + +; Default Auth Level +; If use_auth is set to false then this option is used +; to determine the permission level of the 'default' users +; default is administrator. This setting only takes affect +; if use_auth is false +; POSSIBLE VALUES: user, admin, manager, guest +; DEFAULT: guest +default_auth_level = "guest" + +; Skip Timer Threshold +; This allows custom times to decide when a track is skipped +; Allows an integer to denote seconds, or a float to denote percentage +; POSSIBLE VALUES: +; 20 = 20 seconds. +; 0.3 = 30% +; DEFAULT: 20 +;skip_timer = 20 + +; 5 Star Ratings +; This allows ratings for almost any object in Ampache +: It also allows users to flag objects as a favorite +; POSSIBLE VALUES: false true +; DEFAULT: "true" +ratings = "true" + +; Enable filtering on browse pages for artists and albums. +; If you enable this setting the get_random and browse pages +; will remove artists and albums that are <= to that rating +; DEFAULT: "false" +;rating_browse_filter = "true" + +; Set the rating that will be filtered +; e.g. 2 will filter 1 and 2 star albums and artists +; this setting must be set for the filter to work +; DEFAULT: null +;rating_browse_minimum_stars = 1 + +; By default Ampache assigns the rating set in files tags to +; the system user (-1). If you would like ALL file tags to be +; assigned to a specific user set their user ID here. +; DEFAULT: -1 +;rating_file_tag_user = 1 + +; Direct play +; This allows user to play directly a song or album +; POSSIBLE VALUES: false true +; DEFAULT: "true" +directplay = "true" + +; Sociable +; This turns on / off all of the "social" features of Ampache +; default is on, but if you don't care and just want music +; turn this off to disable all social features. +; DEFAULT: "true" +sociable = "true" + +; License +; This turns on / off all licensing features on Ampache +; DEFAULT: "false" +licensing = "false" + +; This options will turn on/off Demo Mode +; If Demo mode is on you can not play songs or update your catalog +; in other words.. leave this commented out +; DEFAULT: "false" +;demo_mode = "true" + +; This options will turn on/off Simple User Mode +; If simple_user_mode is true you can not edit your profile or make +; changes to your account unless you are an admin +; This is for shared accounts or simple sites like the ampache demo +; DEFAULT: "false" +;simple_user_mode = "true" + +; Caching +; This turns the caching mechanisms on or off, due to a large number of +; problems with people with very large catalogs and low memory settings +; this is off by default as it does significantly increase the memory +; requirements on larger catalogs. If you have the memory this can create +; a 2-3x speed improvement. +; DEFAULT: "false" +;memory_cache = "true" + +; Memory Limit +; This defines the "Min" memory limit for PHP if your php.ini +; has a lower value set Ampache will set it up to this. If you +; set it below 16MB getid3() will not work! +; DEFAULT: 32 +;memory_limit = 32 + +; Album Art Preferred Filename +; Specify a filename to look for if you always give the same filename +; i.e. "folder.jpg" Ampache currently only supports jpg/gif and png +; Especially useful if you have a front and a back image in a folder +; comment out if Ampache should search for any jpg, gif or png +; DEFAULT: folder.jpg +;album_art_preferred_filename = "folder.jpg" + +; Artist Art Preferred Filename +; Specify a filename to look for artist specific art, either in the +; same folder as your files or in the parent folder +; e.g. /mnt/music/Artist/Album/artist.jpg +; /mnt/music/Artist/artist.jpg +; DEFAULT: folder.jpg +;artist_art_preferred_filename = "folder.jpg" + +; Artist Art Dump Folder +; Specify a local folder to search for art using name/title to identify +; i.e. "/var/www/art/" is filled with artist images named by artist +; e.g. "Megadeth.jpg", "Judas Priest.png", "The Smashing Pumpkins.jpg" +; When enabled; gather_folder will check this folder for artist images +; DEFAULT: "false" +;artist_art_folder = "/var/www/art" + +; Album Art Store on Disk +; This defines if arts should be stored on disk instead of database. +; DEFAULT: "false" +;album_art_store_disk = "true" + +; Local Metadata Directory +; This define a local metadata directory with write access where to store +; heavy data if enabled (album arts, ...) +; DEFAULT: none +;local_metadata_dir = "/metadata" + +; Maximal upload size +; Specify the maximal allowed upload size for images, in bytes. +; DEFAULT: 1048576 +;max_upload_size = 1048576 + +; Album Art Minimum Width +; Specify the minimum width for arts (in pixel). +; DEFAULT: none +;album_art_min_width = 100 + +; Album Art Maximum Width +; Specify the maximum width for arts (in pixel). +; DEFAULT: none +;album_art_max_width = 1024 + +; Album Art Minimum Height +; Specify the minimum height for arts (in pixel). +; DEFAULT: none +;album_art_min_height = 100 + +; Album Art Maximum Height +; Specify the maximum height for arts (in pixel). +; DEFAULT: none +;album_art_max_height = 1024 + +; Resize Images * Requires PHP-GD * +; Set this to true if you want Ampache to resize the Album +; art on the fly, this increases load time and CPU usage +; and also requires the PHP-GD library. This is very useful +; If you have high-quality album art and a small upload cap +; DEFAULT: "false" +;resize_images = "true" + +; Playlist Cover Art +; Set this to true if you want Ampache to generate +; cover art for playlists automatically based on +; the content. +; DEFAULT: "true" +playlist_art = "true" + +; Statistical Graphs * Requires PHP-GD * +; Set this to true if you want Ampache to generate statistical graphs on usages / users. +; This is false by default due to issues around the licensing of c-pchart. +; https://github.com/ampache/ampache/issues/1515 +; http://www.pchart.net/license +; REFERENCE: https://github.com/ampache/ampache/wiki/chart-faq +; You can enable c-chart with the following command +; composer require szymach/c-pchart "2.*" +; DEFAULT: "false" +;statistical_graphs = "true" + +; Art Gather Order +; Simply arrange the following in the order you would like +; Ampache to search. If you want to disable one of the search +; methods simply leave it out. DB should be left as the first +; method unless you want it to overwrite what's already in the +; database +; POSSIBLE VALUES (builtins): db tags folder lastfm musicbrainz google +; POSSIBLE VALUES (plugins): Amazon,TheAudioDb,Tmdb,Omdb,Flickr +; DEFAULT: db,tags,folder,spotify,musicbrainz,lastfm,google +art_order = "db,spotify,TheAudioDb,musicbrainz,lastfm,tags,folder" + +; Gather song art +; Gather song art additionally to the album art. This will add each +; image of a song into the database. If your database contains +; many songs not related to an album this should possibly be set to true. +; Otherwise for an environment that contains mainly full albums +; this should remain false to avoid multiple entries of the same image. +; You need to set show_song_art to true if you want to make the song images visible. +; DEFAULT = false +;gather_song_art = "true" + +; Show song art +; Show song art instead of album art in web UI and Subsonic API. +; This will only work when gather_song_art is set to true AND when +; there is song art in the database. +; DEFAULT: false +;show_song_art = "true" + +; Spotify Album art search filter +; Narrow the search. +; POSSIBLE VALUES: artist,(year:1991 or year:1991-2000) +; POSSIBLE VALUES: empty string("") or commented out for no filter +; DEFAULT: none; +;spotify_art_filter = "artist" + +; Art search limit +; Limit the total images returned +; DEFAULT: 15 +;art_search_limit = 15 + +; Recommendations +; Set this to true to enable display of similar artists or albums +; while browsing. Requires Last.FM. +; DEFAULT: "false" +;show_similar = "true" + +; Hide Searches from the API +; Set this to true and the get_indexes and playlists methods will not automatically include +; searches/smart lists in the results. +; Older versions of the Kodi Add-on will require this for playlists to work +; DEFAULT: "false" +;hide_search = "true" + +; Allow or disallow upload scripts on the server +; Enable this if you trust your users +; DEFAULT: "false" +;allow_upload_scripts = "true" + +;######################################################### +; API keys # +;######################################################### + +; Last.FM API Key +; Set this to your Last.FM api key to actually use Last.FM for +; recommendations and metadata. +lastfm_api_key = "d5df942424c71b754e54ce1832505ae2" + +; Last.FM API secret +; Set this to your Last.FM api secret to actually use Last.FM for +; scrobbling. +; DEFAULT: none +lastfm_api_secret = "" + +; Spotify client id +; Set this to your Spotify client id to actually use Spotify for +; accessing their catalog API. (https://developer.spotify.com/dashboard/) +; DEFAULT: none +;spotify_client_id = "" + +; Spotify client secret +; Both id and secret are required to access the spotify catalog. +; DEFAULT: none +;spotify_client_secret = "" + +; Wanted +; Set this to true to enable display missing albums and the +; possibility for users to mark it as wanted. +; DEFAULT: "false" +wanted = "false" + +; Wanted types +; Set the allowed types of wanted releases (album,compilation,single,ep,live,remix,promotion,official) +; DEFAULT: album,official +wanted_types = "album,official" + +; Wanted Auto Accept +; Mark wanted requests as accepted by default (no content manager agreement required) +; DEFAULT: "false" +;wanted_auto_accept = "true" + +; Labels +; Use labels to browse artists per label membership. +; DEFAULT: "false" +;label = "true" + +; Broadcasts +; Allow users to broadcast music. +; This feature requires advanced server configuration, please take a look on the wiki for more information. +; DEFAULT: "false" +;broadcast = "true" + +; Channels +; Set this to true to enable channels and the +; possibility for users to create channels from playlists +; DEFAULT: "false" +channel = "false" + +; Live Streams +; Set this to true to enable live streams (radio) and the +; possibility for users to add new live streams. +; DEFAULT: "true" +live_stream = "true" + +; Podcasts +; Set this to true to enable podcasts and the +; possibility for admins to subscribe to new podcasts. +; DEFAULT: "true" +podcast = "true" + +; Web Socket address +; Declare the web socket server address +; DEFAULT: determined automatically +;websocket_address = "ws://localhost:8100" + +; Refresh Limit +; This defines the default refresh limit in seconds for +; pages with dynamic content, such as Now Playing +; DEFAULT: 60 +; Possible Values: Int > 5 +refresh_limit = "60" + +; Embedded Now Playing Page +; Set this to true to enable the embedded Now Playing page (now_playing.php). +; This page allows for embedding a Now Playing badge into a stream +; or status page. Use with the parameter 'user_id' to filter by a +; specific user. This page is like rss in that it doesn't require a +; login to view. +; DEFAULT: "false" +;use_now_playing_embedded = "true" + +; Now Playing Refresh Limit +; This defines the refresh limit in seconds for the +; Now Playing embedded page. This (now_playing.php) is not +; part of the normal application and is designed to be +; embedded in another app, like a stream or status page. +; If this value is not valid, automatic refresh will be disabled. +; DEFAULT: -1 +; Possible Values; Int > 1 +;now_playing_refresh_limit = "-1" + +; Now Playing Custom CSS +; This defines the custom css file for the Now Playing embedded +; page. This (now_playing.php) is not part of the normal +; application and is designed to be embedded in another app, like +; a stream or status page. +; If this value is not set, no CSS will be used. Custom CSS can +; still be applied in the other application, like OBS. +; DEFAULT: Not enabled +;now_playing_css_file = "templates/now-playing.css" + +; Footer Statistics +; This defines whether statistics (Queries, Cache Hits, Load Time) +; are shown in the page footer. +; DEFAULT: "true" +show_footer_statistics = "true" + +; RSS Feeds +; Set this to true to enable rss feeds. +; (latest albums, shouts, albums of artist, ...) +; use_rss = false (values true | false) +; DEFAULT: "false" +;use_rss = "true" + +; This setting allows themes to overwrite PHP template files. This can be really +; dangerous. Do this only if you trust every theme in your themes/ directory. +; DEFAULT: "false" +;allow_php_themes = "true" + +;######################################################### +; Debugging # +;######################################################### + +; Debug +; If this is enabled Ampache will write debugging information to the log file +; DEFAULT: "false" +;debug = "true" + +; Debug Level +; This should always be set in conjunction with the +; debug option, it defines how prolific you want the +; debugging in Ampache to be. values are 1-5. +; 1 == Basic Errors +; 2 == Errors + Failures (login attempts etc.) +; 3 == Full Error Information +; 4 == General Information +; 5 == Full Information (cataloging progress etc.) +; DEFAULT: 5 +debug_level = 5 + +; Path to Log File +; This defines where you want Ampache to log events to +; this will only happen if debug is turned on. Do not +; include trailing slash. You will need to make sure that +; the specified directory exists and your HTTP server has +; write access. +; DEFAULT: none +log_path = "/var/log/ampache" + +; Log filename pattern +; This defines where the log file name pattern +; %name.%Y%m%d.log will create a different log file every day. +; DEFAULT: %name.%Y%m%d.log +log_filename = "%name.%Y%m%d.log" + +;######################################################### +; Encoding Settings # +;######################################################### + +; Charset of generated HTML pages +; Default of UTF-8 should work for most people +; DEFAULT: UTF-8 +site_charset = "UTF-8" + +; Locale Charset +; Local charset (mainly for file operations) if different +; from site_charset. +; This is disabled by default, enable only if needed +; (for Windows please set lc_charset to ISO8859-1) +; DEFAULT: ISO8859-1 +;lc_charset = "ISO8859-1" + +; Multibyte +; See http://php.net/manual/mbstring.supported-encodings.php +; If you want ID3v1 encoding detection to work, you should uncomment this line +; so that the ordering is sane. +; DEFAULT: auto +;mb_detect_order = "ASCII,UTF-8,EUC-JP,ISO-2022-JP,SJIS,JIS" + +;######################################################### +; Custom actions (optional) # +;######################################################### + +; Your custom play action title +;custom_play_action_title_0 = "" +; Your custom play action icon name (stored as /images/icon_[your_image].png) +;custom_play_action_icon_0 = "" +; Your custom action script, where: +; - %f: the media file path +; - %c: the excepted codec target (mp3, ogg, ...) +; - %a: the artist name +; - %A: the album name +; - %t: the song title +; DEFAULT: none +;custom_play_action_run_0 = "" + +; Example for Karaoke playing +;custom_play_action_title_0 = "Karaoke" +;custom_play_action_icon_0 = "microphone" +;custom_play_action_run_0 = "sox \"%f\" -p oops | ffmpeg -i pipe:0 -f %c pipe:1" + +;######################################################### +; LDAP login info (optional) # +;######################################################### + +; LDAP server URL (required) +; DEFAULT: none +;ldap_url = "ldap://localhost/" +;ldap_url = "ldaps://localhost/" + +; LDAP credentials (optional) +; DEFAULT: none +;ldap_username = "" +;ldap_password = "" + +; LDAP Base DN for the search (required) +; DEFAULT: none +;ldap_search_dn = "ou=People,dc=yoursubdomain,dc=yourdomain,dc=yourtld" + +; LDAP objectClass (required) +; DEFAULT: none +;ldap_objectclass = "posixAccount" ; OpenLDAP +;ldap_objectclass = "organizationalPerson" ; Microsoft Active Directory + +; LDAP filter for search (required) +; DEFAULT: none +;ldap_filter = "(uid=%v)" ; OpenLDAP +;ldap_filter = "(sAMAccountName=%v)" ; Microsoft Active Directory + +; Require that the user is in a specific group (optional) +; DEFAULT: none +;ldap_require_group = "cn=yourgroup,ou=yourorg,dc=yoursubdomain,dc=yourdomain,dc=yourtld" + +; LDAP name field +; DEFAULT: cn +;ldap_name_field = "cn" +;ldap_name_field = "displayName" + +; LDAP email field +; DEFAULT: mail +;ldap_email_field = "mail" + +; LDAP avatar field +; DEFAULT: none +;ldap_avatar_field = "jpegPhoto" + +; LDAP avatar mime type +; DEFAULT: image/jpeg +;ldap_avatar_mime = "image/jpeg" + +; LDAP protocol version to use +; DEFAULT: 3 +;ldap_protocol_version = 3 + +; LDAP StartTLS +; DEFAULT: "false" +;ldap_start_tls = "true" + +; LDAP member attribute name. +; That's the name of the attribute of the group that will contain +; the usernames. +; DEFAULT: member +;ldap_member_attribute = "member" +;ldap_member_attribute = "memberuid" + +;######################################################### +; OpenID login info (optional) # +;######################################################### + +; Requires specific OpenID Provider Authentication Policy +; DEFAULT: none +; VALUES: PAPE_AUTH_MULTI_FACTOR_PHYSICAL,PAPE_AUTH_MULTI_FACTOR,PAPE_AUTH_PHISHING_RESISTANT +;openid_required_pape = "" + +;######################################################### +; Public Registration settings, defaults to disabled # +;######################################################### + +; This setting will silently create an Ampache account +; for anyone who can login using LDAP (or any other login +; extension). The default is to create new users as guests +; see auto_user config option if you would like to change this +; DEFAULT: "false" +;auto_create = "true" + +; This setting will silently update an Ampache account's +; information for anyone who can login using LDAP +; (or any other login extension). +; DEFAULT: "false" +;external_auto_update = "true" + +; This setting turns on/off public registration. It is +; recommended you leave this off, as it will allow anyone to +; sign up for an account on your server. +; REMEMBER: don't forget to set the mail from address further down in the config. +; DEFAULT: "false" +allow_public_registration = "false" + +; Require Captcha Text on Image confirmation +; Turning this on requires the user to correctly +; type in the letters in the image created by Captcha +; Default is off because its very hard to detect if it failed +; to draw, or they failed to enter it. +; DEFAULT: "false" +;captcha_public_reg = "true" + +; This setting turns on/off admin notification of registration. +; DEFAULT: "false" +;admin_notify_reg = "true" + +; This setting determines whether the user will be created as a disabled user. +; If this is on, an administrator will need to manually enable the account +; before it's usable. +; DEFAULT: "false" +;admin_enable_required = "true" + +; This setting will allow all registrants/ldap/http users +; to be auto-approved as a user. By default, they will be +; added as a guest and must be promoted by the admin. +; POSSIBLE VALUES: guest, user, admin +; DEFAULT: guest +;auto_user = "guest" + +; This will display the user agreement when registering +; For agreement text, edit config/registration_agreement.php +; User will need to accept the agreement before they can register +; DEFAULT: "false" +;user_agreement = "true" + +; This disable email confirmation when registering. +; DEFAULT: "false" +;user_no_email_confirm = "true" + +; This will display the cookie disclaimer (EU Cookie Law) +; DEFAULT: "false" +cookie_disclaimer = "false" + +; The fields that will be shown on Registration page +; If a user wants to register. +; Username and email fields are forced. +; POSSIBLE VALUES: fullname,website,state,city +; DEFAULT: fullname,website +registration_display_fields = "fullname,website" + +; The fields that will be mandatory +; This controls which fields are mandatory for registration. +; Username and email fields are forced mandatory. +; POSSIBLE VALUES: fullname,website,state,city +; DEFAULT: fullname +registration_mandatory_fields = "fullname" + +;######################################################### +; These options control the dynamic downsampling based # +; on current usage # +; *Note* Transcoding must be enabled and working # +;######################################################### + +; Attempt to optimize bandwidth by dynamically lowering the bit rate of new +; streams. Since the bit rate is only adjusted at the beginning of a song, the +; actual cumulative bitrate for concurrent streams can be up to around +; double the configured value. It also only applies to streams that are +; transcoded. +; DEFAULT: none +;max_bit_rate = 576 + +; New dynamically downsampled streams will be denied if they are forced below +; this value. +; DEFAULT: 8 +;min_bit_rate = 48 + +;######################################################### +; Transcode Settings # +;######################################################### + +; These are commands used to transcode non-streaming +; formats to the target file type for streaming. +; This can be useful in re-encoding file types that don't stream +; very well, or if your player doesn't support some file types. +; +; 'Downsampling' will also use these commands. +; +; To state the bleeding obvious, any programs referenced in the transcode +; commands must be installed, in the web server's search path (or referenced +; by their full path), and executable by the web server. + +; Input type selection +; TYPE is the extension. 'allowed' certifies that transcoding works properly for +; this input format. 'required' further forbids the direct streaming of a format +; (e.g. if you store everything in FLAC, but don't want to ever stream that.) +; transcode_TYPE = {allowed|required|false} +; DEFAULT: "false" +;;; Audio +;transcode_m4a = "allowed" +;transcode_flac = "required" +;transcode_mpc = "required" +;transcode_ogg = "required" +;transcode_oga = "required" +;transcode_opus = "required" +;transcode_wav = "required" +;transcode_wma = "required" +;transcode_aif = "required" +;transcode_aiff = "required" +;transcode_ape = "required" +;transcode_shn = "required" +transcode_mp3 = "allowed" +;;; Video +;transcode_avi = "allowed" +;transcode_flv = "allowed" +;transcode_mkv = "allowed" +;transcode_mpg = "allowed" +;transcode_mpeg = "allowed" +;transcode_m4v = "allowed" +;transcode_mp4 = "allowed" +;transcode_mov = "allowed" +;transcode_wmv = "allowed" +;transcode_ogv = "allowed" +;transcode_divx = "allowed" +;transcode_m2ts = "allowed" +;transcode_webm = "allowed" + +; Default audio output format +; DEFAULT: none +;encode_target = mp3 + +; Default video output format +; DEFAULT: none +;encode_video_target = webm + +; Override the default output format on a per-type basis, for example, +; to stream lossless encoded files in lossy formats. +; encode_target_TYPE = TYPE +; DEFAULT: none +;encode_target_flac = opus + +; Override the default TYPE transcoding behavior on a per-player basis, for example, +; to stream lossless using the api and lossy using the web interface. +; transcode_player_PLAYER_TYPE = TYPE +; Valid PLAYER is: webplayer, api +; DEFAULT: none +;transcode_player_webplayer_m4a = "required" +;transcode_player_webplayer_flac = "required" +;transcode_player_webplayer_mpc = "required" + +; Override the default output format on a per-player basis +; encode_player_PLAYER_target = TYPE +; Valid PLAYER is: webplayer, api +; DEFAULT: none +;encode_player_webplayer_target = mp3 +;encode_player_api_target = mp3 + +; Allow clients to override transcode settings (output type, bitrate, codec ...) +; DEFAULT: "true" +transcode_player_customize = "true" + +; Command configuration. Substitutions will be made as follows: +; %FILE% => filename +; %BITRATE% => target bit rate (as chosen by the admin or users in the +; preferences, if transcode_player_customize = "true") +; You can do fancy things like VBR, but consider whether the consequences are +; acceptable in your environment. + +; Master transcode command +; transcode_cmd should be a single command that supports multiple file types, +; such as ffmpeg or avconv. It's still possible to make a configuration that's +; equivalent to the old default, but if you find that necessary you should be +; clever enough to figure out how on your own. +; DEFAULT: none +;transcode_cmd = "ffmpeg" +;transcode_cmd = "avconv" +;transcode_cmd = "/usr/bin/neatokeen" + +; Transcode input file argument +transcode_input = "-i %FILE%" + +; Specific transcode commands +; It shouldn't be necessary in most cases, but you can override the transcode +; command for specific source formats. It still needs to accept the +; encoding arguments, so the easiest approach is to use your normal command as +; a clearing-house. +; transcode_cmd_TYPE = TRANSCODE_CMD +;transcode_cmd_mid = "timidity -Or -o – %FILE% | ffmpeg -f s16le -i pipe:0" + +; Encoding arguments +; For each output format, you should provide the necessary arguments for +; your transcode_cmd. +; encode_args_TYPE = TRANSCODE_CMD_ARGS +encode_args_mp3 = "-vn -b:a %BITRATE%K -c:a libmp3lame -f mp3 pipe:1" +encode_args_ogg = "-vn -b:a %BITRATE%K -c:a libvorbis -f ogg pipe:1" +encode_args_opus = "-vn -b:a %BITRATE%K -c:a libopus -compression_level 10 -vsync 2 -f ogg pipe:1" +encode_args_m4a = "-vn -b:a %BITRATE%K -c:a libfdk_aac -f adts pipe:1" +encode_args_wav = "-vn -b:a %BITRATE%K -c:a pcm_s16le -f wav pipe:1" +encode_args_flv = "-b:a %BITRATE%K -ar 44100 -ac 2 -v 0 -f flv -c:v libx264 -preset superfast -threads 0 pipe:1" +encode_args_webm = "-b:a %BITRATE%K -f webm -c:v libvpx -preset superfast -threads 0 pipe:1" +encode_args_ts = "-q %QUALITY% -s %RESOLUTION% -f mpegts -c:v libx264 -c:a libmp3lame -maxrate %MAXBITRATE%k -preset superfast -threads 0 pipe:1" +encode_args_ogv = "-codec:v libtheora -qscale:v 7 -codec:a libvorbis -qscale:a 5 -f ogg pipe:1" + +; Encoding arguments to retrieve an image from a single frame +encode_get_image = "-ss %TIME% -f image2 -vframes 1 pipe:1" + +; Encoding argument to encrust subtitle +encode_srt = "-vf \"subtitles='%SRTFILE%'\"" + +; Encode segment frame argument +encode_ss_frame = "-ss %TIME%" + +; Encode segment duration argument +encode_ss_duration = "-t %DURATION%" + +; Use segments for transcoding or send it all in one go. +; Useful if you are having issues streaming the full track. +; You can set it for all streams or a specific player +; POSSIBLE VALUES: true webplayer api +; DEFAULT: "webplayer" +send_full_stream = "webplayer" + +;######################################################### +; Transcode Caching # +;######################################################### + +; These are commands used to pre-cache a file format for streaming. +; This helps avoid waiting for transcodes to finish and makes +; files immediately available to the client. + +; Path to your cache folder. +; This is where the pre-transcoded files will be stored +; DEFAULT: none +;cache_path = "/tmp" + +; Default audio output format +; DEFAULT: none +;cache_target = "mp3" + +; Look in your local catalogs for these file extensions and pre-cache to the +; 'cache_path'. This could be helpful to reduce space needed on your +; web server or to make sure that clients get files quickly. +; Execute "php bin/cli run:cacheProcess" to process these files. +; DEFAULT: "false" +;cache_m4a = "true" +;cache_flac = "true" +;cache_mpc = "true" +;cache_ogg = "true" +;cache_oga = "true" +;cache_opus = "true" +;cache_wav = "true" +;cache_wma = "true" +;cache_aif = "true" +;cache_aiff = "true" +;cache_ape = "true" +;cache_shn = "true" +;cache_mp3 = "true" + +; REMOTE CATALOGS ONLY +; Enabling cache_remote on remote catalogs will cache every file on the remote server +; DEFAULT: "false" +;cache_remote = "true" + +;######################################################### +; Proxy Settings (optional) # +;######################################################### + +; If Ampache is behind an http proxy, specify the hostname or IP address +; port, proxy username, and proxy password here. +; DEFAULT: not in use +;proxy_host = "192.168.0.1" +;proxy_port = "8080" +;proxy_user = "" +;proxy_pass = "" + +; If Ampache is behind an https reverse proxy, force use HTTPS protocol. +; DEFAULT: "false" +;force_ssl = "true" + +;######################################################### +; Mail Settings # +;######################################################### + +; Enable or disable email server features +; otherwise, you can reset your password +; and never receive an email with the new one +; Default: false +;mail_enable = "true" + +; Method used to send mail +; POSSIBLE VALUES: smtp sendmail php +; DEFAULT: php +;mail_type = "php" + +; Mail domain. +; DEFAULT: example.com +;mail_domain = "example.com" + +; This will be combined with mail_domain and used as the source address for +; emails generated by Ampache. For example, setting this to 'me' will set the +; sender to 'me@example.com'. +; DEFAULT: info +;mail_user = "info" + +; A name to go with the email address. +; DEFAULT: Ampache +;mail_name = "Ampache" + +; How strictly email addresses should be checked. +; easy does a regex match, strict actually performs some SMTP transactions +; to see if we can send to this address. +; POSSIBLE VALUES: strict easy none +; DEFAULT: strict +;mail_check = "strict" + +;######################################################### +; sendmail Settings # +;######################################################### + +; DEFAULT: /usr/sbin/sendmail +;sendmail_path = "/usr/sbin/sendmail" + +;######################################################### +; SMTP Settings # +;######################################################### + +; Mail server (hostname or IP address) +; DEFAULT: localhost +;mail_host = "localhost" + +; SMTP port +; DEFAULT: 25 +;mail_port = 25 + +; Secure SMTP +; POSSIBLE VALUES: ssl tls +; DEFAULT: none +;mail_secure_smtp = tls + +; Enable SMTP authentication +; DEFAULT: "false" +;mail_auth = "true" + +; SMTP username +; your mail auth username. +; DEFAULT: none +;mail_auth_user = "" + +; SMTP password +; your mail auth password. +; DEFAULT: none +;mail_auth_pass = "" + +;######################################################### +; Abbreviation Filter # +;######################################################### + +; For file name parsing. Any unnecessary abbreviations in file names can be removed during parsing +; by adding them to the list below so that they will be removed during the parsing process. +common_abbr = "divx,xvid,dvdrip,hdtv,lol,axxo,repack,xor,pdtv,real,vtv,caph,2hd,proper,fqm,uncut,topaz,tvt,notv,fpn,fov,orenji,0tv,omicron,dsr,ws,sys,crimson,wat,hiqt,internal,brrip,boheme,vost,vostfr,fastsub,addiction,x264,LOL,720p,1080p,YIFY,evolve,fihtv,first,bokutox,bluray,tvboom,info" + diff --git a/hcl/default/ampache/templates/nginx.conf.tmpl b/hcl/default/ampache/templates/nginx.conf.tmpl @@ -0,0 +1,14 @@ +server { + listen 443 ssl; + + ssl_certificate /etc/letsencrypt/live/ampache.in0rdr.ch/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/ampache.in0rdr.ch/privkey.pem; + + location / { + proxy_pass http://{{ env "NOMAD_ADDR_ampache" }}; + 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-Proto $scheme; + } +} diff --git a/hcl/default/certbot/README b/hcl/default/certbot/README @@ -0,0 +1,62 @@ +LETSENCRYPT CERTIFICATES FOR NOMAD +---------------------------------- + +Request and renew Letsencrypt certificates with Nomad. + +USAGE +----- + +Register the volume for the Letsencrypt certificates: + + nomad volume register certbot-volume.hcl + +Install the periodic job: + + nomad run certbot.nomad + +LETSENCRYPT EMAIL +----------------- + +Add Letsencrypt plain-text email in Consul KV "certbot/email" + +DOMAINS +------- + +Add Letsencrypt domains in Consul "certbot/domains" as YAML file + + --- + - cn1.example.com,san1.example.com + - cn2.example.com + +Hint: Don't automatically redirect http traffic to https ports. If enabled, +Letsencrypt fails to download the challenge files from the http endpoint +(always redirected straight to https, which is no desired in this scenario). + + # don't redirect to https (disable https redirection to renew certificates) + #redirect scheme https if !{ ssl_fc } is_site + + +RENEWAL +------- + +https://eff-certbot.readthedocs.io/en/stable/using.html#re-creating-and-updating-existing-certificates + +>If a certificate is requested with run or certonly specifying a certificate +>name that already exists, Certbot updates the existing certificate. Otherwise a +>new certificate is created and assigned the specified name. + +LEVANT +------ + +Alternatively, use levant to dynamically template the tasks of this job. + +Spins up n tasks for n certificates. + +- Pro: Use JSON file on the host to provide domains as input. The approach with the + simple shell scripts orders all certificates in the same task (same script). +- Contra: No Consul KV to provide domains as input + +Howto: +- adjust the defaults file +- levant render certbot.levant +- levant {plan,run} certbot.levant diff --git a/hcl/default/certbot/certbot-volume.hcl b/hcl/default/certbot/certbot-volume.hcl @@ -0,0 +1,30 @@ +# Register external nfs volume with Nomad CSI +# https://www.nomadproject.io/docs/commands/volume/register +type = "csi" +# Unique ID of the volume, volume.source field in a job +id = "certbot" +# Display name of the volume. +name = "certbot" +# ID of the physical volume from the storage provider +external_id = "csi-certbot" +plugin_id = "nfs" + +# You must provide at least one capability block +# You must provide a block for each capability +# youintend to use in a job's volume block +# https://www.nomadproject.io/docs/commands/volume/register +capability { + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" +} + +# https://github.com/kubernetes-csi/csi-driver-nfs/blob/master/docs/driver-parameters.md +context { + server = "192.168.1.1" + share = "csi-certbot" +} + +mount_options { + # mount.nfs: Either use '-o nolock' to keep locks local, or start statd. + mount_flags = ["nolock"] +} diff --git a/hcl/default/certbot/certbot.levant b/hcl/default/certbot/certbot.levant @@ -0,0 +1,57 @@ +# https://learn.hashicorp.com/tutorials/nomad/levant-abstract-jobs +[[- /* Template defaults as json */ -]] +[[- $Values := (fileContents "defaults.json" | parseJSON ) ]] + +job "certbot" { + datacenters = ["dc1"] + type = "batch" + + periodic { + cron = "@daily" + } + + group "certbot" { + count = 1 + + volume "certbot-data" { + type = "csi" + source = "certbot" + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" + } + + network { + port "http" { + to = 80 + static = 8080 + } + port "https" { + to = 443 + static = 4433 + } + } + +[[ range $i,$d := $Values.domains ]] + task "request-[[ $d ]]" { + driver = "docker" + + volume_mount { + volume = "certbot-data" + destination = "/etc/letsencrypt" + } + + config { + image = "certbot/certbot:arm64v8-latest" + ports = ["http", "https"] + # https://eff-certbot.readthedocs.io/en/stable/using.html#re-creating-and-updating-existing-certificates + args = ["certonly", "--standalone", "-n", "-m", "mail@example.com", "--agree-tos", "--domains", "[[ $d ]]", "--staging"] + } + + resources { + memory = 32 + cpu = 100 + } + } +[[ end ]] + } +} diff --git a/hcl/default/certbot/certbot.nomad b/hcl/default/certbot/certbot.nomad @@ -0,0 +1,66 @@ +# Create a kv entry certbot/domains in Consul with the following YAML config: +# --- +# - cn1.example.com,san1.example.com +# - cn2.example.com +# +# Put the letsencrypt email address as plain-text in kv certbot/email + +job "certbot" { + datacenters = ["dc1"] + type = "batch" + + periodic { + cron = "@daily" + } + + group "certbot" { + count = 1 + + volume "certbot-data" { + type = "csi" + source = "certbot" + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" + } + + network { + port "http" { + to = 80 + static = 8080 + } + port "https" { + to = 443 + static = 4433 + } + } + + task "request" { + driver = "docker" + + volume_mount { + volume = "certbot-data" + destination = "/etc/letsencrypt" + } + + template { + destination = "${NOMAD_TASK_DIR}/certbot-request.sh" + data = file("./templates/certbot-request.sh.tmpl") + perms = 755 + } + + config { + image = "certbot/certbot:arm64v8-latest" + ports = ["http", "https"] + volumes = [ + "local/certbot-request.sh:/opt/certbot/tools/certbot-request.sh" + ] + entrypoint = ["./tools/certbot-request.sh"] + } + + resources { + memory = 128 + cpu = 300 + } + } + } +} diff --git a/hcl/default/certbot/defaults.json b/hcl/default/certbot/defaults.json @@ -0,0 +1,7 @@ +{ + "domains": [ + "cn1.example.com,san1.example.com", + "cn2.example.com" + ] +} + diff --git a/hcl/default/certbot/templates/certbot-request.sh.tmpl b/hcl/default/certbot/templates/certbot-request.sh.tmpl @@ -0,0 +1,5 @@ +#!/usr/bin/env sh +# https://eff-certbot.readthedocs.io/en/stable/using.html#re-creating-and-updating-existing-certificates +{{ range $i, $d := key "certbot/domains" | parseYAML }} +certbot certonly --standalone -n -m {{ key "certbot/email" }} --agree-tos --domains {{ $d }} # --force-renewal --staging +{{- end }} diff --git a/hcl/default/cv/cv.nomad b/hcl/default/cv/cv.nomad @@ -0,0 +1,66 @@ +job "cv" { + datacenters = ["dc1"] + + group "server" { + count = 1 + + volume "tls" { + type = "csi" + source = "certbot" + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" + } + + network { + port "jekyll" { + to = 4000 + } + port "https" { + to = 443 + static = 44361 + } + } + + task "jekyll" { + driver = "docker" + + config { + image = "127.0.0.1:5000/jekyll-cv:latest" + ports = ["jekyll"] + } + + resources { + memory = 128 + cpu = 200 + } + } + + task "nginx" { + driver = "docker" + + config { + image = "nginx:stable-alpine" + ports = ["https"] + volumes = [ + # mount the templated config from the task directory to the container + "local/cv.conf:/etc/nginx/conf.d/cv.conf", + ] + } + + volume_mount { + volume = "tls" + destination = "/etc/letsencrypt" + } + + template { + destination = "${NOMAD_TASK_DIR}/cv.conf" + data = file("./templates/nginx.conf.tmpl") + } + + resources { + memory = 128 + cpu = 200 + } + } + } +} diff --git a/hcl/default/cv/templates/nginx.conf.tmpl b/hcl/default/cv/templates/nginx.conf.tmpl @@ -0,0 +1,10 @@ +server { + listen 443 ssl; + + ssl_certificate /etc/letsencrypt/live/cv.in0rdr.ch/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/cv.in0rdr.ch/privkey.pem; + + location / { + proxy_pass http://{{ env "NOMAD_ADDR_jekyll" }}; + } +} diff --git a/hcl/default/diary/diary.nomad b/hcl/default/diary/diary.nomad @@ -0,0 +1,66 @@ +job "diary" { + datacenters = ["dc1"] + + group "server" { + count = 1 + + volume "tls" { + type = "csi" + source = "certbot" + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" + } + + network { + port "jekyll" { + to = 4000 + } + port "https" { + to = 443 + static = 44362 + } + } + + task "jekyll" { + driver = "docker" + + config { + image = "127.0.0.1:5000/jekyll-diary:latest" + ports = ["jekyll"] + } + + resources { + memory = 128 + cpu = 200 + } + } + + task "nginx" { + driver = "docker" + + config { + image = "nginx:stable-alpine" + ports = ["https"] + volumes = [ + # mount the templated config from the task directory to the container + "local/diary.conf:/etc/nginx/conf.d/diary.conf", + ] + } + + volume_mount { + volume = "tls" + destination = "/etc/letsencrypt" + } + + template { + destination = "${NOMAD_TASK_DIR}/diary.conf" + data = file("./templates/nginx.conf.tmpl") + } + + resources { + memory = 128 + cpu = 200 + } + } + } +} diff --git a/hcl/default/diary/templates/nginx.conf.tmpl b/hcl/default/diary/templates/nginx.conf.tmpl @@ -0,0 +1,10 @@ +server { + listen 443 ssl; + + ssl_certificate /etc/letsencrypt/live/diary.in0rdr.ch/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/diary.in0rdr.ch/privkey.pem; + + location / { + proxy_pass http://{{ env "NOMAD_ADDR_jekyll" }}; + } +} diff --git a/hcl/default/git/README.md b/hcl/default/git/README.md @@ -0,0 +1,67 @@ +nomad volume register volume-git.hcl +nomad volume register volume-stagit.hcl + +The git server job runs several tasks with distinct purpose: + * smarthttp - A Smart HTTP frontend for Git (Port 80) + * stagit - A HTTP frontend to display static files (Port 80) + + +Stagit Web Interface +-------------------- + +- http://192.168.1.174:8018/ + + +- Explain `git-daemon-export-ok` file + +/srv/git # touch /srv/git/public/test.git/git-daemon-export-ok + + +If not set, results in "access denied" on clone (http/git): + +$ git clone git://192.168.1.174/public/test.git +Cloning into 'test'... +fatal: remote error: access denied or repository not exported: /test.git + +Or it also shows a 404 in Stagit Web UI (because static files are not created or removed). + + +- Explain url/description/owner files + + + +Explain where help for stagit can be found (man page, upstream url) + +$ nomad exec -task stagit d129cfa4 sh +/ # which stagit +/usr/local/bin/stagit + +- Explain `create.sh``file + * Only generates the static fils when git-daemon-export-ok file exists + * The repo will always be in the index, but 404 on the web interface if the file does not exist + * The index will only show repos with at least 1 commit + +/srv/git # /opt/stagit/create.sh +test... done + +The post-receive hook to updated on further changes is automatically added during `create.sh`. + + +SMART-HTTP read/write access +---------------------------- + +Explain how to clone repos + +git clone http://192.168.1.174:8028/test.git +git clone http://192.168.1.174:8028/test + +- Explain that only HTTP with credentials has push access +- Mention git-daemon-export-ok file for smarthttp clones + +Write access is only granted with Smart HTTP (HTTPS) protocol. + +https://git-scm.com/book/en/v2/Git-on-the-Server-Smart-HTTP + +$ nomad exec -task smarthttp 1dc64f2f sh +/ # htpasswd -c /srv/git/.htpasswd in0rdr + diff --git a/hcl/default/git/git.nomad b/hcl/default/git/git.nomad @@ -0,0 +1,109 @@ +job "git" { + datacenters = ["dc1"] + + group "server" { + count = 1 + + volume "git" { + type = "csi" + source = "git" + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" + } + volume "stagit" { + type = "csi" + source = "stagit" + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" + } + volume "tls" { + type = "csi" + source = "certbot" + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" + } + + network { + port "stagit" { + to = 443 + static = 44328 + } + port "smarthttp" { + to = 443 + static = 44318 + } + } + + task "smarthttp" { + driver = "docker" + + config { + image = "127.0.0.1:5000/git:latest" + ports = ["smarthttp"] + volumes = [ + # mount the templated config from the task directory to the container + "local/smarthttp.conf:/etc/apache2/conf.d/smarthttp.conf", + ] + } + + template { + destination = "${NOMAD_TASK_DIR}/smarthttp.conf" + data = file("./templates/smarthttp.conf.tmpl") + } + + volume_mount { + volume = "git" + destination = "/srv/git" + } + volume_mount { + volume = "stagit" + destination = "/var/www/localhost/htdocs" + } + volume_mount { + volume = "tls" + destination = "/etc/letsencrypt" + } + + resources { + memory = 64 + cpu = 100 + } + } + + task "stagit" { + driver = "docker" + + config { + image = "127.0.0.1:5000/git:latest" + ports = ["stagit"] + volumes = [ + # mount the templated config from the task directory to the container + "local/stagit.conf:/etc/apache2/conf.d/stagit.conf", + ] + } + + template { + destination = "${NOMAD_TASK_DIR}/stagit.conf" + data = file("./templates/stagit.conf.tmpl") + } + + volume_mount { + volume = "git" + destination = "/srv/git" + } + volume_mount { + volume = "stagit" + destination = "/var/www/localhost/htdocs" + } + volume_mount { + volume = "tls" + destination = "/etc/letsencrypt" + } + + resources { + memory = 64 + cpu = 100 + } + } + } +} diff --git a/hcl/default/git/templates/smarthttp.conf.tmpl b/hcl/default/git/templates/smarthttp.conf.tmpl @@ -0,0 +1,39 @@ +<VirtualHost *:443> +ServerName git.in0rdr.ch + +ErrorLog /dev/stderr +TransferLog /dev/stdout + +SSLCertificateFile "/etc/letsencrypt/live/git.in0rdr.ch/fullchain.pem" +SSLCertificateKeyFile "/etc/letsencrypt/live/git.in0rdr.ch/privkey.pem" + +# Enable CGI for Smart HTTP +# https://git-scm.com/book/en/v2/Git-on-the-Server-Smart-HTTP +#sed -E -i 's~(\s)*#(LoadModule cgi_module modules/mod_cgi.so)~\1\2~g' /etc/apache2/httpd.conf +LoadModule cgi_module modules/mod_cgi.so + +SetEnv GIT_PROJECT_ROOT /srv/git +#SetEnv GIT_HTTP_EXPORT_ALL +ScriptAlias / /usr/libexec/git-core/git-http-backend/ + +<Directory "/usr/libexec"> + AllowOverride None + Options None + Require all granted +</Directory> + +<Files "git-http-backend"> + AuthType Basic + AuthName "Git Access" + AuthUserFile /srv/git/.htpasswd + Require expr !(%{QUERY_STRING} -strmatch '*service=git-receive-pack*' || %{REQUEST_URI} =~ m#/git-receive-pack$#) + Require valid-user +</Files> + +<Location "/private"> + AuthType Basic + AuthName "Git Access" + AuthUserFile /srv/git/.htpasswd + Require valid-user +</Location> +</VirtualHost> diff --git a/hcl/default/git/templates/stagit.conf.tmpl b/hcl/default/git/templates/stagit.conf.tmpl @@ -0,0 +1,16 @@ +<VirtualHost *:443> +DocumentRoot /var/www/localhost/htdocs +ServerName code.in0rdr.ch + +ErrorLog /dev/stderr +TransferLog /dev/stdout + +SSLCertificateFile "/etc/letsencrypt/live/code.in0rdr.ch/fullchain.pem" +SSLCertificateKeyFile "/etc/letsencrypt/live/code.in0rdr.ch/privkey.pem" + +<Directory /var/www/localhost/htdocs> + Order allow,deny + Allow from all + Require all granted +</Directory> +</VirtualHost> diff --git a/hcl/default/git/volume-git.hcl b/hcl/default/git/volume-git.hcl @@ -0,0 +1,31 @@ +# Register external nfs volume with Nomad CSI +# https://www.nomadproject.io/docs/commands/volume/register +type = "csi" +# Unique ID of the volume, volume.source field in a job +id = "git" +# Display name of the volume. +name = "git" +# ID of the physical volume from the storage provider +external_id = "csi-git" +plugin_id = "nfs" + +# You must provide at least one capability block +# You must provide a block for each capability +# youintend to use in a job's volume block +# https://www.nomadproject.io/docs/commands/volume/register +capability { + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" +} + +# https://github.com/kubernetes-csi/csi-driver-nfs/blob/master/docs/driver-parameters.md +context { + server = "192.168.1.1" + share = "csi-git" +} + +mount_options { + # mount.nfs: Either use '-o nolock' to keep locks local, or start statd. + mount_flags = ["nolock"] +} + diff --git a/hcl/default/git/volume-stagit.hcl b/hcl/default/git/volume-stagit.hcl @@ -0,0 +1,31 @@ +# Register external nfs volume with Nomad CSI +# https://www.nomadproject.io/docs/commands/volume/register +type = "csi" +# Unique ID of the volume, volume.source field in a job +id = "stagit" +# Display name of the volume. +name = "stagit" +# ID of the physical volume from the storage provider +external_id = "csi-stagit" +plugin_id = "nfs" + +# You must provide at least one capability block +# You must provide a block for each capability +# youintend to use in a job's volume block +# https://www.nomadproject.io/docs/commands/volume/register +capability { + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" +} + +# https://github.com/kubernetes-csi/csi-driver-nfs/blob/master/docs/driver-parameters.md +context { + server = "192.168.1.1" + share = "csi-stagit" +} + +mount_options { + # mount.nfs: Either use '-o nolock' to keep locks local, or start statd. + mount_flags = ["nolock"] +} + diff --git a/hcl/default/mastodon/data-volume.hcl b/hcl/default/mastodon/data-volume.hcl @@ -0,0 +1,31 @@ +# Register external nfs volume with Nomad CSI +# https://www.nomadproject.io/docs/commands/volume/register +type = "csi" +# Unique ID of the volume, volume.source field in a job +id = "mastodon" +# Display name of the volume. +name = "mastodon" +# ID of the physical volume from the storage provider +external_id = "csi-mastodon" +plugin_id = "nfs" + +# You must provide at least one capability block +# You must provide a block for each capability +# youintend to use in a job's volume block +# https://www.nomadproject.io/docs/commands/volume/register +capability { + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" +} + +# https://github.com/kubernetes-csi/csi-driver-nfs/blob/master/docs/driver-parameters.md +context { + server = "192.168.1.1" + share = "csi-mastodon" +} + +mount_options { + # mount.nfs: Either use '-o nolock' to keep locks local, or start statd. + mount_flags = ["nolock"] +} + diff --git a/hcl/default/mastodon/mastodon.nomad b/hcl/default/mastodon/mastodon.nomad @@ -0,0 +1,198 @@ +# https://github.com/mastodon/mastodon/blob/main/docker-compose.yml + +job "mastodon" { + datacenters = ["dc1"] + + vault { + policies = ["mastodon"] + change_mode = "noop" + } + + group "server" { + count = 1 + + volume "tls" { + type = "csi" + source = "certbot" + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" + } + volume "mastodon" { + type = "csi" + source = "mastodon" + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" + } + + network { + port "redis" { + to = 6379 + } + port "https" { + to = 443 + static = 44393 + } + port "mastodon_web" { + to = 3000 + } + port "mastodon_streaming" { + to = 4000 + } + } + + # Prepare database migrations + task "db-upgrade" { + driver = "docker" + + volume_mount { + volume = "mastodon" + destination = "/mastodon/public/system" + } + + template { + destination = "${NOMAD_TASK_DIR}/env.production" + data = file("./templates/env.production.tmpl") + env = true + } + + config { + image = "tootsuite/mastodon:latest" + command = "/opt/ruby/bin/bundle" + args = ["exec", "rake", "db:migrate"] + } + + resources { + memory = 256 + cpu = 200 + } + + lifecycle { + hook = "prestart" + sidecar = false + } + } + + task "nginx" { + driver = "docker" + + config { + image = "nginx:stable-alpine" + ports = ["https"] + volumes = [ + # mount the templated config from the task directory to the container + "local/mastodon.conf:/etc/nginx/conf.d/mastodon.conf", + ] + } + + volume_mount { + volume = "tls" + destination = "/etc/letsencrypt" + } + + template { + destination = "${NOMAD_TASK_DIR}/mastodon.conf" + data = file("./templates/nginx.conf.tmpl") + } + + resources { + memory = 256 + cpu = 200 + } + } + + task "mastodon-web" { + driver = "docker" + + config { + image = "tootsuite/mastodon:latest" + ports = ["mastodon_web"] + command = "bash" + args = ["-c", "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000"] + } + + volume_mount { + volume = "mastodon" + destination = "/mastodon/public/system" + } + + template { + destination = "${NOMAD_TASK_DIR}/env.production" + data = file("./templates/env.production.tmpl") + env = true + } + + resources { + memory = 512 + cpu = 200 + } + } + + task "mastodon-streaming" { + driver = "docker" + + config { + image = "tootsuite/mastodon:latest" + ports = ["mastodon_streaming"] + command = "node" + args = ["./streaming"] + } + + template { + destination = "${NOMAD_TASK_DIR}/env.production" + data = file("./templates/env.production.tmpl") + env = true + } + + resources { + memory = 300 + cpu = 200 + } + } + + task "mastodon-sidekiq" { + driver = "docker" + + config { + image = "tootsuite/mastodon:latest" + command = "/opt/ruby/bin/bundle" + args = ["exec", "sidekiq"] + } + + volume_mount { + volume = "mastodon" + destination = "/mastodon/public/system" + } + + template { + destination = "${NOMAD_TASK_DIR}/env.production" + data = file("./templates/env.production.tmpl") + env = true + } + + resources { + memory = 300 + cpu = 200 + } + } + + task "redis" { + driver = "docker" + + config { + image = "redis:7-alpine" + ports = ["redis"] + } + + resources { + memory = 128 + cpu = 100 + } + + lifecycle { + hook = "prestart" + sidecar = true + } + } + + } +} diff --git a/hcl/default/mastodon/templates/env.production.tmpl b/hcl/default/mastodon/templates/env.production.tmpl @@ -0,0 +1,50 @@ +# https://raw.githubusercontent.com/mastodon/mastodon/main/.env.production.sample + +# Federation +# ---------- +# This identifies your server and cannot be changed safely later +# ---------- +LOCAL_DOMAIN=m.in0rdr.ch + +# Redis +# ----- +REDIS_HOST={{ env "NOMAD_IP_redis" }} +REDIS_PORT={{ env "NOMAD_HOST_PORT_redis" }} + +# PostgreSQL +# ---------- +DB_HOST=postgres.lan +DB_USER=mastodon +DB_NAME=mastodon +DB_PASS="{{with secret "kv/mastodon"}}{{index .Data.data.db_password}}{{end}}" +DB_PORT=5432 + +# Secrets +# ------- +# Make sure to use `rake secret` to generate secrets +# ------- +SECRET_KEY_BASE={{with secret "kv/mastodon"}}{{index .Data.data.secret_key_base}}{{end}} +OTP_SECRET={{with secret "kv/mastodon"}}{{index .Data.data.otp_secret}}{{end}} + +# Web Push +# -------- +# Generate with `rake mastodon:webpush:generate_vapid_key` +# -------- +VAPID_PRIVATE_KEY={{with secret "kv/mastodon"}}{{index .Data.data.vapid_private_key}}{{end}} +VAPID_PUBLIC_KEY=BMNtaIhR6nNC3-up0lcBA_kcshabe2mE9XeD7iYECf908w6miCeZoPEgNql3vIBi_XyuhaaHzV6Xu0k27IjSTyQ= + +# Sending mail +# ------------ +SMTP_SERVER=smtp.mailgun.org +SMTP_PORT=587 +SMTP_LOGIN=postmaster@sandboxbba7fd32d470463d8a79b76645b9575c.mailgun.org +SMTP_PASSWORD={{with secret "kv/mastodon"}}{{index .Data.data.smtp_password}}{{end}} +SMTP_FROM_ADDRESS=no-reply@m.in0rdr.ch + +# IP and session retention +# ----------------------- +# Make sure to modify the scheduling of ip_cleanup_scheduler in config/sidekiq.yml +# to be less than daily if you lower IP_RETENTION_PERIOD below two days (172800). +# ----------------------- +IP_RETENTION_PERIOD=31556952 +SESSION_RETENTION_PERIOD=31556952 diff --git a/hcl/default/mastodon/templates/nginx.conf.tmpl b/hcl/default/mastodon/templates/nginx.conf.tmpl @@ -0,0 +1,108 @@ +# https://github.com/mastodon/mastodon/blob/main/dist/nginx.conf + +map $http_upgrade $connection_upgrade { + default upgrade; + '' close; +} + +upstream backend { + server {{ env "NOMAD_ADDR_mastodon_web" }} fail_timeout=0; +} + +upstream streaming { + server {{ env "NOMAD_ADDR_mastodon_streaming" }} fail_timeout=0; +} + +proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=CACHE:10m inactive=7d max_size=1g; + +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + server_name m.in0rdr.ch; + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!MEDIUM:!LOW:!aNULL:!NULL:!SHA; + ssl_prefer_server_ciphers on; + ssl_session_cache shared:SSL:10m; + ssl_session_tickets off; + + ssl_certificate /etc/letsencrypt/live/m.in0rdr.ch/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/m.in0rdr.ch/privkey.pem; + + keepalive_timeout 70; + sendfile on; + client_max_body_size 80m; + + root /mastodon/public; + + gzip on; + gzip_disable "msie6"; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_buffers 16 8k; + gzip_http_version 1.1; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml image/x-icon; + + add_header Strict-Transport-Security "max-age=31536000" always; + + location / { + try_files $uri @proxy; + } + + location ~ ^/(emoji|packs|system/accounts/avatars|system/media_attachments/files) { + add_header Cache-Control "public, max-age=31536000, immutable"; + add_header Strict-Transport-Security "max-age=31536000" always; + try_files $uri @proxy; + } + + location /sw.js { + add_header Cache-Control "public, max-age=0"; + add_header Strict-Transport-Security "max-age=31536000" always; + try_files $uri @proxy; + } + + location @proxy { + 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-Proto $scheme; + proxy_set_header Proxy ""; + proxy_pass_header Server; + + proxy_pass http://backend; + proxy_buffering on; + proxy_redirect off; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + + proxy_cache CACHE; + proxy_cache_valid 200 7d; + proxy_cache_valid 410 24h; + proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; + add_header X-Cached $upstream_cache_status; + add_header Strict-Transport-Security "max-age=31536000" always; + + tcp_nodelay on; + } + + location /api/v1/streaming { + 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-Proto $scheme; + proxy_set_header Proxy ""; + + proxy_pass http://streaming; + proxy_buffering off; + proxy_redirect off; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + + tcp_nodelay on; + } + + error_page 500 501 502 503 504 /500.html; +} diff --git a/hcl/default/myheats/myheats.nomad b/hcl/default/myheats/myheats.nomad @@ -0,0 +1,84 @@ +job "myheats" { + datacenters = ["dc1"] + + vault { + policies = ["myheats"] + change_mode = "noop" + } + + group "server" { + count = 1 + + volume "tls" { + type = "csi" + source = "certbot" + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" + } + + network { + port "myheats" { + to = 3000 + } + port "https" { + to = 443 + static = 44395 + } + } + + task "myheats" { + driver = "docker" + + config { + image = "127.0.0.1:5000/myheats:latest" + ports = ["myheats"] + } + + env { + REACT_APP_DOC_TITLE = "Savognin Heats 🔥" + } + + template { + # render sensitive env vars in a template from Vault secrets + env = true + destination = "${NOMAD_SECRETS_DIR}/env" + data = <<EOT +REACT_APP_SUPABASE_KEY = "{{with secret "kv/myheats"}}{{index .Data.data.supabase_key}}{{end}}" +EOT + } + + resources { + memory = 512 + cpu = 300 + } + } + + task "nginx" { + driver = "docker" + + config { + image = "nginx:stable-alpine" + ports = ["https"] + volumes = [ + # mount the templated config from the task directory to the container + "local/myheats.conf:/etc/nginx/conf.d/myheats.conf", + ] + } + + volume_mount { + volume = "tls" + destination = "/etc/letsencrypt" + } + + template { + destination = "${NOMAD_TASK_DIR}/myheats.conf" + data = file("./templates/nginx.conf.tmpl") + } + + resources { + memory = 128 + cpu = 200 + } + } + } +} diff --git a/hcl/default/myheats/templates/nginx.conf.tmpl b/hcl/default/myheats/templates/nginx.conf.tmpl @@ -0,0 +1,10 @@ +server { + listen 443 ssl; + + ssl_certificate /etc/letsencrypt/live/myheats.in0rdr.ch/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/myheats.in0rdr.ch/privkey.pem; + + location / { + proxy_pass http://{{ env "NOMAD_ADDR_myheats" }}; + } +} diff --git a/hcl/default/postfix/postfix.nomad b/hcl/default/postfix/postfix.nomad @@ -0,0 +1,30 @@ +job "postfix" { + datacenters = ["dc1"] + + group "server" { + count = 1 + + network { + port "postfix_smtp" { + to = 25 + } + port "postfix_submission" { + to = 587 + } + } + + task "postfix" { + driver = "docker" + + config { + image = "127.0.0.1:5000/postfix:latest" + ports = ["postfix_smtp", "postfix_submission"] + } + + resources { + memory = 128 + cpu = 100 + } + } + } +} diff --git a/hcl/default/snibox/data-volume.hcl b/hcl/default/snibox/data-volume.hcl @@ -0,0 +1,31 @@ +# Register external nfs volume with Nomad CSI +# https://www.nomadproject.io/docs/commands/volume/register +type = "csi" +# Unique ID of the volume, volume.source field in a job +id = "snibox" +# Display name of the volume. +name = "snibox" +# ID of the physical volume from the storage provider +external_id = "csi-snibox" +plugin_id = "nfs" + +# You must provide at least one capability block +# You must provide a block for each capability +# youintend to use in a job's volume block +# https://www.nomadproject.io/docs/commands/volume/register +capability { + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" +} + +# https://github.com/kubernetes-csi/csi-driver-nfs/blob/master/docs/driver-parameters.md +context { + server = "192.168.1.1" + share = "csi-snibox" +} + +mount_options { + # mount.nfs: Either use '-o nolock' to keep locks local, or start statd. + mount_flags = ["nolock"] +} + diff --git a/hcl/default/snibox/snibox.nomad b/hcl/default/snibox/snibox.nomad @@ -0,0 +1,103 @@ +job "snibox" { + datacenters = ["dc1"] + + vault { + policies = ["snibox"] + change_mode = "noop" + } + + group "server" { + count = 1 + + volume "tls" { + type = "csi" + source = "certbot" + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" + } + volume "snibox" { + type = "csi" + source = "snibox" + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" + } + + network { + port "server" { + to = 3000 + } + port "https" { + to = 443 + static = 44392 + } + } + + task "nginx" { + driver = "docker" + + config { + image = "nginx:stable-alpine" + ports = ["https"] + volumes = [ + # mount the templated config from the task directory to the container + "local/snibox.conf:/etc/nginx/conf.d/snibox.conf", + ] + } + + volume_mount { + volume = "tls" + destination = "/etc/letsencrypt" + } + + template { + destination = "${NOMAD_TASK_DIR}/snibox.conf" + data = file("./templates/nginx.conf.tmpl") + } + + resources { + memory = 128 + cpu = 100 + } + } + + task "server" { + driver = "docker" + + env { + RAILS_LOG_TO_STDOUT = true + # https://github.com/snibox/snibox/issues/25 + RAILS_SERVE_STATIC_FILES = true + DB_NAME = "snibox" + DB_USER = "snibox" + DB_HOST = "postgres.lan" + } + + template { + # render sensitive env vars in a template from Vault secrets + env = true + destination = "${NOMAD_SECRETS_DIR}/env" + data = <<EOT +DB_PASS = "{{with secret "kv/snibox"}}{{index .Data.data.db_password}}{{end}}" +SECRET_KEY_BASE = "{{with secret "kv/snibox"}}{{index .Data.data.secret_key_base}}{{end}}" +EOT + } + + config { + image = "127.0.0.1:5000/snibox:latest" + command = "/bin/sh" + args = ["-c", "rm -rf tmp/pids && ./bin/rails s -p 3000 -b '0.0.0.0'"] + ports = ["server"] + } + + volume_mount { + volume = "snibox" + destination = "/app/public" + } + + resources { + memory = 256 + cpu = 200 + } + } + } +} diff --git a/hcl/default/snibox/templates/nginx.conf.tmpl b/hcl/default/snibox/templates/nginx.conf.tmpl @@ -0,0 +1,14 @@ +server { + listen 443 ssl; + + ssl_certificate /etc/letsencrypt/live/snibox.in0rdr.ch/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/snibox.in0rdr.ch/privkey.pem; + + location / { + proxy_pass http://{{ env "NOMAD_ADDR_server" }}; + 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-Proto $scheme; + } +} diff --git a/hcl/default/vault-tls/README b/hcl/default/vault-tls/README @@ -0,0 +1,25 @@ +NOMAD VAULT TLS +--------------- + +Run periodic batch job to replace the Vault API certificate. + +The job can be started with the Vault API name and the user (sudoer): + + nomad run -var user=sudoer -var api_addr=vault.example.com nomad-vault-tls.nomad + +The job uses variables and is no `parametrized`, because the parameters seem to +only evaluate inside the tasks `config` stanza (`user` is under `task`). + +SUDOER USER +----------- + +The sudoer user needs permissions to change the TLS certificate permissions and +reload Vault: + + - chown vault:vault cert.pem + - reload vault (SIGHUP) + +TODO: SYSBATCH +-------------- + +See comment in nomad-vault-tls.hcl diff --git a/hcl/default/vault-tls/nomad-vault-tls.nomad b/hcl/default/vault-tls/nomad-vault-tls.nomad @@ -0,0 +1,72 @@ +variable "api_addr" { + type = string + description = "Vault domain name (API address)" +} + +variable "user" { + type = string + description = "Unix user to launch the job with. Needs sudo privileges to reload vault and copy certificates." +} + +job "vault-tls" { + datacenters = ["dc1"] + + # Use batch + distinct_host constraint until system/sysbatch jobs work + # properly w/ CSI. + # + # The sysbatch variant even fails to place the allocation, even though + # this should be a "multi-node-multi-write" type of access mode: + # + # Constraint "missing CSI Volume certbot": 9 nodes excluded by filter + # + # 3x3 = 9, but why 9 nodes? I only have 3.. + # + type = "batch" + + periodic { + cron = "@daily" + } + + group "vault-tls" { + # All groups in this job should be scheduled on different hosts. + count = 5 + + constraint { + distinct_hosts = true + } + + volume "tls" { + type = "csi" + source = "certbot" + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" + } + + task "vault-tls" { + driver = "exec" + user = var.user + + template { + destination = "${NOMAD_TASK_DIR}/vault-tls.sh" + data = file("./templates/vault-tls.sh.tmpl") + perms = 755 + } + + config { + pid_mode = "host" # need to send SIGHUP to vault to reload config file + command = "${NOMAD_TASK_DIR}/vault-tls.sh" + args = [var.api_addr] + } + + volume_mount { + volume = "tls" + destination = "/etc/letsencrypt" + } + + resources { + memory = 16 + cpu = 20 + } + } + } +} diff --git a/hcl/default/vault-tls/templates/vault-tls.sh.tmpl b/hcl/default/vault-tls/templates/vault-tls.sh.tmpl @@ -0,0 +1,21 @@ +#!/usr/bin/env sh +# +# Script to replace Vault API certificates +# +# Run with Vault API domain name as first parameter. +# Example: ./vault-tls.sh vault.example.com + +set -o errexit +set -o nounset +set -o xtrace + +{{- $host := env "node.unique.name" }} + +# copy certificate files +sudo cp -f /etc/letsencrypt/live/$1/fullchain.pem /etc/vault.d/tls/{{ $host }}.pem +sudo cp -f /etc/letsencrypt/live/$1/privkey.pem /etc/vault.d/tls/{{ $host }}.key + +# change ownership for vault +sudo chown vault:vault /etc/vault.d/tls/{{ $host }}* + +sudo pkill -HUP vault diff --git a/hcl/default/writefreely/data-volume.hcl b/hcl/default/writefreely/data-volume.hcl @@ -0,0 +1,31 @@ +# Register external nfs volume with Nomad CSI +# https://www.nomadproject.io/docs/commands/volume/register +type = "csi" +# Unique ID of the volume, volume.source field in a job +id = "writefreely" +# Display name of the volume. +name = "writefreely" +# ID of the physical volume from the storage provider +external_id = "csi-writefreely" +plugin_id = "nfs" + +# You must provide at least one capability block +# You must provide a block for each capability +# youintend to use in a job's volume block +# https://www.nomadproject.io/docs/commands/volume/register +capability { + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" +} + +# https://github.com/kubernetes-csi/csi-driver-nfs/blob/master/docs/driver-parameters.md +context { + server = "192.168.1.1" + share = "csi-writefreely" +} + +mount_options { + # mount.nfs: Either use '-o nolock' to keep locks local, or start statd. + mount_flags = ["nolock"] +} + diff --git a/hcl/default/writefreely/templates/config.ini.tmpl b/hcl/default/writefreely/templates/config.ini.tmpl @@ -0,0 +1,102 @@ +[server] +hidden_host = +port = 8080 +bind = 0.0.0.0 +tls_cert_path = +tls_key_path = +autocert = false +templates_parent_dir = +static_parent_dir = +pages_parent_dir = +keys_parent_dir = +hash_seed = +gopher_port = 0 + +[database] +type = mysql +filename = +username = writefreely +password = {{with secret "kv/writefreely"}}{{index .Data.data.db_password}}{{end}} +database = writefreely +host = mariadb.lan +port = 3306 +tls = false + +[app] +site_name = write.in0rdr.ch +site_description = +host = https://write.in0rdr.ch +theme = +editor = +disable_js = false +webfonts = false +landing = +simple_nav = false +wf_modesty = false +chorus = false +forest = false +disable_drafts = false +single_user = true +open_registration = false +open_deletion = false +min_username_len = 0 +max_blogs = 0 +federation = true +public_stats = true +monetization = false +notes_only = false +private = false +local_timeline = false +user_invites = +default_visibility = +update_checks = false +disable_password_auth = false + +[oauth.slack] +client_id = +client_secret = +team_id = +callback_proxy = +callback_proxy_api = + +[oauth.writeas] +client_id = +client_secret = +auth_location = +token_location = +inspect_location = +callback_proxy = +callback_proxy_api = + +[oauth.gitlab] +client_id = +client_secret = +host = +display_name = +callback_proxy = +callback_proxy_api = + +[oauth.gitea] +client_id = +client_secret = +host = +display_name = +callback_proxy = +callback_proxy_api = + +[oauth.generic] +client_id = +client_secret = +host = +display_name = +callback_proxy = +callback_proxy_api = +token_endpoint = +inspect_endpoint = +auth_endpoint = +scope = +allow_disconnect = false +map_user_id = +map_username = +map_display_name = +map_email = +\ No newline at end of file diff --git a/hcl/default/writefreely/templates/my.cnf.tmpl b/hcl/default/writefreely/templates/my.cnf.tmpl @@ -0,0 +1,5 @@ +[client] +host = mariadb.lan +database = writefreely +user = writefreely +password = "{{with secret "kv/writefreely"}}{{index .Data.data.db_password}}{{end}}" +\ No newline at end of file diff --git a/hcl/default/writefreely/templates/nginx.conf.tmpl b/hcl/default/writefreely/templates/nginx.conf.tmpl @@ -0,0 +1,14 @@ +server { + listen 443 ssl; + + ssl_certificate /etc/letsencrypt/live/write.in0rdr.ch/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/write.in0rdr.ch/privkey.pem; + + location / { + proxy_pass http://{{ env "NOMAD_ADDR_web" }}; + 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-Proto $scheme; + } +} diff --git a/hcl/default/writefreely/templates/schema.sql.tmpl b/hcl/default/writefreely/templates/schema.sql.tmpl @@ -0,0 +1,241 @@ +-- +-- Database: `writefreely` +-- + +-- -------------------------------------------------------- + +-- +-- Table structure for table `accesstokens` +-- + +CREATE TABLE IF NOT EXISTS `accesstokens` ( + `token` binary(16) NOT NULL, + `user_id` int(6) NOT NULL, + `sudo` tinyint(1) NOT NULL DEFAULT '0', + `one_time` tinyint(1) NOT NULL DEFAULT '0', + `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `expires` datetime DEFAULT NULL, + `user_agent` varchar(255) DEFAULT NULL, + PRIMARY KEY (`token`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `appcontent` +-- + +CREATE TABLE IF NOT EXISTS `appcontent` ( + `id` varchar(36) NOT NULL, + `content` mediumtext CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, + `updated` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `appmigrations` +-- + +CREATE TABLE IF NOT EXISTS `appmigrations` ( + `version` int(11) NOT NULL, + `migrated` datetime NOT NULL, + `result` text NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `collectionattributes` +-- + +CREATE TABLE IF NOT EXISTS `collectionattributes` ( + `collection_id` int(6) NOT NULL, + `attribute` varchar(128) NOT NULL, + `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + PRIMARY KEY (`collection_id`,`attribute`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `collectionkeys` +-- + +CREATE TABLE IF NOT EXISTS `collectionkeys` ( + `collection_id` int(6) NOT NULL, + `public_key` blob NOT NULL, + `private_key` blob NOT NULL, + PRIMARY KEY (`collection_id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `collectionpasswords` +-- + +CREATE TABLE IF NOT EXISTS `collectionpasswords` ( + `collection_id` int(6) NOT NULL, + `password` char(60) NOT NULL, + PRIMARY KEY (`collection_id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `collectionredirects` +-- + +CREATE TABLE IF NOT EXISTS `collectionredirects` ( + `prev_alias` varchar(100) NOT NULL, + `new_alias` varchar(100) NOT NULL, + PRIMARY KEY (`prev_alias`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `collections` +-- + +CREATE TABLE IF NOT EXISTS `collections` ( + `id` int(6) NOT NULL AUTO_INCREMENT, + `alias` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL, + `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + `description` varchar(160) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + `style_sheet` text, + `script` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin, + `format` varchar(8) DEFAULT NULL, + `privacy` tinyint(1) NOT NULL, + `owner_id` int(6) NOT NULL, + `view_count` int(6) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `alias` (`alias`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `posts` +-- + +CREATE TABLE IF NOT EXISTS `posts` ( + `id` char(16) NOT NULL, + `slug` varchar(100) DEFAULT NULL, + `modify_token` char(32) DEFAULT NULL, + `text_appearance` char(4) NOT NULL DEFAULT 'norm', + `language` char(2) DEFAULT NULL, + `rtl` tinyint(1) DEFAULT NULL, + `privacy` tinyint(1) NOT NULL, + `owner_id` int(6) DEFAULT NULL, + `collection_id` int(6) DEFAULT NULL, + `pinned_position` tinyint(1) UNSIGNED DEFAULT NULL, + `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `view_count` int(6) NOT NULL, + `title` varchar(160) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + `content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `id_slug` (`collection_id`,`slug`), + UNIQUE KEY `owner_id` (`owner_id`,`id`), + KEY `privacy_id` (`privacy`,`id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `remotefollows` +-- + +CREATE TABLE IF NOT EXISTS `remotefollows` ( + `collection_id` int(11) NOT NULL, + `remote_user_id` int(11) NOT NULL, + `created` datetime NOT NULL, + PRIMARY KEY (`collection_id`,`remote_user_id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `remoteuserkeys` +-- + +CREATE TABLE IF NOT EXISTS `remoteuserkeys` ( + `id` varchar(255) NOT NULL, + `remote_user_id` int(11) NOT NULL, + `public_key` blob NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `follower_id` (`remote_user_id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `remoteusers` +-- + +CREATE TABLE IF NOT EXISTS `remoteusers` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `actor_id` varchar(255) NOT NULL, + `inbox` varchar(255) NOT NULL, + `shared_inbox` varchar(255) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `collection_id` (`actor_id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `userattributes` +-- + +CREATE TABLE IF NOT EXISTS `userattributes` ( + `user_id` int(6) NOT NULL, + `attribute` varchar(64) NOT NULL, + `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + PRIMARY KEY (`user_id`,`attribute`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `userinvites` +-- + +CREATE TABLE IF NOT EXISTS `userinvites` ( + `id` char(6) NOT NULL, + `owner_id` int(11) NOT NULL, + `max_uses` smallint(6) DEFAULT NULL, + `created` datetime NOT NULL, + `expires` datetime DEFAULT NULL, + `inactive` tinyint(1) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `users` +-- + +CREATE TABLE IF NOT EXISTS `users` ( + `id` int(6) NOT NULL AUTO_INCREMENT, + `username` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, + `password` char(60) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL, + `email` varbinary(255) DEFAULT NULL, + `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `username` (`username`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `usersinvited` +-- + +CREATE TABLE IF NOT EXISTS `usersinvited` ( + `invite_id` char(6) NOT NULL, + `user_id` int(11) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1; diff --git a/hcl/default/writefreely/writefreely.nomad b/hcl/default/writefreely/writefreely.nomad @@ -0,0 +1,169 @@ +job "writefreely" { + datacenters = ["dc1"] + + vault { + policies = ["writefreely"] + change_mode = "noop" + } + + group "server" { + count = 1 + + volume "tls" { + type = "csi" + source = "certbot" + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" + } + volume "writefreely" { + type = "csi" + source = "writefreely" + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" + } + + network { + port "web" { + to = 8080 + } + port "https" { + to = 443 + static = 44394 + } + } + + # Prepare database schema + # https://github.com/writefreely/writefreely/blob/develop/docker-setup.sh + task "db-upgrade" { + driver = "docker" + + config { + image = "docker.io/arm64v8/mysql:latest" + command = "/bin/sh" + args = ["-c", "mysql < $NOMAD_TASK_DIR/schema.sql"] + volumes = [ + # mount the templated config from the task directory to the container + "local/my.cnf:/etc/mysql/conf.d/my.cnf", + ] + + } + + template { + # Fetch sensitive db connection vars from Vault secrets + destination = "${NOMAD_TASK_DIR}/my.cnf" + data = file("./templates/my.cnf.tmpl") + } + template { + # Create db tables + destination = "${NOMAD_TASK_DIR}/schema.sql" + data = file("./templates/schema.sql.tmpl") + } + + resources { + memory = 128 + cpu = 100 + } + + lifecycle { + hook = "prestart" + sidecar = false + } + } + + # Generate keys used for the encryption of certain user data. + # Because user data becomes unrecoverable without these keys, + # this won't overwrite any existing key, and instead outputs a message. + # https://github.com/writefreely/writefreely/blob/develop/docker-setup.sh + task "gen-keys" { + driver = "docker" + + config { + image = "127.0.0.1:5000/writefreely:latest" + entrypoint = [""] + command = "/bin/sh" + args = ["-c", "cmd/writefreely/writefreely --gen-keys || true"] + volumes = [ + # mount the templated config from the task directory to the container + "local/config.ini:/go/config.ini", + ] + } + + volume_mount { + volume = "writefreely" + destination = "/go/keys" + } + + template { + destination = "${NOMAD_TASK_DIR}/config.ini" + data = file("./templates/config.ini.tmpl") + } + + resources { + memory = 128 + cpu = 100 + } + + lifecycle { + hook = "prestart" + sidecar = false + } + } + + task "writefreely" { + driver = "docker" + + config { + image = "127.0.0.1:5000/writefreely:latest" + ports = ["web"] + volumes = [ + # mount the templated config from the task directory to the container + "local/config.ini:/go/config.ini", + ] + } + + volume_mount { + volume = "writefreely" + destination = "/go/keys" + } + + template { + destination = "${NOMAD_TASK_DIR}/config.ini" + data = file("./templates/config.ini.tmpl") + } + + resources { + memory = 256 + cpu = 200 + } + } + + task "nginx" { + driver = "docker" + + config { + image = "nginx:stable-alpine" + ports = ["https"] + volumes = [ + # mount the templated config from the task directory to the container + "local/writefreely.conf:/etc/nginx/conf.d/writefreely.conf", + ] + } + + volume_mount { + volume = "tls" + destination = "/etc/letsencrypt" + } + + template { + destination = "${NOMAD_TASK_DIR}/writefreely.conf" + data = file("./templates/nginx.conf.tmpl") + } + + resources { + memory = 256 + cpu = 200 + } + } + + } +} diff --git a/hcl/infra/garbage/docker-prune.nomad b/hcl/infra/garbage/docker-prune.nomad @@ -0,0 +1,34 @@ +job "docker-prune" { + datacenters = ["dc1"] + type = "sysbatch" + +# periodic { +# # At minute 36 past every hour +# #cron = "36 */1 * * *" +# # All 4 minutes +# cron = "4/1 * * * *" +# prohibit_overlap = true +# } + + group "prune" { + count = 1 + + network { + mode = "host" + } + + task "docker" { + driver = "raw_exec" + + config { + command = "/usr/bin/sh" + args = ["-c", "docker system prune --force"] + } + + resources { + memory = 16 + cpu = 100 + } + } + } +} diff --git a/hcl/infra/garbage/garbage.nomad b/hcl/infra/garbage/garbage.nomad @@ -0,0 +1,36 @@ +job "garbage" { + datacenters = ["dc1"] + type = "sysbatch" + + parameterized { + payload = "forbidden" + meta_required = ["job"] + } + + group "garbage" { + count = 1 + + #restart { + # attempts = 1 + # mode = "fail" + #} + + network { + mode = "host" + } + + task "docker" { + driver = "raw_exec" + + config { + command = "/usr/bin/sh" + args = ["-c", "j=$(docker ps -f \"name=${NOMAD_META_JOB}*\" --format \"{{.ID}}\") && docker stop $j && docker rm $j"] + } + + resources { + memory = 16 + cpu = 100 + } + } + } +} diff --git a/hcl/infra/nfs/README b/hcl/infra/nfs/README @@ -0,0 +1,48 @@ +NFS CSI DRIVER +-------------- + +Run stateful workloads with the NFS CSI driver: +- https://learn.hashicorp.com/tutorials/nomad/stateful-workloads-csi-volumes +- https://github.com/kubernetes-csi/csi-driver-nfs + +ON NOMAD +-------- + +Start development agent with client.hcl file: + +$ sudo nomad agent -dev -bind 0.0.0.0 -log-level INFO -config=client.hcl + +Install the plugin on the controllers and nodes: +$ nomad job run plugin-nfs-controller.nomad +$ nomad job run plugin-nfs-nodes.nomad + +Register the volume: +$ nomad volume register nfs-volume.hcl + +Deploy a test job: +$ nomad job run testjob.nomad + +ON THE SERVER +-------------- + +Spin up a Docker development NFS server (see "docker-compose.yaml"): + +$ docker-compose up -d + +Create the export: +$ docker exec nfs mkdir /data/csi-test + +If external NFS server (not the docker-compose example here), don't forget to: +- add export in /etc/exports +- exportfs -ar + +Otherwise, you will encounter the following error message: + +> Error message on the server when the server directory for the export does not exist: +> rpc.mountd[6073]: can't stat exported dir /srv/nfs/csi-test: No such file or directory + +TEST +---- + +$ nomad alloc exec 737b86a3 /bin/sh +/ # echo "hello world" > /mnt/test/hi diff --git a/hcl/infra/nfs/client.hcl b/hcl/infra/nfs/client.hcl @@ -0,0 +1,5 @@ +plugin "docker" { + config { + allow_privileged = true + } +} diff --git a/hcl/infra/nfs/docker-compose.yaml b/hcl/infra/nfs/docker-compose.yaml @@ -0,0 +1,13 @@ +version: "2.1" +services: + # https://hub.docker.com/r/itsthenetwork/nfs-server-alpine + nfs: + image: itsthenetwork/nfs-server-alpine:12 + container_name: nfs + privileged: true + environment: + - SHARED_DIRECTORY=/data + volumes: + - /tmp/nfs-storage:/data + ports: + - 2049:2049 diff --git a/hcl/infra/nfs/nfs-volume.hcl b/hcl/infra/nfs/nfs-volume.hcl @@ -0,0 +1,30 @@ +# Register external nfs volume with Nomad CSI +# https://www.nomadproject.io/docs/commands/volume/register +type = "csi" +# Unique ID of the volume, volume.source field in a job +id = "test" +# Display name of the volume. +name = "test" +# ID of the physical volume from the storage provider +external_id = "csi-test" +plugin_id = "nfs" + +# You must provide at least one capability block +# You must provide a block for each capability +# you intend to use in a job's volume block +# https://www.nomadproject.io/docs/commands/volume/register +capability { + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" +} + +# https://github.com/kubernetes-csi/csi-driver-nfs/blob/master/docs/driver-parameters.md +context { + server = "172.17.0.1" + share = "csi-test" +} + +mount_options { + # mount.nfs: Either use '-o nolock' to keep locks local, or start statd. + mount_flags = ["nolock,vers=4.2"] +} diff --git a/hcl/infra/nfs/plugin-nfs-controller.nomad b/hcl/infra/nfs/plugin-nfs-controller.nomad @@ -0,0 +1,44 @@ +variable "affinity_node" { + type = string + default = "na" + description = "Prefer to schedule controller on this node. If no nodes match, placement is still successful." +} + +job "plugin-nfs-controller" { + datacenters = ["dc1"] + + affinity { + attribute = "${node.unique.name}" + value = var.affinity_node + } + + priority = 100 + + group "controller" { + task "plugin" { + driver = "docker" + + config { + image = "mcr.microsoft.com/k8s/csi/nfs-csi:latest" + + args = [ + "--v=5", + "--nodeid=${attr.unique.hostname}", + "--endpoint=unix:///csi/csi.sock", + "--drivername=nfs.csi.k8s.io" + ] + } + + csi_plugin { + id = "nfs" + type = "controller" + mount_dir = "/csi" + } + + resources { + memory = 128 + cpu = 100 + } + } + } +} diff --git a/hcl/infra/nfs/plugin-nfs-nodes.nomad b/hcl/infra/nfs/plugin-nfs-nodes.nomad @@ -0,0 +1,42 @@ +job "plugin-nfs-nodes" { + datacenters = ["dc1"] + + # you can run node plugins as service jobs as well, but this ensures + # that all nodes in the DC have a copy. + type = "system" + + priority = 99 + + group "nodes" { + task "plugin" { + driver = "docker" + + config { + image = "mcr.microsoft.com/k8s/csi/nfs-csi:latest" + + args = [ + "--v=5", + "--nodeid=${attr.unique.hostname}", + "--endpoint=unix:///csi/csi.sock", + "--drivername=nfs.csi.k8s.io" + ] + + # node plugins must run as privileged jobs because they + # mount disks to the host + privileged = true + } + + csi_plugin { + id = "nfs" + type = "node" + mount_dir = "/csi" + } + + resources { + memory = 128 + cpu = 100 + } + } + } +} + diff --git a/hcl/infra/nfs/testjob.nomad b/hcl/infra/nfs/testjob.nomad @@ -0,0 +1,26 @@ +job "test" { + datacenters = ["dc1"] + type = "service" + group "server" { + count = 2 + + volume "test" { + type = "csi" + source = "test" + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" + } + + task "server" { + driver = "docker" + config { + image = "alpine" + args = ["sleep", "3600"] + } + volume_mount { + volume = "test" + destination = "/mnt/test" + } + } + } +} diff --git a/hcl/infra/registry/README b/hcl/infra/registry/README @@ -0,0 +1,62 @@ +DOCKER REGISTRY ON NOMAD +------------------------ + +Add a host network "private" to expose the registry on the loopback of the Nomad clients: +https://www.nomadproject.io/docs/configuration/client#host_network-stanza + + # client.hcl + client { + host_network "private" { + cidr = "127.0.0.1/32" + } + } + + +PUSHING IMAGES +-------------- + +- Login to any client +- docker image tag $NAME:$TAG 127.0.0.1:5000/$NAME:$TAG +- docker push 127.0.0.1:5000/$NAME:$TAG + +LIST IMAGES +----------- + +- curl http://127.0.0.1:5000/v2/_catalog +- https://docs.docker.com/registry/spec/api/#listing-repositories + +LIST TAGS +--------- + +- curl http://127.0.0.1:5000/v2/$NAME/tags/list +- https://docs.docker.com/registry/spec/api/#listing-image-tags + +PUBLIC REGISTRY WITH BASIC AUTH AND TLS +--------------------------------------- + +Ensure prerequisites for Vault integration in Nomad: +https://www.nomadproject.io/docs/integrations/vault-integration + +Add htpasswd to the Vault secret in kv/registry: + + vault kv put kv/registry htpasswd='admin:bcrypt-hash' + +Create Vault policy public-registry.hcl: + + path "kv/data/registry*" { + capabilities = ["read"] + } + +Create a KV entry "registry/host" in Consul with the public name of the +registry in plain-text. For example: + + hub.docker.com + +GARBAGE COLLECTION +------------------ + +TODO: add job for gc + + /bin/registry garbage-collect /etc/docker/registry/config.yml + +https://docs.docker.com/registry/garbage-collection/#run-garbage-collection diff --git a/hcl/infra/registry/config/config.yml.default b/hcl/infra/registry/config/config.yml.default @@ -0,0 +1,20 @@ +--- +version: 0.1 +log: + fields: + service: registry +storage: + cache: + blobdescriptor: inmemory + filesystem: + rootdirectory: /var/lib/registry +http: + addr: :5000 + headers: + X-Content-Type-Options: [nosniff] +health: + storagedriver: + enabled: true + interval: 10s + threshold: 3 + diff --git a/hcl/infra/registry/config/config.yml.tmpl b/hcl/infra/registry/config/config.yml.tmpl @@ -0,0 +1,28 @@ +--- +version: 0.1 +log: + fields: + service: registry +storage: + cache: + blobdescriptor: inmemory + filesystem: + rootdirectory: /var/lib/registry + delete: + enabled: true +http: + addr: :5000 + headers: + X-Content-Type-Options: [nosniff] + tls: + certificate: "/etc/letsencrypt/live/{{ key "registry/host" }}/fullchain.pem" + key: "/etc/letsencrypt/live/{{ key "registry/host" }}/privkey.pem" +auth: + htpasswd: + realm: basic-realm + path: {{ env "NOMAD_SECRETS_DIR" }}/htpasswd +health: + storagedriver: + enabled: true + interval: 10s + threshold: 3 diff --git a/hcl/infra/registry/data-volume.hcl b/hcl/infra/registry/data-volume.hcl @@ -0,0 +1,30 @@ +# Register external nfs volume with Nomad CSI +# https://www.nomadproject.io/docs/commands/volume/register +type = "csi" +# Unique ID of the volume, volume.source field in a job +id = "registry" +# Display name of the volume. +name = "registry" +# ID of the physical volume from the storage provider +external_id = "csi-registry" +plugin_id = "nfs" + +# You must provide at least one capability block +# You must provide a block for each capability +# youintend to use in a job's volume block +# https://www.nomadproject.io/docs/commands/volume/register +capability { + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" +} + +# https://github.com/kubernetes-csi/csi-driver-nfs/blob/master/docs/driver-parameters.md +context { + server = "192.168.1.1" + share = "csi-registry" +} + +mount_options { + # mount.nfs: Either use '-o nolock' to keep locks local, or start statd. + mount_flags = ["nolock"] +} diff --git a/hcl/infra/registry/public-registry.nomad b/hcl/infra/registry/public-registry.nomad @@ -0,0 +1,72 @@ +job "public-registry" { + datacenters = ["dc1"] + type = "service" + priority = 98 + + group "server" { + count = 5 + + volume "registry-data" { + type = "csi" + source = "registry" + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" + } + volume "tls" { + type = "csi" + source = "certbot" + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" + } + + network { + port "https" { + to = 5000 + static = 5050 + } + } + + task "registry" { + driver = "docker" + + volume_mount { + volume = "registry-data" + destination = "/var/lib/registry" + } + volume_mount { + volume = "tls" + destination = "/etc/letsencrypt" + } + + vault { + policies = ["public-registry"] + change_mode = "noop" + } + + template { + destination = "${NOMAD_SECRETS_DIR}/htpasswd" + # https://www.nomadproject.io/docs/job-specification/template#vault-kv-api-v2 + data = "{{ with secret \"kv/registry\" }}{{ .Data.data.htpasswd }}{{end}}" + } + + template { + destination = "${NOMAD_TASK_DIR}/config.yml" + data = file("./config/config.yml.tmpl") + } + + config { + image = "registry" + ports = ["https"] + volumes = [ + # mount the templated config from the task directory to the container + "local/config.yml:/etc/docker/registry/config.yml", + ] + } + + resources { + memory = 64 + cpu = 100 + } + } + } +} diff --git a/hcl/infra/registry/simple-registry.nomad b/hcl/infra/registry/simple-registry.nomad @@ -0,0 +1,43 @@ +job "simple-registry" { + datacenters = ["dc1"] + type = "service" + priority = 98 + + group "server" { + count = 5 + + volume "registry-data" { + type = "csi" + source = "registry" + access_mode = "multi-node-multi-writer" + attachment_mode = "file-system" + } + + network { + port "http" { + to = 5000 + static = 5000 + host_network = "private" + } + } + + task "registry" { + driver = "docker" + + volume_mount { + volume = "registry-data" + destination = "/var/lib/registry" + } + + config { + image = "registry" + ports = ["http"] + } + + resources { + memory = 128 + cpu = 200 + } + } + } +} diff --git a/hcl/infra/snapshots/README b/hcl/infra/snapshots/README @@ -0,0 +1,48 @@ +NOMAD SNAPSHOTS +--------------- + +Run periodic Raft state snapshots for Consul and Nomad and store snapshots on NFS. + +This creates a parameterized job: +- https://www.nomadproject.io/docs/job-specification/parameterized +- https://www.nomadproject.io/docs/commands/job/dispatch + +nomad run nomad-snapshots.nomad + +The jobs can be dispatched to either take snapshots of Nomad or Consul state: +- nomad job dispatch -meta service=nomad snapshot +- nomad job dispatch -meta service=consul snapshot + +The service meta parameter is required. + +Prevent "volume max claim reached" on the CSI volume. Don't dispatch periodic +jobs for different services (meta param) at the same time (adapt cron schedule). + +EXEC DRIVER +----------- + +Exec driver is used (instead of raw_exec), because raw_exec does not support +CSI volumes. Host volumes suck, because they require client reconfiguration. + +CONSUL CONNECT +-------------- + +This job leverages Consul connect integration to automagically access the +Consul API from the job. The consul API and the required CONSUL_ environment +variables are automatically exposed to the job in the "bridge" or "host" +network modes: + +>CONSUL_HTTP_ADDR: Will be automatically set to a unix domain socket in bridge +>networking mode, or a tcp address in host networking mode. +https://www.nomadproject.io/docs/runtime/environment + +The connection from the job to the Consul API is established as follows: +- "bridge" network mode: As socket mounted to the alloc dir, + CONSUL_HTTP_ADDR=unix:///alloc/tmp/consul_http.sock. +- "host" network mode: Directly through the tcp socket of the host network, + CONSUL_HTTP_ADDR=127.0.0.1:8501 + +This job uses the "host" network mode. No CNI plugins are required. + +>To use bridge mode, you must have the reference CNI plugins installed +https://www.nomadproject.io/docs/job-specification/network diff --git a/hcl/infra/snapshots/nomad-snapshots.nomad b/hcl/infra/snapshots/nomad-snapshots.nomad @@ -0,0 +1,65 @@ +job "snapshot" { + datacenters = ["dc1"] + type = "batch" + + parameterized { + payload = "forbidden" + meta_required = ["service"] + } + + periodic { + # At minute 6 past every hour + cron = "12 */1 * * *" + } + + group "snapshot" { + count = 1 + + volume "nomad-snapshots" { + type = "csi" + source = "nomad-snapshots" + access_mode = "single-node-writer" + attachment_mode = "file-system" + } + + network { + mode = "host" + } + + service { + task = "snapshot-save" + connect { + # start this job as Consul connect native service + # to expose the CONSUL_ environment variables + native = true + } + } + + task "snapshot-save" { + driver = "exec" + + template { + destination = "${NOMAD_TASK_DIR}/snapshot.sh" + data = file("./templates/snapshot.sh.tmpl") + perms = 755 + } + + config { + command = "${NOMAD_TASK_DIR}/snapshot.sh" + # Note: This meta variable (the parametrized service name) + # could also be directly accessed inside the template + args = [NOMAD_META_SERVICE] + } + + volume_mount { + volume = "nomad-snapshots" + destination = "/mnt" + } + + resources { + memory = 16 + cpu = 100 + } + } + } +} diff --git a/hcl/infra/snapshots/snapshot-volume.hcl b/hcl/infra/snapshots/snapshot-volume.hcl @@ -0,0 +1,25 @@ +# Register external nfs volume with Nomad CSI +# https://www.nomadproject.io/docs/commands/volume/register +type = "csi" +# Unique ID of the volume, volume.source field in a job +id = "nomad-snapshots" +# Display name of the volume. +name = "nomad-snapshots" +# ID of the physical volume from the storage provider +external_id = "csi-nomad-snapshots" +plugin_id = "nfs" + +# You must provide at least one capability block +# You must provide a block for each capability +# youintend to use in a job's volume block +# https://www.nomadproject.io/docs/commands/volume/register +capability { + access_mode = "single-node-writer" + attachment_mode = "file-system" +} + +# https://github.com/kubernetes-csi/csi-driver-nfs/blob/master/docs/driver-parameters.md +context { + server = "192.168.1.1" + share = "csi-nomad-snapshots" +} diff --git a/hcl/infra/snapshots/templates/snapshot.sh.tmpl b/hcl/infra/snapshots/templates/snapshot.sh.tmpl @@ -0,0 +1,27 @@ +#!/usr/bin/env sh +# +# Script to create snapshots from Nomad or Consul storage. +# Invoke with name of the service [nomad consul] as first arg. +# +# Example: ./snapshot.sh consul + +set -o errexit +set -o nounset +set -o xtrace + +mkdir -p /mnt/$1 +cd /mnt/$1 + +case "$1" in + nomad) + nomad operator snapshot save + ;; + consul) + consul snapshot save consul-state-{{ timestamp "20060102" }}-{{ timestamp "unix" }}.snap + ;; + *) + echo "Usage: $0 {nomad|consul}" + ;; +esac + +find -mtime 2 -exec rm -rf {} \;