Compare commits
No commits in common. "main" and "claude/2" have entirely different histories.
@ -1 +0,0 @@
|
||||
{"sessionId":"9cea077c-56da-4223-a765-be38c688106b","pid":1384,"procStart":"1332","acquiredAt":1780168110981}
|
||||
11
.gitattributes
vendored
11
.gitattributes
vendored
@ -1,11 +0,0 @@
|
||||
# Paths excluded from release tarballs. `git archive` (used by
|
||||
# scripts/release/make_release.sh) honours `export-ignore`, so these dev-only
|
||||
# trees never ship in libreportal-<ver>.tar.gz.
|
||||
scripts/unused export-ignore
|
||||
scripts/release export-ignore
|
||||
scripts/dev export-ignore
|
||||
site export-ignore
|
||||
docs export-ignore
|
||||
.claude export-ignore
|
||||
.gitignore export-ignore
|
||||
.gitattributes export-ignore
|
||||
11
.gitignore
vendored
11
.gitignore
vendored
@ -10,14 +10,3 @@
|
||||
# Node dependencies — installed via `npm ci` at image build, never vendored.
|
||||
node_modules/
|
||||
npm-debug.log*
|
||||
|
||||
# Release build output (scripts/release/make_release.sh).
|
||||
/dist/
|
||||
|
||||
# Eleventy site build output + generator-produced data (scripts/gen-data.mjs).
|
||||
# site/ is the legacy website location (active site now lives in
|
||||
# containers/weblibreportal); these are build artifacts, not source.
|
||||
site/dist/
|
||||
site/src/_data/apps.json
|
||||
site/src/_data/categories.json
|
||||
|
||||
|
||||
57
CLAUDE.md
57
CLAUDE.md
@ -1,57 +0,0 @@
|
||||
# LibrePortal — agent notes
|
||||
|
||||
## Verify WebUI changes visually before marking them done
|
||||
After changing anything user-visible in the WebUI (`containers/libreportal/frontend/`),
|
||||
confirm it actually renders correctly — syntax checks and type-correctness don't
|
||||
catch layout or visual regressions.
|
||||
|
||||
The maintainer's dev environment provides a headless screenshot helper, `lp-shot`,
|
||||
that captures a WebUI route (or a single element, via a trailing CSS selector) to a
|
||||
PNG for review:
|
||||
|
||||
```
|
||||
lp-shot /admin/system # full route -> /tmp/webui-shot.png
|
||||
lp-shot /admin/system /tmp/x.png 12 ".sys-strip" # just one element, crisp
|
||||
```
|
||||
|
||||
Use it (and read the PNG) to self-check UI work instead of assuming it looks right or
|
||||
asking the user to look. Skip it for purely backend/non-visual edits. If `lp-shot`
|
||||
isn't present, fall back to asking the user for a screenshot.
|
||||
|
||||
Every route except `/` is behind the WebUI login, and `lp-shot` handles that
|
||||
itself — it signs a one-hour session from the `jwtSecret` the backend stores in
|
||||
`frontend/.auth.json`, the same token `/api/auth/login` would issue. No password
|
||||
is involved (the stored one is a bcrypt hash). So no setup: just run it. Override
|
||||
with `LP_SHOT_TOKEN`, or `LP_SHOT_USER`+`LP_SHOT_PASS`, when shooting a remote
|
||||
instance. `lp-shot --help` lists the rest (`LP_SHOT_URL`, `LP_SHOT_VIEWPORT`,
|
||||
`LP_SHOT_SCALE`, …).
|
||||
|
||||
If a shot comes back as the boot splash, the page wasn't ready — `lp-shot` waits
|
||||
for `#libreportal-loading-screen` to leave the DOM, so a splash in the PNG means
|
||||
boot genuinely stalled. Read the `page error:` lines it prints to stderr.
|
||||
|
||||
Testing against the live WebUI means updating the running install, not just the
|
||||
repo: `/libreportal-containers/libreportal/frontend/` is bind-mounted into the
|
||||
container, so copying changed files there (owned `dockerinstall:dockerinstall`)
|
||||
takes effect on the next browser load — no rebuild or restart. Diff before you
|
||||
copy; the live tree can hold changes the repo doesn't.
|
||||
|
||||
## Check task names after adding a dispatched command
|
||||
|
||||
Task titles all come from one table in `components/tasks/js/tasks-format.js`,
|
||||
whose final fallback returns the raw command string. A command added to
|
||||
`core/tasks/js/task-commands.js` without a matching row does not error — it just
|
||||
renders as `libreportal instance remove bookstack_work` next to properly named
|
||||
neighbours like `Bookstack - Create Backup`. That silence is why bad titles kept
|
||||
coming back.
|
||||
|
||||
`lp-task-names` reads both files and fails if any dispatched command has no
|
||||
proper name:
|
||||
|
||||
```
|
||||
scripts/dev/lp-task-names # table of every command and its title
|
||||
scripts/dev/lp-task-names --quiet # failures only
|
||||
```
|
||||
|
||||
Run it after touching either file. It needs a node and borrows the running
|
||||
container's if the host has none.
|
||||
@ -7,7 +7,7 @@ to you — in plain language, so you can hold us to it.
|
||||
|
||||
You can **run, study, modify, share, and fully use 100% of LibrePortal —
|
||||
every feature — for free, forever.** The entire platform is licensed under
|
||||
the GNU AGPLv3 (see [LICENSE](../../LICENSE)). There are **no feature paywalls in
|
||||
the GNU AGPLv3 (see [LICENSE](LICENSE)). There are **no feature paywalls in
|
||||
the software, no crippled "community edition," and no telemetry** phoning
|
||||
home.
|
||||
|
||||
23
README.md
23
README.md
@ -21,7 +21,7 @@ toggle — it's the whole point.
|
||||
|
||||
The entire platform is **free software under the [GNU AGPLv3](LICENSE)**.
|
||||
Self-host it and you get **everything** — every feature, no paywalls, no
|
||||
telemetry. See [our Promise](docs/guide/promise.md) for exactly what that means.
|
||||
telemetry. See [our Promise](PROMISE.md) for exactly what that means.
|
||||
|
||||
## What you get
|
||||
|
||||
@ -34,22 +34,11 @@ telemetry. See [our Promise](docs/guide/promise.md) for exactly what that means.
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
curl -fsSL https://get.libreportal.org/install.sh | sudo bash
|
||||
git clone https://gitea.scottwebstar.co.uk/Webstar/LibrePortal.git
|
||||
cd LibrePortal
|
||||
./init.sh
|
||||
```
|
||||
|
||||
This installs a versioned, checksum-verified release (Debian/Ubuntu, root). Put
|
||||
data on separate disks with `--system-dir=` / `--containers-dir=` / `--backups-dir=`.
|
||||
|
||||
> The `get.libreportal.org` host is still being set up — until it's live, build a
|
||||
> release and install from it locally (see the docs below).
|
||||
|
||||
## Documentation
|
||||
|
||||
- **[docs/guide/install-and-use.md](docs/guide/install-and-use.md)** — install, place data on separate disks/drives,
|
||||
update, back up, uninstall.
|
||||
- **[docs/contributing/development.md](docs/contributing/development.md)** — run a dev copy, cut stable/edge
|
||||
releases, and test them before publishing.
|
||||
|
||||
## LibrePortal Connect (optional)
|
||||
|
||||
Self-hosting is free and complete. If you'd rather not fiddle with the tricky
|
||||
@ -59,11 +48,11 @@ different: we work like a **courier carrying a sealed box.** We move your data
|
||||
between your devices and store backup copies, but it stays locked and *you*
|
||||
hold the only key — we can't open it, and we never run your apps for you.
|
||||
**Everything we offer, you can also set up yourself for free.**
|
||||
[Our Promise](docs/guide/promise.md) spells out exactly where that line sits.
|
||||
[Our Promise](PROMISE.md) spells out exactly where that line sits.
|
||||
|
||||
## Contributing
|
||||
|
||||
PRs welcome — see [CONTRIBUTING.md](docs/contributing/contributing.md). We use a lightweight
|
||||
PRs welcome — see [CONTRIBUTING.md](CONTRIBUTING.md). We use a lightweight
|
||||
DCO sign-off (`git commit -s`), no CLA.
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
@ -2,4 +2,4 @@ TITLE=Backup
|
||||
DESCRIPTION=Backup schedules, retention, and engine settings
|
||||
ICON=backup
|
||||
ORDER=3
|
||||
SUBCATEGORY_ORDER=backup_general,backup_retention,backup_engine
|
||||
SUBCATEGORY_ORDER=backup_general,backup_retention,backup_advanced
|
||||
|
||||
8
configs/backup/backup_advanced
Normal file
8
configs/backup/backup_advanced
Normal file
@ -0,0 +1,8 @@
|
||||
# ================================================================================
|
||||
# Backup Advanced - **ADVANCED** Engine-level knobs most users won't need to touch
|
||||
# ================================================================================
|
||||
CFG_BACKUP_ENGINE=restic # Default Backup Engine - Fallback engine for new locations (each location can override) [restic:restic|borg:BorgBackup|kopia:Kopia]
|
||||
CFG_BACKUP_STRATEGY=stop-snapshot-start # Backup Strategy - How containers are quiesced before snapshotting [stop-snapshot-start:Stop → snapshot → start (safe default)|pause-snapshot-unpause:Pause → snapshot → unpause (less downtime)|live:Live — snapshot while running (only with DB dump hooks)]
|
||||
CFG_BACKUP_VERIFY_AFTER=true # Verify After Backup - Run integrity check after each backup
|
||||
CFG_BACKUP_VERIFY_DATA_PERCENT=5 # Verify Data Sample % - Percentage of repo data to checksum-verify weekly
|
||||
CFG_BACKUP_PARALLEL_REPOS=true # Parallel Repos - Push to all enabled locations in parallel
|
||||
@ -1,11 +0,0 @@
|
||||
# ================================================================================
|
||||
# Backup Engine - **ADVANCED** Engine-level knobs most users won't need to touch
|
||||
# @icon ⚙️
|
||||
# ================================================================================
|
||||
CFG_BACKUP_ENGINE=restic # Default Backup Engine - Fallback engine for new locations (each location can override) [restic:Restic|borg:BorgBackup|kopia:Kopia]
|
||||
CFG_BACKUP_DEFAULT_PATH= # Default Backup Location - Base directory for locations using Automatic path mode. Empty = the LibrePortal backups root.
|
||||
CFG_BACKUP_STRATEGY=auto # Backup Strategy - How containers are quiesced before snapshotting [auto:Automatic — live where safe, stop otherwise (recommended)|stop-snapshot-start:Stop → snapshot → start (always safe)|pause-snapshot-unpause:Pause → snapshot → unpause (less downtime)|live:Live — snapshot while running (force)]
|
||||
CFG_BACKUP_VERIFY_AFTER=true # Verify After Backup - Run integrity check after each backup
|
||||
CFG_BACKUP_VERIFY_DATA_PERCENT=5 # Verify Data Sample % - Percentage of repo data to checksum-verify weekly
|
||||
CFG_BACKUP_PARALLEL_REPOS=true # Parallel Repos - Push to all enabled locations in parallel
|
||||
CFG_BACKUP_SSH_MULTIPLEX=true # SSH Connection Reuse - Share one SSH connection across the restic calls to each SFTP location
|
||||
@ -1,8 +1,5 @@
|
||||
# ================================================================================
|
||||
# Backup General - Scheduling
|
||||
# @icon 💾
|
||||
# ================================================================================
|
||||
CFG_BACKUP_CONNECT_ENDPOINT= # LibrePortal Connect - Base URL of the hosted backup service. Empty means Connect is not available yet and the WebUI offers it greyed out; setting it is all that turns it on. **ADVANCED**
|
||||
CFG_BACKUP_MODE=automatic # Backups - Automatic runs them on the schedule below; Manual means you start them yourself from the Backup page [automatic:Automatic|manual:Manual]
|
||||
CFG_BACKUP_CRONTAB_APP="0 5 * * *" # App Backup Schedule - Crontab schedule for application backups
|
||||
CFG_BACKUP_DASHBOARD_REFRESH_INTERVAL=30 # Dashboard Refresh Interval - Minutes between routine restic pulls that refresh the Backups dashboard
|
||||
CFG_BACKUP_CRONTAB_APP_INTERVAL=3 # App Backup Interval - Minutes between app backup checks
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
# ================================================================================
|
||||
# Backup Retention - Default retention policy applied at every forget pass.
|
||||
# @icon 🗓️
|
||||
# Per-location overrides supported via the Locations edit modal.
|
||||
#
|
||||
# Most users should pick a "Backup style" preset on the Schedule page rather
|
||||
|
||||
@ -2,23 +2,23 @@
|
||||
# Edit via the Locations page on /backup, or directly here.
|
||||
CFG_BACKUP_LOC_1_NAME="Local disk" # Location Name - Friendly label shown in the UI
|
||||
CFG_BACKUP_LOC_1_ENABLED=true # Enabled - Snapshot to this location
|
||||
CFG_BACKUP_LOC_1_ENGINE=restic # Engine - Backup engine used at this location [restic:Restic|borg:BorgBackup|kopia:Kopia] **ADVANCED**
|
||||
CFG_BACKUP_LOC_1_ENGINE=restic # Engine - Backup engine used at this location [restic:restic|borg:BorgBackup|kopia:Kopia]
|
||||
CFG_BACKUP_LOC_1_PASSWORD=RANDOMIZEDPASSWORD1 # Repository Password - Used to encrypt/decrypt snapshots — back up offline!
|
||||
CFG_BACKUP_LOC_1_TYPE=local # Type - Backend [local:Local / mounted path|sftp:SFTP|rest:REST|s3:S3|b2:Backblaze B2|gs:Google Cloud Storage|azure:Azure|rclone:rclone]
|
||||
CFG_BACKUP_LOC_1_PATH_MODE=auto # Path Mode - Automatic uses the Default Backup Location; Custom uses the path below [auto:Automatic|custom:Custom path]
|
||||
CFG_BACKUP_LOC_1_PATH_MODE=auto # Path Mode - Where this location stores its data [auto:Automatic (/docker/backups/<id>)|custom:Custom path]
|
||||
CFG_BACKUP_LOC_1_PATH= # Custom Path - Filesystem path on this server (used when Path Mode = Custom)
|
||||
CFG_BACKUP_LOC_1_URI= # URI Override - Custom restic URI (leave blank to build from the fields below) **ADVANCED**
|
||||
CFG_BACKUP_LOC_1_URI= # URI Override - Custom restic URI (leave blank to build from the fields below)
|
||||
CFG_BACKUP_LOC_1_SSH_USER= # SSH User - For sftp type
|
||||
CFG_BACKUP_LOC_1_SSH_HOST= # SSH Host - For sftp type
|
||||
CFG_BACKUP_LOC_1_SSH_PORT=22 # SSH Port - For sftp type **ADVANCED**
|
||||
CFG_BACKUP_LOC_1_SSH_PORT=22 # SSH Port - For sftp type
|
||||
CFG_BACKUP_LOC_1_SSH_PATH= # SSH Remote Path - Path on the remote host where the repo lives
|
||||
CFG_BACKUP_LOC_1_SSH_AUTH=key # SSH Authentication - [key:SSH key (managed by LibrePortal)|password:Password (via sshpass)]
|
||||
CFG_BACKUP_LOC_1_SSH_AUTH=key # SSH Authentication - [key:SSH key (~/.ssh/id_rsa)|password:Password (via sshpass)]
|
||||
CFG_BACKUP_LOC_1_SSH_PASS= # SSH Password - Used only when SSH Authentication is set to Password
|
||||
CFG_BACKUP_LOC_1_S3_ACCESS_KEY= # S3 Access Key - For s3 type
|
||||
CFG_BACKUP_LOC_1_S3_SECRET_KEY= # S3 Secret Key - For s3 type
|
||||
CFG_BACKUP_LOC_1_B2_ACCOUNT_ID= # B2 Account ID - For b2 type
|
||||
CFG_BACKUP_LOC_1_B2_ACCOUNT_KEY= # B2 Account Key - For b2 type
|
||||
CFG_BACKUP_LOC_1_APPEND_ONLY=false # Append-only - Refuse forget/prune for this location (ransomware-safe) **ADVANCED**
|
||||
CFG_BACKUP_LOC_1_APPEND_ONLY=false # Append-only - Refuse forget/prune for this location (ransomware-safe)
|
||||
CFG_BACKUP_LOC_1_CUSTOM_RETENTION=false # Custom Retention - Override the global retention for this location
|
||||
CFG_BACKUP_LOC_1_KEEP_LAST= # Keep Last - Snapshots to always retain (blank = global)
|
||||
CFG_BACKUP_LOC_1_KEEP_DAILY= # Keep Daily - Days (blank = global)
|
||||
|
||||
5
configs/features/.category
Executable file
5
configs/features/.category
Executable file
@ -0,0 +1,5 @@
|
||||
TITLE=Features
|
||||
DESCRIPTION=Toggle system components and features
|
||||
ICON=features
|
||||
ORDER=5
|
||||
SUBCATEGORY_ORDER=features_core,features_security,features_terminal
|
||||
18
configs/features/features_core
Executable file
18
configs/features/features_core
Executable file
@ -0,0 +1,18 @@
|
||||
# ================================================================================
|
||||
# Core Features - Essential LibrePortal functionality and core services
|
||||
# ================================================================================
|
||||
CFG_REQUIREMENT_CONFIG=true # Configuration Management - Enable configuration management system for LibrePortal settings
|
||||
CFG_REQUIREMENT_COMMAND=true # Command Line Tool - Install the libreportal command line tool for system management
|
||||
CFG_REQUIREMENT_WEBUI=true # Web Interface - Install and manage the LibrePortal web based management interface
|
||||
CFG_REQUIREMENT_WEBUI_SERVICE=true # Web Task Service - Install the task management systemd service for the web interface
|
||||
CFG_REQUIREMENT_DATABASE=true # Database Support - Install and configure database support for application data storage
|
||||
CFG_REQUIREMENT_PASSWORDS=true # Password Management - Enable password generation and management features
|
||||
CFG_REQUIREMENT_DOCKER_CE=true # Docker CE - Install Docker Community Edition instead of the default Docker version
|
||||
CFG_REQUIREMENT_DOCKER_COMPOSE=true # Docker Compose - Install Docker Compose for multi container application management
|
||||
CFG_REQUIREMENT_DOCKER_NETWORK=true # Docker Network - Create and manage Docker network for container communication
|
||||
CFG_REQUIREMENT_UFW=true # Firewall Protection - Install and configure the Uncomplicated Firewall for system security
|
||||
CFG_REQUIREMENT_UFWD=true # Docker Firewall - Install UFW Docker for container aware firewall management which is rooted Docker specific
|
||||
CFG_REQUIREMENT_SSLCERTS=true # SSL Certificates - Generate and manage SSL certificates for secure HTTPS connections
|
||||
CFG_REQUIREMENT_CRONTAB=true # Scheduled Tasks - Setup scheduled tasks and automated maintenance jobs
|
||||
CFG_REQUIREMENT_WHITELIST_PORT_UPDATER=true # Auto Port Management - Automatically update port whitelist when applications are installed or removed
|
||||
CFG_REQUIREMENT_BCRYPT_SAVE=true # Password Encryption - Encrypt saved passwords using bcrypt for enhanced security
|
||||
7
configs/features/features_security
Executable file
7
configs/features/features_security
Executable file
@ -0,0 +1,7 @@
|
||||
# ================================================================================
|
||||
# Security and Authentication - SSH access and security configuration
|
||||
# ================================================================================
|
||||
CFG_REQUIREMENT_SSHKEY_DOWNLOADER=false # SSH Key Downloader - Enable SSH key download functionality for remote access
|
||||
CFG_REQUIREMENT_SSH_DISABLE_PASSWORDS=false # SSH Password Disable - Disable password authentication for SSH requiring key based access only
|
||||
CFG_REQUIREMENT_GLUETUN_FOR_ALL=false # Gluetun For All Apps - Allow routing through Gluetun VPN for every app (default: only curated categories)
|
||||
|
||||
12
configs/features/features_terminal
Executable file
12
configs/features/features_terminal
Executable file
@ -0,0 +1,12 @@
|
||||
# ================================================================================
|
||||
# Terminal Only - Advanced terminal based features and utilities **ADVANCED**
|
||||
# ================================================================================
|
||||
CFG_REQUIREMENT_SUGGEST_INSTALLS=false # Install Suggestions - Enable application suggestions and recommendations during installation
|
||||
CFG_REQUIREMENT_SUGGEST_METRICS=true # Metrics Suggestions - Offer Prometheus and Grafana during first install (requires Install Suggestions enabled)
|
||||
CFG_REQUIREMENT_CONTINUE_PROMPT=false # Continue Prompts - Show continue prompts during installation for user confirmation
|
||||
CFG_REQUIREMENT_CONFIGS_CHECK=true # Config Validation - Validate configuration files on startup for errors and consistency
|
||||
CFG_REQUIREMENT_CONFIGS_AUTO_UPDATE=true # Auto Config Updates - Add new config options from the template (non-interactive)
|
||||
CFG_REQUIREMENT_CONFIGS_AUTO_DELETE=true # Auto Config Deletes - Remove config options no longer present in the template
|
||||
CFG_REQUIREMENT_MISSING_IPS=false # IP Configuration Check - Check for and alert about missing IP configurations
|
||||
CFG_REQUIREMENT_DOCKER_NETWORK_PRUNE=true # Docker Network Cleanup - Enable automatic cleanup of unused Docker networks
|
||||
CFG_REQUIREMENT_DOCKER_SWITCHER=true # Docker Switcher - Install Docker version switching utility for managing multiple Docker versions
|
||||
@ -2,4 +2,4 @@ TITLE=General
|
||||
DESCRIPTION=Basic system settings and identification
|
||||
ICON=general
|
||||
ORDER=1
|
||||
SUBCATEGORY_ORDER=general_basic,general_mail,general_install,general_core,general_docker_install,general_terminal,general_catalogs,general_libreportal
|
||||
SUBCATEGORY_ORDER=general_basic,general_mail,general_install,general_docker_install,general_terminal,general_libreportal
|
||||
|
||||
@ -1,8 +1,5 @@
|
||||
# ================================================================================
|
||||
# General - Basic system settings and identification
|
||||
# @icon 🏷️
|
||||
# ================================================================================
|
||||
CFG_INSTALL_NAME=Change-Me # Installation Name - The name for your LibrePortal instance
|
||||
CFG_TIMEZONE=Etc/UTC # Container Timezone - Timezone handed to app containers; scheduled tasks follow the host clock
|
||||
CFG_INSTALL_LEVEL=beginner # Experience Level - How much technical detail the WebUI shows [beginner:Beginner — simple|advanced:Advanced — show everything]
|
||||
CFG_STORAGE_DEFAULT=primary # Default App Storage - Where a newly installed app keeps its data, unless that app says otherwise. Change an individual app from its own config, or with `libreportal app move` [primary:System disk]
|
||||
CFG_TIMEZONE=Etc/UTC # System Timezone - Timezone for scheduled tasks and logging timestamps
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
# ================================================================================
|
||||
# App Catalogs - Extra app-catalog sources the App Center browses
|
||||
# @icon 🗂️
|
||||
# ================================================================================
|
||||
# Each slot is a catalog base URL served with a signed /<channel>/index.json
|
||||
# (channel defaults to stable; append |<channel> for another). Sources are tried
|
||||
# in priority order 1..9. The official catalog (from the Release Host) is always
|
||||
# source #1 and is NOT listed here. Third-party catalogs are UNVERIFIED — you are
|
||||
# trusting that host. Edit these from the App Center's Catalog Sources block.
|
||||
CFG_CATALOG_OFFICIAL_ENABLED=true # Official Catalog - Include the official LibrePortal catalog (source #1) in the App Center [true:On|false:Off] **ADVANCED**
|
||||
CFG_CATALOG_1= # Catalog 1 - Extra catalog base URL, e.g. https://catalog.example.org
|
||||
CFG_CATALOG_2= # Catalog 2 - Extra catalog base URL
|
||||
CFG_CATALOG_3= # Catalog 3 - Extra catalog base URL
|
||||
CFG_CATALOG_4= # Catalog 4 - Extra catalog base URL
|
||||
CFG_CATALOG_5= # Catalog 5 - Extra catalog base URL
|
||||
CFG_CATALOG_6= # Catalog 6 - Extra catalog base URL
|
||||
CFG_CATALOG_7= # Catalog 7 - Extra catalog base URL
|
||||
CFG_CATALOG_8= # Catalog 8 - Extra catalog base URL
|
||||
CFG_CATALOG_9= # Catalog 9 - Extra catalog base URL
|
||||
@ -1,12 +0,0 @@
|
||||
# ================================================================================
|
||||
# Core Components - Core LibrePortal components installed at setup time **ADVANCED**
|
||||
# @icon 🧩
|
||||
# ================================================================================
|
||||
CFG_REQUIREMENT_CONFIG=true # Configuration Management - Install the configuration management system. Disabling this on an existing install will brick the system. **ADVANCED** **DEV**
|
||||
CFG_REQUIREMENT_COMMAND=true # Command Line Tool - Install the libreportal command line tool. Disabling this on an existing install will brick the system. **ADVANCED** **DEV**
|
||||
CFG_REQUIREMENT_WEBUI=true # Web Interface - Install the LibrePortal WebUI. Disabling this on an existing install will brick the system. **ADVANCED** **DEV**
|
||||
CFG_REQUIREMENT_WEBUI_SERVICE=true # Web Task Service - Install the task-processor systemd service that backs the WebUI. Disabling this on an existing install will brick the system. **ADVANCED** **DEV**
|
||||
CFG_REQUIREMENT_DATABASE=true # Database Support - Install database support for application data. Install-time choice only. **ADVANCED** **DEV**
|
||||
CFG_REQUIREMENT_PASSWORDS=true # Password Management - Enable password generation and management features. Install-time choice only. **ADVANCED** **DEV**
|
||||
CFG_REQUIREMENT_DOCKER_CE=true # Docker CE - Install Docker Community Edition instead of the distro default. Install-time choice only. **ADVANCED** **DEV**
|
||||
CFG_REQUIREMENT_DOCKER_COMPOSE=true # Docker Compose - Install Docker Compose for multi-container application management. Install-time choice only. **ADVANCED** **DEV**
|
||||
@ -1,8 +1,7 @@
|
||||
# ================================================================================
|
||||
# Docker - Container runtime installation and configuration **ADVANCED**
|
||||
# @icon 🐳
|
||||
# ================================================================================
|
||||
CFG_DOCKER_INSTALL_TYPE=rootless # Docker Installation Type - Rootless runs containers unprivileged (recommended); rooted runs them as root [rootless|rooted]
|
||||
CFG_DOCKER_INSTALL_TYPE=rooted # Docker Installation Type - Security based setup rooted or rootless Docker installation [rooted|rootless]
|
||||
CFG_DOCKER_INSTALL_USER=dockerinstall # Docker Install User - Username for Docker installation operations
|
||||
CFG_DOCKER_INSTALL_PASS=RANDOMIZEDPASSWORD2 # Docker Install Password - Password for Docker install user
|
||||
|
||||
|
||||
@ -1,13 +1,9 @@
|
||||
# ================================================================================
|
||||
# Installation Setup - Local or Git Repository configuration and version control
|
||||
# @icon 📦
|
||||
# ================================================================================
|
||||
CFG_INSTALL_MODE=release # Installation Mode - How LibrePortal is fetched and updated **DEV** [release:Release - Stable|git:Git clone|local:Local folder]
|
||||
CFG_RELEASE_BASE_URL=https://get.libreportal.org # Release Host - Base URL serving the release channels (override for self-hosting) **ADVANCED**
|
||||
CFG_RELEASE_CHANNEL=stable # Release Channel - Release channel for the tarball installer **DEV** [stable:Release - Stable|edge:Release - Bleeding Edge]
|
||||
CFG_DEV_MODE=false # Developer Mode - Reveal developer / dev-install options across the WebUI **ADVANCED** [true:On|false:Off]
|
||||
CFG_INSTALL_MODE=local # Installation Mode - Method used for installation of LibrePortal
|
||||
CFG_GIT_URL=changeme # Git Repository URL - Git repository URL for LibrePortal configuration
|
||||
CFG_GIT_USER=changeme # Git Username - Git username for repository authentication
|
||||
CFG_GIT_KEY=changeme # Git Access Key - SSH key or API key for Git repository access
|
||||
CFG_GIT_UPDATES=true # Auto Check Updates - Check for updates automatically
|
||||
CFG_GIT_AUTO_UPDATES=true # Auto Apply Updates - Automatically apply updates when available
|
||||
CFG_GIT_UPDATES=true # Auto Check Updates - Check for Git repository updates automatically
|
||||
CFG_GIT_AUTO_UPDATES=true # Auto Apply Updates - Automatically apply Git updates when available
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
# ================================================================================
|
||||
# LibrePortal - Specific LibrePortal configurations **ADVANCED**
|
||||
# @icon 🪐
|
||||
# ================================================================================
|
||||
CFG_LIBREPORTAL_USER_PASS=changeme # LibrePortal User Password - Password for the LibrePortal system user account
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
# ================================================================================
|
||||
# Mail - Mail Server Settings
|
||||
# @icon ✉️
|
||||
# ================================================================================
|
||||
CFG_MAIL_ENABLED=false # Mail Enabled - Enable mail server configuration for applications
|
||||
CFG_MAIL_HOST=mail.domain.com # Mail Server Host - Your mail server hostname
|
||||
|
||||
@ -1,24 +0,0 @@
|
||||
# ================================================================================
|
||||
# Notifications - Alerts when background work fails (updates, backups, installs)
|
||||
# @icon 🔔
|
||||
# ================================================================================
|
||||
CFG_NOTIFY_EVENTS=failures # What to send - Which task results send a notification [failures:Failures only|all:All task results|off:Off]
|
||||
CFG_NOTIFY_EMAIL=false # 📧 Email - Send alerts by email (uses the Mail settings above)
|
||||
CFG_NOTIFY_EMAIL_TO= # Email To - Recipient address for alert emails
|
||||
CFG_NOTIFY_NTFY=false # 🔔 Ntfy - Send alerts to an ntfy topic (self-hosted or ntfy.sh)
|
||||
CFG_NOTIFY_NTFY_URL=https://ntfy.sh # Ntfy Server URL - Base URL of the ntfy server
|
||||
CFG_NOTIFY_NTFY_TOPIC= # Ntfy Topic - Topic name to publish alerts to
|
||||
CFG_NOTIFY_NTFY_TOKEN= # Ntfy Access Token - Optional bearer token for protected topics
|
||||
CFG_NOTIFY_GOTIFY=false # 🔔 Gotify - Send alerts to a self-hosted Gotify server
|
||||
CFG_NOTIFY_GOTIFY_URL= # Gotify Server URL - e.g. https://gotify.example.com
|
||||
CFG_NOTIFY_GOTIFY_TOKEN= # Gotify App Token - Application token from the Gotify UI
|
||||
CFG_NOTIFY_DISCORD=false # 💬 Discord - Send alerts to a Discord channel via webhook
|
||||
CFG_NOTIFY_DISCORD_WEBHOOK= # Discord Webhook URL - https://discord.com/api/webhooks/...
|
||||
CFG_NOTIFY_SLACK=false # 💬 Slack - Send alerts to a Slack channel via incoming webhook
|
||||
CFG_NOTIFY_SLACK_WEBHOOK= # Slack Webhook URL - https://hooks.slack.com/services/...
|
||||
CFG_NOTIFY_TELEGRAM=false # 📱 Telegram - Send alerts via a Telegram bot
|
||||
CFG_NOTIFY_TELEGRAM_TOKEN= # Telegram Bot Token - From @BotFather, e.g. 123456:ABC-DEF...
|
||||
CFG_NOTIFY_TELEGRAM_CHAT_ID= # Telegram Chat ID - The chat/channel the bot posts to
|
||||
CFG_NOTIFY_PUSHOVER=false # 🔔 Pushover - Send alerts via Pushover
|
||||
CFG_NOTIFY_PUSHOVER_TOKEN= # Pushover App Token - Application API token
|
||||
CFG_NOTIFY_PUSHOVER_USER= # Pushover User Key - Your user (or group) key
|
||||
@ -1,6 +1,5 @@
|
||||
# ================================================================================
|
||||
# Terminal - System utilities and advanced settings **ADVANCED**
|
||||
# @icon 🖥️
|
||||
# ================================================================================
|
||||
CFG_UPDATER_CHECK=60 # Update Check Interval - Hours between system update checks
|
||||
CFG_SWAPFILE_SIZE=2G # Swap File Size - Size of swap file for memory management
|
||||
@ -8,12 +7,3 @@ CFG_GENERATED_PASS_LENGTH=14 # Password Length - Len
|
||||
CFG_GENERATED_USER_LENGTH=8 # Username Length - Length for auto generated usernames
|
||||
CFG_UFW_LOGGING=off # Firewall Logging - UFW firewall logging level [off|low|medium|high|full]
|
||||
CFG_TEXT_EDITOR=nano # Text Editor - Default text editor for system operations [nano|vim]
|
||||
CFG_REQUIREMENT_CRONTAB=true # Scheduled Tasks - Install scheduled tasks and automated maintenance jobs
|
||||
CFG_REQUIREMENT_CONFIGS_CHECK=true # Config Validation - Validate configuration files on startup for errors and consistency
|
||||
CFG_REQUIREMENT_CONFIGS_AUTO_UPDATE=true # Auto Config Updates - Add new config options from the template (non-interactive)
|
||||
CFG_REQUIREMENT_CONFIGS_AUTO_DELETE=true # Auto Config Deletes - Remove config options no longer present in the template
|
||||
CFG_REQUIREMENT_MISSING_IPS=false # IP Configuration Check - Check for and alert about missing IP configurations
|
||||
CFG_REQUIREMENT_CONTINUE_PROMPT=false # Continue Prompts - Show continue prompts during installation for user confirmation
|
||||
CFG_REQUIREMENT_CONTINUE_ON_ERROR=true # Continue On Error - Log failures to error_report.log and continue instead of aborting
|
||||
CFG_REQUIREMENT_SUGGEST_INSTALLS=false # Install Suggestions - Enable application suggestions and recommendations during installation
|
||||
CFG_REQUIREMENT_SUGGEST_METRICS=true # Metrics Suggestions - Offer Prometheus and Grafana during first install (requires Install Suggestions enabled)
|
||||
|
||||
@ -2,4 +2,4 @@ TITLE=Network
|
||||
DESCRIPTION=Network configuration and domain management
|
||||
ICON=network
|
||||
ORDER=4
|
||||
SUBCATEGORY_ORDER=network_domains,network_whitelist,network_firewall,network_dns,network_docker,network_rootless,network_ports,network_headscale
|
||||
SUBCATEGORY_ORDER=network_domains,network_whitelist,network_dns,network_docker,network_ports,network_headscale
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
# ================================================================================
|
||||
# DNS - Dynamic Name Server Addresses
|
||||
# @icon 🌐
|
||||
# ================================================================================
|
||||
|
||||
CFG_DNS_SERVER_1=9.9.9.9 # Primary DNS - Primary DNS server for network resolution
|
||||
CFG_DNS_SERVER_2=9.9.9.11 # Secondary DNS - Secondary DNS server for network resolution
|
||||
|
||||
CFG_REQUIREMENT_DNS_UPDATER=false # DNS Updater - Use AdGuard or Pi-hole as this server's DNS resolver (rewrites /etc/resolv.conf)
|
||||
CFG_REQUIREMENT_DNS_UPDATER=false # DNS Updater - Use AdGuard or Pi-hole as this server's DNS resolver when installed (rewrites /etc/resolv.conf). Off by default.
|
||||
|
||||
@ -1,11 +1,7 @@
|
||||
# ================================================================================
|
||||
# Docker Network - Network settings for the Docker Network **ADVANCED**
|
||||
# @icon 🕸️
|
||||
# ================================================================================
|
||||
|
||||
CFG_NETWORK_NAME=vpn # Network Name - Docker network name for container communication
|
||||
CFG_NETWORK_SUBNET=10.100.0.0/16 # Network Subnet - Subnet range for Docker network
|
||||
CFG_NETWORK_MTU=auto # Network MTU - Maximum transmission unit for network packets; 'auto' probes the path MTU **ADVANCED**
|
||||
CFG_REQUIREMENT_DOCKER_NETWORK=true # Docker Network - Create and manage the Docker network for container communication
|
||||
CFG_REQUIREMENT_DOCKER_NETWORK_PRUNE=true # Network Cleanup - Automatically prune unused Docker networks
|
||||
CFG_REQUIREMENT_DOCKER_SWITCHER=true # Docker Switcher - Install the Docker version switching utility
|
||||
CFG_NETWORK_MTU=1500 # Network MTU - Maximum transmission unit for network packets
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
# ================================================================================
|
||||
# Domains - Domain configuration for Traefik web services
|
||||
# @icon 🌍
|
||||
# ================================================================================
|
||||
CFG_DOMAIN_1= # Domain 1 - Domain slot 1 for a Traefik
|
||||
CFG_DOMAIN_2= # Domain 2 - Domain slot 2 for a Traefik
|
||||
@ -11,4 +10,3 @@ CFG_DOMAIN_6= # Domain 6 - Domain slo
|
||||
CFG_DOMAIN_7= # Domain 7 - Domain slot 7 for a Traefik
|
||||
CFG_DOMAIN_8= # Domain 8 - Domain slot 8 for a Traefik
|
||||
CFG_DOMAIN_9= # Domain 9 - Domain slot 9 for a Traefik
|
||||
CFG_REQUIREMENT_SSLCERTS=true # SSL Certificates - Generate and manage SSL certificates for secure HTTPS connections **ADVANCED**
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
# ================================================================================
|
||||
# Firewall - Host firewall and port-whitelist automation **ADVANCED**
|
||||
# @icon 🧱
|
||||
# ================================================================================
|
||||
CFG_REQUIREMENT_UFW=true # Firewall Protection - Install and configure the Uncomplicated Firewall (UFW) for system security
|
||||
CFG_REQUIREMENT_UFWD=true # Docker Firewall - Install UFW-Docker for container-aware firewall management (rooted Docker only)
|
||||
CFG_REQUIREMENT_WHITELIST_PORT_UPDATER=true # Auto Port Whitelisting - Update the port whitelist automatically when applications are installed or removed
|
||||
@ -1,6 +1,5 @@
|
||||
# ================================================================================
|
||||
# Headscale - VPN service configuration **ADVANCED**
|
||||
# @icon 🛰️
|
||||
# ================================================================================
|
||||
CFG_HEADSCALE_HOST= # Headscale Host - Headscale server hostname for VPN services
|
||||
CFG_HEADSCALE_KEY= # Headscale Key - Authentication key for Headscale server
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
# ================================================================================
|
||||
# Ports - Settings for the Network Ports **ADVANCED**
|
||||
# @icon 🔌
|
||||
# ================================================================================
|
||||
|
||||
CFG_PORT_RANGE=3000-9999 # Port allocation range - Range for port allocation Start to End
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
# ================================================================================
|
||||
# Rootless Networking - Network stack and behaviour for rootless Docker **ADVANCED**
|
||||
# @icon 🔀
|
||||
# ================================================================================
|
||||
|
||||
CFG_ROOTLESS_NET=pasta # Rootless Network Driver - Network stack for rootless Docker; the port driver follows automatically **ADVANCED** [pasta:Pasta (default, actively maintained)|slirp4netns:slirp4netns (legacy fallback)]
|
||||
@ -1,5 +1,4 @@
|
||||
# ================================================================================
|
||||
# Whitelist - Allow specific IPs for Specified Treafik Apps
|
||||
# @icon ✅
|
||||
# ================================================================================
|
||||
CFG_IPS_WHITELIST=HOSTIPHERE # IP Whitelist - Comma separated list of allowed IP addresses
|
||||
|
||||
@ -2,4 +2,4 @@ TITLE=Security
|
||||
DESCRIPTION=Intrusion prevention, bouncers, and host firewall configuration
|
||||
ICON=security
|
||||
ORDER=5
|
||||
SUBCATEGORY_ORDER=security_logins,security_ssh
|
||||
SUBCATEGORY_ORDER=security_logins
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
# ================================================================================
|
||||
# Logins - User accounts and authentication credentials
|
||||
# @icon 🔑
|
||||
# ================================================================================
|
||||
CFG_TRAEFIK_USER=RANDOMIZEDUSERNAME1 # Traefik Username - Username for Traefik Admin Panel login and protected apps
|
||||
CFG_TRAEFIK_PASS=RANDOMIZEDPASSWORD2 # Traefik Password - Password for Traefik Admin Panel login and protected apps
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
# ================================================================================
|
||||
# SSH & Access Hardening - Secure remote access and auth-storage toggles **ADVANCED**
|
||||
# @icon 🔐
|
||||
# ================================================================================
|
||||
CFG_REQUIREMENT_SSHKEY_DOWNLOADER=false # SSH Key Downloader - Enable SSH key download functionality for remote access
|
||||
CFG_REQUIREMENT_SSH_DISABLE_PASSWORDS=false # Disable SSH Passwords - Disable password authentication for SSH (requires key-based access only)
|
||||
CFG_REQUIREMENT_BCRYPT_SAVE=true # Password Encryption - Encrypt saved passwords using bcrypt for enhanced security
|
||||
CFG_REQUIREMENT_GLUETUN_FOR_ALL=false # Gluetun For All Apps - Allow routing through Gluetun VPN for every app (default: only curated categories)
|
||||
@ -2,4 +2,4 @@ TITLE=WebUI
|
||||
DESCRIPTION=Web interface settings and preferences
|
||||
ICON=webui
|
||||
ORDER=2
|
||||
SUBCATEGORY_ORDER=webui_logins,webui_logs,webui_updater
|
||||
SUBCATEGORY_ORDER=webui_logins,webui_logs
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
# ================================================================================
|
||||
# WebUI Logins - Web interface authentication settings
|
||||
# @icon 🪟
|
||||
# ================================================================================
|
||||
CFG_WEBUI_USERNAME=RANDOMIZEDUSERNAME1 # WebUI Username - Username for web interface login
|
||||
CFG_WEBUI_PASSWORD=RANDOMIZEDPASSWORD1 # WebUI Password - Password for web interface login
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
# ================================================================================
|
||||
# WebUI Logs - Log-streaming behaviour for the Services tab **ADVANCED**
|
||||
# @icon 📜
|
||||
# ================================================================================
|
||||
CFG_WEBUI_LOG_STREAM_IDLE_TIMEOUT_MINUTES=10 # Idle Timeout - Disconnect a log stream after this much silence. 0 disables.
|
||||
CFG_WEBUI_LOG_STREAM_IDLE_TIMEOUT_MINUTES=10 # Idle Timeout - Disconnect a log stream after this much silence. The viewer overlays a Resume button so the user can re-open the stream. 0 disables.
|
||||
CFG_WEBUI_LOG_STREAM_MAX_DURATION_MINUTES=60 # Max Duration - Hard cap on a single stream. Resume button appears at the cap. 0 disables (not recommended).
|
||||
CFG_WEBUI_LOG_STREAM_MAX_LINES_PER_SEC=200 # Max Lines per Second - Burst ceiling. Excess lines drop with a per-second notice.
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
# ================================================================================
|
||||
# WebUI Updater - Automatic app update, CVE & improvement scanning **ADVANCED**
|
||||
# @icon 🔄
|
||||
# ================================================================================
|
||||
CFG_UPDATER_SCAN_INTERVAL=30 # App Scan Interval - Minutes between automatic app update/CVE/improvement scans. 0 disables.
|
||||
CFG_UPDATER_REGISTRY_INTERVAL=360 # Registry Check Interval - Minutes between registry lookups for new image builds. 0 = only on Check now.
|
||||
CFG_HOTFIX_AUTO=security-breakage # Hotfix Auto-Apply - Which signed hotfix severities apply automatically on the update check [security-breakage|all|off]
|
||||
CFG_UPDATER_AUTO=true # Automatic App Updates - Master switch for per-app automatic updates; off makes every app manual [true:On|false:Off]
|
||||
CFG_UPDATER_LADDER_AUTO=true # Automatic Version Steps - Whether automatic updates may also move an app to the NEXT published version, not just rebuild the one it is on. One step per app per day, each snapshotted and verified before the next is considered, so an app is never more than one version from a state that worked. Off keeps version moves to the Upgrade button. [true:On|false:Off]
|
||||
CFG_UPDATER_STALE_DAYS=365 # Unmaintained Warning After - Days without an upstream rebuild before an app is flagged as possibly unmaintained. 0 disables.
|
||||
CFG_UPDATER_UPGRADE_PRUNE=true # Clean Up After Version Upgrades - After a stepped upgrade, delete the intermediate images it stepped through [true:On|false:Off]
|
||||
CFG_UPDATER_WINDOW=06:00-08:00 # Automatic Update Window - When automatic updates may install, in host local time (HH:MM-HH:MM, or 'always')
|
||||
@ -5,7 +5,6 @@
|
||||
# APP_NAME = name of application for use in scripts
|
||||
# COMPOSE_FILE = default for no app_name in docker-compose file name, app if there is
|
||||
# BACKUP = if true, include this application in backup operations
|
||||
# UPDATE_TYPE = auto: new image builds are applied automatically (a recovery snapshot is taken first), manual: only when you press Update
|
||||
# HEALTHCHECK = if true, default docker health checks for that container will be enabled
|
||||
# AUTHELIA = if true, use Authelia authentication, if false turned off.
|
||||
# HEADSCALE = options : false, local, remote (see general config). e.g false or local,remote
|
||||
@ -14,23 +13,13 @@
|
||||
# MONITORING = if true, export this app's metrics to Prometheus + Grafana (needs both apps installed)
|
||||
#
|
||||
CFG_ADGUARD_APP_NAME=adguard
|
||||
# MULTI_INSTANCE = if true, this app can run as multiple isolated instances
|
||||
# (own data/DB/subdomain/backups) via `libreportal instance create`. Only set on
|
||||
# apps whose compose identity (container_name, Traefik routers, backup labels)
|
||||
# is instance-safe — see scripts/instance/instance_create.sh.
|
||||
# Not instanced by design. A DNS server is what clients point at; a second copy on a different port resolves for nobody. One-per-host in practice, like pihole and unbound.
|
||||
CFG_ADGUARD_MULTI_INSTANCE=false
|
||||
CFG_ADGUARD_BACKUP=true
|
||||
# STORAGE = which storage location holds this app's data.
|
||||
CFG_ADGUARD_STORAGE=default # Storage Location - Fixed: other apps reference adguard by path **READONLY**
|
||||
CFG_ADGUARD_BACKUP_STRATEGY=auto
|
||||
CFG_ADGUARD_UPDATE_TYPE=auto
|
||||
CFG_ADGUARD_COMPOSE_FILE=default
|
||||
CFG_ADGUARD_HEALTHCHECK=true
|
||||
CFG_ADGUARD_AUTHELIA=false
|
||||
CFG_ADGUARD_HEADSCALE=false
|
||||
CFG_ADGUARD_USER=admin
|
||||
CFG_ADGUARD_PASSWORD_1=RANDOMIZEDPASSWORD1
|
||||
CFG_ADGUARD_PASSWORD=RANDOMIZEDPASSWORD1
|
||||
CFG_ADGUARD_MONITORING=false
|
||||
#
|
||||
# =============================================================================
|
||||
@ -46,7 +35,7 @@ CFG_ADGUARD_MONITORING=false
|
||||
CFG_ADGUARD_CATEGORY="networking"
|
||||
CFG_ADGUARD_TITLE="AdGuard"
|
||||
CFG_ADGUARD_DESCRIPTION="DNS based Ad Blocking"
|
||||
CFG_ADGUARD_LONG_DESCRIPTION="A network-wide ad and tracker blocker that protects every device on your network, with no software to install on them"
|
||||
CFG_ADGUARD_LONG_DESCRIPTION="AdGuard Home is a network-wide software for advertisements and tracking blocking that operates as a DNS server and returns the IP address of a local, blackhole DNS server for domains that should be blocked"
|
||||
CFG_ADGUARD_URL="https://github.com/AdguardTeam/AdGuardHome"
|
||||
CFG_ADGUARD_ACTIONS="configure|install|restart|shutdown|uninstall"
|
||||
#
|
||||
@ -82,5 +71,7 @@ CFG_ADGUARD_PORT_5="adguard-service|dot|random:853|disabled|tcp|false|false|fals
|
||||
CFG_ADGUARD_PORT_6="adguard-exporter|metrics|9617:9617|disabled|tcp|false|false|false|Metrics Exporter (sidecar, docker-network only)|"
|
||||
|
||||
|
||||
# AUTH_PROFILE = capability tier for the WebUI auth tools (single_password | user_password | multi_user)
|
||||
CFG_ADGUARD_AUTH_PROFILE=single_password
|
||||
CFG_ADGUARD_ADMIN_USER=
|
||||
CFG_ADGUARD_ADMIN_PASSWORD_1=RANDOMIZEDPASSWORD2
|
||||
CFG_ADGUARD_ADMIN_PASSWORD=RANDOMIZEDPASSWORD2
|
||||
|
||||
276
containers/adguard/adguard.sh
Normal file
276
containers/adguard/adguard.sh
Normal file
@ -0,0 +1,276 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Category : Networking
|
||||
# Description : AdGuard - DNS based Ad Blocking (c/u/s/r/i):
|
||||
|
||||
installAdguard()
|
||||
{
|
||||
local config_variables="$1"
|
||||
|
||||
if [[ "$adguard" == *[cCtTuUsSrRiI]* ]]; then
|
||||
dockerConfigSetupToContainer silent adguard;
|
||||
local app_name=$CFG_ADGUARD_APP_NAME
|
||||
initializeAppVariables $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$adguard" == *[cC]* ]]; then
|
||||
editAppConfig $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$adguard" == *[uU]* ]]; then
|
||||
dockerUninstallApp $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$adguard" == *[sS]* ]]; then
|
||||
dockerComposeDown $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$adguard" == *[rR]* ]]; then
|
||||
dockerComposeRestart $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$adguard" == *[iI]* ]]; then
|
||||
isHeader "Install $app_name"
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Setting up install folder and config file for $app_name."
|
||||
echo ""
|
||||
|
||||
dockerConfigSetupToContainer "loud" "$app_name" "install" "$config_variables";
|
||||
isSuccessful "Install folders and Config files have been setup for $app_name."
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Setting up the $app_name docker-compose.yml file."
|
||||
echo ""
|
||||
|
||||
dockerComposeSetupFile $app_name;
|
||||
|
||||
monitoringToggleAppConfig "$app_name" "docker-compose.yml";
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Updating file permissions before starting."
|
||||
echo ""
|
||||
|
||||
fixPermissionsBeforeStart $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running the docker-compose.yml to install and start $app_name"
|
||||
echo ""
|
||||
|
||||
dockerComposeUpdateAndStartApp $app_name install;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Completing AdGuardHome initial setup automatically"
|
||||
echo ""
|
||||
|
||||
# The legacy `$usedport1` variable is no longer populated by the
|
||||
# current install pipeline; the resolved host port is stored in the
|
||||
# PORTS_TAG_1 docker-compose tag (format `external:internal`). Pull
|
||||
# it from there so the curl + URL printout actually point somewhere.
|
||||
local adguard_compose_file="$containers_dir$app_name/docker-compose.yml"
|
||||
local adguard_port_pair
|
||||
adguard_port_pair=$(tagsManagerGetTagContent "$adguard_compose_file" "PORTS_TAG_1")
|
||||
local adguard_admin_port="${adguard_port_pair%%:*}"
|
||||
|
||||
if [[ -n "$public_ip_v4" && -n "$adguard_admin_port" ]]; then
|
||||
echo " External : http://$public_ip_v4:$adguard_admin_port/"
|
||||
fi
|
||||
if [[ -n "$host_setup" ]]; then
|
||||
echo " Hostname : http://$host_setup/"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# AdGuardHome ships a setup wizard that normally needs five clicks in a
|
||||
# browser before the daemon writes its config file. Same wizard is
|
||||
# exposed as an HTTP API (POST /control/install/configure), so we
|
||||
# drive it from here and skip the manual interaction. We pre-poll the
|
||||
# admin endpoint until the container is up, then send the form, then
|
||||
# let the existing post-install sed edits run against the freshly
|
||||
# written AdGuardHome.yaml.
|
||||
local adguard_setup_url="http://127.0.0.1:${adguard_admin_port}"
|
||||
local adguard_attempts=0
|
||||
local adguard_max_attempts=60
|
||||
while ((adguard_attempts < adguard_max_attempts)); do
|
||||
if curl -fsS -o /dev/null --max-time 2 "${adguard_setup_url}/control/status" 2>/dev/null \
|
||||
|| curl -fsS -o /dev/null --max-time 2 "${adguard_setup_url}/control/install/get_addresses" 2>/dev/null; then
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
((adguard_attempts++))
|
||||
done
|
||||
|
||||
if ((adguard_attempts >= adguard_max_attempts)); then
|
||||
isError "AdGuardHome admin endpoint did not respond on $adguard_setup_url within $((adguard_max_attempts * 2))s — open the URL and complete setup manually, then re-run the installer to apply the post-setup tweaks."
|
||||
else
|
||||
local adguard_user="${CFG_ADGUARD_USER:-admin}"
|
||||
local adguard_pass="${CFG_ADGUARD_PASSWORD:-}"
|
||||
if [[ -z "$adguard_pass" ]]; then
|
||||
adguard_pass=$(generateRandomPassword)
|
||||
updateConfigOption "CFG_ADGUARD_PASSWORD" "$adguard_pass" >/dev/null 2>&1 || true
|
||||
isNotice "Generated a random AdGuardHome admin password and saved it to CFG_ADGUARD_PASSWORD."
|
||||
fi
|
||||
|
||||
# Internal container ports are fixed (3000 admin, 53 DNS); host
|
||||
# mapping is what `usedport1` etc. handle.
|
||||
local adguard_payload
|
||||
adguard_payload=$(cat <<JSON
|
||||
{
|
||||
"web": { "ip": "0.0.0.0", "port": 3000, "autofix": false },
|
||||
"dns": { "ip": "0.0.0.0", "port": 53, "autofix": false },
|
||||
"username": "${adguard_user}",
|
||||
"password": "${adguard_pass}"
|
||||
}
|
||||
JSON
|
||||
)
|
||||
if curl -fsS -X POST \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data "$adguard_payload" \
|
||||
--max-time 15 \
|
||||
"${adguard_setup_url}/control/install/configure" >/dev/null 2>&1; then
|
||||
isSuccessful "AdGuardHome admin setup completed automatically (user: $adguard_user)."
|
||||
else
|
||||
# 422/403 here typically means setup was already done on a
|
||||
# previous install; the post-setup tweaks below are still
|
||||
# safe to run against the existing yaml.
|
||||
isNotice "AdGuardHome /control/install/configure rejected the request — assuming it's already configured. If this is a fresh install, complete setup manually at $adguard_setup_url."
|
||||
fi
|
||||
# Small breather so AdGuardHome finishes flushing AdGuardHome.yaml
|
||||
# to disk before the sed edits below touch it.
|
||||
#sleep 3
|
||||
fi
|
||||
|
||||
#result=$(sudo sed -i "s/address: 0.0.0.0:80/address: 0.0.0.0:${usedport2}/g" "$containers_dir$app_name/conf/AdGuardHome.yaml")
|
||||
#checkSuccess "Changing port 80 to $usedport2 for Admin Panel"
|
||||
|
||||
#result=$(sudo sed -i "s/port: 53/port: ${usedport3}/g" "$containers_dir$app_name/conf/AdGuardHome.yaml")
|
||||
#checkSuccess "Changing port 53 to $usedport3 for DNS Port"
|
||||
|
||||
#result=$(sudo sed -i "s/port_https: 443/port_https: ${usedport4}/g" "$containers_dir$app_name/conf/AdGuardHome.yaml")
|
||||
#checkSuccess "Changing port 443 to $usedport4 for DNS Port"
|
||||
|
||||
#result=$(sudo sed -i "s/port_dns_over_tls: 853/port_dns_over_tls: ${usedport5}/g" "$containers_dir$app_name/conf/AdGuardHome.yaml")
|
||||
#checkSuccess "Changing port 853 to $usedport5 for port_dns_over_tls"
|
||||
|
||||
#result=$(sudo sed -i "s/port_dns_over_quic: 853/port_dns_over_quic: ${usedport5}/g" "$containers_dir$app_name/conf/AdGuardHome.yaml")
|
||||
#checkSuccess "Changing port 853 to $usedport5 for port_dns_over_quic"
|
||||
|
||||
# NOTE: We deliberately do *not* force `tls.enabled: true` here.
|
||||
# That section configures encrypted DNS (DoT/DoH/DoQ) and AdGuardHome
|
||||
# crash-loops on startup with `[fatal] creating dns server: parsing
|
||||
# tls key pair: tls: failed to find any PEM data in certificate input`
|
||||
# if `enabled: true` is set without a real certificate pair pointed
|
||||
# at by `certificate_path` / `private_key_path`. The admin user can
|
||||
# opt into encrypted DNS from Settings → Encryption once they've
|
||||
# provided a cert.
|
||||
|
||||
if [[ $public == "true" ]]; then
|
||||
result=$(sudo sed -i "s|allow_unencrypted_doh: false|allow_unencrypted_doh: true|g" "$containers_dir$app_name/conf/AdGuardHome.yaml")
|
||||
checkSuccess "Setting allow_unencrypted_doh to false for Traefik"
|
||||
fi
|
||||
|
||||
result=$(sudo sed -i "s|anonymize_client_ip: false: false|anonymize_client_ip: true|g" "$containers_dir$app_name/conf/AdGuardHome.yaml")
|
||||
checkSuccess "Setting anonymize_client_ip to true for privacy reasons"
|
||||
|
||||
# Force the admin web bind back to 0.0.0.0:3000 inside the container.
|
||||
# The docker-compose mapping is `<host_port>:3000`, so the container
|
||||
# MUST listen on 3000 internally for the host port to reach it. After
|
||||
# the install API call AdGuardHome sometimes ends up bound to 0.0.0.0:80
|
||||
# (its build-time default) instead of the port we sent — which is
|
||||
# exactly what causes "unable to connect" on the host port.
|
||||
local adguard_yaml="$containers_dir$app_name/conf/AdGuardHome.yaml"
|
||||
if [[ -f "$adguard_yaml" ]]; then
|
||||
# New schema (v0.107+): single `address: 0.0.0.0:NN` line under `http:`.
|
||||
sudo sed -i 's|^\(\s*address:\s*\)0\.0\.0\.0:[0-9]\+|\10.0.0.0:3000|' "$adguard_yaml"
|
||||
# Old schema fallback: separate `bind_host:` / `bind_port:` keys.
|
||||
sudo sed -i 's|^\(\s*bind_host:\s*\).*|\10.0.0.0|' "$adguard_yaml"
|
||||
sudo sed -i 's|^\(\s*bind_port:\s*\)[0-9]\+|\13000|' "$adguard_yaml"
|
||||
checkSuccess "Pinned AdGuardHome admin bind to 0.0.0.0:3000 (matches the compose port mapping)."
|
||||
fi
|
||||
|
||||
dockerComposeRestart "$app_name";
|
||||
|
||||
# Health-check after the restart so the user finds out *here* if
|
||||
# AdGuardHome didn't come back up cleanly, rather than later when
|
||||
# they try to open the URL and just see "unable to connect".
|
||||
#
|
||||
# Drop `-f` and accept any HTTP status code: now that the admin
|
||||
# account is configured, `/control/status` returns 401 to an
|
||||
# unauthenticated request — which is fine, it means the server is
|
||||
# up and answering. We only care whether the connection succeeded
|
||||
# at all, not what the response body says. `-w '%{http_code}'`
|
||||
# gives us a 3-digit code on success and an empty string on a
|
||||
# connection failure / timeout.
|
||||
local adguard_health_attempts=0
|
||||
while ((adguard_health_attempts < 20)); do
|
||||
local adguard_health_code
|
||||
adguard_health_code=$(curl -sS -o /dev/null --max-time 2 \
|
||||
-w '%{http_code}' "${adguard_setup_url}/control/status" 2>/dev/null)
|
||||
if [[ "$adguard_health_code" =~ ^[1-5][0-9][0-9]$ ]]; then
|
||||
isSuccessful "AdGuardHome admin UI is reachable on $adguard_setup_url (HTTP $adguard_health_code)"
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
((adguard_health_attempts++))
|
||||
done
|
||||
if ((adguard_health_attempts >= 20)); then
|
||||
isError "AdGuardHome admin UI did not respond after restart on $adguard_setup_url. Check the container logs (\`docker logs adguard-service\`) and the conf/AdGuardHome.yaml bind address."
|
||||
fi
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running Application specific updates (if required)"
|
||||
echo ""
|
||||
|
||||
appUpdateSpecifics $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running Headscale setup (if required)"
|
||||
echo ""
|
||||
|
||||
setupHeadscale $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Adding $app_name to the Apps Database table."
|
||||
echo ""
|
||||
|
||||
databaseInstallApp $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Updating the WebUI config file."
|
||||
echo ""
|
||||
|
||||
webuiContainerSetup $app_name install;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Refreshing monitoring integration."
|
||||
echo ""
|
||||
|
||||
monitoringRefreshAll;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. You can find $app_name files at $containers_dir$app_name"
|
||||
echo ""
|
||||
echo " You can now navigate to your $app_name service using any of the options below : "
|
||||
echo ""
|
||||
|
||||
# Same final-summary call shape as wireguard / vaultwarden. Pass the
|
||||
# admin user/password we just configured so the user sees the
|
||||
# credentials exactly once, at the end of the install.
|
||||
menuShowFinalMessages "$app_name" "${CFG_ADGUARD_USER:-admin}" "$CFG_ADGUARD_PASSWORD";
|
||||
|
||||
menu_number=0
|
||||
#sleep 3s
|
||||
cd
|
||||
fi
|
||||
adguard=n
|
||||
}
|
||||
@ -5,7 +5,7 @@ networks:
|
||||
services:
|
||||
adguard-service: #LIBREPORTAL|SERVICE_TAG_1|adguard-service
|
||||
container_name: adguard-service
|
||||
image: adguard/adguardhome:latest #LIBREPORTAL|ADGUARD_VERSION_TAG|latest
|
||||
image: adguard/adguardhome
|
||||
restart: unless-stopped
|
||||
hostname: adguard
|
||||
# GLUETUN_OFF_BEGIN
|
||||
@ -54,7 +54,7 @@ services:
|
||||
# - ADGUARD_PROTOCOL=http
|
||||
# - ADGUARD_HOSTNAME=adguard-service:PORT_INTERNAL_DATA_1 #LIBREPORTAL|PORT_INTERNAL_TAG_1|PORT_INTERNAL_DATA_1
|
||||
# - ADGUARD_USERNAME=ADGUARD_USER_DATA #LIBREPORTAL|ADGUARD_USER_TAG|ADGUARD_USER_DATA
|
||||
# - ADGUARD_PASSWORD=ADGUARD_PASSWORD_1_DATA #LIBREPORTAL|ADGUARD_PASSWORD_1_TAG|ADGUARD_PASSWORD_1_DATA
|
||||
# - ADGUARD_PASSWORD=ADGUARD_PASSWORD_DATA #LIBREPORTAL|ADGUARD_PASSWORD_TAG|ADGUARD_PASSWORD_DATA
|
||||
# - INTERVAL=30s
|
||||
# - LOG_LIMIT=10000
|
||||
# - SERVER_PORT=PORT_INTERNAL_DATA_6 #LIBREPORTAL|PORT_INTERNAL_TAG_6|PORT_INTERNAL_DATA_6
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
authAdapter_adguard_setPassword() {
|
||||
local user="$1" password="$2"
|
||||
user="${user:-${CFG_ADGUARD_ADMIN_USER:-admin}}"
|
||||
[[ -z "$password" ]] && password=$(generateRandomPassword)
|
||||
|
||||
local yaml="$(appDir adguard)/conf/AdGuardHome.yaml"
|
||||
[[ ! -f "$yaml" ]] && { isError "AdGuardHome.yaml not found at $yaml."; return 1; }
|
||||
if ! command -v htpasswd >/dev/null 2>&1; then
|
||||
isError "htpasswd is required to bcrypt the new password."
|
||||
return 1
|
||||
fi
|
||||
|
||||
local bcrypt
|
||||
bcrypt=$(htpasswd -bnBC 10 "" "$password" | tr -d ':\n')
|
||||
[[ -z "$bcrypt" ]] && { isError "bcrypt failed."; return 1; }
|
||||
|
||||
# The yaml is owned by the in-container uid, so the rewrite runs in the
|
||||
# root-owned appcfg helper (fixed path, validated user + bcrypt).
|
||||
if ! runAppCfg adguard-auth "$user" "$bcrypt"; then
|
||||
isError "Could not update AdGuardHome.yaml (no users password line, or invalid input)."
|
||||
return 1
|
||||
fi
|
||||
|
||||
authPersistCfg adguard ADMIN_USER "$user"
|
||||
authPersistCfg adguard ADMIN_PASSWORD "$password"
|
||||
dockerComposeRestart adguard
|
||||
isSuccessful "AdGuard admin set. User: $user — Password: $password"
|
||||
}
|
||||
@ -1,142 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# AdGuard Home install hooks — drive the first-boot setup wizard via its
|
||||
# HTTP API so the admin doesn't have to click through five pages, then
|
||||
# pin the admin bind back to 0.0.0.0:3000 (matches the compose mapping)
|
||||
# and health-check the result.
|
||||
|
||||
adguard_install_post_start()
|
||||
{
|
||||
local app_name="$1"
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Completing AdGuardHome initial setup automatically"
|
||||
echo ""
|
||||
|
||||
# The legacy `$usedport1` variable isn't populated by the current
|
||||
# install pipeline; the resolved host port is stored in the PORTS_TAG_1
|
||||
# docker-compose tag (format `external:internal`). Pull it from there
|
||||
# so the curl + URL printout actually point somewhere real.
|
||||
local adguard_compose_file="$(appDir "$app_name")/docker-compose.yml"
|
||||
local adguard_port_pair
|
||||
adguard_port_pair=$(tagsManagerGetTagContent "$adguard_compose_file" "PORTS_TAG_1")
|
||||
local adguard_admin_port="${adguard_port_pair%%:*}"
|
||||
|
||||
if [[ -n "$public_ip_v4" && -n "$adguard_admin_port" ]]; then
|
||||
echo " External : http://$public_ip_v4:$adguard_admin_port/"
|
||||
fi
|
||||
if [[ -n "$host_setup" ]]; then
|
||||
echo " Hostname : http://$host_setup/"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# AdGuardHome ships a setup wizard that normally needs five clicks in
|
||||
# a browser before the daemon writes its config file. Same wizard is
|
||||
# exposed as an HTTP API (POST /control/install/configure), so drive
|
||||
# it from here and skip the manual interaction. Pre-poll the admin
|
||||
# endpoint until the container is up, then send the form, then let
|
||||
# the post-install sed edits run against the freshly written
|
||||
# AdGuardHome.yaml.
|
||||
local adguard_setup_url="http://127.0.0.1:${adguard_admin_port}"
|
||||
local adguard_attempts=0
|
||||
local adguard_max_attempts=60
|
||||
while ((adguard_attempts < adguard_max_attempts)); do
|
||||
if curl -fsS -o /dev/null --max-time 2 "${adguard_setup_url}/control/status" 2>/dev/null \
|
||||
|| curl -fsS -o /dev/null --max-time 2 "${adguard_setup_url}/control/install/get_addresses" 2>/dev/null; then
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
((adguard_attempts++))
|
||||
done
|
||||
|
||||
if ((adguard_attempts >= adguard_max_attempts)); then
|
||||
isError "AdGuardHome admin endpoint did not respond on $adguard_setup_url within $((adguard_max_attempts * 2))s — open the URL and complete setup manually, then re-run the installer to apply the post-setup tweaks."
|
||||
else
|
||||
local adguard_user="${CFG_ADGUARD_USER:-admin}"
|
||||
local adguard_pass="${CFG_ADGUARD_PASSWORD_1:-}"
|
||||
if [[ -z "$adguard_pass" ]]; then
|
||||
adguard_pass=$(generateRandomPassword)
|
||||
updateConfigOption "CFG_ADGUARD_PASSWORD_1" "$adguard_pass" >/dev/null 2>&1 || true
|
||||
isNotice "Generated a random AdGuardHome admin password and saved it to CFG_ADGUARD_PASSWORD_1."
|
||||
fi
|
||||
|
||||
# Internal container ports are fixed (3000 admin, 53 DNS); host
|
||||
# mapping is what `usedport1` etc. handle.
|
||||
local adguard_payload
|
||||
adguard_payload=$(cat <<JSON
|
||||
{
|
||||
"web": { "ip": "0.0.0.0", "port": 3000, "autofix": false },
|
||||
"dns": { "ip": "0.0.0.0", "port": 53, "autofix": false },
|
||||
"username": "${adguard_user}",
|
||||
"password": "${adguard_pass}"
|
||||
}
|
||||
JSON
|
||||
)
|
||||
if curl -fsS -X POST \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data "$adguard_payload" \
|
||||
--max-time 15 \
|
||||
"${adguard_setup_url}/control/install/configure" >/dev/null 2>&1; then
|
||||
isSuccessful "AdGuardHome admin setup completed automatically (user: $adguard_user)."
|
||||
else
|
||||
# 422/403 here typically means setup was already done on a
|
||||
# previous install; the post-setup tweaks below are still
|
||||
# safe to run against the existing yaml.
|
||||
isNotice "AdGuardHome /control/install/configure rejected the request — assuming it's already configured. If this is a fresh install, complete setup manually at $adguard_setup_url."
|
||||
fi
|
||||
fi
|
||||
|
||||
local result
|
||||
if [[ "$public" == "true" ]]; then
|
||||
result=$(runFileOp sed -i "s|allow_unencrypted_doh: false|allow_unencrypted_doh: true|g" "$(appDir "$app_name")/conf/AdGuardHome.yaml")
|
||||
checkSuccess "Setting allow_unencrypted_doh to false for Traefik"
|
||||
fi
|
||||
|
||||
result=$(runFileOp sed -i "s|anonymize_client_ip: false: false|anonymize_client_ip: true|g" "$(appDir "$app_name")/conf/AdGuardHome.yaml")
|
||||
checkSuccess "Setting anonymize_client_ip to true for privacy reasons"
|
||||
|
||||
# Force the admin web bind back to 0.0.0.0:3000 inside the container.
|
||||
# The docker-compose mapping is `<host_port>:3000`, so the container
|
||||
# MUST listen on 3000 internally for the host port to reach it. After
|
||||
# the install API call AdGuardHome sometimes ends up bound to
|
||||
# 0.0.0.0:80 (its build-time default) — exactly what causes "unable
|
||||
# to connect" on the host port.
|
||||
local adguard_yaml="$(appDir "$app_name")/conf/AdGuardHome.yaml"
|
||||
if [[ -f "$adguard_yaml" ]]; then
|
||||
runFileOp sed -i 's|^\(\s*address:\s*\)0\.0\.0\.0:[0-9]\+|\10.0.0.0:3000|' "$adguard_yaml"
|
||||
runFileOp sed -i 's|^\(\s*bind_host:\s*\).*|\10.0.0.0|' "$adguard_yaml"
|
||||
runFileOp sed -i 's|^\(\s*bind_port:\s*\)[0-9]\+|\13000|' "$adguard_yaml"
|
||||
checkSuccess "Pinned AdGuardHome admin bind to 0.0.0.0:3000 (matches the compose port mapping)."
|
||||
fi
|
||||
|
||||
dockerComposeRestart "$app_name"
|
||||
|
||||
# Drop `-f` and accept any HTTP status code: now that the admin
|
||||
# account is configured, /control/status returns 401 to an
|
||||
# unauthenticated request — which is fine, it means the server is up
|
||||
# and answering. We only care whether the connection succeeded at
|
||||
# all, not what the response body says.
|
||||
local adguard_health_attempts=0
|
||||
local adguard_health_code
|
||||
while ((adguard_health_attempts < 20)); do
|
||||
adguard_health_code=$(curl -sS -o /dev/null --max-time 2 \
|
||||
-w '%{http_code}' "${adguard_setup_url}/control/status" 2>/dev/null)
|
||||
if [[ "$adguard_health_code" =~ ^[1-5][0-9][0-9]$ ]]; then
|
||||
isSuccessful "AdGuardHome admin UI is reachable on $adguard_setup_url (HTTP $adguard_health_code)"
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
((adguard_health_attempts++))
|
||||
done
|
||||
if ((adguard_health_attempts >= 20)); then
|
||||
isError "AdGuardHome admin UI did not respond after restart on $adguard_setup_url. Check the container logs (\`docker logs adguard-service\`) and the conf/AdGuardHome.yaml bind address."
|
||||
fi
|
||||
}
|
||||
|
||||
adguard_install_message_data()
|
||||
{
|
||||
# Echo the admin user + password as space-separated tokens so they
|
||||
# become $username $password positional args to menuShowFinalMessages.
|
||||
echo "${CFG_ADGUARD_USER:-admin} $CFG_ADGUARD_PASSWORD_1"
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Post-install/update specifics for AdGuard Home — dispatched by appUpdateSpecifics
|
||||
# (containers/<app>/scripts/<app>_update_specifics.sh defining appUpdateSpecifics_<app>).
|
||||
appUpdateSpecifics_adguard() {
|
||||
local app_name="$1"
|
||||
if [[ $CFG_REQUIREMENT_DNS_UPDATER == "true" ]]; then
|
||||
updateDNS "$app_name" install
|
||||
fi
|
||||
# Split-horizon local DNS: app subdomains resolve to the box on the LAN.
|
||||
declare -F setupLocalDnsRewrites >/dev/null 2>&1 && setupLocalDnsRewrites
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
{
|
||||
"tools": [
|
||||
{
|
||||
"id": "reset_password",
|
||||
"label": "Reset Admin Password",
|
||||
"description": "Set a new admin password. Leave blank to generate one.",
|
||||
"icon": "🔑",
|
||||
"fields": [
|
||||
{ "name": "password", "label": "New password", "type": "password", "placeholder": "Leave blank to generate" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "apply_dns_updater",
|
||||
"label": "Apply DNS Updater",
|
||||
"description": "Point this server's DNS at AdGuard now.",
|
||||
"icon": "🌐",
|
||||
"fields": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -6,7 +6,6 @@
|
||||
# REQUIRES = comma-separated install prerequisites (see scripts/checks/requirements/check_app_install.sh)
|
||||
# COMPOSE_FILE = default for no app_name in docker-compose file name, app if there is
|
||||
# BACKUP = if true, include this application in backup operations
|
||||
# UPDATE_TYPE = auto: new image builds are applied automatically (a recovery snapshot is taken first), manual: only when you press Update
|
||||
# HEALTHCHECK = if true, default docker health checks for that container will be enabled
|
||||
# AUTHELIA = if true, use Authelia authentication, if false turned off.
|
||||
# HEADSCALE = options : false, local, remote (see general config). e.g false or local,remote
|
||||
@ -16,26 +15,15 @@
|
||||
# MONITORING = if true, export this app's metrics to Prometheus + Grafana (needs both apps installed)
|
||||
#
|
||||
CFG_AUTHELIA_APP_NAME=authelia
|
||||
# MULTI_INSTANCE = if true, this app can run as multiple isolated instances
|
||||
# (own data/DB/subdomain/backups) via `libreportal instance create`. Only set on
|
||||
# apps whose compose identity (container_name, Traefik routers, backup labels)
|
||||
# is instance-safe — see scripts/instance/instance_create.sh.
|
||||
# Not instanced by design. The forward-auth provider every other app's Traefik router points at. Two would mean two session realms with no clear owner.
|
||||
CFG_AUTHELIA_MULTI_INSTANCE=false
|
||||
CFG_AUTHELIA_REQUIRES="domain,traefik"
|
||||
CFG_AUTHELIA_BACKUP=true
|
||||
# STORAGE = which storage location holds this app's data. Change it with
|
||||
# `libreportal app move authelia <location>` — editing here records intent only.
|
||||
CFG_AUTHELIA_STORAGE=default # Storage Location - Which disk holds this app's data [default:Primary]
|
||||
CFG_AUTHELIA_BACKUP_STRATEGY=auto
|
||||
CFG_AUTHELIA_UPDATE_TYPE=auto
|
||||
CFG_AUTHELIA_COMPOSE_FILE=default
|
||||
CFG_AUTHELIA_HEALTHCHECK=true
|
||||
CFG_AUTHELIA_AUTHELIA=false
|
||||
CFG_AUTHELIA_HEADSCALE=false
|
||||
CFG_AUTHELIA_THEME=dark
|
||||
CFG_AUTHELIA_ADMIN_USERNAME=admin
|
||||
CFG_AUTHELIA_ADMIN_PASSWORD_1=RANDOMIZEDPASSWORD1
|
||||
CFG_AUTHELIA_ADMIN_PASSWORD=RANDOMIZEDPASSWORD1
|
||||
CFG_AUTHELIA_MONITORING=false
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
208
containers/authelia/authelia.sh
Executable file
208
containers/authelia/authelia.sh
Executable file
@ -0,0 +1,208 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Category : Security
|
||||
# Description : Authelia - Authentication & SSO (c/u/s/r/i):
|
||||
|
||||
installAuthelia()
|
||||
{
|
||||
local config_variables="$1"
|
||||
|
||||
if [[ "$authelia" == *[cCtTuUsSrRiI]* ]]; then
|
||||
dockerConfigSetupToContainer silent authelia;
|
||||
local app_name=$CFG_AUTHELIA_APP_NAME
|
||||
initializeAppVariables $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$authelia" == *[cC]* ]]; then
|
||||
editAppConfig $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$authelia" == *[uU]* ]]; then
|
||||
dockerUninstallApp $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$authelia" == *[sS]* ]]; then
|
||||
dockerComposeDown $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$authelia" == *[rR]* ]]; then
|
||||
dockerComposeRestart $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$authelia" == *[iI]* ]]; then
|
||||
isHeader "Install $app_name"
|
||||
|
||||
# Pre-flight: bail out before touching any compose/config if the
|
||||
# global prerequisites aren't met. CFG_AUTHELIA_REQUIRES lists
|
||||
# what's needed (currently "domain,traefik"); the helper prints a
|
||||
# clear list of what's missing so the user knows what to fix.
|
||||
if ! appInstallCheckRequirements "$app_name" "$CFG_AUTHELIA_REQUIRES"; then
|
||||
authelia=n
|
||||
return 1
|
||||
fi
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Setting up install folder and config file for $app_name."
|
||||
echo ""
|
||||
|
||||
dockerConfigSetupToContainer "loud" "$app_name" "install" "$config_variables";
|
||||
isSuccessful "Install folders and Config files have been setup for $app_name."
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Setting up the $app_name docker-compose.yml file."
|
||||
echo ""
|
||||
|
||||
dockerComposeSetupFile $app_name;
|
||||
|
||||
local result=$(copyResource "$app_name" "configuration.yml" "config" | sudo tee -a "$logs_dir/$docker_log_file" 2>&1)
|
||||
checkSuccess "Copying configuration.yml to $containers_dir$app_name/config"
|
||||
|
||||
local result=$(copyResource "$app_name" "users_database.yml" "config" | sudo tee -a "$logs_dir/$docker_log_file" 2>&1)
|
||||
checkSuccess "Copying users_database.yml to $containers_dir$app_name/config"
|
||||
|
||||
local authelia_config_file="$containers_dir$app_name/config/configuration.yml"
|
||||
sudo sed -i "s|AUTHELIA_THEME_PLACEHOLDER|$CFG_AUTHELIA_THEME|g" "$authelia_config_file"
|
||||
sudo sed -i "s|AUTHELIA_DOMAIN_PLACEHOLDER|$domain_full|g" "$authelia_config_file"
|
||||
sudo sed -i "s|AUTHELIA_HOST_PLACEHOLDER|$host_setup|g" "$authelia_config_file"
|
||||
checkSuccess "Substituting Authelia configuration values (theme=$CFG_AUTHELIA_THEME domain=$domain_full host=$host_setup)"
|
||||
|
||||
local authelia_secrets_dir="$containers_dir$app_name/secrets"
|
||||
sudo mkdir -p "$authelia_secrets_dir"
|
||||
for secret_name in JWT_SECRET SESSION_SECRET STORAGE_ENCRYPTION_KEY; do
|
||||
local secret_file="$authelia_secrets_dir/$secret_name"
|
||||
if [[ ! -s "$secret_file" ]]; then
|
||||
openssl rand -hex 64 | sudo tee "$secret_file" >/dev/null
|
||||
sudo chmod 600 "$secret_file"
|
||||
fi
|
||||
done
|
||||
sudo chown -R "$docker_install_user":"$docker_install_user" "$authelia_secrets_dir"
|
||||
checkSuccess "Generated Authelia secrets at $authelia_secrets_dir"
|
||||
|
||||
# Enable Authelia's telemetry/metrics endpoint only when
|
||||
# CFG_AUTHELIA_MONITORING=true (toggles the libreportal-monitoring
|
||||
# marker block in configuration.yml).
|
||||
monitoringToggleAppConfig "$app_name" "config/configuration.yml";
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Updating file permissions before starting."
|
||||
echo ""
|
||||
|
||||
fixPermissionsBeforeStart $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running the docker-compose.yml to install and start $app_name"
|
||||
echo ""
|
||||
|
||||
dockerComposeUpdateAndStartApp $app_name install;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Configuring Authelia admin account"
|
||||
echo ""
|
||||
|
||||
local authelia_admin_user="${CFG_AUTHELIA_ADMIN_USERNAME:-admin}"
|
||||
local authelia_admin_pass="${CFG_AUTHELIA_ADMIN_PASSWORD:-authelia}"
|
||||
local authelia_users_file="$containers_dir$app_name/config/users_database.yml"
|
||||
local authelia_attempts=0
|
||||
while ((authelia_attempts < 30)); do
|
||||
if sudo docker exec authelia-service authelia --version >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
((authelia_attempts++))
|
||||
done
|
||||
|
||||
if ((authelia_attempts >= 30)); then
|
||||
isNotice "Authelia container did not become responsive in time — admin left at default (admin / authelia)."
|
||||
else
|
||||
local authelia_hash
|
||||
authelia_hash=$(sudo docker exec authelia-service authelia crypto hash generate argon2 --password "$authelia_admin_pass" 2>/dev/null \
|
||||
| grep -oE '\$argon2[^[:space:]]+')
|
||||
if [[ -z "$authelia_hash" ]]; then
|
||||
isNotice "Could not generate Authelia password hash — admin left at default (admin / authelia)."
|
||||
else
|
||||
sudo tee "$authelia_users_file" >/dev/null <<EOF
|
||||
---
|
||||
users:
|
||||
${authelia_admin_user}:
|
||||
disabled: false
|
||||
displayname: "Admin"
|
||||
password: "${authelia_hash}"
|
||||
email: ${authelia_admin_user}@${domain_full:-example.com}
|
||||
groups:
|
||||
- admins
|
||||
EOF
|
||||
sudo chown "$docker_install_user":"$docker_install_user" "$authelia_users_file"
|
||||
isSuccessful "Configured Authelia admin (user: $authelia_admin_user)."
|
||||
dockerComposeRestart "$app_name";
|
||||
fi
|
||||
fi
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running Application specific updates (if required)"
|
||||
echo ""
|
||||
|
||||
appUpdateSpecifics $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running Headscale setup (if required)"
|
||||
echo ""
|
||||
|
||||
setupHeadscale $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Adding $app_name to the Apps Database table."
|
||||
echo ""
|
||||
|
||||
databaseInstallApp $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Updating WebUI config file."
|
||||
echo ""
|
||||
|
||||
webuiContainerSetup $app_name install;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Refreshing monitoring integration."
|
||||
echo ""
|
||||
|
||||
# Self-correcting: adds Authelia's scrape target + dashboard to
|
||||
# Prometheus/Grafana when CFG_AUTHELIA_MONITORING=true, removes them
|
||||
# when it's off. No-ops with a notice if either app isn't installed.
|
||||
monitoringRefreshAll;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. You can find $app_name files at $containers_dir$app_name"
|
||||
echo ""
|
||||
echo " You can now navigate to your new service using one of the options below : "
|
||||
echo ""
|
||||
|
||||
menuShowFinalMessages $app_name;
|
||||
|
||||
echo ""
|
||||
isNotice "Authelia admin login:"
|
||||
echo ""
|
||||
echo " Username : ${authelia_admin_user}"
|
||||
echo " Password : ${authelia_admin_pass}"
|
||||
echo ""
|
||||
|
||||
menu_number=0
|
||||
#sleep 3s
|
||||
cd
|
||||
fi
|
||||
authelia=n
|
||||
}
|
||||
@ -5,7 +5,7 @@ networks:
|
||||
services:
|
||||
authelia-service: #LIBREPORTAL|SERVICE_TAG_1|authelia-service
|
||||
container_name: authelia-service
|
||||
image: docker.io/authelia/authelia:latest #LIBREPORTAL|AUTHELIA_VERSION_TAG|latest
|
||||
image: docker.io/authelia/authelia:latest
|
||||
restart: unless-stopped
|
||||
# GLUETUN_OFF_BEGIN
|
||||
ports:
|
||||
@ -23,7 +23,6 @@ services:
|
||||
labels:
|
||||
libreportal.category: "CATEGORY_DATA" #LIBREPORTAL|CATEGORY_TAG|CATEGORY_DATA
|
||||
libreportal.title: "TITLE_DATA" #LIBREPORTAL|TITLE_TAG|TITLE_DATA
|
||||
libreportal.backup.db: "sqlite:::config/db.sqlite3"
|
||||
traefik.enable: TRAEFIK_ENABLE_DATA #LIBREPORTAL|TRAEFIK_ENABLE_TAG|TRAEFIK_ENABLE_DATA
|
||||
# TRAEFIK_PORT_1_BEGIN
|
||||
traefik.http.routers.authelia-service.entrypoints: web,websecure
|
||||
|
||||
@ -1,111 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Authelia install hooks — requirements check, config + secrets bootstrap,
|
||||
# admin account provisioning, and an end-of-install credentials notice.
|
||||
|
||||
authelia_install_pre()
|
||||
{
|
||||
local app_name="$1"
|
||||
if ! appInstallCheckRequirements "$app_name" "$CFG_AUTHELIA_REQUIRES"; then
|
||||
authelia=n
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
authelia_install_post_compose()
|
||||
{
|
||||
local app_name="$1"
|
||||
|
||||
local result
|
||||
result=$(copyResource "$app_name" "configuration.yml" "config" | runInstallWrite -a "$logs_dir/$docker_log_file" 2>&1)
|
||||
checkSuccess "Copying configuration.yml to $(appDir "$app_name")/config"
|
||||
|
||||
result=$(copyResource "$app_name" "users_database.yml" "config" | runInstallWrite -a "$logs_dir/$docker_log_file" 2>&1)
|
||||
checkSuccess "Copying users_database.yml to $(appDir "$app_name")/config"
|
||||
|
||||
local authelia_config_file="$(appDir "$app_name")/config/configuration.yml"
|
||||
runFileOp sed -i "s|AUTHELIA_THEME_PLACEHOLDER|$CFG_AUTHELIA_THEME|g" "$authelia_config_file"
|
||||
runFileOp sed -i "s|AUTHELIA_DOMAIN_PLACEHOLDER|$domain_full|g" "$authelia_config_file"
|
||||
runFileOp sed -i "s|AUTHELIA_HOST_PLACEHOLDER|$host_setup|g" "$authelia_config_file"
|
||||
checkSuccess "Substituting Authelia configuration values (theme=$CFG_AUTHELIA_THEME domain=$domain_full host=$host_setup)"
|
||||
|
||||
local authelia_secrets_dir="$(appDir "$app_name")/secrets"
|
||||
runFileOp mkdir -p "$authelia_secrets_dir"
|
||||
local secret_name secret_file
|
||||
for secret_name in JWT_SECRET SESSION_SECRET STORAGE_ENCRYPTION_KEY; do
|
||||
secret_file="$authelia_secrets_dir/$secret_name"
|
||||
if [[ ! -s "$secret_file" ]]; then
|
||||
openssl rand -hex 64 | runFileWrite "$secret_file"
|
||||
runFileOp chmod 600 "$secret_file"
|
||||
fi
|
||||
done
|
||||
runFileOp chown -R "$docker_install_user":"$docker_install_user" "$authelia_secrets_dir"
|
||||
checkSuccess "Generated Authelia secrets at $authelia_secrets_dir"
|
||||
|
||||
# Authelia's metrics block lives in configuration.yml (not the compose),
|
||||
# so toggle it here. The driver already toggled docker-compose.yml.
|
||||
monitoringToggleAppConfig "$app_name" "config/configuration.yml"
|
||||
}
|
||||
|
||||
authelia_install_post_start()
|
||||
{
|
||||
local app_name="$1"
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Configuring Authelia admin account"
|
||||
echo ""
|
||||
|
||||
local authelia_admin_user="${CFG_AUTHELIA_ADMIN_USERNAME:-admin}"
|
||||
local authelia_admin_pass="${CFG_AUTHELIA_ADMIN_PASSWORD_1:-authelia}"
|
||||
local authelia_users_file="$(appDir "$app_name")/config/users_database.yml"
|
||||
local authelia_attempts=0
|
||||
while ((authelia_attempts < 30)); do
|
||||
if runFileOp docker exec authelia-service authelia --version >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
((authelia_attempts++))
|
||||
done
|
||||
|
||||
if ((authelia_attempts >= 30)); then
|
||||
isNotice "Authelia container did not become responsive in time — admin left at default (admin / authelia)."
|
||||
return 0
|
||||
fi
|
||||
|
||||
local authelia_hash
|
||||
authelia_hash=$(runFileOp docker exec authelia-service authelia crypto hash generate argon2 --password "$authelia_admin_pass" 2>/dev/null \
|
||||
| grep -oE '\$argon2[^[:space:]]+')
|
||||
if [[ -z "$authelia_hash" ]]; then
|
||||
isNotice "Could not generate Authelia password hash — admin left at default (admin / authelia)."
|
||||
return 0
|
||||
fi
|
||||
|
||||
runFileWrite "$authelia_users_file" <<EOF
|
||||
---
|
||||
users:
|
||||
${authelia_admin_user}:
|
||||
disabled: false
|
||||
displayname: "Admin"
|
||||
password: "${authelia_hash}"
|
||||
email: ${authelia_admin_user}@${domain_full:-example.com}
|
||||
groups:
|
||||
- admins
|
||||
EOF
|
||||
runFileOp chown "$docker_install_user":"$docker_install_user" "$authelia_users_file"
|
||||
isSuccessful "Configured Authelia admin (user: $authelia_admin_user)."
|
||||
dockerComposeRestart "$app_name"
|
||||
}
|
||||
|
||||
authelia_install_post()
|
||||
{
|
||||
local app_name="$1"
|
||||
local authelia_admin_user="${CFG_AUTHELIA_ADMIN_USERNAME:-admin}"
|
||||
local authelia_admin_pass="${CFG_AUTHELIA_ADMIN_PASSWORD_1:-authelia}"
|
||||
echo ""
|
||||
isNotice "Authelia admin login:"
|
||||
echo ""
|
||||
echo " Username : ${authelia_admin_user}"
|
||||
echo " Password : ${authelia_admin_pass}"
|
||||
echo ""
|
||||
}
|
||||
@ -5,7 +5,6 @@
|
||||
# APP_NAME = name of application for use in scripts
|
||||
# COMPOSE_FILE = default for no app_name in docker-compose file name, app if there is
|
||||
# BACKUP = if true, include this application in backup operations
|
||||
# UPDATE_TYPE = auto: new image builds are applied automatically (a recovery snapshot is taken first), manual: only when you press Update
|
||||
# HEALTHCHECK = if true, default docker health checks for that container will be enabled
|
||||
# AUTHELIA = if true, use Authelia authentication, if false turned off.
|
||||
# HEADSCALE = options : false, local, remote (see general config). e.g false or local,remote
|
||||
@ -13,29 +12,19 @@
|
||||
# ADMIN_PASSWORD = password used for the Bookstack admin account
|
||||
#
|
||||
CFG_BOOKSTACK_APP_NAME=bookstack
|
||||
# MULTI_INSTANCE = if true, this app can run as multiple isolated instances
|
||||
# (own data/DB/subdomain/backups) via `libreportal instance create`. Only set on
|
||||
# apps whose compose identity (container_name, Traefik routers, backup labels)
|
||||
# is instance-safe — see scripts/instance/instance_create.sh.
|
||||
CFG_BOOKSTACK_MULTI_INSTANCE=true
|
||||
CFG_BOOKSTACK_BACKUP=true
|
||||
# STORAGE = which storage location holds this app's data. Change it with
|
||||
# `libreportal app move bookstack <location>` — editing here records intent only.
|
||||
CFG_BOOKSTACK_STORAGE=default # Storage Location - Which disk holds this app's data [default:Primary]
|
||||
CFG_BOOKSTACK_BACKUP_STRATEGY=auto
|
||||
CFG_BOOKSTACK_UPDATE_TYPE=auto
|
||||
CFG_BOOKSTACK_COMPOSE_FILE=default
|
||||
CFG_BOOKSTACK_HEALTHCHECK=true
|
||||
CFG_BOOKSTACK_AUTHELIA=false
|
||||
CFG_BOOKSTACK_HEADSCALE=false
|
||||
CFG_BOOKSTACK_ADMIN_EMAIL=admin@example.com
|
||||
CFG_BOOKSTACK_ADMIN_PASSWORD_1=RANDOMIZEDPASSWORD3
|
||||
CFG_BOOKSTACK_ADMIN_PASSWORD=RANDOMIZEDPASSWORD3
|
||||
# Secrets below feed the compose via #LIBREPORTAL|BOOKSTACK_<KEY>_TAG| tags —
|
||||
# auto-generated, and (unlike a RANDOMIZED* placeholder in the compose)
|
||||
# preserved across reinstalls. DB_PASSWORD is shared by the app + db services.
|
||||
CFG_BOOKSTACK_APP_KEY_1=RANDOMIZEDAPPKEY1
|
||||
CFG_BOOKSTACK_DB_PASSWORD_1=RANDOMIZEDPASSWORD1
|
||||
CFG_BOOKSTACK_DB_ROOT_PASSWORD_1=RANDOMIZEDPASSWORD2
|
||||
CFG_BOOKSTACK_APP_KEY=RANDOMIZEDAPPKEY1
|
||||
CFG_BOOKSTACK_DB_PASSWORD=RANDOMIZEDPASSWORD1
|
||||
CFG_BOOKSTACK_DB_ROOT_PASSWORD=RANDOMIZEDPASSWORD2
|
||||
#
|
||||
# =============================================================================
|
||||
# METADATA
|
||||
@ -80,4 +69,6 @@ CFG_BOOKSTACK_NETWORK=default
|
||||
#
|
||||
CFG_BOOKSTACK_PORT_1="bookstack-service|webui|random:80|public|tcp|false|true|true|Web Interface||bookstack"
|
||||
|
||||
# AUTH_PROFILE = capability tier for the WebUI auth tools (single_password | user_password | multi_user)
|
||||
CFG_BOOKSTACK_AUTH_PROFILE=multi_user
|
||||
CFG_BOOKSTACK_ADMIN_USER=
|
||||
|
||||
180
containers/bookstack/bookstack.sh
Executable file
180
containers/bookstack/bookstack.sh
Executable file
@ -0,0 +1,180 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Category : Knowledge Management
|
||||
# Description : Bookstack - Wiki/Knowledge Base (c/u/s/r/i):
|
||||
|
||||
installBookstack()
|
||||
{
|
||||
local config_variables="$1"
|
||||
|
||||
if [[ "$bookstack" == *[cCtTuUsSrRiI]* ]]; then
|
||||
dockerConfigSetupToContainer silent bookstack;
|
||||
local app_name=$CFG_BOOKSTACK_APP_NAME
|
||||
initializeAppVariables $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$bookstack" == *[cC]* ]]; then
|
||||
editAppConfig $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$bookstack" == *[uU]* ]]; then
|
||||
dockerUninstallApp $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$bookstack" == *[sS]* ]]; then
|
||||
dockerComposeDown $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$bookstack" == *[rR]* ]]; then
|
||||
dockerComposeRestart $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$bookstack" == *[iI]* ]]; then
|
||||
isHeader "Install $app_name"
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Setting up install folder and config file for $app_name."
|
||||
echo ""
|
||||
|
||||
dockerConfigSetupToContainer "loud" "$app_name" "install" "$config_variables";
|
||||
isSuccessful "Install folders and Config files have been setup for $app_name."
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Setting up the $app_name docker-compose.yml file."
|
||||
echo ""
|
||||
|
||||
dockerComposeSetupFile $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Updating file permissions before starting."
|
||||
echo ""
|
||||
|
||||
fixPermissionsBeforeStart $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running the docker-compose.yml to install and start $app_name"
|
||||
echo ""
|
||||
|
||||
dockerComposeUpdateAndStartApp $app_name install;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running Application specific updates (if required)"
|
||||
echo ""
|
||||
|
||||
appUpdateSpecifics $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running Headscale setup (if required)"
|
||||
echo ""
|
||||
|
||||
setupHeadscale $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Adding $app_name to the Apps Database table."
|
||||
echo ""
|
||||
|
||||
databaseInstallApp $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Updating WebUI config file."
|
||||
echo ""
|
||||
|
||||
webuiContainerSetup $app_name install;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. You can find $app_name files at $containers_dir$app_name"
|
||||
echo ""
|
||||
echo " You can now navigate to your $app_name service using any of the options below : "
|
||||
echo ""
|
||||
|
||||
menuShowFinalMessages $app_name;
|
||||
|
||||
bookstack_target_email="${CFG_BOOKSTACK_ADMIN_EMAIL:-admin@admin.com}"
|
||||
bookstack_target_pass="${CFG_BOOKSTACK_ADMIN_PASSWORD:-password}"
|
||||
|
||||
bookstack_compose_file="$containers_dir$app_name/docker-compose.yml"
|
||||
bookstack_port_pair=$(tagsManagerGetTagContent "$bookstack_compose_file" "PORTS_TAG_1")
|
||||
bookstack_host_port="${bookstack_port_pair%%:*}"
|
||||
bookstack_probe_url="http://127.0.0.1:${bookstack_host_port}/login"
|
||||
|
||||
isNotice "Waiting for Bookstack to come online at ${bookstack_probe_url} ..."
|
||||
isNotice "This may take up to 20 seconds, please wait..."
|
||||
|
||||
bookstack_attempts=0
|
||||
bookstack_ready=0
|
||||
while ((bookstack_attempts < 60)); do
|
||||
bookstack_http_code=$(curl -sS -o /dev/null --max-time 3 -w '%{http_code}' "$bookstack_probe_url" 2>/dev/null)
|
||||
if [[ "$bookstack_http_code" =~ ^(200|302)$ ]]; then
|
||||
bookstack_ready=1
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
((bookstack_attempts++))
|
||||
done
|
||||
|
||||
if ((bookstack_ready == 0)); then
|
||||
isNotice "Bookstack did not respond on ${bookstack_probe_url} within $((60 * 2))s — admin account left at upstream defaults."
|
||||
echo ""
|
||||
isNotice "Bookstack admin login (default):"
|
||||
echo ""
|
||||
echo " Email : admin@admin.com"
|
||||
echo " Password : password"
|
||||
echo ""
|
||||
else
|
||||
isSuccessful "Bookstack is online (HTTP ${bookstack_http_code})."
|
||||
|
||||
bookstack_create_output=$(sudo docker exec \
|
||||
-e EZ_BS_NEW_EMAIL="$bookstack_target_email" \
|
||||
-e EZ_BS_NEW_PASS="$bookstack_target_pass" \
|
||||
bookstack sh -c 'cd /app/www && s6-setuidgid abc php artisan bookstack:create-admin --no-ansi --email="$EZ_BS_NEW_EMAIL" --name=Admin --password="$EZ_BS_NEW_PASS" 2>&1')
|
||||
bookstack_create_rc=$?
|
||||
if [[ $bookstack_create_rc -eq 0 ]]; then
|
||||
isSuccessful "Bookstack admin account created (email: $bookstack_target_email)."
|
||||
|
||||
if [[ "$bookstack_target_email" != "admin@admin.com" ]]; then
|
||||
sudo docker exec -i bookstack php /app/www/artisan tinker --no-ansi >/dev/null 2>&1 <<'PHP'
|
||||
$c = class_exists('\BookStack\Users\Models\User') ? '\BookStack\Users\Models\User' : '\BookStack\Auth\User';
|
||||
optional($c::where('email', 'admin@admin.com')->first())->delete();
|
||||
PHP
|
||||
isSuccessful "Removed seeded admin@admin.com account."
|
||||
fi
|
||||
|
||||
echo ""
|
||||
isNotice "Bookstack admin login:"
|
||||
echo ""
|
||||
echo " Email : ${bookstack_target_email}"
|
||||
echo " Password : ${bookstack_target_pass}"
|
||||
echo ""
|
||||
else
|
||||
isNotice "Bookstack admin auto-create failed (exit $bookstack_create_rc). Output:"
|
||||
echo "$bookstack_create_output" | sed 's/^/ /'
|
||||
echo ""
|
||||
isNotice "Falling back to upstream defaults — update from inside Bookstack."
|
||||
echo ""
|
||||
isNotice "Bookstack admin login (default):"
|
||||
echo ""
|
||||
echo " Email : admin@admin.com"
|
||||
echo " Password : password"
|
||||
echo ""
|
||||
fi
|
||||
fi
|
||||
|
||||
menu_number=0
|
||||
#sleep 3s
|
||||
cd
|
||||
fi
|
||||
bookstack=n
|
||||
}
|
||||
@ -4,18 +4,18 @@ networks:
|
||||
|
||||
services:
|
||||
bookstack-service: #LIBREPORTAL|SERVICE_TAG_1|bookstack-service
|
||||
image: lscr.io/linuxserver/bookstack:latest #LIBREPORTAL|BOOKSTACK_VERSION_TAG|latest
|
||||
image: lscr.io/linuxserver/bookstack
|
||||
container_name: bookstack
|
||||
environment:
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
- TZ=TIMEZONE_DATA #LIBREPORTAL|TIMEZONE_TAG|TIMEZONE_DATA
|
||||
- APP_URL=APP_URL_DATA #LIBREPORTAL|APP_URL_TAG|APP_URL_DATA
|
||||
- APP_KEY=BOOKSTACK_APP_KEY_1_DATA #LIBREPORTAL|BOOKSTACK_APP_KEY_1_TAG|BOOKSTACK_APP_KEY_1_DATA
|
||||
- DB_HOST=bookstack-db
|
||||
- APP_KEY=BOOKSTACK_APP_KEY_DATA #LIBREPORTAL|BOOKSTACK_APP_KEY_TAG|BOOKSTACK_APP_KEY_DATA
|
||||
- DB_HOST=bookstack_db
|
||||
- DB_PORT=3306
|
||||
- DB_USERNAME=bookstack
|
||||
- DB_PASSWORD=BOOKSTACK_DB_PASSWORD_1_DATA #LIBREPORTAL|BOOKSTACK_DB_PASSWORD_1_TAG|BOOKSTACK_DB_PASSWORD_1_DATA
|
||||
- DB_PASSWORD=BOOKSTACK_DB_PASSWORD_DATA #LIBREPORTAL|BOOKSTACK_DB_PASSWORD_TAG|BOOKSTACK_DB_PASSWORD_DATA
|
||||
- DB_DATABASE=bookstackapp
|
||||
volumes:
|
||||
- SOCKET_DATA #LIBREPORTAL|SOCKET_TAG|SOCKET_DATA
|
||||
@ -26,14 +26,12 @@ services:
|
||||
# GLUETUN_OFF_END
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- bookstack-db
|
||||
- bookstack_db
|
||||
healthcheck:
|
||||
disable: HEALTHCHECK_DATA #LIBREPORTAL|HEALTHCHECK_TAG|HEALTHCHECK_DATA
|
||||
labels:
|
||||
libreportal.category: "CATEGORY_DATA" #LIBREPORTAL|CATEGORY_TAG|CATEGORY_DATA
|
||||
libreportal.title: "TITLE_DATA" #LIBREPORTAL|TITLE_TAG|TITLE_DATA
|
||||
libreportal.backup.db: "mariadb:bookstack-db:db:"
|
||||
libreportal.backup.files: "bookstack:/config:data"
|
||||
traefik.enable: TRAEFIK_ENABLE_DATA #LIBREPORTAL|TRAEFIK_ENABLE_TAG|TRAEFIK_ENABLE_DATA
|
||||
# TRAEFIK_PORT_1_BEGIN
|
||||
traefik.http.routers.bookstack-service.entrypoints: web,websecure
|
||||
@ -53,17 +51,17 @@ services:
|
||||
# network_mode: "container:gluetun-service"
|
||||
# GLUETUN_ON_END
|
||||
|
||||
bookstack-db: #LIBREPORTAL|SERVICE_TAG_2|bookstack-db
|
||||
bookstack_db: #LIBREPORTAL|SERVICE_TAG_2|bookstack_db
|
||||
image: lscr.io/linuxserver/mariadb
|
||||
container_name: bookstack-db
|
||||
container_name: bookstack_db
|
||||
environment:
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
- TZ=TIMEZONE_DATA #LIBREPORTAL|TIMEZONE_TAG|TIMEZONE_DATA
|
||||
- MYSQL_ROOT_PASSWORD=BOOKSTACK_DB_ROOT_PASSWORD_1_DATA #LIBREPORTAL|BOOKSTACK_DB_ROOT_PASSWORD_1_TAG|BOOKSTACK_DB_ROOT_PASSWORD_1_DATA
|
||||
- MYSQL_ROOT_PASSWORD=BOOKSTACK_DB_ROOT_PASSWORD_DATA #LIBREPORTAL|BOOKSTACK_DB_ROOT_PASSWORD_TAG|BOOKSTACK_DB_ROOT_PASSWORD_DATA
|
||||
- MYSQL_DATABASE=bookstackapp
|
||||
- MYSQL_USER=bookstack
|
||||
- MYSQL_PASSWORD=BOOKSTACK_DB_PASSWORD_1_DATA #LIBREPORTAL|BOOKSTACK_DB_PASSWORD_1_TAG|BOOKSTACK_DB_PASSWORD_1_DATA
|
||||
- MYSQL_PASSWORD=BOOKSTACK_DB_PASSWORD_DATA #LIBREPORTAL|BOOKSTACK_DB_PASSWORD_TAG|BOOKSTACK_DB_PASSWORD_DATA
|
||||
volumes:
|
||||
- ./db:/config
|
||||
restart: unless-stopped
|
||||
|
||||
@ -1,99 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Bookstack install hooks — drive the post-start admin account bootstrap.
|
||||
# Generic installApp driver handles compose / start / db / monitoring; this
|
||||
# adds the readiness probe + first-admin provisioning the original
|
||||
# installBookstack() did inline.
|
||||
|
||||
bookstack_install_post_start()
|
||||
{
|
||||
local app_name="$1"
|
||||
|
||||
local bookstack_target_email="${CFG_BOOKSTACK_ADMIN_EMAIL:-admin@admin.com}"
|
||||
local bookstack_target_pass="${CFG_BOOKSTACK_ADMIN_PASSWORD_1:-password}"
|
||||
|
||||
local bookstack_compose_file="$(appDir "$app_name")/docker-compose.yml"
|
||||
local bookstack_port_pair
|
||||
bookstack_port_pair=$(tagsManagerGetTagContent "$bookstack_compose_file" "PORTS_TAG_1")
|
||||
local bookstack_host_port="${bookstack_port_pair%%:*}"
|
||||
local bookstack_probe_url="http://127.0.0.1:${bookstack_host_port}/login"
|
||||
|
||||
isNotice "Waiting for Bookstack to come online at ${bookstack_probe_url} ..."
|
||||
isNotice "This may take up to 20 seconds, please wait..."
|
||||
|
||||
local bookstack_attempts=0
|
||||
local bookstack_ready=0
|
||||
local bookstack_http_code
|
||||
while ((bookstack_attempts < 60)); do
|
||||
bookstack_http_code=$(curl -sS -o /dev/null --max-time 3 -w '%{http_code}' "$bookstack_probe_url" 2>/dev/null)
|
||||
if [[ "$bookstack_http_code" =~ ^(200|302)$ ]]; then
|
||||
bookstack_ready=1
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
((bookstack_attempts++))
|
||||
done
|
||||
|
||||
if ((bookstack_ready == 0)); then
|
||||
isNotice "Bookstack did not respond on ${bookstack_probe_url} within $((60 * 2))s — admin account left at upstream defaults."
|
||||
echo ""
|
||||
isNotice "Bookstack admin login (default):"
|
||||
echo ""
|
||||
echo " Email : admin@admin.com"
|
||||
echo " Password : password"
|
||||
echo ""
|
||||
return 0
|
||||
fi
|
||||
isSuccessful "Bookstack is online (HTTP ${bookstack_http_code})."
|
||||
|
||||
# Target the container by $app_name, never the literal "bookstack": under
|
||||
# multi-instance this hook is cloned for each instance, and a hardcoded name
|
||||
# pointed every instance's admin bootstrap at the BASE app's container —
|
||||
# provisioning (and, in the branch below, DELETING) users in the wrong
|
||||
# database. instanceCreate's rewriter can't catch it either, since the
|
||||
# container name here doesn't directly follow `docker exec`.
|
||||
local bookstack_create_output
|
||||
bookstack_create_output=$(runFileOp docker exec \
|
||||
-e EZ_BS_NEW_EMAIL="$bookstack_target_email" \
|
||||
-e EZ_BS_NEW_PASS="$bookstack_target_pass" \
|
||||
"$app_name" sh -c 'cd /app/www && s6-setuidgid abc php artisan bookstack:create-admin --no-ansi --email="$EZ_BS_NEW_EMAIL" --name=Admin --password="$EZ_BS_NEW_PASS" 2>&1')
|
||||
local bookstack_create_rc=$?
|
||||
if [[ $bookstack_create_rc -eq 0 ]]; then
|
||||
isSuccessful "Bookstack admin account created (email: $bookstack_target_email)."
|
||||
|
||||
if [[ "$bookstack_target_email" != "admin@admin.com" ]]; then
|
||||
runFileOp docker exec -i "$app_name" php /app/www/artisan tinker --no-ansi >/dev/null 2>&1 <<'PHP'
|
||||
$c = class_exists('\BookStack\Users\Models\User') ? '\BookStack\Users\Models\User' : '\BookStack\Auth\User';
|
||||
optional($c::where('email', 'admin@admin.com')->first())->delete();
|
||||
PHP
|
||||
isSuccessful "Removed seeded admin@admin.com account."
|
||||
fi
|
||||
|
||||
echo ""
|
||||
isNotice "Bookstack admin login:"
|
||||
echo ""
|
||||
echo " Email : ${bookstack_target_email}"
|
||||
echo " Password : ${bookstack_target_pass}"
|
||||
echo ""
|
||||
elif [[ "$bookstack_create_output" == *"already exists"* ]]; then
|
||||
# Reinstall over existing data: the account is already provisioned, so
|
||||
# create-admin refuses. NOT a failure, and printing the upstream
|
||||
# defaults here would be actively wrong — those credentials were
|
||||
# replaced on the first install, and the password in the config was
|
||||
# never re-applied to the live account.
|
||||
isSuccessful "Bookstack admin account already exists (email: $bookstack_target_email) — kept as-is."
|
||||
isNotice "Its password was NOT reset by this install. Sign in with the credentials from the original install, or reset from inside Bookstack."
|
||||
echo ""
|
||||
else
|
||||
isNotice "Bookstack admin auto-create failed (exit $bookstack_create_rc). Output:"
|
||||
echo "$bookstack_create_output" | sed 's/^/ /'
|
||||
echo ""
|
||||
isNotice "Falling back to upstream defaults — update from inside Bookstack."
|
||||
echo ""
|
||||
isNotice "Bookstack admin login (default):"
|
||||
echo ""
|
||||
echo " Email : admin@admin.com"
|
||||
echo " Password : password"
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
@ -1,106 +0,0 @@
|
||||
{
|
||||
"tools": [
|
||||
{
|
||||
"id": "reset_password",
|
||||
"category": "users",
|
||||
"label": "Reset User Password",
|
||||
"description": "Set a new password for an existing user. Leave blank to generate one.",
|
||||
"icon": "🔑",
|
||||
"fields": [
|
||||
{
|
||||
"name": "email",
|
||||
"label": "User email",
|
||||
"type": "text",
|
||||
"placeholder": "user@example.com",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "password",
|
||||
"label": "New password",
|
||||
"type": "password",
|
||||
"placeholder": "Leave blank to generate"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "create_account",
|
||||
"category": "users",
|
||||
"label": "Create User Account",
|
||||
"description": "Add a new user. Tick \"Make admin\" for full rights.",
|
||||
"icon": "👤",
|
||||
"fields": [
|
||||
{
|
||||
"name": "email",
|
||||
"label": "Email",
|
||||
"type": "text",
|
||||
"placeholder": "user@example.com",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"label": "Display name",
|
||||
"type": "text",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "password",
|
||||
"label": "Password",
|
||||
"type": "password",
|
||||
"placeholder": "Leave blank to generate"
|
||||
},
|
||||
{
|
||||
"name": "admin",
|
||||
"label": "Make admin",
|
||||
"type": "checkbox",
|
||||
"default": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "list_users",
|
||||
"category": "users",
|
||||
"label": "List Users",
|
||||
"description": "List every user and their role.",
|
||||
"icon": "📋",
|
||||
"fields": []
|
||||
},
|
||||
{
|
||||
"id": "delete_user",
|
||||
"category": "users",
|
||||
"label": "Delete User Account",
|
||||
"description": "Permanently remove a user.",
|
||||
"icon": "🗑",
|
||||
"destructive": true,
|
||||
"confirm": "This cannot be undone.",
|
||||
"fields": [
|
||||
{
|
||||
"name": "email",
|
||||
"label": "User email",
|
||||
"type": "text",
|
||||
"required": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "set_admin",
|
||||
"category": "users",
|
||||
"label": "Set Admin Status",
|
||||
"description": "Promote or demote a user.",
|
||||
"icon": "👑",
|
||||
"fields": [
|
||||
{
|
||||
"name": "email",
|
||||
"label": "User email",
|
||||
"type": "text",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "admin",
|
||||
"label": "Make admin",
|
||||
"type": "checkbox",
|
||||
"default": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -5,32 +5,20 @@
|
||||
# APP_NAME = name of application for use in scripts
|
||||
# HOST_INSTALL = true means apt + systemd install on the host, not Docker
|
||||
# HOST_PACKAGE = dpkg package name; drives the "installed" badge
|
||||
# HOST_SERVICE = primary systemd unit; stop/restart actions hit this
|
||||
# HOST_SERVICES = all units; feeds the Services + Logs tabs
|
||||
# HOST_LOG_FILES = <unit>|<path>,... mapping for the log viewer
|
||||
# BACKUP = include in backup operations
|
||||
# UPDATE_TYPE = auto: new image builds are applied automatically (a recovery snapshot is taken first), manual: only when you press Update
|
||||
# MONITORING = if true, export this app's metrics to Prometheus + Grafana (needs both apps installed; ships the official CrowdSec Grafana dashboards)
|
||||
# PROMETHEUS_LISTEN = address CrowdSec's metrics endpoint binds to; must be reachable from the Prometheus container (default: all interfaces, port 6060 — keep the :6060 port)
|
||||
#
|
||||
CFG_CROWDSEC_APP_NAME=crowdsec
|
||||
# MULTI_INSTANCE = if true, this app can run as multiple isolated instances
|
||||
# (own data/DB/subdomain/backups) via `libreportal instance create`. Only set on
|
||||
# apps whose compose identity (container_name, Traefik routers, backup labels)
|
||||
# is instance-safe — see scripts/instance/instance_create.sh.
|
||||
# Not instanced by design. CrowdSec ships no docker-compose.yml at all — it is a
|
||||
# host-level security agent plus Traefik bouncer, not a composed app, so there is
|
||||
# no service identity for `instance create` to clone. It is also a singleton by
|
||||
# nature: one decision engine watching the whole box is the point.
|
||||
CFG_CROWDSEC_MULTI_INSTANCE=false
|
||||
CFG_CROWDSEC_HOST_INSTALL=true
|
||||
CFG_CROWDSEC_HOST_PACKAGE=crowdsec
|
||||
CFG_CROWDSEC_HOST_SERVICE=crowdsec
|
||||
CFG_CROWDSEC_HOST_SERVICES=crowdsec.service,crowdsec-firewall-bouncer.service
|
||||
CFG_CROWDSEC_HOST_LOG_FILES="crowdsec.service|/var/log/crowdsec.log,crowdsec-firewall-bouncer.service|/var/log/crowdsec-firewall-bouncer.log"
|
||||
CFG_CROWDSEC_BACKUP=true
|
||||
# STORAGE = which storage location holds this app's data.
|
||||
CFG_CROWDSEC_STORAGE=default # Storage Location - Fixed: other apps reference crowdsec by path **READONLY**
|
||||
CFG_CROWDSEC_BACKUP_STRATEGY=auto
|
||||
CFG_CROWDSEC_UPDATE_TYPE=auto
|
||||
CFG_CROWDSEC_MONITORING=false
|
||||
CFG_CROWDSEC_PROMETHEUS_LISTEN=0.0.0.0:6060
|
||||
#
|
||||
@ -45,6 +33,7 @@ CFG_CROWDSEC_PROMETHEUS_LISTEN=0.0.0.0:6060
|
||||
# BOUNCER = attach the Traefik bouncer middleware to every public route
|
||||
#
|
||||
CFG_CROWDSEC_ENABLED=true
|
||||
CFG_CROWDSEC_AUTO_UPDATE=true
|
||||
CFG_CROWDSEC_COMMUNITY_BLOCKLIST=true
|
||||
CFG_CROWDSEC_CONSOLE_ENROLL=false
|
||||
CFG_CROWDSEC_CONSOLE_TOKEN=
|
||||
@ -63,7 +52,7 @@ CFG_CROWDSEC_BOUNCER=true
|
||||
CFG_CROWDSEC_CATEGORY="security,recommended"
|
||||
CFG_CROWDSEC_TITLE="CrowdSec"
|
||||
CFG_CROWDSEC_DESCRIPTION="Intrusion Prevention"
|
||||
CFG_CROWDSEC_LONG_DESCRIPTION="An open-source intrusion prevention system: it spots brute-force, scans and web exploits in your logs and blocks the offending IPs at the firewall"
|
||||
CFG_CROWDSEC_LONG_DESCRIPTION="CrowdSec is an open-source intrusion prevention system. It detects attacks from log patterns — brute-force, scans, web exploits — and blocks offending IPs at the firewall. Includes community-shared threat intelligence."
|
||||
CFG_CROWDSEC_URL="https://www.crowdsec.net"
|
||||
CFG_CROWDSEC_ACTIONS="configure|install|restart|shutdown|uninstall|tools"
|
||||
#
|
||||
@ -71,7 +60,9 @@ CFG_CROWDSEC_ACTIONS="configure|install|restart|shutdown|uninstall|tools"
|
||||
# ADVANCED
|
||||
# =============================================================================
|
||||
# LAPI_HOST = LAPI bind address; 0.0.0.0 so Traefik can reach via host.docker.internal
|
||||
# TRAEFIK_LAPI_KEY = auto-generated on install; use the "Rotate Traefik Bouncer Key" tool to replace it (editing this value does not re-register the bouncer)
|
||||
# BOUNCER_NAME_TRAEFIK = bouncer name registered with cscli bouncers add
|
||||
# TRAEFIK_LAPI_KEY = auto-generated by installCrowdsec; use the rotate Tools action to change
|
||||
#
|
||||
CFG_CROWDSEC_LAPI_HOST=0.0.0.0:8080
|
||||
CFG_CROWDSEC_BOUNCER_NAME_TRAEFIK=traefik-bouncer
|
||||
CFG_CROWDSEC_TRAEFIK_LAPI_KEY=
|
||||
|
||||
@ -4,8 +4,8 @@
|
||||
# Description : CrowdSec - Intrusion Prevention (c/u/s/r/i):
|
||||
#
|
||||
# Host-installed agent (apt + systemd) — no Docker container. Host install
|
||||
# logic lives in scripts/crowdsec_install_host.sh (installCrowdsecHost) beside
|
||||
# this file; install registration uses the shared hostAppInstall helper
|
||||
# logic lives in scripts/install/install_crowdsec.sh (installCrowdsecHost);
|
||||
# install registration uses the shared hostAppInstall helper
|
||||
# (scripts/install/host_app.sh). uninstall/stop/restartCrowdsec (below) are the
|
||||
# host-side hooks dockerUninstallApp / dockerStopApp / dockerRestartApp invoke.
|
||||
|
||||
@ -58,18 +58,18 @@ uninstallCrowdsec()
|
||||
echo ""
|
||||
echo "---- $menu_number. Stopping CrowdSec host services."
|
||||
echo ""
|
||||
local result; result=$(runSystem systemctl disable --now crowdsec-firewall-bouncer 2>&1)
|
||||
local result=$(sudo systemctl disable --now crowdsec-firewall-bouncer 2>&1)
|
||||
checkSuccess "Disabling firewall bouncer"
|
||||
local result; result=$(runSystem systemctl disable --now crowdsec 2>&1)
|
||||
local result=$(sudo systemctl disable --now crowdsec 2>&1)
|
||||
checkSuccess "Disabling agent"
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Removing CrowdSec packages."
|
||||
echo ""
|
||||
local result; result=$(runSystem DEBIAN_FRONTEND=noninteractive apt-get purge -y -q crowdsec crowdsec-firewall-bouncer-nftables </dev/null 2>&1)
|
||||
local result=$(sudo DEBIAN_FRONTEND=noninteractive apt-get purge -y -q crowdsec crowdsec-firewall-bouncer-nftables </dev/null 2>&1)
|
||||
checkSuccess "Purged packages"
|
||||
local result; result=$(runSystem DEBIAN_FRONTEND=noninteractive apt-get autoremove -y -q </dev/null 2>&1)
|
||||
local result=$(sudo DEBIAN_FRONTEND=noninteractive apt-get autoremove -y -q </dev/null 2>&1)
|
||||
checkSuccess "Removed orphaned dependencies"
|
||||
|
||||
crowdsecToggleLibrePortalLogMounts off
|
||||
@ -81,9 +81,9 @@ uninstallCrowdsec()
|
||||
stopCrowdsec()
|
||||
{
|
||||
isNotice "Stopping CrowdSec host services..."
|
||||
local result; result=$(runSystem systemctl stop crowdsec-firewall-bouncer 2>&1)
|
||||
local result=$(sudo systemctl stop crowdsec-firewall-bouncer 2>&1)
|
||||
checkSuccess "Stopped firewall bouncer"
|
||||
local result; result=$(runSystem systemctl stop crowdsec 2>&1)
|
||||
local result=$(sudo systemctl stop crowdsec 2>&1)
|
||||
checkSuccess "Stopped agent"
|
||||
}
|
||||
|
||||
@ -93,8 +93,8 @@ stopCrowdsec()
|
||||
restartCrowdsec()
|
||||
{
|
||||
isNotice "Restarting CrowdSec host services..."
|
||||
local result; result=$(runSystem systemctl restart crowdsec 2>&1)
|
||||
local result=$(sudo systemctl restart crowdsec 2>&1)
|
||||
checkSuccess "Restarted agent"
|
||||
local result; result=$(runSystem systemctl restart crowdsec-firewall-bouncer 2>&1)
|
||||
local result=$(sudo systemctl restart crowdsec-firewall-bouncer 2>&1)
|
||||
checkSuccess "Restarted firewall bouncer"
|
||||
}
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
appCrowdSecFixPriority() {
|
||||
local cfg="/etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml"
|
||||
if [[ ! -f "$cfg" ]]; then
|
||||
isNotice "Bouncer config not found at $cfg — is CrowdSec installed?"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# The bouncer yaml is root-owned under /etc/crowdsec; the backup + nftables
|
||||
# ipv4/ipv6 priority rewrite (to -100) runs in the root-owned crowdsec helper.
|
||||
runCrowdsec bouncer-priority
|
||||
checkSuccess "Patched nftables priority to -100 in $cfg"
|
||||
|
||||
runSystem systemctl restart crowdsec-firewall-bouncer
|
||||
checkSuccess "Restarted crowdsec-firewall-bouncer"
|
||||
|
||||
isSuccessful "Priority updated. Run 'crowdsec_verify_firewall' to confirm CrowdSec now runs before UFW."
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
{
|
||||
"tools": [
|
||||
{
|
||||
"id": "rotate_bouncer_key",
|
||||
"category": "security",
|
||||
"label": "Rotate Traefik Bouncer Key",
|
||||
"description": "Issue a new API key for the Traefik bouncer and restart Traefik to load it. Use this if the key is lost or may have been exposed. Traefik briefly rejects bouncer checks while it restarts.",
|
||||
"icon": "🔑",
|
||||
"fields": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Issue a new API key for the Traefik CrowdSec bouncer.
|
||||
#
|
||||
# This is the action crowdsec.config and the install script both point at when
|
||||
# the key is lost or should be replaced. cscli cannot re-issue a key for an
|
||||
# existing bouncer, so rotating means delete + re-add; the privileged helper
|
||||
# does both and rewrites /etc/crowdsec/traefik_bouncer.key.
|
||||
#
|
||||
# The old key stops working the instant the bouncer is deleted, and Traefik
|
||||
# holds the key file open — so Traefik is restarted afterwards to pick up the new
|
||||
# one. Between those two points requests are authenticated with a dead key, which
|
||||
# is why this is a deliberate action and not something the installer does on its
|
||||
# own.
|
||||
appCrowdsecRotateBouncerKey()
|
||||
{
|
||||
local app_name="crowdsec"
|
||||
|
||||
local result
|
||||
result=$(runCrowdsec bouncer-traefik-rotate 2>&1)
|
||||
|
||||
if [[ "$result" != GENERATED:* ]]; then
|
||||
isError "Could not rotate the Traefik bouncer key: $result"
|
||||
isNotice "The previous key may already have been revoked — check 'cscli bouncers list' before retrying."
|
||||
return 1
|
||||
fi
|
||||
|
||||
local bouncer_key="${result#GENERATED:}"
|
||||
isSuccessful "New Traefik bouncer API key issued."
|
||||
|
||||
# Mirror it the same way the installer does, so the config page and the key
|
||||
# file agree. updateConfigOption escapes the value, writes as the owner of
|
||||
# the containers tree, and re-sources.
|
||||
local cfg_file="$(appDir "$app_name")/${app_name}.config"
|
||||
if [[ -f "$cfg_file" ]]; then
|
||||
updateConfigOption "CFG_CROWDSEC_TRAEFIK_LAPI_KEY" "$bouncer_key" "$cfg_file"
|
||||
else
|
||||
isNotice "crowdsec.config is not deployed — the key is in /etc/crowdsec/traefik_bouncer.key only."
|
||||
fi
|
||||
|
||||
# Traefik reads the key from the bind-mounted file at startup, so it keeps
|
||||
# presenting the revoked key until it restarts. Without this the rotation
|
||||
# looks successful while every bouncer check fails with 403.
|
||||
if [[ -d "$(appDir traefik)" ]]; then
|
||||
dockerComposeRestart traefik
|
||||
checkSuccess "Restarting Traefik to load the new bouncer key"
|
||||
else
|
||||
isNotice "Traefik is not installed here — nothing to restart."
|
||||
fi
|
||||
}
|
||||
@ -5,22 +5,12 @@
|
||||
# APP_NAME = name of application for use in scripts
|
||||
# COMPOSE_FILE = default for no app_name in docker-compose file name, app if there is
|
||||
# BACKUP = if true, include this application in backup operations
|
||||
# UPDATE_TYPE = auto: new image builds are applied automatically (a recovery snapshot is taken first), manual: only when you press Update
|
||||
# HEALTHCHECK = if true, default docker health checks for that container will be enabled
|
||||
# AUTHELIA = if true, use Authelia authentication, if false turned off.
|
||||
# HEADSCALE = options : false, local, remote (see general config). e.g false or local,remote
|
||||
#
|
||||
CFG_DASHY_APP_NAME=dashy
|
||||
# MULTI_INSTANCE = if true, this app can run as multiple isolated instances
|
||||
# (own data/DB/subdomain/backups) via `libreportal instance create`. Only set on
|
||||
# apps whose compose identity (container_name, Traefik routers, backup labels)
|
||||
# is instance-safe — see scripts/instance/instance_create.sh.
|
||||
CFG_DASHY_MULTI_INSTANCE=true
|
||||
CFG_DASHY_BACKUP=true
|
||||
# STORAGE = which storage location holds this app's data.
|
||||
CFG_DASHY_STORAGE=default # Storage Location - Fixed: other apps reference dashy by path **READONLY**
|
||||
CFG_DASHY_BACKUP_STRATEGY=auto
|
||||
CFG_DASHY_UPDATE_TYPE=auto
|
||||
CFG_DASHY_COMPOSE_FILE=default
|
||||
CFG_DASHY_HEALTHCHECK=true
|
||||
CFG_DASHY_AUTHELIA=false
|
||||
|
||||
114
containers/dashy/dashy.sh
Executable file
114
containers/dashy/dashy.sh
Executable file
@ -0,0 +1,114 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Category : Miscellaneous
|
||||
# Description : Dashy - Dashboard Tool (c/t/u/s/r/i):
|
||||
|
||||
installDashy()
|
||||
{
|
||||
local config_variables="$1"
|
||||
|
||||
if [[ "$dashy" == *[cCtTuUsSrRiI]* ]]; then
|
||||
dockerConfigSetupToContainer silent dashy;
|
||||
local app_name=$CFG_DASHY_APP_NAME
|
||||
initializeAppVariables $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$dashy" == *[cC]* ]]; then
|
||||
editAppConfig $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$dashy" == *[tT]* ]]; then
|
||||
dashyToolsMenu;
|
||||
fi
|
||||
|
||||
if [[ "$dashy" == *[uU]* ]]; then
|
||||
dockerUninstallApp $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$dashy" == *[sS]* ]]; then
|
||||
dockerComposeDown $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$dashy" == *[rR]* ]]; then
|
||||
dockerComposeRestart $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$dashy" == *[iI]* ]]; then
|
||||
isHeader "Install $app_name"
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Setting up install folder and config file for $app_name."
|
||||
echo ""
|
||||
|
||||
dockerConfigSetupToContainer "loud" "$app_name" "install" "$config_variables";
|
||||
isSuccessful "Install folders and Config files have been setup for $app_name."
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Setting up the $app_name docker-compose.yml file."
|
||||
echo ""
|
||||
|
||||
dockerComposeSetupFile $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Updating file permissions before starting."
|
||||
echo ""
|
||||
|
||||
fixPermissionsBeforeStart $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running the docker-compose.yml to install and start $app_name"
|
||||
echo ""
|
||||
|
||||
dockerComposeUpdateAndStartApp $app_name install;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running Application specific updates (if required)"
|
||||
echo ""
|
||||
|
||||
appUpdateSpecifics $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running Headscale setup (if required)"
|
||||
echo ""
|
||||
|
||||
setupHeadscale $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Adding $app_name to the Apps Database table."
|
||||
echo ""
|
||||
|
||||
databaseInstallApp $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Updating WebUI config file."
|
||||
echo ""
|
||||
|
||||
webuiContainerSetup $app_name install;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. You can find $app_name files at $containers_dir$app_name"
|
||||
echo ""
|
||||
echo " You can now navigate to your new service using one of the options below : "
|
||||
echo ""
|
||||
|
||||
menuShowFinalMessages $app_name;
|
||||
|
||||
menu_number=0
|
||||
#sleep 3s
|
||||
cd
|
||||
fi
|
||||
dashy=n
|
||||
}
|
||||
@ -4,7 +4,7 @@ networks:
|
||||
|
||||
services:
|
||||
dashy-service: #LIBREPORTAL|SERVICE_TAG_1|dashy-service
|
||||
image: lissy93/dashy:latest #LIBREPORTAL|DASHY_VERSION_TAG|latest
|
||||
image: lissy93/dashy
|
||||
container_name: dashy
|
||||
volumes:
|
||||
- SOCKET_DATA #LIBREPORTAL|SOCKET_TAG|SOCKET_DATA
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Post-install/update specifics for Dashy — dispatched by appUpdateSpecifics.
|
||||
appUpdateSpecifics_dashy() {
|
||||
# Refresh apps-services.json (the source of truth appDashyUpdateConf reads)
|
||||
# before generating dashy's conf.yml. On a first dashy install the file may
|
||||
# not yet reflect dashy itself; on a re-install the previous selection survives.
|
||||
webuiLibrePortalUpdate
|
||||
appDashyUpdateConf
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
{
|
||||
"tools": [
|
||||
{
|
||||
"id": "manage_shortcuts",
|
||||
"label": "Manage Shortcuts",
|
||||
"description": "Pick which apps appear on the Dashy dashboard.",
|
||||
"icon": "🧩",
|
||||
"fields": [
|
||||
{
|
||||
"name": "selected",
|
||||
"label": "Apps to show on the dashboard",
|
||||
"type": "app_urls_multi",
|
||||
"prefillFromCfgKey": "CFG_DASHY_SHORTCUTS",
|
||||
"excludeApps": [
|
||||
"dashy"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
46
containers/focalboard/docker-compose.yml
Executable file
46
containers/focalboard/docker-compose.yml
Executable file
@ -0,0 +1,46 @@
|
||||
networks:
|
||||
DOCKER_NETWORK_DATA: #LIBREPORTAL|DOCKER_NETWORK_TAG|DOCKER_NETWORK_DATA
|
||||
external: true
|
||||
|
||||
services:
|
||||
focalboard-service: #LIBREPORTAL|SERVICE_TAG_1|focalboard-service
|
||||
image: mattermost/focalboard
|
||||
container_name: focalboard-service
|
||||
# GLUETUN_OFF_BEGIN
|
||||
ports:
|
||||
- "PORTS_DATA_1" #LIBREPORTAL|PORTS_TAG_1|PORTS_DATA_1
|
||||
# GLUETUN_OFF_END
|
||||
volumes:
|
||||
- ./data:/data
|
||||
environment:
|
||||
- VIRTUAL_HOST:DOMAINSUBNAME_DATA #LIBREPORTAL|DOMAINSUBNAME_TAG|DOMAINSUBNAME_DATA
|
||||
- VIRTUAL_PORT:8000
|
||||
- TZ:TIMEZONE_DATA #LIBREPORTAL|TIMEZONE_TAG|TIMEZONE_DATA
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
disable: HEALTHCHECK_DATA #LIBREPORTAL|HEALTHCHECK_TAG|HEALTHCHECK_DATA
|
||||
labels:
|
||||
libreportal.category: "CATEGORY_DATA" #LIBREPORTAL|CATEGORY_TAG|CATEGORY_DATA
|
||||
libreportal.title: "TITLE_DATA" #LIBREPORTAL|TITLE_TAG|TITLE_DATA
|
||||
traefik.enable: TRAEFIK_ENABLE_DATA #LIBREPORTAL|TRAEFIK_ENABLE_TAG|TRAEFIK_ENABLE_DATA
|
||||
# TRAEFIK_PORT_1_BEGIN
|
||||
traefik.http.routers.focalboard-service.entrypoints: web,websecure
|
||||
traefik.http.routers.focalboard-service.rule: Host(`DOMAINSUBNAME_DATA_1`) #LIBREPORTAL|DOMAINSUBNAME_TAG_1|DOMAINSUBNAME_DATA_1
|
||||
traefik.http.routers.focalboard-service.tls: true
|
||||
traefik.http.routers.focalboard-service.tls.certresolver: production
|
||||
traefik.http.services.focalboard-service.loadbalancer.server.port: PORT_INTERNAL_DATA_1 #LIBREPORTAL|PORT_INTERNAL_TAG_1|PORT_INTERNAL_DATA_1
|
||||
traefik.http.routers.focalboard-service.middlewares: MIDDLEWARE_DATA_1 #LIBREPORTAL|MIDDLEWARE_TAG_1|MIDDLEWARE_DATA_1
|
||||
# TRAEFIK_PORT_1_END
|
||||
traefik.docker.network: DOCKER_NETWORK_DATA #LIBREPORTAL|DOCKER_NETWORK_TAG|DOCKER_NETWORK_DATA
|
||||
# GLUETUN_OFF_BEGIN
|
||||
networks:
|
||||
DOCKER_NETWORK_DATA: #LIBREPORTAL|DOCKER_NETWORK_TAG|DOCKER_NETWORK_DATA
|
||||
ipv4_address: IP_DATA_1 #LIBREPORTAL|IP_TAG_1|IP_DATA_1
|
||||
# GLUETUN_OFF_END
|
||||
# GLUETUN_ON_BEGIN
|
||||
# network_mode: "container:gluetun-service"
|
||||
# GLUETUN_ON_END
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "1m"
|
||||
57
containers/navidrome/navidrome.config → containers/focalboard/focalboard.config
Normal file → Executable file
57
containers/navidrome/navidrome.config → containers/focalboard/focalboard.config
Normal file → Executable file
@ -5,28 +5,17 @@
|
||||
# APP_NAME = name of application for use in scripts
|
||||
# COMPOSE_FILE = default for no app_name in docker-compose file name, app if there is
|
||||
# BACKUP = if true, include this application in backup operations
|
||||
# UPDATE_TYPE = auto: new image builds are applied automatically (a recovery snapshot is taken first), manual: only when you press Update
|
||||
# HEALTHCHECK = if true, default docker health checks for that container will be enabled
|
||||
# AUTHELIA = if true, use Authelia authentication, if false turned off.
|
||||
# HEADSCALE = options : false, local, remote (see general config). e.g false or local,remote
|
||||
#
|
||||
CFG_NAVIDROME_APP_NAME=navidrome
|
||||
# MULTI_INSTANCE = if true, this app can run as multiple isolated instances
|
||||
# (own data/DB/subdomain/backups) via `libreportal instance create`. Only set on
|
||||
# apps whose compose identity (container_name, Traefik routers, backup labels)
|
||||
# is instance-safe — see scripts/instance/instance_create.sh.
|
||||
CFG_NAVIDROME_MULTI_INSTANCE=true
|
||||
CFG_NAVIDROME_BACKUP=true
|
||||
# STORAGE = which storage location holds this app's data. Change it with
|
||||
# `libreportal app move navidrome <location>` — editing here records intent only.
|
||||
CFG_NAVIDROME_STORAGE=default # Storage Location - Which disk holds this app's data [default:Primary]
|
||||
CFG_NAVIDROME_BACKUP_STRATEGY=auto
|
||||
CFG_NAVIDROME_UPDATE_TYPE=auto
|
||||
CFG_NAVIDROME_COMPOSE_FILE=default
|
||||
CFG_NAVIDROME_HEALTHCHECK=true
|
||||
CFG_NAVIDROME_AUTHELIA=false
|
||||
CFG_NAVIDROME_HEADSCALE=false
|
||||
#
|
||||
CFG_FOCALBOARD_APP_NAME=focalboard
|
||||
CFG_FOCALBOARD_BACKUP=true
|
||||
CFG_FOCALBOARD_COMPOSE_FILE=default
|
||||
CFG_FOCALBOARD_HEALTHCHECK=true
|
||||
CFG_FOCALBOARD_AUTHELIA=false
|
||||
CFG_FOCALBOARD_HEADSCALE=false
|
||||
|
||||
# =============================================================================
|
||||
# METADATA
|
||||
# =============================================================================
|
||||
@ -37,27 +26,27 @@ CFG_NAVIDROME_HEADSCALE=false
|
||||
# URL = source repository or documentation URL
|
||||
# ACTIONS = available actions for this application
|
||||
#
|
||||
CFG_NAVIDROME_CATEGORY="media"
|
||||
CFG_NAVIDROME_TITLE="Navidrome"
|
||||
CFG_NAVIDROME_DESCRIPTION="Music Server"
|
||||
CFG_NAVIDROME_LONG_DESCRIPTION="A self-hosted music streaming server with a Subsonic-compatible API — stream your collection to any device from a modern web player"
|
||||
CFG_NAVIDROME_URL="https://github.com/navidrome/navidrome"
|
||||
CFG_NAVIDROME_ACTIONS="configure|install|restart|shutdown|uninstall"
|
||||
#
|
||||
CFG_FOCALBOARD_CATEGORY="productivity"
|
||||
CFG_FOCALBOARD_TITLE="Focalboard"
|
||||
CFG_FOCALBOARD_DESCRIPTION="Project Management"
|
||||
CFG_FOCALBOARD_LONG_DESCRIPTION="Focalboard is an open source, self-hosted alternative to Trello, Notion, and Asana that helps organize projects and tasks"
|
||||
CFG_FOCALBOARD_URL="https://github.com/mattermost/focalboard"
|
||||
CFG_FOCALBOARD_ACTIONS="configure|install|restart|shutdown|uninstall"
|
||||
|
||||
# =============================================================================
|
||||
# NETWORK CONFIGURATION
|
||||
# =============================================================================
|
||||
# DOMAIN = number of domain from the general config, useful when using multiple domains
|
||||
# WHITELIST = if true only allow whitelisted ips (see general config), if false allow all
|
||||
#
|
||||
CFG_NAVIDROME_DOMAIN=1
|
||||
CFG_NAVIDROME_WHITELIST=false
|
||||
CFG_NAVIDROME_NETWORK=default
|
||||
#
|
||||
CFG_FOCALBOARD_DOMAIN=1
|
||||
CFG_FOCALBOARD_WHITELIST=false
|
||||
CFG_FOCALBOARD_NETWORK=default
|
||||
|
||||
# =============================================================================
|
||||
# PORT CONFIGURATION
|
||||
# =============================================================================
|
||||
# PORT_ = port configuration: app|name|external:internal|access|protocol|login|traefik|webui|description|paths|subdomain
|
||||
# PORT_ = port configuration: app|name|external:internal|access|protocol|login|traefik|webui|description
|
||||
# - app: application name
|
||||
# - name: service identifier (webui, dns, ssh, etc.)
|
||||
# - external:internal: port mapping (external can be 'random' for auto-allocation)
|
||||
@ -68,4 +57,10 @@ CFG_NAVIDROME_NETWORK=default
|
||||
# - webui: if true, this port serves the main web interface
|
||||
# - description: human-readable description of the service
|
||||
#
|
||||
CFG_NAVIDROME_PORT_1="navidrome-service|webui|random:4533|public|tcp|false|true|true|Music Server||music"
|
||||
CFG_FOCALBOARD_PORT_1="focalboard-service|webui|random:8000|public|tcp|false|true|true|Web Interface||board"
|
||||
|
||||
# AUTH_PROFILE = capability tier for the WebUI auth tools (single_password | user_password | multi_user)
|
||||
CFG_FOCALBOARD_AUTH_PROFILE=multi_user
|
||||
CFG_FOCALBOARD_ADMIN_USER=
|
||||
CFG_FOCALBOARD_ADMIN_EMAIL=
|
||||
CFG_FOCALBOARD_ADMIN_PASSWORD=RANDOMIZEDPASSWORD1
|
||||
110
containers/focalboard/focalboard.sh
Executable file
110
containers/focalboard/focalboard.sh
Executable file
@ -0,0 +1,110 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Category : Productivity
|
||||
# Description : Focalboard - Project Management (c/u/s/r/i):
|
||||
|
||||
installFocalboard()
|
||||
{
|
||||
local config_variables="$1"
|
||||
|
||||
if [[ "$focalboard" == *[cCtTuUsSrRiI]* ]]; then
|
||||
dockerConfigSetupToContainer silent focalboard;
|
||||
local app_name=$CFG_FOCALBOARD_APP_NAME
|
||||
initializeAppVariables $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$focalboard" == *[cC]* ]]; then
|
||||
editAppConfig $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$focalboard" == *[uU]* ]]; then
|
||||
dockerUninstallApp $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$focalboard" == *[sS]* ]]; then
|
||||
dockerComposeDown $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$focalboard" == *[rR]* ]]; then
|
||||
dockerComposeRestart $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$focalboard" == *[iI]* ]]; then
|
||||
isHeader "Install $app_name"
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Setting up install folder and config file for $app_name."
|
||||
echo ""
|
||||
|
||||
dockerConfigSetupToContainer "loud" "$app_name" "install" "$config_variables";
|
||||
isSuccessful "Install folders and Config files have been setup for $app_name."
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Setting up the $app_name docker-compose.yml file."
|
||||
echo ""
|
||||
|
||||
dockerComposeSetupFile $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Updating file permissions before starting."
|
||||
echo ""
|
||||
|
||||
fixPermissionsBeforeStart $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running the docker-compose.yml to install and start $app_name"
|
||||
echo ""
|
||||
|
||||
dockerComposeUpdateAndStartApp $app_name install;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running Application specific updates (if required)"
|
||||
echo ""
|
||||
|
||||
appUpdateSpecifics $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running Headscale setup (if required)"
|
||||
echo ""
|
||||
|
||||
setupHeadscale $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Adding $app_name to the Apps Database table."
|
||||
echo ""
|
||||
|
||||
databaseInstallApp $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Updating WebUI config file."
|
||||
echo ""
|
||||
|
||||
webuiContainerSetup $app_name install;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. You can find $app_name files at $containers_dir$app_name"
|
||||
echo ""
|
||||
echo " You can now navigate to your $app_name service using one of the options below : "
|
||||
echo ""
|
||||
|
||||
menuShowFinalMessages $app_name;
|
||||
|
||||
menu_number=0
|
||||
#sleep 3s
|
||||
cd
|
||||
fi
|
||||
focalboard=n
|
||||
}
|
||||
0
containers/libreportal/frontend/core/icons/apps/focalboard.svg → containers/focalboard/focalboard.svg
Normal file → Executable file
0
containers/libreportal/frontend/core/icons/apps/focalboard.svg → containers/focalboard/focalboard.svg
Normal file → Executable file
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@ -5,7 +5,7 @@ networks:
|
||||
services:
|
||||
gitea-service: #LIBREPORTAL|SERVICE_TAG_1|gitea-service
|
||||
container_name: gitea-service
|
||||
image: gitea/gitea:latest #LIBREPORTAL|GITEA_VERSION_TAG|latest
|
||||
image: gitea/gitea:latest
|
||||
restart: unless-stopped
|
||||
# GLUETUN_OFF_BEGIN
|
||||
depends_on:
|
||||
@ -41,7 +41,7 @@ services:
|
||||
- TZ:TIMEZONE_DATA #LIBREPORTAL|TIMEZONE_TAG|TIMEZONE_DATA
|
||||
# >>> libreportal-monitoring >>>
|
||||
#- GITEA__metrics__ENABLED=true
|
||||
#- GITEA__metrics__TOKEN=GITEA_METRICS_TOKEN_1_DATA #LIBREPORTAL|GITEA_METRICS_TOKEN_1_TAG|GITEA_METRICS_TOKEN_1_DATA
|
||||
#- GITEA__metrics__TOKEN=GITEA_METRICS_TOKEN_DATA #LIBREPORTAL|GITEA_METRICS_TOKEN_TAG|GITEA_METRICS_TOKEN_DATA
|
||||
# <<< libreportal-monitoring <<<
|
||||
# GLUETUN_OFF_BEGIN
|
||||
ports:
|
||||
@ -64,8 +64,6 @@ services:
|
||||
labels:
|
||||
libreportal.category: "CATEGORY_DATA" #LIBREPORTAL|CATEGORY_TAG|CATEGORY_DATA
|
||||
libreportal.title: "TITLE_DATA" #LIBREPORTAL|TITLE_TAG|TITLE_DATA
|
||||
libreportal.backup.db: "sqlite:::data/gitea/gitea/gitea.db"
|
||||
libreportal.backup.files: "gitea-service:/data:data/gitea"
|
||||
traefik.enable: TRAEFIK_ENABLE_DATA #LIBREPORTAL|TRAEFIK_ENABLE_TAG|TRAEFIK_ENABLE_DATA
|
||||
# TRAEFIK_PORT_1_BEGIN
|
||||
traefik.http.routers.gitea-service.entrypoints: web,websecure
|
||||
|
||||
@ -5,7 +5,6 @@
|
||||
# APP_NAME = name of application for use in scripts
|
||||
# COMPOSE_FILE = default for no app_name in docker-compose file name, app if there is
|
||||
# BACKUP = if true, include this application in backup operations
|
||||
# UPDATE_TYPE = auto: new image builds are applied automatically (a recovery snapshot is taken first), manual: only when you press Update
|
||||
# HEALTHCHECK = if true, default docker health checks for that container will be enabled
|
||||
# AUTHELIA = if true, use Authelia authentication, if false turned off.
|
||||
# HEADSCALE = options : false, local, remote (see general config). e.g false or local,remote
|
||||
@ -13,23 +12,13 @@
|
||||
# METRICS_TOKEN = bearer token guarding /metrics (rides the public web port); auto-generated, mirrored into the Prometheus scrape config
|
||||
#
|
||||
CFG_GITEA_APP_NAME=gitea
|
||||
# MULTI_INSTANCE = if true, this app can run as multiple isolated instances
|
||||
# (own data/DB/subdomain/backups) via `libreportal instance create`. Only set on
|
||||
# apps whose compose identity (container_name, Traefik routers, backup labels)
|
||||
# is instance-safe — see scripts/instance/instance_create.sh.
|
||||
CFG_GITEA_MULTI_INSTANCE=true
|
||||
CFG_GITEA_BACKUP=true
|
||||
# STORAGE = which storage location holds this app's data. Change it with
|
||||
# `libreportal app move gitea <location>` — editing here records intent only.
|
||||
CFG_GITEA_STORAGE=default # Storage Location - Which disk holds this app's data [default:Primary]
|
||||
CFG_GITEA_BACKUP_STRATEGY=auto
|
||||
CFG_GITEA_UPDATE_TYPE=auto
|
||||
CFG_GITEA_COMPOSE_FILE=default
|
||||
CFG_GITEA_HEALTHCHECK=true
|
||||
CFG_GITEA_AUTHELIA=false
|
||||
CFG_GITEA_HEADSCALE=false
|
||||
CFG_GITEA_MONITORING=false
|
||||
CFG_GITEA_METRICS_TOKEN_1=RANDOMIZEDPASSWORD1
|
||||
CFG_GITEA_METRICS_TOKEN=RANDOMIZEDPASSWORD1
|
||||
|
||||
# =============================================================================
|
||||
# METADATA
|
||||
@ -44,7 +33,7 @@ CFG_GITEA_METRICS_TOKEN_1=RANDOMIZEDPASSWORD1
|
||||
CFG_GITEA_CATEGORY="development"
|
||||
CFG_GITEA_TITLE="Gitea"
|
||||
CFG_GITEA_DESCRIPTION="Git Repository Management"
|
||||
CFG_GITEA_LONG_DESCRIPTION="A lightweight, self-hosted Git service for your own repositories, issues and pull requests"
|
||||
CFG_GITEA_LONG_DESCRIPTION="Gitea is a lightweight, self-hosted Git service written in Go that provides a painless self-hosted Git service with a minimal setup"
|
||||
CFG_GITEA_URL="https://github.com/go-gitea/gitea"
|
||||
CFG_GITEA_ACTIONS="configure|install|restart|shutdown|uninstall"
|
||||
#
|
||||
@ -75,13 +64,8 @@ CFG_GITEA_NETWORK=default
|
||||
CFG_GITEA_PORT_1="gitea-service|webui|random:3000|public|tcp|false|true|true|Web Interface||gitea"
|
||||
CFG_GITEA_PORT_2="gitea-service|ssh|random:22|private|tcp|false|false|false|Git SSH Access|"
|
||||
|
||||
# AUTH_PROFILE = capability tier for the WebUI auth tools (single_password | user_password | multi_user)
|
||||
CFG_GITEA_AUTH_PROFILE=multi_user
|
||||
CFG_GITEA_ADMIN_USER=
|
||||
#
|
||||
# Recorded by the auth tools, not by the installer. Gitea seeds no account at
|
||||
# install — the first admin is created from the WebUI (Create Account), and the
|
||||
# adapter writes the username and password here so the credentials card can show
|
||||
# them. Deliberately empty and unslotted: a generated password would name an
|
||||
# account that does not exist, and the slot number marks a value the installer
|
||||
# generates.
|
||||
#
|
||||
CFG_GITEA_ADMIN_PASSWORD=
|
||||
CFG_GITEA_ADMIN_EMAIL=
|
||||
CFG_GITEA_ADMIN_PASSWORD=RANDOMIZEDPASSWORD1
|
||||
|
||||
139
containers/gitea/gitea.sh
Executable file
139
containers/gitea/gitea.sh
Executable file
@ -0,0 +1,139 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Category : Development & Version Control
|
||||
# Description : Gitea - Git Repository Management (c/u/s/r/i):
|
||||
|
||||
installGitea()
|
||||
{
|
||||
local config_variables="$1"
|
||||
|
||||
if [[ "$gitea" == *[cCtTuUsSrRiI]* ]]; then
|
||||
dockerConfigSetupToContainer silent gitea;
|
||||
local app_name=$CFG_GITEA_APP_NAME
|
||||
initializeAppVariables $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$gitea" == *[cC]* ]]; then
|
||||
editAppConfig $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$gitea" == *[uU]* ]]; then
|
||||
dockerUninstallApp $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$gitea" == *[sS]* ]]; then
|
||||
dockerComposeDown $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$gitea" == *[rR]* ]]; then
|
||||
dockerComposeRestart $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$gitea" == *[iI]* ]]; then
|
||||
isHeader "Install $app_name"
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Setting up install folder and config file for $app_name."
|
||||
echo ""
|
||||
|
||||
dockerConfigSetupToContainer "loud" "$app_name" "install" "$config_variables";
|
||||
isSuccessful "Install folders and Config files have been setup for $app_name."
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Setting up the $app_name docker-compose.yml file."
|
||||
echo ""
|
||||
|
||||
dockerComposeSetupFile $app_name;
|
||||
|
||||
# Enable Gitea's /metrics endpoint only when CFG_GITEA_MONITORING=true
|
||||
# (toggles the libreportal-monitoring marker block in the compose).
|
||||
monitoringToggleAppConfig "$app_name" "docker-compose.yml";
|
||||
|
||||
# /metrics rides Gitea's public web port, so it's locked behind a
|
||||
# bearer token. CFG_GITEA_METRICS_TOKEN lives in the .config (filled
|
||||
# once by the RANDOMIZEDPASSWORD scanner, preserved across reinstalls)
|
||||
# and reaches the compose via the GITEA_METRICS_TOKEN_TAG tag — mirror
|
||||
# that same value into the Prometheus scrape fragment so the two agree.
|
||||
if monitoringAppEnabled "$app_name"; then
|
||||
if [[ -n "$CFG_GITEA_METRICS_TOKEN" ]]; then
|
||||
sudo sed -i "s|GITEA_METRICS_TOKEN_PLACEHOLDER|${CFG_GITEA_METRICS_TOKEN}|g" \
|
||||
"$containers_dir$app_name/resources/monitoring/prometheus-scrape.yml"
|
||||
checkSuccess "Synced Gitea /metrics token to the Prometheus scrape config"
|
||||
else
|
||||
isNotice "CFG_GITEA_METRICS_TOKEN is empty — Gitea /metrics scrape may 401."
|
||||
fi
|
||||
fi
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Updating file permissions before starting."
|
||||
echo ""
|
||||
|
||||
fixPermissionsBeforeStart $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running the docker-compose.yml to install and start $app_name"
|
||||
echo ""
|
||||
|
||||
dockerComposeUpdateAndStartApp $app_name install;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running Application specific updates (if required)"
|
||||
echo ""
|
||||
|
||||
appUpdateSpecifics $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running Headscale setup (if required)"
|
||||
echo ""
|
||||
|
||||
setupHeadscale $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Adding $app_name to the Apps Database table."
|
||||
echo ""
|
||||
|
||||
databaseInstallApp $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Updating WebUI config file."
|
||||
echo ""
|
||||
|
||||
webuiContainerSetup $app_name install;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Refreshing monitoring integration."
|
||||
echo ""
|
||||
|
||||
# Self-correcting: adds Gitea's scrape target + dashboard to
|
||||
# Prometheus/Grafana when CFG_GITEA_MONITORING=true, removes them when
|
||||
# it's off. No-ops with a notice if either app isn't installed.
|
||||
monitoringRefreshAll;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. You can find $app_name files at $containers_dir$app_name"
|
||||
echo ""
|
||||
echo " You can now navigate to your $app_name service using one of the options below : "
|
||||
echo ""
|
||||
|
||||
menuShowFinalMessages $app_name;
|
||||
|
||||
menu_number=0
|
||||
#sleep 3s
|
||||
cd
|
||||
fi
|
||||
gitea=n
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Gitea install hooks — mirror CFG_GITEA_METRICS_TOKEN_1 into the Prometheus
|
||||
# scrape fragment so the bearer token in the compose env matches what the
|
||||
# Prometheus side sends.
|
||||
|
||||
gitea_install_post_compose()
|
||||
{
|
||||
local app_name="$1"
|
||||
|
||||
# The driver already ran monitoringToggleAppConfig "$app_name" docker-compose.yml,
|
||||
# so the metrics block reflects CFG_GITEA_MONITORING. /metrics rides
|
||||
# Gitea's public web port and is token-protected; sync the token into
|
||||
# the scrape config so the two sides agree.
|
||||
if monitoringAppEnabled "$app_name"; then
|
||||
if [[ -n "$CFG_GITEA_METRICS_TOKEN_1" ]]; then
|
||||
local result
|
||||
result=$(runFileOp sed -i "s|GITEA_METRICS_TOKEN_PLACEHOLDER|${CFG_GITEA_METRICS_TOKEN_1}|g" \
|
||||
"$(appDir "$app_name")/resources/monitoring/prometheus-scrape.yml")
|
||||
checkSuccess "Synced Gitea /metrics token to the Prometheus scrape config"
|
||||
else
|
||||
isNotice "CFG_GITEA_METRICS_TOKEN_1 is empty — Gitea /metrics scrape may 401."
|
||||
fi
|
||||
fi
|
||||
}
|
||||
@ -1,105 +0,0 @@
|
||||
{
|
||||
"tools": [
|
||||
{
|
||||
"id": "reset_password",
|
||||
"category": "users",
|
||||
"label": "Reset User Password",
|
||||
"description": "Set a new password for an existing user. Leave blank to generate one.",
|
||||
"icon": "🔑",
|
||||
"fields": [
|
||||
{
|
||||
"name": "username",
|
||||
"label": "Username",
|
||||
"type": "text",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "password",
|
||||
"label": "New password",
|
||||
"type": "password",
|
||||
"placeholder": "Leave blank to generate"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "create_account",
|
||||
"category": "users",
|
||||
"label": "Create User Account",
|
||||
"description": "Add a new user. Tick \"Make admin\" for full rights.",
|
||||
"icon": "👤",
|
||||
"fields": [
|
||||
{
|
||||
"name": "username",
|
||||
"label": "Username",
|
||||
"type": "text",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "email",
|
||||
"label": "Email",
|
||||
"type": "text",
|
||||
"placeholder": "user@example.com",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "password",
|
||||
"label": "Password",
|
||||
"type": "password",
|
||||
"placeholder": "Leave blank to generate"
|
||||
},
|
||||
{
|
||||
"name": "admin",
|
||||
"label": "Make admin",
|
||||
"type": "checkbox",
|
||||
"default": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "list_users",
|
||||
"category": "users",
|
||||
"label": "List Users",
|
||||
"description": "List every user.",
|
||||
"icon": "📋",
|
||||
"fields": []
|
||||
},
|
||||
{
|
||||
"id": "delete_user",
|
||||
"category": "users",
|
||||
"label": "Delete User Account",
|
||||
"description": "Permanently remove a user.",
|
||||
"icon": "🗑",
|
||||
"destructive": true,
|
||||
"confirm": "This cannot be undone.",
|
||||
"fields": [
|
||||
{
|
||||
"name": "username",
|
||||
"label": "Username",
|
||||
"type": "text",
|
||||
"required": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "set_admin",
|
||||
"category": "users",
|
||||
"label": "Set Admin Status",
|
||||
"description": "Promote or demote a user.",
|
||||
"icon": "👑",
|
||||
"fields": [
|
||||
{
|
||||
"name": "username",
|
||||
"label": "Username",
|
||||
"type": "text",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "admin",
|
||||
"label": "Make admin",
|
||||
"type": "checkbox",
|
||||
"default": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -5,7 +5,7 @@ networks:
|
||||
services:
|
||||
gluetun-service: #LIBREPORTAL|SERVICE_TAG_1|gluetun-service
|
||||
container_name: gluetun-service
|
||||
image: qmcgaw/gluetun:latest #LIBREPORTAL|GLUETUN_VERSION_TAG|latest
|
||||
image: qmcgaw/gluetun:latest
|
||||
restart: unless-stopped
|
||||
hostname: gluetun
|
||||
cap_add:
|
||||
|
||||
@ -5,24 +5,13 @@
|
||||
# APP_NAME = name of application for use in scripts
|
||||
# COMPOSE_FILE = default for no app_name in docker-compose file name, app if there is
|
||||
# BACKUP = if true, include this application in backup operations
|
||||
# UPDATE_TYPE = auto: new image builds are applied automatically (a recovery snapshot is taken first), manual: only when you press Update
|
||||
# HEALTHCHECK = if true, default docker health checks for that container will be enabled
|
||||
# AUTHELIA = if true, use Authelia authentication, if false turned off.
|
||||
# HEADSCALE = options : false, local, remote (see general config). e.g false or local,remote
|
||||
# MONITORING = if true, export this app's metrics to Prometheus + Grafana (needs both apps installed)
|
||||
#
|
||||
CFG_GLUETUN_APP_NAME=gluetun
|
||||
# MULTI_INSTANCE = if true, this app can run as multiple isolated instances
|
||||
# (own data/DB/subdomain/backups) via `libreportal instance create`. Only set on
|
||||
# apps whose compose identity (container_name, Traefik routers, backup labels)
|
||||
# is instance-safe — see scripts/instance/instance_create.sh.
|
||||
# Not instanced by design. A network provider, not a destination: other apps join it with network_mode "container:gluetun-service". A second copy leaves that name ambiguous.
|
||||
CFG_GLUETUN_MULTI_INSTANCE=false
|
||||
CFG_GLUETUN_BACKUP=true
|
||||
# STORAGE = which storage location holds this app's data.
|
||||
CFG_GLUETUN_STORAGE=default # Storage Location - Fixed: other apps reference gluetun by path **READONLY**
|
||||
CFG_GLUETUN_BACKUP_STRATEGY=auto
|
||||
CFG_GLUETUN_UPDATE_TYPE=auto
|
||||
CFG_GLUETUN_COMPOSE_FILE=default
|
||||
CFG_GLUETUN_HEALTHCHECK=true
|
||||
CFG_GLUETUN_AUTHELIA=false
|
||||
@ -48,6 +37,7 @@ CFG_GLUETUN_OPENVPN_USER=
|
||||
CFG_GLUETUN_OPENVPN_PASSWORD=
|
||||
CFG_GLUETUN_WIREGUARD_PRIVATE_KEY=
|
||||
CFG_GLUETUN_WIREGUARD_ADDRESSES=
|
||||
CFG_GLUETUN_CONTROL_SERVER_API_KEY=RANDOMIZEDPASSWORD1
|
||||
# HEALTH_TARGETS = comma-separated host:port list pinged over HTTPS to
|
||||
# confirm the VPN tunnel is healthy. Defaults are privacy-respecting
|
||||
# (Mullvad — your VPN provider; EFF — privacy non-profit). Override
|
||||
@ -58,13 +48,6 @@ CFG_GLUETUN_WIREGUARD_ADDRESSES=
|
||||
#
|
||||
CFG_GLUETUN_HEALTH_TARGETS="mullvad.net:443,eff.org:443"
|
||||
CFG_GLUETUN_HEALTH_ICMP_IPS="9.9.9.9"
|
||||
# PROVIDERS_REFRESH_HOURS = how often (hours) to re-fetch gluetun's upstream
|
||||
# server list, which powers the provider/country pickers. It's a few MB and
|
||||
# rarely changes, so we don't pull it on every WebUI update. Lower it for a
|
||||
# fresher list, raise it on a slow/metered link, or set 0 to never auto-fetch
|
||||
# (refresh on demand from the Tools tab instead). Default 24.
|
||||
#
|
||||
CFG_GLUETUN_PROVIDERS_REFRESH_HOURS=24
|
||||
#
|
||||
# =============================================================================
|
||||
# METADATA
|
||||
@ -79,7 +62,7 @@ CFG_GLUETUN_PROVIDERS_REFRESH_HOURS=24
|
||||
CFG_GLUETUN_CATEGORY="networking,recommended"
|
||||
CFG_GLUETUN_TITLE="Gluetun"
|
||||
CFG_GLUETUN_DESCRIPTION="VPN Container Router"
|
||||
CFG_GLUETUN_LONG_DESCRIPTION="Run all of your containers through a VPN provider. 30+ providers over WireGuard and OpenVPN, with a built-in kill-switch and port forwarding"
|
||||
CFG_GLUETUN_LONG_DESCRIPTION="Run all of your containers through a VPN provider. Supports 30+ providers over WireGuard and OpenVPN with a built-in kill-switch, DNS-over-TLS, port forwarding, and an HTTP control server."
|
||||
CFG_GLUETUN_URL="https://github.com/qdm12/gluetun"
|
||||
CFG_GLUETUN_ACTIONS="configure|install|restart|shutdown|uninstall"
|
||||
#
|
||||
|
||||
136
containers/gluetun/gluetun.sh
Normal file
136
containers/gluetun/gluetun.sh
Normal file
@ -0,0 +1,136 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Category : Networking
|
||||
# Description : Gluetun - VPN client for routing other containers (c/u/s/r/i):
|
||||
|
||||
installGluetun()
|
||||
{
|
||||
local config_variables="$1"
|
||||
|
||||
if [[ "$gluetun" == *[cCtTuUsSrRiI]* ]]; then
|
||||
dockerConfigSetupToContainer silent gluetun;
|
||||
local app_name=$CFG_GLUETUN_APP_NAME
|
||||
initializeAppVariables $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$gluetun" == *[cC]* ]]; then
|
||||
editAppConfig $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$gluetun" == *[uU]* ]]; then
|
||||
dockerUninstallApp $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$gluetun" == *[sS]* ]]; then
|
||||
dockerComposeDown $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$gluetun" == *[rR]* ]]; then
|
||||
dockerComposeRestart $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$gluetun" == *[iI]* ]]; then
|
||||
isHeader "Install $app_name"
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Setting up install folder and config file for $app_name."
|
||||
echo ""
|
||||
|
||||
dockerConfigSetupToContainer "loud" "$app_name" "install" "$config_variables";
|
||||
isSuccessful "Install folders and Config files have been setup for $app_name."
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Setting up the $app_name docker-compose.yml file."
|
||||
echo ""
|
||||
|
||||
dockerComposeSetupFile $app_name;
|
||||
|
||||
monitoringToggleAppConfig "$app_name" "docker-compose.yml";
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Updating file permissions before starting."
|
||||
echo ""
|
||||
|
||||
fixPermissionsBeforeStart $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running the docker-compose.yml to install and start $app_name"
|
||||
echo ""
|
||||
|
||||
dockerComposeUpdateAndStartApp $app_name install;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running Application specific updates (if required)"
|
||||
echo ""
|
||||
|
||||
appUpdateSpecifics $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running Headscale setup (if required)"
|
||||
echo ""
|
||||
|
||||
setupHeadscale $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Adding $app_name to the Apps Database table."
|
||||
echo ""
|
||||
|
||||
databaseInstallApp $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Updating the WebUI config file."
|
||||
echo ""
|
||||
|
||||
webuiContainerSetup $app_name install;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Refreshing Gluetun provider snapshot."
|
||||
echo ""
|
||||
|
||||
webuiGenerateGluetunProviders;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Re-attaching gluetun-routed apps (post-recreate)."
|
||||
echo ""
|
||||
|
||||
# Gluetun was just (re)created — every existing routed app holds a
|
||||
# stale container ID in its network_mode. Reattach them now so the
|
||||
# user doesn't have to chase silent netns drift later.
|
||||
appGluetunRoutedRecreate
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Routing existing apps through Gluetun (optional)."
|
||||
echo ""
|
||||
|
||||
gluetunRouteExistingAppsPrompt;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Refreshing monitoring integration."
|
||||
echo ""
|
||||
|
||||
monitoringRefreshAll;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. You can find $app_name files at $containers_dir$app_name"
|
||||
echo ""
|
||||
|
||||
menuShowFinalMessages "$app_name";
|
||||
|
||||
menu_number=0
|
||||
cd
|
||||
fi
|
||||
gluetun=n
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# App-specific compose tags for Gluetun (VPN gateway) + its forwarded-port wiring.
|
||||
appSetupComposeTags_gluetun() {
|
||||
local full_file_path="$1"
|
||||
tagsManagerUpdateUniversalTag "$full_file_path" "GLUETUN_VPN_SERVICE_PROVIDER_TAG" "$CFG_GLUETUN_VPN_SERVICE_PROVIDER"
|
||||
tagsManagerUpdateUniversalTag "$full_file_path" "GLUETUN_VPN_TYPE_TAG" "$CFG_GLUETUN_VPN_TYPE"
|
||||
tagsManagerUpdateUniversalTag "$full_file_path" "GLUETUN_VPN_COUNTRIES_TAG" "$CFG_GLUETUN_VPN_COUNTRIES"
|
||||
tagsManagerUpdateUniversalTag "$full_file_path" "GLUETUN_OPENVPN_USER_TAG" "$CFG_GLUETUN_OPENVPN_USER"
|
||||
tagsManagerUpdateUniversalTag "$full_file_path" "GLUETUN_OPENVPN_PASSWORD_TAG" "$CFG_GLUETUN_OPENVPN_PASSWORD"
|
||||
tagsManagerUpdateUniversalTag "$full_file_path" "GLUETUN_WIREGUARD_PRIVATE_KEY_TAG" "$CFG_GLUETUN_WIREGUARD_PRIVATE_KEY"
|
||||
tagsManagerUpdateUniversalTag "$full_file_path" "GLUETUN_WIREGUARD_ADDRESSES_TAG" "$CFG_GLUETUN_WIREGUARD_ADDRESSES"
|
||||
tagsManagerUpdateUniversalTag "$full_file_path" "GLUETUN_HEALTH_TARGETS_TAG" "${CFG_GLUETUN_HEALTH_TARGETS:-mullvad.net:443,eff.org:443}"
|
||||
tagsManagerUpdateUniversalTag "$full_file_path" "GLUETUN_HEALTH_ICMP_IPS_TAG" "${CFG_GLUETUN_HEALTH_ICMP_IPS:-9.9.9.9}"
|
||||
appNetworkRegisterPorts_gluetun
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Gluetun install hooks — post-start provider snapshot refresh + reattach
|
||||
# any apps routed through gluetun (their network_mode holds a stale
|
||||
# container ID after gluetun was just recreated) + offer to onboard
|
||||
# existing apps.
|
||||
|
||||
gluetun_install_post_start()
|
||||
{
|
||||
local app_name="$1"
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Refreshing Gluetun provider snapshot."
|
||||
echo ""
|
||||
|
||||
GLUETUN_PROVIDERS_FORCE=1 appWebuiRefresh_gluetun
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Re-attaching gluetun-routed apps (post-recreate)."
|
||||
echo ""
|
||||
|
||||
# Gluetun was just (re)created — every existing routed app holds a
|
||||
# stale container ID in its network_mode. Reattach them now so the
|
||||
# user doesn't have to chase silent netns drift later.
|
||||
appGluetunRoutedRecreate
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Routing existing apps through Gluetun (optional)."
|
||||
echo ""
|
||||
|
||||
gluetunRouteExistingAppsPrompt
|
||||
}
|
||||
@ -1,122 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Per-app routine WebUI-refresh hook (appWebuiRefresh_<app>): webui_updater calls
|
||||
# it on every WebUI update while gluetun is installed; the installer (gluetun.sh)
|
||||
# and the 'gluetun_refresh_providers' tool call it directly too.
|
||||
#
|
||||
# Fetches gluetun's upstream servers.json, slims it down to a
|
||||
# { providers: { <name>: { vpnTypes, countries } } } shape, and writes it
|
||||
# to the WebUI data dir so the per-app config dropdowns stay honest as
|
||||
# gluetun adds/removes providers and protocols. Falls back silently to the
|
||||
# previous snapshot (or the bundled default) on network failure.
|
||||
appWebuiRefresh_gluetun() {
|
||||
local output_file="$(webuiDir)/frontend/data/apps/generated/gluetun-providers.json"
|
||||
local upstream="https://raw.githubusercontent.com/qdm12/gluetun/master/internal/storage/servers.json"
|
||||
local tmp="$(mktemp)"
|
||||
local raw="${output_file}.raw.$$"
|
||||
|
||||
runFileOp mkdir -p "$(dirname "$output_file")"
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
isNotice "jq not installed; skipping gluetun provider refresh."
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Refresh throttle. The WebUI updater calls this hook on every routine
|
||||
# update (the task processor fires it repeatedly), but the upstream server
|
||||
# list is a few MB and changes rarely — re-fetching it each time adds a
|
||||
# silent, slow-on-a-bad-link stall to every update. So only reach upstream
|
||||
# once per refresh window. Direct callers that want a guaranteed pull (the
|
||||
# Tools "refresh" button, the install hook) set GLUETUN_PROVIDERS_FORCE=1.
|
||||
# CFG_GLUETUN_PROVIDERS_REFRESH_HOURS=0 disables auto-fetch (manual only).
|
||||
local refresh_hours="${CFG_GLUETUN_PROVIDERS_REFRESH_HOURS:-24}"
|
||||
local stamp="/tmp/libreportal_gluetun_providers_checked"
|
||||
if [[ "${GLUETUN_PROVIDERS_FORCE:-0}" != "1" ]]; then
|
||||
if [[ "$refresh_hours" == "0" ]]; then
|
||||
return 0
|
||||
fi
|
||||
if [[ -s "$output_file" ]]; then
|
||||
local _now _last
|
||||
_now=$(date +%s)
|
||||
_last=$(stat -c '%Y' "$stamp" 2>/dev/null || echo 0)
|
||||
if (( _now - _last < refresh_hours * 3600 )); then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# GitHub raw only sends ETag (no Last-Modified), so use If-None-Match
|
||||
# via a sidecar to skip the multi-MB body when nothing has changed
|
||||
# upstream. --compressed asks for gzip too (this JSON shrinks ~7x), and
|
||||
# --connect-timeout fails fast on a dead link instead of hanging.
|
||||
local etag_file="${output_file}.etag"
|
||||
local etag=""
|
||||
[[ -s "$etag_file" ]] && etag=$(<"$etag_file")
|
||||
local headers="${output_file}.hdr.$$"
|
||||
local http_code
|
||||
http_code=$(curl -sSL --compressed \
|
||||
${etag:+-H "If-None-Match: $etag"} \
|
||||
--connect-timeout 15 \
|
||||
--speed-limit 1000 --speed-time 30 \
|
||||
--retry 2 --retry-delay 2 --retry-all-errors \
|
||||
-D "$headers" -o "$raw" \
|
||||
-w '%{http_code}' "$upstream") || http_code=""
|
||||
|
||||
# Whatever the outcome (fresh copy, 304, or a failed/slow link), record
|
||||
# that we just checked so the throttle window restarts — a persistent
|
||||
# outage then can't re-stall every subsequent update.
|
||||
touch "$stamp" 2>/dev/null || true
|
||||
|
||||
# $raw and $headers live next to $output_file (under containers_dir/
|
||||
# libreportal/frontend/data/, dockerinstall-owned in rootless). The
|
||||
# manager can't `rm` them directly without a Permission denied — same
|
||||
# class of bug as the updateConfigOption sed-i issue. runFileOp routes
|
||||
# the rm through the right user. $tmp is from mktemp (/tmp), so
|
||||
# `rm -f $tmp` stays unwrapped.
|
||||
if [[ "$http_code" == "304" ]]; then
|
||||
runFileOp rm -f "$raw" "$headers"
|
||||
return 0
|
||||
fi
|
||||
if [[ "$http_code" != "200" ]]; then
|
||||
isNotice "Upstream fetch failed (${http_code:-no response}); keeping existing snapshot."
|
||||
runFileOp rm -f "$raw" "$headers"
|
||||
return 0
|
||||
fi
|
||||
local new_etag
|
||||
new_etag=$(awk 'tolower($1)=="etag:"{print $2}' "$headers" | tr -d '\r')
|
||||
runFileOp rm -f "$headers"
|
||||
|
||||
# servers.json is a top-level object keyed by provider; each provider
|
||||
# entry has a `servers` array whose items have `vpn` (wireguard|openvpn),
|
||||
# `country`, `city`, etc. We collapse that into per-provider unique
|
||||
# vpn-type and country lists. Drop the `version` key (it's not a provider).
|
||||
if ! jq '
|
||||
[ to_entries[]
|
||||
| select(.key != "version")
|
||||
| { key: .key,
|
||||
value: {
|
||||
vpnTypes: ((.value.servers // []) | map(.vpn) | unique | map(select(. != null and . != ""))),
|
||||
countries: ((.value.servers // []) | map(.country) | unique | map(select(. != null and . != "")))
|
||||
}
|
||||
}
|
||||
]
|
||||
| from_entries
|
||||
| { providers: . }
|
||||
' "$raw" > "$tmp" 2>/dev/null; then
|
||||
isNotice "Failed to parse gluetun servers.json; keeping existing provider snapshot."
|
||||
runFileOp rm -f "$raw"
|
||||
rm -f "$tmp"
|
||||
return 0
|
||||
fi
|
||||
|
||||
runFileOp rm -f "$raw"
|
||||
|
||||
if [ -s "$tmp" ]; then
|
||||
runFileWrite "$output_file" < "$tmp"; rm -f "$tmp"
|
||||
[[ -n "$new_etag" ]] && echo "$new_etag" | runFileWrite "$etag_file"
|
||||
isSuccessful "Refreshed gluetun provider snapshot ($(jq '.providers | length' "$output_file") providers)."
|
||||
else
|
||||
rm -f "$tmp"
|
||||
isNotice "Empty gluetun snapshot generated; ignoring."
|
||||
fi
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
{
|
||||
"tools": [
|
||||
{
|
||||
"id": "refresh_providers",
|
||||
"label": "Refresh VPN Providers",
|
||||
"description": "Refresh the VPN provider and country lists.",
|
||||
"icon": "🔄",
|
||||
"fields": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -4,7 +4,7 @@ networks:
|
||||
|
||||
services:
|
||||
grafana-service: #LIBREPORTAL|SERVICE_TAG_1|grafana-service
|
||||
image: grafana/grafana-enterprise:latest #LIBREPORTAL|GRAFANA_VERSION_TAG|latest
|
||||
image: grafana/grafana-enterprise
|
||||
container_name: grafana-service
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
|
||||
@ -6,24 +6,14 @@
|
||||
# REQUIRES = comma-separated install prerequisites (see scripts/checks/requirements/check_app_install.sh)
|
||||
# COMPOSE_FILE = default for no app_name in docker-compose file name, app if there is
|
||||
# BACKUP = if true, include this application in backup operations
|
||||
# UPDATE_TYPE = auto: new image builds are applied automatically (a recovery snapshot is taken first), manual: only when you press Update
|
||||
# HEALTHCHECK = if true, default docker health checks for that container will be enabled
|
||||
# AUTHELIA = if true, use Authelia authentication, if false turned off.
|
||||
# HEADSCALE = options : false, local, remote (see general config). e.g false or local,remote
|
||||
# MONITORING = if true, export this app's metrics to Prometheus + Grafana (needs both apps installed)
|
||||
#
|
||||
CFG_GRAFANA_APP_NAME=grafana
|
||||
# MULTI_INSTANCE = if true, this app can run as multiple isolated instances
|
||||
# (own data/DB/subdomain/backups) via `libreportal instance create`. Only set on
|
||||
# apps whose compose identity (container_name, Traefik routers, backup labels)
|
||||
# is instance-safe — see scripts/instance/instance_create.sh.
|
||||
CFG_GRAFANA_MULTI_INSTANCE=true
|
||||
CFG_GRAFANA_REQUIRES="prometheus"
|
||||
CFG_GRAFANA_BACKUP=true
|
||||
# STORAGE = which storage location holds this app's data.
|
||||
CFG_GRAFANA_STORAGE=default # Storage Location - Fixed: other apps reference grafana by path **READONLY**
|
||||
CFG_GRAFANA_BACKUP_STRATEGY=auto
|
||||
CFG_GRAFANA_UPDATE_TYPE=auto
|
||||
CFG_GRAFANA_COMPOSE_FILE=default
|
||||
CFG_GRAFANA_HEALTHCHECK=true
|
||||
CFG_GRAFANA_AUTHELIA=false
|
||||
|
||||
133
containers/grafana/grafana.sh
Executable file
133
containers/grafana/grafana.sh
Executable file
@ -0,0 +1,133 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Category : Development & Version Control
|
||||
# Description : Grafana - Metrics Visualizer (c/u/s/r/i):
|
||||
|
||||
installGrafana()
|
||||
{
|
||||
local config_variables="$1"
|
||||
|
||||
if [[ "$grafana" == *[cCtTuUsSrRiI]* ]]; then
|
||||
dockerConfigSetupToContainer silent grafana;
|
||||
local app_name=$CFG_GRAFANA_APP_NAME
|
||||
initializeAppVariables $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$grafana" == *[cC]* ]]; then
|
||||
editAppConfig $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$grafana" == *[uU]* ]]; then
|
||||
dockerUninstallApp $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$grafana" == *[sS]* ]]; then
|
||||
dockerComposeDown $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$grafana" == *[rR]* ]]; then
|
||||
dockerComposeRestart $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$grafana" == *[iI]* ]]; then
|
||||
isHeader "Install $app_name"
|
||||
|
||||
if ! appInstallCheckRequirements "$app_name" "$CFG_GRAFANA_REQUIRES"; then
|
||||
grafana=n
|
||||
return 1
|
||||
fi
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Setting up install folder and config file for $app_name."
|
||||
echo ""
|
||||
|
||||
dockerConfigSetupToContainer "loud" "$app_name" "install" "$config_variables";
|
||||
isSuccessful "Install folders and Config files have been setup for $app_name."
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Setting up the $app_name docker-compose.yml file."
|
||||
echo ""
|
||||
|
||||
dockerComposeSetupFile $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Updating file permissions before starting."
|
||||
echo ""
|
||||
|
||||
fixPermissionsBeforeStart $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running the docker-compose.yml to install and start $app_name"
|
||||
echo ""
|
||||
|
||||
dockerComposeUpdateAndStartApp $app_name install;
|
||||
|
||||
# Grafana
|
||||
if [ -d "${containers_dir}grafana/grafana_storage" ]; then
|
||||
local result=$(sudo chmod -R 777 "${containers_dir}grafana/grafana_storage")
|
||||
checkSuccess "Set permissions to grafana_storage folder."
|
||||
fi
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running Application specific updates (if required)"
|
||||
echo ""
|
||||
|
||||
appUpdateSpecifics $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running Headscale setup (if required)"
|
||||
echo ""
|
||||
|
||||
setupHeadscale $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Adding $app_name to the Apps Database table."
|
||||
echo ""
|
||||
|
||||
databaseInstallApp $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Updating WebUI config file."
|
||||
echo ""
|
||||
|
||||
webuiContainerSetup $app_name install;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Provisioning monitoring dashboards from installed apps."
|
||||
echo ""
|
||||
|
||||
# Re-gather the Prometheus datasource + every monitoring-enabled app's
|
||||
# dashboards into provisioning/ — so a fresh (or re-)install of Grafana
|
||||
# picks up the apps that already had CFG_<APP>_MONITORING=true.
|
||||
# monitoringRefreshAll also covers Grafana's own scrape target when
|
||||
# CFG_GRAFANA_MONITORING=true.
|
||||
monitoringRefreshAll;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. You can find $app_name files at $containers_dir$app_name"
|
||||
echo ""
|
||||
echo " You can now navigate to your $app_name service using any of the options below : "
|
||||
echo ""
|
||||
|
||||
menuShowFinalMessages $app_name;
|
||||
|
||||
menu_number=0
|
||||
#sleep 3s
|
||||
cd
|
||||
fi
|
||||
grafana=n
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Grafana install hooks — pre-flight prereq check + post-start 0777 on the
|
||||
# storage folder so Grafana can write its sqlite db regardless of host UID.
|
||||
|
||||
grafana_install_pre()
|
||||
{
|
||||
local app_name="$1"
|
||||
if ! appInstallCheckRequirements "$app_name" "$CFG_GRAFANA_REQUIRES"; then
|
||||
grafana=n
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
grafana_install_post_start()
|
||||
{
|
||||
local app_name="$1"
|
||||
# The DIRECTORY, not its contents. Grafana runs as uid 472, which rootless
|
||||
# maps to a subuid this user has no authority over; it needs write on the dir
|
||||
# itself to create grafana.db and its plugin/png dirs on first boot, and from
|
||||
# then on those files are its own. `chmod -R` walked them as the docker
|
||||
# install user and failed per file with "Operation not permitted", failing the
|
||||
# step on every reinstall — while a fresh install passed, because the dir was
|
||||
# still empty. Grafana's files must keep Grafana's ownership regardless.
|
||||
if [ -d "$(appDir grafana)/grafana_storage" ]; then
|
||||
local result
|
||||
result=$(runFileOp chmod 0777 "$(appDir grafana)/grafana_storage")
|
||||
checkSuccess "Set permissions to grafana_storage folder."
|
||||
fi
|
||||
}
|
||||
@ -5,7 +5,7 @@ networks:
|
||||
services:
|
||||
headscale-service: #LIBREPORTAL|SERVICE_TAG_1|headscale-service
|
||||
container_name: headscale-service
|
||||
image: headscale/headscale:latest #LIBREPORTAL|HEADSCALE_VERSION_TAG|latest
|
||||
image: headscale/headscale:latest
|
||||
volumes:
|
||||
- ./config:/etc/headscale/
|
||||
- ./data:/var/lib/headscale
|
||||
@ -20,8 +20,6 @@ services:
|
||||
labels:
|
||||
libreportal.category: "CATEGORY_DATA" #LIBREPORTAL|CATEGORY_TAG|CATEGORY_DATA
|
||||
libreportal.title: "TITLE_DATA" #LIBREPORTAL|TITLE_TAG|TITLE_DATA
|
||||
libreportal.backup.db: "sqlite:::data/db.sqlite"
|
||||
libreportal.backup.files: "headscale-service:/var/lib/headscale:data"
|
||||
healthcheck:
|
||||
disable: HEALTHCHECK_DATA #LIBREPORTAL|HEALTHCHECK_TAG|HEALTHCHECK_DATA
|
||||
# GLUETUN_OFF_BEGIN
|
||||
@ -49,7 +47,7 @@ services:
|
||||
- LOG_LEVEL=info # Log level. "DEBUG", "ERROR", "WARNING", or "INFO". Default "INFO"
|
||||
# ENV for Basic Auth (Used only if AUTH_TYPE is "Basic"). Can be omitted if you aren't using Basic Auth
|
||||
- BASIC_AUTH_USER=libreportal # Used for basic auth
|
||||
- BASIC_AUTH_PASS=HEADSCALE_BASIC_AUTH_PASS_1_DATA #LIBREPORTAL|HEADSCALE_BASIC_AUTH_PASS_1_TAG|HEADSCALE_BASIC_AUTH_PASS_1_DATA
|
||||
- BASIC_AUTH_PASS=HEADSCALE_BASIC_AUTH_PASS_DATA #LIBREPORTAL|HEADSCALE_BASIC_AUTH_PASS_TAG|HEADSCALE_BASIC_AUTH_PASS_DATA
|
||||
# ENV for OIDC (Used only if AUTH_TYPE is "OIDC"). Can be omitted if you aren't using OIDC
|
||||
#- OIDC_AUTH_URL=https://yourauthserver.com/application/o/headscale/.well-known/openid-configuration # URL for your OIDC issuer's well-known endpoint
|
||||
#- OIDC_CLIENT_ID=your-auth-server-client-id-info-here # Your OIDC Issuer's Client ID for Headscale-WebUI
|
||||
|
||||
@ -5,26 +5,15 @@
|
||||
# APP_NAME = name of application for use in scripts
|
||||
# COMPOSE_FILE = default for no app_name in docker-compose file name, app if there is
|
||||
# BACKUP = if true, include this application in backup operations
|
||||
# UPDATE_TYPE = auto: new image builds are applied automatically (a recovery snapshot is taken first), manual: only when you press Update
|
||||
# HEALTHCHECK = if true, default docker health checks for that container will be enabled
|
||||
# BASIC_AUTH_PASS = password for the headscale-ui basic auth; auto-generated, fed to the compose via HEADSCALE_BASIC_AUTH_PASS_1_TAG
|
||||
# BASIC_AUTH_PASS = password for the headscale-ui basic auth; auto-generated, fed to the compose via HEADSCALE_BASIC_AUTH_PASS_TAG
|
||||
# MONITORING = if true, export this app's metrics to Prometheus + Grafana (needs both apps installed)
|
||||
#
|
||||
CFG_HEADSCALE_APP_NAME=headscale
|
||||
# MULTI_INSTANCE = if true, this app can run as multiple isolated instances
|
||||
# (own data/DB/subdomain/backups) via `libreportal instance create`. Only set on
|
||||
# apps whose compose identity (container_name, Traefik routers, backup labels)
|
||||
# is instance-safe — see scripts/instance/instance_create.sh.
|
||||
# Not instanced by design. The control server a tailnet is defined by — nodes register against exactly one.
|
||||
CFG_HEADSCALE_MULTI_INSTANCE=false
|
||||
CFG_HEADSCALE_BACKUP=true
|
||||
# STORAGE = which storage location holds this app's data.
|
||||
CFG_HEADSCALE_STORAGE=default # Storage Location - Fixed: other apps reference headscale by path **READONLY**
|
||||
CFG_HEADSCALE_BACKUP_STRATEGY=auto
|
||||
CFG_HEADSCALE_UPDATE_TYPE=auto
|
||||
CFG_HEADSCALE_COMPOSE_FILE=default
|
||||
CFG_HEADSCALE_HEALTHCHECK=true
|
||||
CFG_HEADSCALE_BASIC_AUTH_PASS_1=RANDOMIZEDPASSWORD1
|
||||
CFG_HEADSCALE_BASIC_AUTH_PASS=RANDOMIZEDPASSWORD1
|
||||
CFG_HEADSCALE_MONITORING=false
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
127
containers/headscale/headscale.sh
Executable file
127
containers/headscale/headscale.sh
Executable file
@ -0,0 +1,127 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Category : Networking
|
||||
# Description : Self-hosted WireGuard orchestrator (c/u/s/r/i):
|
||||
|
||||
installHeadscale()
|
||||
{
|
||||
local config_variables="$1"
|
||||
|
||||
if [[ "$headscale" == *[cCtTuUsSrRiI]* ]]; then
|
||||
dockerConfigSetupToContainer silent headscale;
|
||||
local app_name=$CFG_HEADSCALE_APP_NAME
|
||||
initializeAppVariables $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$headscale" == *[cC]* ]]; then
|
||||
editAppConfig $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$headscale" == *[uU]* ]]; then
|
||||
dockerUninstallApp $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$headscale" == *[sS]* ]]; then
|
||||
dockerComposeDown $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$headscale" == *[rR]* ]]; then
|
||||
dockerComposeRestart $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$headscale" == *[iI]* ]]; then
|
||||
isHeader "Install $app_name"
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Setting up install folder and config file for $app_name."
|
||||
echo ""
|
||||
|
||||
dockerConfigSetupToContainer "loud" "$app_name" "install" "$config_variables";
|
||||
isSuccessful "Install folders and Config files have been setup for $app_name."
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Setting up the $app_name docker-compose.yml file."
|
||||
echo ""
|
||||
|
||||
dockerComposeSetupFile $app_name;
|
||||
|
||||
local result=$(createFolders "loud" $docker_install_user $containers_dir$app_name/config)
|
||||
checkSuccess "Create config folder"
|
||||
|
||||
local result=$(copyResource "$app_name" "config.yaml" "config" | sudo tee -a "$logs_dir/$docker_log_file" 2>&1)
|
||||
checkSuccess "Copying config.yaml to config folder."
|
||||
|
||||
configSetupFileWithData $app_name "config.yaml" "config";
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Updating file permissions before starting."
|
||||
echo ""
|
||||
|
||||
fixPermissionsBeforeStart $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running the docker-compose.yml to install and start $app_name"
|
||||
echo ""
|
||||
|
||||
dockerComposeUpdateAndStartApp $app_name install;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running Application specific updates (if required)"
|
||||
echo ""
|
||||
|
||||
appUpdateSpecifics $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running Headscale setup (if required)"
|
||||
echo ""
|
||||
|
||||
setupHeadscale $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Setting up database records"
|
||||
echo ""
|
||||
|
||||
databaseInstallApp $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Updating WebUI config file."
|
||||
echo ""
|
||||
|
||||
webuiContainerSetup $app_name install;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Refreshing monitoring integration."
|
||||
echo ""
|
||||
|
||||
monitoringRefreshAll;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. You can find $app_name files at $containers_dir$app_name"
|
||||
echo ""
|
||||
echo " You can now navigate to your $app_name service using any of the options below : "
|
||||
echo ""
|
||||
echo " NOTE - The password to login in defined in the yml install file that was installed"
|
||||
echo ""
|
||||
|
||||
menuShowFinalMessages $app_name;
|
||||
|
||||
menu_number=0
|
||||
#sleep 3s
|
||||
cd
|
||||
fi
|
||||
headscale=n
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Headscale install hooks — drop the config.yaml template into the
|
||||
# container's config folder before start so the daemon has its config on
|
||||
# first boot.
|
||||
|
||||
headscale_install_post_compose()
|
||||
{
|
||||
local app_name="$1"
|
||||
|
||||
local result
|
||||
result=$(createFolders "loud" $docker_install_user $(appDir "$app_name")/config)
|
||||
checkSuccess "Create config folder"
|
||||
|
||||
result=$(copyResource "$app_name" "config.yaml" "config" | runInstallWrite -a "$logs_dir/$docker_log_file" 2>&1)
|
||||
checkSuccess "Copying config.yaml to config folder."
|
||||
|
||||
configSetupFileWithData $app_name "config.yaml" "config"
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
tailscaleInstallToContainer()
|
||||
{
|
||||
local app_name="$1"
|
||||
local type="$2"
|
||||
|
||||
local result; result=$(createFolders "loud" $docker_install_user $(appDir "$app_name")/tailscale)
|
||||
checkSuccess "Creating Tailscale folder"
|
||||
|
||||
copyFile "loud" "${install_containers_dir}headscale/resources/tailscale.sh" "$(appDir "$app_name")/tailscale/tailscale.sh" $docker_install_user | runInstallWrite -a "$logs_dir/$docker_log_file" 2>&1
|
||||
|
||||
if [[ "$type" != "install" ]]; then
|
||||
dockerComposeRestart $app_name;
|
||||
fi
|
||||
|
||||
dockerCommandRun "docker exec -it $app_name /usr/local/bin/tailscale.sh"
|
||||
checkSuccess "Executing Tailscale installer script in the $app_name container"
|
||||
}
|
||||
@ -5,7 +5,7 @@ networks:
|
||||
services:
|
||||
invidious-service: #LIBREPORTAL|SERVICE_TAG_1|invidious-service
|
||||
container_name: invidious-service
|
||||
image: quay.io/invidious/invidious:latest #LIBREPORTAL|INVIDIOUS_VERSION_TAG|latest
|
||||
image: quay.io/invidious/invidious:latest
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- invidious-db
|
||||
@ -26,7 +26,7 @@ services:
|
||||
# domain:
|
||||
# https_only: false
|
||||
# statistics_enabled: false
|
||||
hmac_key: "INVIDIOUS_HMAC_KEY_1_DATA" #LIBREPORTAL|INVIDIOUS_HMAC_KEY_1_TAG|INVIDIOUS_HMAC_KEY_1_DATA
|
||||
hmac_key: "INVIDIOUS_HMAC_KEY_DATA" #LIBREPORTAL|INVIDIOUS_HMAC_KEY_TAG|INVIDIOUS_HMAC_KEY_DATA
|
||||
healthcheck:
|
||||
test: wget -nv --tries=1 --spider http://127.0.0.1:3000/api/v1/comments/jNQXAC9IVRw || exit 1
|
||||
interval: 30s
|
||||
@ -42,7 +42,6 @@ services:
|
||||
labels:
|
||||
libreportal.category: "CATEGORY_DATA" #LIBREPORTAL|CATEGORY_TAG|CATEGORY_DATA
|
||||
libreportal.title: "TITLE_DATA" #LIBREPORTAL|TITLE_TAG|TITLE_DATA
|
||||
libreportal.backup.db: "postgres:invidious-db:postgresdata:"
|
||||
traefik.enable: TRAEFIK_ENABLE_DATA #LIBREPORTAL|TRAEFIK_ENABLE_TAG|TRAEFIK_ENABLE_DATA
|
||||
# TRAEFIK_PORT_1_BEGIN
|
||||
traefik.http.routers.invidious-service.entrypoints: web,websecure
|
||||
|
||||
@ -5,30 +5,19 @@
|
||||
# APP_NAME = name of application for use in scripts
|
||||
# COMPOSE_FILE = default for no app_name in docker-compose file name, app if there is
|
||||
# BACKUP = if true, include this application in backup operations
|
||||
# UPDATE_TYPE = auto: new image builds are applied automatically (a recovery snapshot is taken first), manual: only when you press Update
|
||||
# HEALTHCHECK = if true, default docker health checks for that container will be enabled
|
||||
# AUTHELIA = if true, use Authelia authentication, if false turned off.
|
||||
# HEADSCALE = options : false, local, remote (see general config). e.g false or local,remote
|
||||
#
|
||||
CFG_INVIDIOUS_APP_NAME=invidious
|
||||
# MULTI_INSTANCE = if true, this app can run as multiple isolated instances
|
||||
# (own data/DB/subdomain/backups) via `libreportal instance create`. Only set on
|
||||
# apps whose compose identity (container_name, Traefik routers, backup labels)
|
||||
# is instance-safe — see scripts/instance/instance_create.sh.
|
||||
CFG_INVIDIOUS_MULTI_INSTANCE=true
|
||||
CFG_INVIDIOUS_BACKUP=false
|
||||
# STORAGE = which storage location holds this app's data. Change it with
|
||||
# `libreportal app move invidious <location>` — editing here records intent only.
|
||||
CFG_INVIDIOUS_STORAGE=default # Storage Location - Which disk holds this app's data [default:Primary]
|
||||
CFG_INVIDIOUS_BACKUP_STRATEGY=auto
|
||||
CFG_INVIDIOUS_UPDATE_TYPE=auto
|
||||
CFG_INVIDIOUS_COMPOSE_FILE=default
|
||||
CFG_INVIDIOUS_HEALTHCHECK=false
|
||||
CFG_INVIDIOUS_AUTHELIA=false
|
||||
CFG_INVIDIOUS_HEADSCALE=false
|
||||
# HMAC_KEY = signs Invidious tokens/links; auto-generated, fed to the compose
|
||||
# via the INVIDIOUS_HMAC_KEY_1_TAG tag (preserved across reinstalls)
|
||||
CFG_INVIDIOUS_HMAC_KEY_1=RANDOMIZEDPASSWORD1
|
||||
# via the INVIDIOUS_HMAC_KEY_TAG tag (preserved across reinstalls)
|
||||
CFG_INVIDIOUS_HMAC_KEY=RANDOMIZEDPASSWORD1
|
||||
#
|
||||
# =============================================================================
|
||||
# METADATA
|
||||
@ -73,13 +62,8 @@ CFG_INVIDIOUS_NETWORK=default
|
||||
#
|
||||
CFG_INVIDIOUS_PORT_1="invidious-service|webui|random:3000|public|tcp|false|true|true|Web Interface||invidious"
|
||||
|
||||
# AUTH_PROFILE = capability tier for the WebUI auth tools (single_password | user_password | multi_user)
|
||||
CFG_INVIDIOUS_AUTH_PROFILE=multi_user
|
||||
CFG_INVIDIOUS_ADMIN_USER=
|
||||
#
|
||||
# Recorded by the auth tools, not by the installer. Invidious seeds no account at
|
||||
# install — the first admin is created from the WebUI (Create Account), and the
|
||||
# adapter writes the username and password here so the credentials card can show
|
||||
# them. Deliberately empty and unslotted: a generated password would name an
|
||||
# account that does not exist, and the slot number marks a value the installer
|
||||
# generates.
|
||||
#
|
||||
CFG_INVIDIOUS_ADMIN_PASSWORD=
|
||||
CFG_INVIDIOUS_ADMIN_EMAIL=
|
||||
CFG_INVIDIOUS_ADMIN_PASSWORD=RANDOMIZEDPASSWORD1
|
||||
|
||||
114
containers/invidious/invidious.sh
Executable file
114
containers/invidious/invidious.sh
Executable file
@ -0,0 +1,114 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Category : Media & Streaming
|
||||
# Description : Invidious - Privacy-focused YouTube Frontend (c/u/s/r/i/t):
|
||||
|
||||
installInvidious()
|
||||
{
|
||||
local config_variables="$1"
|
||||
|
||||
if [[ "$invidious" == *[cCtTuUsSrRiI]* ]]; then
|
||||
dockerConfigSetupToContainer silent invidious;
|
||||
local app_name=$CFG_INVIDIOUS_APP_NAME
|
||||
initializeAppVariables $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$invidious" == *[cC]* ]]; then
|
||||
editAppConfig $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$invidious" == *[tT]* ]]; then
|
||||
invidiousToolsMenu;
|
||||
fi
|
||||
|
||||
if [[ "$invidious" == *[uU]* ]]; then
|
||||
dockerUninstallApp $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$invidious" == *[sS]* ]]; then
|
||||
dockerComposeDown $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$invidious" == *[rR]* ]]; then
|
||||
dockerComposeRestart $app_name;
|
||||
fi
|
||||
|
||||
if [[ "$invidious" == *[iI]* ]]; then
|
||||
isHeader "Install $app_name"
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Setting up install folder and config file for $app_name."
|
||||
echo ""
|
||||
|
||||
dockerConfigSetupToContainer "loud" "$app_name" "install" "$config_variables";
|
||||
isSuccessful "Install folders and Config files have been setup for $app_name."
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Setting up the $app_name docker-compose.yml file."
|
||||
echo ""
|
||||
|
||||
dockerComposeSetupFile $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Updating file permissions before starting."
|
||||
echo ""
|
||||
|
||||
fixPermissionsBeforeStart $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running the docker-compose.yml to install and start $app_name"
|
||||
echo ""
|
||||
|
||||
dockerComposeUpdateAndStartApp $app_name install;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running Application specific updates (if required)"
|
||||
echo ""
|
||||
|
||||
appUpdateSpecifics $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Running Headscale setup (if required)"
|
||||
echo ""
|
||||
|
||||
setupHeadscale $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Adding $app_name to the Apps Database table."
|
||||
echo ""
|
||||
|
||||
databaseInstallApp $app_name;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. Updating WebUI config file."
|
||||
echo ""
|
||||
|
||||
webuiContainerSetup $app_name install;
|
||||
|
||||
((menu_number++))
|
||||
echo ""
|
||||
echo "---- $menu_number. You can find $app_name files at $containers_dir$app_name"
|
||||
echo ""
|
||||
echo " You can now navigate to your $app_name service using any of the options below : "
|
||||
echo ""
|
||||
|
||||
menuShowFinalMessages $app_name;
|
||||
|
||||
menu_number=0
|
||||
#sleep 3s
|
||||
cd
|
||||
fi
|
||||
invidious=n
|
||||
}
|
||||
@ -1,77 +0,0 @@
|
||||
{
|
||||
"tools": [
|
||||
{
|
||||
"id": "reset_password",
|
||||
"category": "users",
|
||||
"label": "Reset User Password",
|
||||
"description": "Set a new password for an existing user. Leave blank to generate one.",
|
||||
"icon": "🔑",
|
||||
"fields": [
|
||||
{
|
||||
"name": "email",
|
||||
"label": "Email",
|
||||
"type": "text",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "password",
|
||||
"label": "New password",
|
||||
"type": "password",
|
||||
"placeholder": "Leave blank to generate"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "create_account",
|
||||
"category": "users",
|
||||
"label": "Create User Account",
|
||||
"description": "Add a new user. Sign-in uses the email address.",
|
||||
"icon": "👤",
|
||||
"fields": [
|
||||
{
|
||||
"name": "email",
|
||||
"label": "Email",
|
||||
"type": "text",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "password",
|
||||
"label": "Password",
|
||||
"type": "password",
|
||||
"placeholder": "Leave blank to generate"
|
||||
},
|
||||
{
|
||||
"name": "admin",
|
||||
"label": "Make admin",
|
||||
"type": "checkbox",
|
||||
"default": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "list_users",
|
||||
"category": "users",
|
||||
"label": "List Users",
|
||||
"description": "List every user.",
|
||||
"icon": "📋",
|
||||
"fields": []
|
||||
},
|
||||
{
|
||||
"id": "delete_user",
|
||||
"category": "users",
|
||||
"label": "Delete User Account",
|
||||
"description": "Permanently remove a user.",
|
||||
"icon": "🗑",
|
||||
"destructive": true,
|
||||
"confirm": "This cannot be undone.",
|
||||
"fields": [
|
||||
{
|
||||
"name": "email",
|
||||
"label": "Email",
|
||||
"type": "text",
|
||||
"required": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -5,7 +5,7 @@ networks:
|
||||
services:
|
||||
ipinfo-service: #LIBREPORTAL|SERVICE_TAG_1|ipinfo-service
|
||||
container_name: ipinfo-service
|
||||
image: peterdavehello/ipinfo.tw:latest #LIBREPORTAL|IPINFO_VERSION_TAG|latest
|
||||
image: peterdavehello/ipinfo.tw:latest
|
||||
restart: unless-stopped
|
||||
# GLUETUN_OFF_BEGIN
|
||||
ports:
|
||||
|
||||
@ -5,23 +5,12 @@
|
||||
# APP_NAME = name of application for use in scripts
|
||||
# COMPOSE_FILE = default for no app_name in docker-compose file name, app if there is
|
||||
# BACKUP = if true, include this application in backup operations
|
||||
# UPDATE_TYPE = auto: new image builds are applied automatically (a recovery snapshot is taken first), manual: only when you press Update
|
||||
# HEALTHCHECK = if true, default docker health checks for that container will be enabled
|
||||
# AUTHELIA = if true, use Authelia authentication, if false turned off.
|
||||
# HEADSCALE = options : false, local, remote (see general config). e.g false or local,remote
|
||||
#
|
||||
CFG_IPINFO_APP_NAME=ipinfo
|
||||
# MULTI_INSTANCE = if true, this app can run as multiple isolated instances
|
||||
# (own data/DB/subdomain/backups) via `libreportal instance create`. Only set on
|
||||
# apps whose compose identity (container_name, Traefik routers, backup labels)
|
||||
# is instance-safe — see scripts/instance/instance_create.sh.
|
||||
CFG_IPINFO_MULTI_INSTANCE=true
|
||||
CFG_IPINFO_BACKUP=false
|
||||
# STORAGE = which storage location holds this app's data. Change it with
|
||||
# `libreportal app move ipinfo <location>` — editing here records intent only.
|
||||
CFG_IPINFO_STORAGE=default # Storage Location - Which disk holds this app's data [default:Primary]
|
||||
CFG_IPINFO_BACKUP_STRATEGY=auto
|
||||
CFG_IPINFO_UPDATE_TYPE=auto
|
||||
CFG_IPINFO_COMPOSE_FILE=default
|
||||
CFG_IPINFO_HEALTHCHECK=true
|
||||
CFG_IPINFO_AUTHELIA=false
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user