diff --git a/.install/bin/pdm-build b/.install/bin/pdm-build index 5be974c..43baab2 100755 --- a/.install/bin/pdm-build +++ b/.install/bin/pdm-build @@ -9,9 +9,9 @@ epoch=$(date +%s.%3N) today=$(date +%Y-%m-%d-T%H%M) badarg() { - echo -n "$(basename $0): " - echo "$1" - echo "Try '$(basename $0) -h' for more information." + echo -n "$(basename $0): " >&2 + echo "$1" >&2 + echo "Try '$(basename $0) -h' for more information." >&2 exit 2 } diff --git a/.install/bin/pdm-launch b/.install/bin/pdm-launch index e2e2da3..696689c 100755 --- a/.install/bin/pdm-launch +++ b/.install/bin/pdm-launch @@ -3,9 +3,9 @@ set -eEuo pipefail badarg() { - echo -n "$(basename $0): " - echo "$1" - echo "Try '$(basename $0) -h' for more information." + echo -n "$(basename $0): " >&2 + echo "$1" >&2 + echo "Try '$(basename $0) -h' for more information." >&2 exit 2 } @@ -64,7 +64,7 @@ if [[ $opt_config -eq 1 ]]; then echo "Getting deploy options from image metadata label '$opt_label' ..." deployconf=$(podman image inspect -f "{{ .Config.Labels.${opt_label} }}" "$image") if [[ $deployconf == "" ]]; then - echo "Error: could not find image metadata label '$opt_label'" + echo "Error: could not find image metadata label '$opt_label'" >&2 exit 1 fi else diff --git a/.install/bin/pdm-shell b/.install/bin/pdm-shell index 54f7664..edaf36d 100755 --- a/.install/bin/pdm-shell +++ b/.install/bin/pdm-shell @@ -3,9 +3,9 @@ set -eEuo pipefail badarg() { - echo -n "$(basename $0): " - echo "$1" - echo "Try '$(basename $0) -h' for more information." + echo -n "$(basename $0): " >&2 + echo "$1" >&2 + echo "Try '$(basename $0) -h' for more information." >&2 exit 2 } diff --git a/debian/Containerfile b/debian/Containerfile index 9748cc7..779f2b4 100644 --- a/debian/Containerfile +++ b/debian/Containerfile @@ -34,3 +34,9 @@ COPY assets/bash.bashrc /etc/bash.bashrc # copy custom scripts COPY assets/bin/ /usr/local/bin/ + +# Install templating engine +RUN wget https://raw.githubusercontent.com/jirutka/esh/master/esh && \ + chmod +x esh && \ + mkdir -p /usr/local/lib/esh && \ + mv esh /usr/local/lib/esh diff --git a/debian/assets/bin/esh b/debian/assets/bin/esh new file mode 100755 index 0000000..bf272a5 --- /dev/null +++ b/debian/assets/bin/esh @@ -0,0 +1,20 @@ +#!/bin/bash + +esh=/usr/local/lib/esh/esh + +if [[ $# -gt 2 ]]; then + $esh "$@" +else + if [[ $# -eq 2 ]]; then + out="$2" + elif [[ $1 == *.esh ]]; then + out="${1%.esh}" + else + out='-' + fi +fi + +$esh -o "$out" "$1" + +chmod $(stat -c '%a' "$1") "$out" +chown $(stat -c '%U:%G' "$1") "$out" diff --git a/gitea/Containerfile b/gitea/Containerfile index 5521084..3f4ab4c 100644 --- a/gitea/Containerfile +++ b/gitea/Containerfile @@ -11,6 +11,7 @@ LABEL config_default="\ -v /srv/vol/gitea/git:/vol/git \ -v /srv/vol/gitea/data:/vol/data \ -v /srv/vol/gitea/log:/vol/log \ +-v /srv/vol/gitea/keys:/vol/keys \ " # Build variables @@ -27,7 +28,10 @@ ENV DBNAME=gitea ### # install packages we want -RUN apt update -y && apt install -y postgresql postgresql-doc git +RUN apt update -y && apt install -y postgresql postgresql-doc git openssh-server + +# create directories for volumes +RUN mkdir -p /vol/git /vol/data /vol/log /vol/keys # put database variables in /etc/environment so anyone can access them # also autodetect versions of postgres and gitea and put them in /etc/environment as well @@ -39,7 +43,7 @@ RUN echo "DBUSER=$DBUSER" >> /etc/environment && \ # create gitea user with file owner UID RUN addgroup --gid $FILESUID gitea && \ - adduser gitea --ingroup gitea --uid $FILESUID --disabled-password --gecos "Gitea Server" --shell /usr/sbin/nologin --home /var/lib/gitea + adduser gitea --ingroup gitea --uid $FILESUID --disabled-password --gecos "Gitea Server" --shell /bin/bash --home /var/lib/gitea # copy our custom scripts COPY assets/bin/ /usr/local/bin/ @@ -49,6 +53,16 @@ RUN mkdir -p /vol/log && \ rm -r /var/log && \ ln -s /vol/log /var/log +### +### SSH Server ### +### + +# copy sshd config +COPY assets/sshd_config /etc/ssh/sshd_config + +# make link to keys volume +RUN ln -s /vol/keys /etc/ssh/keys + ### ### PostgreSQL ### ### @@ -67,21 +81,24 @@ RUN wget https://dl.gitea.io/gitea/${GITEAV:?}/gitea-${GITEAV:?}-linux-amd64 && chmod +x /usr/local/bin/gitea # make directories gitea needs -RUN mkdir -p /var/lib/gitea/custom /var/lib/gitea/data/ /var/log/gitea && \ +RUN mkdir -p /var/lib/gitea/data/ /var/log/gitea /etc/gitea && \ chown -R gitea:gitea /var/lib/gitea /var/log/gitea && \ chmod -R 750 /var/lib/gitea /var/log/gitea && \ - ln -s /var/log/gitea /var/lib/gitea/log + ln -s /var/log/gitea /var/lib/gitea/log && \ + ln -s /vol/data/custom /var/lib/gitea/custom -# copy gitea config template -COPY assets/app.ini.esh /etc/gitea/ # template config file -RUN cd /etc/gitea/ && \ - esh app.ini.esh > app.ini && \ - rm app.ini.esh && \ - chmod -R +r /etc/gitea/ +COPY assets/app.ini.esh.esh /etc/gitea/ +RUN cd /etc/gitea && \ + esh app.ini.esh.esh && \ + rm app.ini.esh.esh && \ + chmod -R +r /etc/gitea COPY assets/gitea.service /etc/systemd/system/ +# make alias for running admin commands from command line easily +RUN echo "alias gitea='sudo -u gitea gitea --config /etc/gitea/app.ini'" >> /root/.bashrc + ### ### Crontab ### diff --git a/gitea/Systemdfile b/gitea/Systemdfile index 469fa94..4089fd2 100644 --- a/gitea/Systemdfile +++ b/gitea/Systemdfile @@ -9,6 +9,5 @@ sleep 5 # make database for gitea db-make -# enable and start gitea +# enable gitea systemctl enable gitea -systemctl start gitea diff --git a/gitea/assets/app.ini.esh b/gitea/assets/app.ini.esh.esh similarity index 82% rename from gitea/assets/app.ini.esh rename to gitea/assets/app.ini.esh.esh index ca4ff61..27c764c 100644 --- a/gitea/assets/app.ini.esh +++ b/gitea/assets/app.ini.esh.esh @@ -1,4 +1,5 @@ -APP_NAME = Gitea: Git with a cup of tea +<%% . /vol/data/hostvars -%> +APP_NAME = <%%= $pagename %> RUN_USER = gitea RUN_MODE = prod @@ -26,13 +27,14 @@ PATH = /var/lib/gitea/data/gitea.db ROOT = /vol/git/repos [server] -DOMAIN = git.alemor.org -ROOT_URL = https://git.alemor.org/ +DOMAIN = <%% $domain %> +ROOT_URL = https://<%% $domain %>/ HTTP_PORT = 80 -START_SSH_SERVER = true -SSH_PORT = 4323 -SSH_LISTEN_PORT = 22 -LFS_START_SERVER = trxue +START_SSH_SERVER = false +SSH_PORT = <%% $sshport %%> +SSH_CREATE_AUTHORIZED_KEYS_FILE = true +SSH_AUTHORIZED_KEYS_BACKUP = false +LFS_START_SERVER = true LFS_CONTENT_PATH = /vol/git/lfs LFS_JWT_SECRET = <%= $(gitea generate secret JWT_SECRET) %> OFFLINE_MODE = true diff --git a/gitea/assets/bin/db-load b/gitea/assets/bin/db-load index 41415c7..b46e0eb 100755 --- a/gitea/assets/bin/db-load +++ b/gitea/assets/bin/db-load @@ -1,10 +1,10 @@ #!/bin/bash status=$(systemctl show gitea -p ActiveState --value) -if [[ $status == active]]; then +if [[ $status == active ]]; then systemctl stop gitea fi db-make psql -U $DBUSER -d $DBNAME -f $1 -if [[ $status == active]]; then +if [[ $status == active ]]; then systemctl start gitea fi diff --git a/gitea/assets/bin/esh b/gitea/assets/bin/esh deleted file mode 100755 index 71698a3..0000000 --- a/gitea/assets/bin/esh +++ /dev/null @@ -1,366 +0,0 @@ -#!/bin/sh -# vim: set ts=4: -#---help--- -# USAGE: -# esh [options] [--] [...] -# esh <-h | -V> -# -# Process and evaluate an ESH template. -# -# ARGUMENTS: -# Path of the template file or "-" to read from STDIN. -# Variable(s) specified as = to pass into the -# template (the have higher priority than environment -# variables). -# -# OPTIONS: -# -d Don't evaluate template, just dump a shell script. -# -o Output file or "-" for STDOUT. Defaults to "-". -# -s Command name or path of the shell to use for template -# evaluation. It must not contain spaces. -# Defaults to "/bin/sh". -# -h Show this help message and exit. -# -V Print version and exit. -# -# ENVIRONMENT: -# ESH_AWK Command name of path of the awk program to use. -# It must not contain spaces. Defaults to "awk". -# ESH_MAX_DEPTH Maximum include depth. Defaults to 3. -# ESH_SHELL Same as -s. -# -# EXIT STATUS: -# 0 Clean exit, no error has encountered. -# 1 Generic error. -# 10 Invalid usage. -# 11 ESH syntax error. -# 12 Include error: file not found. -# 13 Include error: exceeded max include depth (ESH_MAX_DEPTH). -# -# Please report bugs at . -#---help--- -set -eu - -readonly PROGNAME='esh' -readonly VERSION='0.3.0' -readonly SCRIPTPATH="$0" - -AWK_CONVERTER=$(cat <<'AWK' -function fail(code, msg) { - state = "ERROR" - # FIXME: /dev/stderr is not portable - printf("%s: %s\n", line_info(), msg) > "/dev/stderr" - exit code -} -function line_info() { - return FILENAME ? (filenames[depth] ":" linenos[depth]) : "(init)" # (init) if inside BEGIN -} -# IMPORTANT: This is the only function that should print a newline. -function puts(str) { - print(line_info()) > MAP_FILE - print(str) -} -function fputs(str) { - printf("%s", str) -} -function trim(str) { - gsub(/^[ \t\r\n]+|[ \t\r\n]+$/, "", str) - return str -} -function read(len, _str) { - if (len == "") { - _str = buff - buff = "" - } else if (len > 0) { - _str = substr(buff, 1, len) - buff = substr(buff, len + 1, length(buff)) - } - return _str -} -function skip(len) { - buff = substr(buff, len + 1, length(buff)) -} -function flush(len, _str) { - _str = read(len) - - if (state == "TEXT") { - gsub("'", "'\\''", _str) - } - if (state != "COMMENT") { - fputs(_str) - } -} -function file_exists(filename, _junk) { - if ((getline _junk < filename) >= 0) { - close(filename) - return 1 - } - return 0 -} -function dirname(path) { - return sub(/\/[^\/]+\/*$/, "/", path) ? path : "" -} -function include(filename) { - if (index(filename, "/") != 1) { # if does not start with "/" - filename = dirname(filenames[depth]) filename - } - if (!file_exists(filename)) { - fail(12, "cannot include " filename ": not a file or not readable") - } - if (depth > MAX_DEPTH) { - fail(13, "cannot include " filename ": exceeded maximum depth of " MAX_DEPTH) - } - buffs[depth] = buff - states[depth] = state - filenames[depth + 1] = filename - depth++ - - init() - while ((getline buff < filename) > 0) { - if (print_nl && state != "COMMENT") { - puts("") - } - process_line() - } - end_text() - close(filename) - - depth-- - buff = buffs[depth] - state = states[depth] -} -function init() { - buff = "" - linenos[depth] = 0 - print_nl = 0 - start_text() -} -function start_text() { - puts("") - fputs("printf '%s' '") - state = "TEXT" -} -function end_text() { - if (state != "TEXT") { return } - puts("' #< " line_info()) - state = "UNDEF" -} -function process_line() { - linenos[depth]++ - - while (buff != "") { - print_nl = 1 - - if (state == "TEXT" && match(buff, /<%/)) { - flush(RSTART - 1) # print buff before "<%" - skip(2) # skip "<%" - - flag = substr(buff, 1, 1) - if (flag != "%") { - end_text() - } - if (flag == "%") { # <%% - skip(1) - fputs("<%") - } else if (flag == "=") { # <%= - skip(1) - fputs("__print ") - state = "TAG" - } else if (flag == "+") { # <%+ - if (!match(buff, /[^%]%>/)) { - fail(11, "syntax error: <%+ must be closed on the same line") - } - filename = trim(substr(buff, 2, match(buff, /.-?%>/) - 1)) - skip(RSTART) - include(filename) - state = "TAG" - } else if (flag == "#") { # <%# - state = "COMMENT" - } else { - state = "TAG" - } - } else if (state != "TEXT" && match(buff, /%>/)) { - flag = RSTART > 1 ? substr(buff, RSTART - 1, 1) : "" - - if (flag == "%") { # %%> - flush(RSTART - 2) - skip(1) - flush(2) - } else if (flag == "-") { # -%> - flush(RSTART - 2) - skip(3) - print_nl = 0 - } else { # %> - flush(RSTART - 1) - skip(2) - } - if (flag != "%") { - start_text() - } - } else { - flush() - } - } -} -BEGIN { - FS = "" - depth = 0 - - puts("#!" (SHELL ~ /\// ? SHELL : "/usr/bin/env " SHELL)) - puts("set -eu") - puts("if ( set -o pipefail 2>/dev/null ); then set -o pipefail; fi") - puts("__print() { printf '%s' \"$*\"; }") - - split(VARS, _lines, /\n/) - for (_i in _lines) { - puts(_lines[_i]) - } - init() -} -{ - if (NR == 1) { - filenames[0] = FILENAME # this var is not defined in BEGIN so we must do it here - } - buff = $0 - process_line() - - if (print_nl && state != "COMMENT") { - puts("") - } -} -END { - end_text() -} -AWK -) -AWK_ERR_FILTER=$(cat <<'AWK' -function line_info(lno, _line, _i) { - while ((getline _line < MAPFILE) > 0 && _i++ < lno) { } - close(MAPFILE) - return _line -} -{ - if (match($0, "^" SRCFILE ":( line)? ?[0-9]+:") && match(substr($0, 1, RLENGTH), /[0-9]+:$/)) { - lno = substr($0, RSTART, RLENGTH - 1) + 0 - msg = substr($0, RSTART + RLENGTH + 1) # v-- some shells duplicate filename - msg = index(msg, SRCFILE ":") == 1 ? substr(msg, length(SRCFILE) + 3) : msg - print(line_info(lno) ": " msg) - } else if ($0 != "") { - print($0) - } -} -AWK -) -readonly AWK_CONVERTER AWK_ERR_FILTER - -print_help() { - sed -En '/^#---help---/,/^#---help---/p' "$SCRIPTPATH" | sed -E 's/^# ?//; 1d;$d;' -} - -filter_shell_stderr() { - $ESH_AWK \ - -v SRCFILE="$1" \ - -v MAPFILE="$2" \ - -- "$AWK_ERR_FILTER" -} - -evaluate() { - local srcfile="$1" - local mapfile="$2" - - # This FD redirection magic is for swapping stdout/stderr back and forth. - exec 3>&1 - { set +e; $ESH_SHELL "$srcfile" 2>&1 1>&3; echo $? >>"$mapfile"; } \ - | filter_shell_stderr "$srcfile" "$mapfile" >&2 - exec 3>&- - - return $(tail -n 1 "$mapfile") -} - -convert() { - local input="$1" - local vars="$2" - local map_file="${3:-"/dev/null"}" - - $ESH_AWK \ - -v MAX_DEPTH="$ESH_MAX_DEPTH" \ - -v SHELL="$ESH_SHELL" \ - -v MAP_FILE="$map_file" \ - -v VARS="$vars" \ - -- "$AWK_CONVERTER" "$input" -} - -process() { - local input="$1" - local vars="$2" - local evaluate="${3:-yes}" - local ret=0 tmpfile mapfile - - if [ "$evaluate" = yes ]; then - tmpfile=$(mktemp) - mapfile=$(mktemp) - - convert "$input" "$vars" "$mapfile" > "$tmpfile" || ret=$? - test $ret -ne 0 || evaluate "$tmpfile" "$mapfile" || ret=$? - - rm -f "$tmpfile" "$mapfile" - else - convert "$input" "$vars" || ret=$? - fi - return $ret -} - -: ${ESH_AWK:="awk"} -: ${ESH_MAX_DEPTH:=3} -: ${ESH_SHELL:="/bin/sh"} -EVALUATE='yes' -OUTPUT='' - -while getopts ':dho:s:V' OPT; do - case "$OPT" in - d) EVALUATE=no;; - h) print_help; exit 0;; - o) OUTPUT="$OPTARG";; - s) ESH_SHELL="$OPTARG";; - V) echo "$PROGNAME $VERSION"; exit 0;; - '?') echo "$PROGNAME: unknown option: -$OPTARG" >&2; exit 10;; - esac -done -shift $(( OPTIND - 1 )) - -if [ $# -eq 0 ]; then - printf "$PROGNAME: %s\n\n" 'missing argument ' >&2 - print_help >&2 - exit 10 -fi - -INPUT="$1"; shift -[ "$INPUT" != '-' ] || INPUT='' -if [ "$INPUT" ] && ! [ -f "$INPUT" -a -r "$INPUT" ]; then - echo "$PROGNAME: can't read $INPUT: not a file or not readable" >&2; exit 10 -fi - -# Validate arguments. -for arg in "$@"; do - case "$arg" in - *=*) ;; - *) echo "$PROGNAME: illegal argument: $arg" >&2; exit 10;; - esac -done - -# Format variables into shell variable assignments. -vars=''; for item in "$@"; do - vars="$vars\n${item%%=*}='$( - printf %s "${item#*=}" | $ESH_AWK "{ gsub(/'/, \"'\\\\\\\''\"); print }" - )'" -done - -export ESH="$0" - -if [ "${OUTPUT#-}" ]; then - tmpfile="$(mktemp)" - trap "rm -f '$tmpfile'" EXIT HUP INT TERM - process "$INPUT" "$vars" "$EVALUATE" > "$tmpfile" - mv "$tmpfile" "$OUTPUT" -else - process "$INPUT" "$vars" "$EVALUATE" -fi diff --git a/gitea/assets/gitea.service b/gitea/assets/gitea.service index 6f7908a..6780d43 100644 --- a/gitea/assets/gitea.service +++ b/gitea/assets/gitea.service @@ -19,6 +19,8 @@ WorkingDirectory=/var/lib/gitea/ # If using Unix socket: tells systemd to create the /run/gitea folder, which will contain the gitea.sock file # (manually creating /run/gitea doesn't work, because it would not persist across reboots) #RuntimeDirectory=gitea +ExecStartPre=/usr/local/bin/esh /etc/gitea/app.ini.esh +ExecStartPre=/usr/local/bin/gitea --config /etc/gitea/app.ini admin regenerate keys ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini Restart=always Environment=USER=gitea HOME=/var/lib/gitea GITEA_WORK_DIR=/var/lib/gitea diff --git a/gitea/assets/sshd_config b/gitea/assets/sshd_config new file mode 100644 index 0000000..4044d96 --- /dev/null +++ b/gitea/assets/sshd_config @@ -0,0 +1,32 @@ +# The strategy used for options in the default sshd_config shipped with +# OpenSSH is to specify options with their default value where +# possible, but leave them commented. Uncommented options override the +# default value. + +# Listening: +#Port 22 +#AddressFamily any +#ListenAddress 0.0.0.0 +#ListenAddress :: + +# Host Keys: +HostKey /etc/ssh/keys/ssh_host_rsa_key +HostKey /etc/ssh/keys/ssh_host_ecdsa_key +HostKey /etc/ssh/keys/ssh_host_ed25519_key + +# Authentication: +PermitRootLogin no +PubkeyAuthentication yes +PasswordAuthentication no +PermitEmptyPasswords no +ChallengeResponseAuthentication no + +# Settings: +X11Forwarding no +PrintMotd no + +# Allow client to pass locale environment variables +AcceptEnv LANG LC_* + +# override default of no subsystems +Subsystem sftp /usr/lib/openssh/sftp-server diff --git a/gitea/docs/Build.md b/gitea/docs/Build.md index 8455410..09fa0ac 100644 --- a/gitea/docs/Build.md +++ b/gitea/docs/Build.md @@ -106,7 +106,7 @@ ROOT = /srv/gitea/repos SSH_DOMAIN = localhost DOMAIN = localhost HTTP_PORT = 3000 -ROOT_URL = http://medusa.casa.alemor.org/git/ +ROOT_URL = https://${host_url}/ DISABLE_SSH = true LFS_START_SERVER = true LFS_CONTENT_PATH = /srv/gitea/lfs diff --git a/mailsrv/assets/bin/mkvirt b/mailsrv/assets/bin/mkvirt index 36fdd86..f88a9ad 100755 --- a/mailsrv/assets/bin/mkvirt +++ b/mailsrv/assets/bin/mkvirt @@ -1,5 +1,9 @@ #!/bin/sh +### Domains ### +# /vol/data/domains should contain the configuration parameters for myhostname and virtual_alias_domains +cat /vol/data/domains /etc/postfix/main.cf.part > /etc/postfix/main.cf + ### Users ### # copy users db to dovecot config diff --git a/mailsrv/assets/postfix/main.cf b/mailsrv/assets/postfix/main.cf.part similarity index 91% rename from mailsrv/assets/postfix/main.cf rename to mailsrv/assets/postfix/main.cf.part index 33cd6c6..f695171 100644 --- a/mailsrv/assets/postfix/main.cf +++ b/mailsrv/assets/postfix/main.cf.part @@ -2,8 +2,6 @@ # interfaces & protocols to listen on inet_interfaces = all inet_protocols = all -# primary name of server -myhostname = mail.alemor.org myorigin = $mydomain # what host to use as a relay relayhost = @@ -13,8 +11,6 @@ relayhost = mynetworks_style = host # domains to consider primary (local) endpoints mydestination = $myhostname, $mydomain, localhost.localdomain, localhost -# domains to consider secondary (virtual) endpoints -virtual_alias_domains = epic.alemor.org, home.alemor.org, alegre.alemor.org, daniel.alemor.org, fernando.alemor.org, juana.alemor.org, mario.alemor.org, moreno.alemor.org, mar.alemor.org ### Users ### # get list of valid users from here instead of /etc/passwd diff --git a/mailsrv/docs/Deploy.md b/mailsrv/docs/Deploy.md index 6a0b8da..9071e20 100644 --- a/mailsrv/docs/Deploy.md +++ b/mailsrv/docs/Deploy.md @@ -57,15 +57,15 @@ git clone https://github.com/acmesh-official/acme.sh.git cd acme.sh ./acme.sh --install --home /usr/local/lib/acme-sh \ --config-home /etc/acme-sh \ - --accountemail letsencrypt@mail.alemor.org + --accountemail ${email:?} ``` Then exit and relogin to refresh the bash hash ``` acme.sh --issue --alpn --pre-hook 'systemctl stop haproxy' \ - --post-hook 'systemctl start haproxy' -d mail.alemor.org -acme.sh --install-cert -d mail.alemor.org \ + --post-hook 'systemctl start haproxy' -d ${domain:?} +acme.sh --install-cert -d ${domain:?} \ --fullchain-file /srv/vol/mailsrv/ssl/fullchain.pem \ --key-file /srv/vol/mailsrv/ssl/privkey.pem ```