diff --git a/.env.local.sz b/.env.local.sz new file mode 100644 index 0000000..9a741de --- /dev/null +++ b/.env.local.sz @@ -0,0 +1,25 @@ +# user-config/.env..local.yml +ACME_EMAIL=acme.szk.li@reg.lksz.me +#CF_DNS_API_TOKEN=ArkB0sbzv-dHVjTrum-MxQuDtpdhNrbi5feyiXDa + +HOST_UID=2001 +HOST_GID=1000 + +INTERNAL_IP=192.168.222.5 + +# RUNTIPI's root on the docker host +# TIPI_VERSION=v3.8.0 +# RUNTIPI_ROOT_FOLDER_HOST=/mnt//data/apps/runtipi +# RUNTIPI_ROOT_FOLDER_HOST=/srv/runtipi +RUNTIPI_ROOT_FOLDER_HOST=/mnt/szmedia/_apps.docker/runtipi + +# ALT_ROOT_DOMAIN= # optional, alternative public domain +ROOT_DOMAIN=szk.li +DOMAIN=szk.li +LOCAL_DOMAIN=tipi.local +ALT_ROOT_DOMAIN=lksz.me + +TZ=America/New_York + +# vi: ft=sh + diff --git a/_bin/rtpctl.d b/_bin/rtpctl.d index 9231b0b..8517439 100755 --- a/_bin/rtpctl.d +++ b/_bin/rtpctl.d @@ -20,13 +20,15 @@ set -e cd "${RUNTIPI_ROOT}" > /dev/null +. ./user-config/.env.local + runtipi-cli() { if ! [ -x "${RUNTIPI_CLI}" ]; then >&2 printf '%s\n' \ "ERROR: ${RUNTIPI_CLI} not found or not executable!" return 1 fi - "${RUNTIPI_CLI}" "${@}" + "${RUNTIPI_CLI}" "${@}" } append_file_param() { @@ -34,7 +36,137 @@ append_file_param() { echo "${*}" } +sort-tipi() { + # 111 is followed an NBSP literal + # the 111 lines will be added only if `runtipi` is found + # sort --unique makes sure only one such line exists + # This in effect creates a methodical spacing between runtipi + # and the rest of the list + sed -Ee 's|^|555|; s|^555runtipi|111 \n000runtipi|;' \ + | sort --unique \ + | sed -Ee 's/^[[:digit:]]{3}//' +} +dls() { + local base='{{.Status}}\t{{.ID}}\t{{.Names}}\t{{.Image}}' #'\t{{.Networks}}\t{{.Ports}}\t{{.Mounts}}' + local compose='{{.Label "com.docker.compose.project"}}\t{{.Label "com.docker.compose.service"}}' + local format="table $compose\t$base" + docker container ls --all --format "$format" | (sed -u '1s/.*/\U&/; q'; sort-tipi) +} + +get-docker-list() { + local POST_PROCESS=" | sed -Ee '#PP1 #PP2 #PP3' #PP_SORT" + local PP1='s/@.+//g;' + local PP2='s/ ix-/\nix-/g; s/(^|\n)ix-/\1/g;' + local PP3='' + local PP_SORT='| sort-tipi' + local SRC="" SRC_DOCKER="docker ps -a --format '{{.Label \"com.docker.compose.project\"}}@{{.Names}}' | sed -Ee 's/^@/<_no-compose-project_>:/;'" + local FILTER="" + local LIST='' + local MK_SEARCH_LIST="| xargs -r | sed 's/@//g; s/^/^(/; s/ *$/)@/; s/ /|/g;'" + local ARG=${1:---help} + while [ -n "$ARG" ]; do + case "$ARG" in + --debug) + set -x + ;; + -G) + POST_PROCESS="${MK_SEARCH_LIST}" + PP_SORT='' + ;; + -a) + SRC="${SRC_DOCKER}" + if [ -z "$LIST" ]; then + ARG="+X" + continue + fi + ;; + -c) + PP1='' + if [ -z "$SRC" ]; then + ARG="-r" + continue + fi + ;; + -r) + SRC="${SRC_DOCKER/ps -a /ps }" + if [ -z "$LIST" ]; then + ARG="+X" + continue + fi + ;; + +X) + PP2='' + ;; + ix|truenas) + LIST="find /mnt/.ix-apps/app_configs/* -maxdepth 0 -printf 'ix-%f@ '" + ;; + tp|runtipi) + LIST="find apps/* -maxdepth 0 -printf '%f@ ' ; echo runtipi" + ;; + other) + FILTER="grep -vE \""$(get-docker-list ix -G)"\" | grep -vE \""$(get-docker-list tp -G)"\"" + if [ -z "$LIST" ] && [ -z "$SRC" ]; then + ARG="-c" + continue + fi + ;; + *) + printf '%s %-12s %s\n' \ + "Usage:" "" ""\ + " ${BASE_NAME} ls " "" ""\ + "" "" "" \ + "Available options:" "" ""\ + "" "-G" "Create grep -E matching logic" \ + "" "-a" "All containers (including inactive ones)" \ + "" "-r" "Runing containers only" \ + "" "+X" "for IX apps - preserve the ix- prefix" \ + "" "" "" \ + "" "ix|truenas" "IX (TrueNAS SCALE) apps" \ + "" "tp|runtipi" "Runtipi apps" \ + "" "other" "All other apps" \ + && return 1 + ;; + esac + shift + ARG="${1}" + done + if [ 0 -eq "${#LIST}" ]; then + LIST="${SRC}" + SRC='' + fi + if [ 0 -eq "${#LIST}" ]; then + printf "Source missing, must specify one of the following: -a | -r | ix | tp | other\n" >&2 + return 1 + fi + POST_PROCESS="${POST_PROCESS/\#PP1/${PP1}}" + POST_PROCESS="${POST_PROCESS/\#PP2/${PP2}}" + POST_PROCESS="${POST_PROCESS/\#PP3/${PP3}}" + POST_PROCESS="${POST_PROCESS/\#PP_SORT/${PP_SORT}}" + + [ -n "$SRC" ] \ + && CLI="$SRC | grep -E \"\$(($LIST) $MK_SEARCH_LIST)\"" \ + || CLI="($LIST)" + CLI="$CLI ${FILTER:+| $FILTER} ${POST_PROCESS}" + printf '%s\n' $(eval "$CLI") +} + +list-docker-apps() { + local LIST="$(get-docker-list "$@")" + + local RUNNING="$(get-docker-list "$@" -r | sort-tipi)" + local LIMBO="$(get-docker-list "$@" -a | grep -xF "$LIST" | grep -vxF "$RUNNING" | sort-tipi)" + local DOWN="$(grep -Fxv -f <(printf '%s\n' "$RUNNING" "$LIMBO") <<<"$LIST" | sort-tipi)" + + printf 'RUNNING\n' && printf ' %s\n' ${RUNNING:-""} + [ -n "$LIMBO" ] && printf '\nLIMBO \n' && printf ' %s\n' $LIMBO || true + [ -n "$DOWN" ] && printf '\nDOWN \n' && printf ' %s\n' $DOWN || true +} + runtipi-app-docker-compose() { + if [ $# -eq 0 ]; then + list-docker-apps tp + return 1 + fi local APP="${1:-Must supply app name}" docker compose \ @@ -50,14 +182,100 @@ runtipi-app-docker-compose() { append_file_param --file "docker-compose.yml" append_file_param --file "user-config/tipi-compose.yml" fi) \ - ${@:2} + ${2:-ps -a} ${3:+"${@:3}"} } -dls() { - local base='{{.Status}}\t{{.ID}}\t{{.Names}}\t{{.Image}}' #'\t{{.Networks}}\t{{.Ports}}\t{{.Mounts}}' - local compose='{{.Label "com.docker.compose.project"}}\t{{.Label "com.docker.compose.service"}}' - local format="table $compose\t$base" - docker container ls --all --format "$format" | ( sed -u '1s/.*/\U&/; q'; sed -Ee 's|^|555|; s|^555runtipi|000runtipi|;' | sort | sed -Ee 's/^[[:digit:]]{3}//' ) +ix-app-docker-compose() { + if [ $# -eq 0 ]; then + list-docker-apps ix + + return 1 + fi + local APP="${1:-Must supply app name}" + + DCSRC="$( + find /mnt/.ix-apps/app_configs/ -type f \ + -path "*/${APP}/*/rendered/docker-compose.yaml" -printf '%T@ %p\n' \ + | sort -n | cut -d' ' -f2- | head -1 + )" + set -x + docker compose "--file=${DCSRC}" "--project-name=ix-${APP}" ${2:-ps -a} ${3:+"${@:3}"} +} + +link-exists() { + local EXIT_CODE=0 + printf 'Current status:\n link: ' + ip -br -c link show "${1:?Must supply device name}" 2>&1 || return 1 +} +manage-ipvlan() { + + local IPVLAN_IF="${2:-ipvlan-lan}" + local NET_IP="${NET_IP:-${INTERNAL_IP:?}}" + local IP_BASE="${NET_IP%\.[[:digit:]]*}" + local NET_IF="${NET_IF:-"$(ip -4 -br a s to "${NET_IP}" | cut -d' ' -f1)"}" + NET_IF="${NET_IF:-$($(ip -4 -br a s to "${IP_BASE}.0/24" | cut -d' ' -f1))}" + NET_IF="${NET_IF:?"Could not detect network interface for ${NET_IP}"}" + + case "${1}" in + rm) + manage-ipvlan status "${IPVLAN_IF}" && ( \ + ip link delete "${IPVLAN_IF}" \ + && printf '%s\n' "${IPVLAN_IF} removed" + ) || return 1 + ;; + add) + link-exists "${IPVLAN_IF}" > /dev/null 2>&1 \ + && link-exists "${IPVLAN_IF}" || ( \ + ip link add "${IPVLAN_IF}" link "${NET_IF}" type ipvlan mode l2 \ + && ip addr add ${NET_IP} dev "${IPVLAN_IF}" \ + && ip link set "${IPVLAN_IF}" up \ + && printf '%s\n' "${IPVLAN_IF} created"\ + && link-exists "${IPVLAN_IF}" \ + || ip link delete "${IPVLAN_IF}" + ) + ;; + route) + local ROUTE_SCOPE="${3:?Must supply routing scope in the form of ###.###.###.###[/##]}" + + manage-ipvlan add "${IPVLAN_IF}" > /dev/null 2>&1 \ + && for IP in "${@:3}"; do + # Only add route if it does not already exists + printf 'Adding route for %-15s ' "$IP" + ip route get "$IP" 2> /dev/null | grep -q "dev ${IPVLAN_IF} src ${NET_IP//\./\\.}" \ + && printf 'Skipping, already exists.' \ + || ip route add "$IP" dev "${IPVLAN_IF}" \ + && printf '\n' \ + || (printf 'Could NOT add %s\n' "$IP" >&2; return 1) + done + + link-exists "${IPVLAN_IF}" + ;; + status) + link-exists "${IPVLAN_IF}" || return 1 + ;; + *) + printf '%s %-12s %s\n' \ + "$(manage-ipvlan status)" "" ""\ + "" "" ""\ + "Usage:" "" ""\ + " ${BASE_NAME} ipvlan " "" ""\ + "" "" "" \ + "Available commands:" "" ""\ + "" "" "" \ + "" "fix" "implement ipvlan fix" \ + "" "rm" "remove ipvlan fix" \ + "" "status" "output interface status" \ + return 0 + ;; + esac + + ( printf 'addr: ' && ip -4 -br -c addr show "${IPVLAN_IF}" \ + && printf 'Routes:\n' \ + && ip -c route show dev "${IPVLAN_IF}" \ + | awk '{print} END{if (NR==0) print ""}' + ) 2>&1 | sed -e '/[^:]$/s/^/ /' \ + || return 1 + # ip link show [ DEVICE | group GROUP ] [up] [master DEV] [vrf NAME] [type TYPE] [nomaster] } case "${1}" in @@ -69,7 +287,7 @@ case "${1}" in ;; start) ROOT_FOLDER_HOST="${RUNTIPI_ROOT}" RUNTIPI_APP_DATA_PATH="${RUNTIPI_ROOT}" \ - runtipi-cli start --env-file user-config/.env.local --no-permissions + runtipi-cli start --env-file user-config/.env.local ${2:---no-permissions} ;; update) if [ -z "${2}" ]; then @@ -78,22 +296,24 @@ case "${1}" in runtipi-cli update --env-file user-config/.env.local --no-permissions "${2:?Must supply version}" "${@:3}" fi ;; - docker) - docker "${@:2}" - ;; dls) dls "${@:2}" ;; - app) + ls) + get-docker-list "${@:2}" + ;; + tpcompose) runtipi-app-docker-compose "${@:2}" ;; - dockge) - cd "${RUNTIPI_ROOT}/../dockge/stacks/${2:?Must supply stack name}" > /dev/null - docker compose "${@:3}" + ixcompose) + ix-app-docker-compose "${@:2}" ;; shell) runtipi-app-docker-compose "${2:?}" exec ${5:+"${@:5}"} -it "${4:-${2}}" "${3:-bash}" ;; + ipvlan) + manage-ipvlan "${@:2}" + ;; setup) ln -s $2 "$(cd -- "${SCRIPT_DIR}" && pwd)/${BASE_NAME}" "${3:-$HOME/.local/bin/}" ;; @@ -115,11 +335,16 @@ case "${1}" in "" "update" "update runtipi to a specific version" \ "" "" "" \ "docker/docker-compose" "" ""\ - "" "app" "docker compose for runtipi apps" \ + "" "tpcompose" "docker compose for runtipi apps" \ + "" "ixcompose" "docker compose for ix/TrueNAS SCALE docker based app" \ + "" "ls" "list applications (or contrainers)" \ "" "dls" "stylized docker ls" \ - "" "docker" "docker" \ "" "dockge" "docker compose for dockge stacks" \ "" "shell" "enter an insteractive shell" \ + "" "down-all" "stop and remove everything" \ + "" "" "" \ + "networking" "" ""\ + "" "ipvlan" "manage ipvlan networking interface fix" \ "" "" "" \ "misc." "" ""\ "" "exec" "execute within the shell, START_DIR env applies" \ diff --git a/code-server/wrokspace.code-workspace b/code-server/wrokspace.code-workspace index cef6485..2b97e4e 100644 --- a/code-server/wrokspace.code-workspace +++ b/code-server/wrokspace.code-workspace @@ -1,13 +1,10 @@ { "folders": [ - { - "path": "../../../dockge/stacks" - }, { "path": ".." }, { - "path": "../.." + "path": "../../.." } ], "settings": {} diff --git a/ddns-updater/config.json b/ddns-updater/config.json new file mode 100644 index 0000000..45cda3f --- /dev/null +++ b/ddns-updater/config.json @@ -0,0 +1,14 @@ +{ + "settings": [ + { + "provider": "cloudflare", + "zone_identifier": "cfa643a7268a07bddafdb3054bc07c4f", + "domain": "home.lksz.me", + "proxied": false, + "ttl": 600, + "token": "tIlizVv2GWLV7qbr9L3lU1xBDLW0aKXcOfWMp9Pq", + "ip_version": "ipv4", + "ipv6_suffix": "" + } + ] + } \ No newline at end of file diff --git a/ddns-updater/docker-compose.yml b/ddns-updater/docker-compose.yml index 2db7c3d..77890ba 100644 --- a/ddns-updater/docker-compose.yml +++ b/ddns-updater/docker-compose.yml @@ -1,8 +1,10 @@ services: ddns-updater: + dns: + - 1.1.1.1 environment: - USER_UID: "${HOST_UID}" - USER_GID: "${HOST_GID}" + USER_UID: "${HOST_UID:?Must specify HOST_UID}" + USER_GID: "${HOST_GID:-${HOST_UID}}" ### Configuration # DATADIR: "/updater/data" diff --git a/forgejo/docker-compose.yml b/forgejo/docker-compose.yml index d15c891..c1bab74 100644 --- a/forgejo/docker-compose.yml +++ b/forgejo/docker-compose.yml @@ -7,12 +7,13 @@ services: FORGEJO__server__DOMAIN: "code.${ALT_ROOT_DOMAIN}" FORGEJO__server__ROOT_URL: "https://code.${ALT_ROOT_DOMAIN}" FORGEJO__server__SSH_DOMAIN: "code.${ALT_ROOT_DOMAIN}" + FORGEJO__service__ENABLE_CAPTCHA: "true" + FORGEJO__service__REQUIRE_EXTERNAL_REGISTRATION_CAPTCHA: "true" + FORGEJO__service__DEFAULT_KEEP_EMAIL_PRIVATE: "true" + FORGEJO__service__DEFAULT_USER_IS_RESTRICTED: "true" + FORGEJO__service__ALLOW_ONLY_EXTERNAL_REGISTRATION: "true" + FORGEJO__service_explore__DISABLE_USERS_PAGE: "true" labels: # Websecure - traefik.http.routers.forgejo-more.rule: Host(`code.${ROOT_DOMAIN}`)${APP_ROUTE_OPTIONAL:-} - traefik.http.routers.forgejo-more.entrypoints: websecure - traefik.http.routers.forgejo-more.service: forgejo - # traefik.http.routers.forgejo-more.middlewares: authentik_sysmgr@file - traefik.http.routers.forgejo-more.tls: true - traefik.http.routers.forgejo-more.tls.certresolver: myresolver + traefik.http.routers.forgejo.rule: Host(`code.${ROOT_DOMAIN}`)${APP_ROUTE_OPTIONAL:-} diff --git a/overseerr/docker-compose.yml b/jellyseerr/docker-compose.yml similarity index 78% rename from overseerr/docker-compose.yml rename to jellyseerr/docker-compose.yml index 0a138ce..8688966 100644 --- a/overseerr/docker-compose.yml +++ b/jellyseerr/docker-compose.yml @@ -1,6 +1,6 @@ services: - overseerr: - hostname: overseerr.docker + jellyseerr: + hostname: jellyseerr.docker environment: PUID: "${HOST_UID}" PGID: "${HOST_GID}" @@ -16,4 +16,4 @@ services: RUNTIPI_ROOT_DOMAIN: "${ROOT_DOMAIN}" labels: # Websecure - traefik.http.routers.overseerr.rule: Host(`req.${ROOT_DOMAIN}`)${APP_ROUTE_OPTIONAL:-} \ No newline at end of file + traefik.http.routers.jellyseerr.rule: Host(`req.${ROOT_DOMAIN}`)${APP_ROUTE_OPTIONAL:-} \ No newline at end of file diff --git a/tipi-compose.yml b/tipi-compose.yml index 70ac835..33bdadd 100644 --- a/tipi-compose.yml +++ b/tipi-compose.yml @@ -13,7 +13,7 @@ secrets: services: runtipi-reverse-proxy: secrets: - - traefik_cf_dns_api_token + - traefik_cf_dns_api_token volumes: - type: bind source: ./traefik/shared @@ -28,7 +28,7 @@ services: target: /srv/traefik/dynamic/ read_only: true - type: bind - source: ./user-config/_traefik/static.${TRAEFIK_STATIC:-full}.yml + source: ./user-config/_traefik/static.yml target: /srv/traefik/static.yml read_only: true logging: @@ -49,15 +49,27 @@ services: networks: - tipi_main_network - tipi_internal_network -# - ix-dockge + - ix-dozzle networks: + tipi_main_network: + attachable: true + name: runtipi_tipi_main_network + driver: bridge + driver_opts: + com.docker.network.bridge.name: "tipi-br1" tipi_internal_network: internal: true attachable: true - name: runtipi_internal_network - ix-dockge: + name: runtipi_tipi_internal_network + driver: bridge + driver_opts: + com.docker.network.bridge.name: "tipi-br0" + ix-dozzle: external: true - name: ix-dockge_default + name: ix-dozzle_default +# ix-dockge: +# external: true +# name: ix-dockge_default # vim: set ft=yaml expandtab tabstop=2 shiftwidth=2: diff --git a/uptime-kuma/docker-compose.yml b/uptime-kuma/docker-compose.yml new file mode 100644 index 0000000..e1b1952 --- /dev/null +++ b/uptime-kuma/docker-compose.yml @@ -0,0 +1,18 @@ +services: + uptime-kuma: + hostname: uptime-kuma + environment: + # RUNTIPI Environment + RUNTIPI_APP_PORT: "${APP_PORT}" + RUNTIPI_APP_ID: "${APP_ID}" + RUNTIPI_APP_DATA_DIR: "${APP_DATA_DIR}" + RUNTIPI_APP_EXPOSED: "${APP_EXPOSED}" + RUNTIPI_APP_DOMAIN: "${APP_DOMAIN}" + RUNTIPI_APP_HOST: "${APP_HOST}" + # labels: + # # Websecure + # traefik.http.routers.uptime-kuma-more.rule: Host(`vault.lksz.me`)${APP_ROUTE_OPTIONAL:-} + # traefik.http.routers.uptime-kuma-more.entrypoints: websecure + # traefik.http.routers.uptime-kuma-more.service: uptime-kuma + # traefik.http.routers.uptime-kuma-more.tls: true + # traefik.http.routers.uptime-kuma-more.tls.certresolver: myresolver diff --git a/vaultwarden/docker-compose.yml b/vaultwarden/docker-compose.yml index 73496ef..716cc94 100644 --- a/vaultwarden/docker-compose.yml +++ b/vaultwarden/docker-compose.yml @@ -12,13 +12,14 @@ services: RUNTIPI_APP_HOST: "${APP_HOST}" networks: # - tipi_main_network - - vaultwarden_pg_dockge + - vaultwarden-pg volumes: - - /srv/vaultwarden/data:/data + - ../vaultwarden/data:/data labels: # Websecure traefik.http.routers.vaultwarden.rule: Host(`vault.${ROOT_DOMAIN}`)${APP_ROUTE_OPTIONAL:-} networks: - vaultwarden_pg_dockge: + vaultwarden-pg: external: true - name: vaultwarden-pg_dockge + name: vaultwarden-pg +