feat: webtrees docker build setup with patched PHP 8.4.26 (CVE-2026-109618 / CVE-2026-109619)

This commit is contained in:
2026-10-21 20:55:55 +02:00
parent 83e64a0d5d
commit 1080424e4c
10 changed files with 937 additions and 5 deletions
+19
View File
@@ -0,0 +1,19 @@
RewriteEngine On
# https://webtrees.net/faq/urls/
# GIT config files can contain credentials or other sensitive data.
RewriteRule \.git - [F]
# User data is stored here by default.
RewriteRule ^data(/|$) - [F]
# Nothing sensitive here, but there is no need to publish it.
RewriteRule ^app(/|$) - [F]
RewriteRule ^modules - [F]
RewriteRule ^resources(/|$) - [F]
RewriteRule ^vendor(/|$) - [F]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
+78
View File
@@ -0,0 +1,78 @@
ARG PHP_VERSION=8.4
FROM docker.io/library/php:$PHP_VERSION-apache
# https://hub.docker.com/_/php
# https://github.com/NathanVaughn/webtrees-docker/issues/160
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
ENV WEBTREES_HOME="/var/www/webtrees"
WORKDIR $WEBTREES_HOME
# install pre-reqs
# postgresql-client provides pg_isready
# mariadb-client provides mysqladmin
RUN apt-get update \
&& apt-get install -y \
curl \
libmagickwand-dev \
libpq-dev \
libzip-dev \
postgresql-client \
mariadb-client \
patch \
python3 \
unzip \
--no-install-recommends \
&& rm -rf /var/lib/apt/lists/*
# install php extensions
RUN pecl install imagick \
&& docker-php-ext-enable imagick \
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install -j"$(nproc)" pdo pdo_mysql pdo_pgsql zip intl gd exif opcache
# remove old apt stuff
RUN apt-get purge gcc g++ make -y \
&& apt-get autoremove -y \
&& apt-get clean \
&& rm -rf /var/tmp/* /etc/apache2/sites-enabled/000-*.conf
ARG WEBTREES_VERSION
RUN curl -s -L https://github.com/fisharebest/webtrees/releases/download/${WEBTREES_VERSION}/webtrees-${WEBTREES_VERSION}.zip -o webtrees.zip \
&& unzip -q webtrees.zip -d /var/www/ && rm webtrees.zip \
&& rm $WEBTREES_HOME/*.md
# Disable version update prompt. Webtrees should not be upgrading itself,
# users should be using tagged container versions
ARG UPGRADE_PATCH_VERSION
COPY patches/UpgradeService${UPGRADE_PATCH_VERSION}.patch /UpgradeService.patch
RUN patch app/Services/UpgradeService.php /UpgradeService.patch \
&& rm /UpgradeService.patch \
# Delete file that caused email issues
# https://www.webtrees.net/index.php/fr/forum/help-for-2-0/36616-email-error-after-update-to-2-0-21#89985
# https://github.com/NathanVaughn/webtrees-docker/issues/88
&& rm vendor/egulias/email-validator/src/Validation/MessageIDValidation.php
# enable apache modules
RUN a2enmod rewrite && a2enmod ssl && rm -rf /var/www/html
# copy apache/php configs
RUN mkdir -p /bak/webtrees/data/ \
&& cp $WEBTREES_HOME/data/.htaccess /bak/webtrees/data/.htaccess
COPY .htaccess ./
COPY apache/ /etc/apache2/sites-available/
# entrypoint
COPY docker-entrypoint.py /
# healthcheck
COPY docker-healthcheck.sh /
RUN chmod +x /docker-healthcheck.sh
# final Docker config
EXPOSE 80 443
VOLUME ["$WEBTREES_HOME/data"]
HEALTHCHECK CMD /docker-healthcheck.sh
ENTRYPOINT ["python3", "/docker-entrypoint.py"]
+110 -4
View File
@@ -4,10 +4,10 @@
</a>
</p>
<h1 align="center">Name des Projekts</h1>
<h1 align="center">Webtrees Docker (PHP 8.4 Security Patched)</h1>
<h4 align="center">
Kurzbeschreibung des Projekts/Anwendung, um die es geht
Webtrees Docker Image mit gepatchtem PHP 8.4.26 (Schutz vor CVE-2026-109618 / CVE-2026-109619 "PHP2Shell")
</h4>
<h6 align="center">
@@ -21,9 +21,115 @@
</h6>
<br><br>
## 📌 Übersicht & Hintergrund
CONTENT BEREICH
Dieses Repository stellt vorgefertigte Docker-Images für die führende Open-Source-Genealogie-Software **[webtrees](https://webtrees.net/)** bereit.
### 🛡️ Warum dieser Fork / Rebuild?
Am 21. Oktober 2026 wurde die kritische Zero-Day-Schwachstelle **"PHP2Shell"** ([CVE-2026-109618](https://nvd.nist.gov/vuln/detail/CVE-2026-109618) & CVE-2026-109619, CVSS 9.2) im PHP-Kern bekannt.
Da bisherige INI-Workarounds (`session.upload_progress.enabled = Off`) laut Sicherheitsberichten (u. a. Heise Security) unvollständig sind bzw. umgangen werden können, ist der Einsatz der offiziell gepatchten PHP-Binaries zwingend erforderlich.
Da das beliebte Upstream-Image von [NathanVaughn/webtrees-docker](https://github.com/NathanVaughn/webtrees-docker) nur bei neuen Webtrees-Releases automatisiert baut, wurde dieses Image direkt auf Basis des heute veröffentlichten Upstream-Patches **`php:8.4-apache` (PHP 8.4.26, Build: 21.10.2026 00:32:44 UTC)** gebaut und getestet.
* **Upstream-Basis:** 1:1 kompatibel zu [NathanVaughn/webtrees-docker](https://github.com/NathanVaughn/webtrees-docker)
* **Webtrees-Versionen:** `2.2.6` und `2.2.1`
* **PHP-Version:** `PHP 8.4.26` (Offizielles Security-Patched Release vom 21.10.2026)
* **Enthaltene Module:** `imagick`, `gd`, `pdo_mysql`, `pdo_pgsql`, `zip`, `intl`, `exif`, `opcache` uvm.
---
## 📦 Verfügbare Container Tags
Die Images können direkt aus der Techniverse Gitea Container Registry bezogen werden:
```bash
# Webtrees 2.2.6 (PHP 8.4.26)
docker pull git.techniverse.net/scriptos/webtrees:2.2.6-php8.4.26
docker pull git.techniverse.net/scriptos/webtrees:latest-php8.4.26
# Webtrees 2.2.1 (PHP 8.4.26)
docker pull git.techniverse.net/scriptos/webtrees:2.2.1-php8.4.26
```
---
## 🚀 Schnellstart mit Docker Compose
Beispiel für eine `docker-compose.yaml`:
```yaml
services:
webtrees-app:
image: git.techniverse.net/scriptos/webtrees:2.2.6-php8.4.26
container_name: webtrees-app
restart: unless-stopped
depends_on:
- webtrees-db
environment:
PRETTY_URLS: "1"
HTTPS: "0"
HTTPS_REDIRECT: "0"
LANG: "de"
TZ: "Europe/Berlin"
BASE_URL: "https://stammbaum.deine-domain.de"
DB_TYPE: "mysql"
DB_HOST: "webtrees-db"
DB_PORT: "3306"
DB_USER: "webtrees"
DB_PASS: "DeinSicheresPasswort"
DB_NAME: "webtrees"
DB_PREFIX: "wt_"
PUID: "33"
PGID: "33"
PHP_UPLOAD_MAX_FILE_SIZE: "50M"
PHP_POST_MAX_SIZE: "50M"
PHP_MEMORY_LIMIT: "1024M"
ports:
- "8080:80"
volumes:
- ./data/app_data:/var/www/webtrees/data/
- ./data/modules_v4:/var/www/webtrees/modules_v4
webtrees-db:
image: mariadb:11
container_name: webtrees-db
restart: unless-stopped
environment:
MARIADB_DATABASE: "webtrees"
MARIADB_USER: "webtrees"
MARIADB_PASSWORD: "DeinSicheresPasswort"
MARIADB_ROOT_PASSWORD: "DeinRootPasswort"
volumes:
- ./data/db_data:/var/lib/mysql
```
---
## 🔍 Verifikation des Sicherheits-Patches
Im laufenden Container lässt sich der Patch-Stand jederzeit über die PHP-CLI prüfen:
```bash
docker exec webtrees-app php -v
```
Ausgabe:
```text
PHP 8.4.26 (cli) (built: Oct 21 2026 00:32:44) (NTS)
Copyright (c) The PHP Group
Built by https://github.com/docker-library/php
Zend Engine v4.4.26, Copyright (c) Zend Technologies
with Zend OPcache v8.4.26, Copyright (c), by Zend Technologies
```
---
## 🔗 Danksagung & Upstream-Referenzen
* **webtrees:** Offizielles Projekt unter [https://webtrees.net/](https://webtrees.net/) und auf [GitHub (fisharebest/webtrees)](https://github.com/fisharebest/webtrees)
* **Docker Image Vorlage:** Nathan Vaughn ([NathanVaughn/webtrees-docker](https://github.com/NathanVaughn/webtrees-docker))
* **PHP Security:** PHP Group ([php.net](https://www.php.net/))
<br><br>
<p align="center">
@@ -32,6 +138,6 @@ CONTENT BEREICH
<p align="center">
<sub>
© Patrick Asmus · Techniverse Network · <a href="./LICENSE">Lizenz</a>
© Patrick Asmus · Techniverse Network · <a href="./LICENSE.gnu">GPL-3.0 Lizenz</a>
</sub>
</p>
+13
View File
@@ -0,0 +1,13 @@
<VirtualHost *:80>
ServerName webtrees
DocumentRoot "/var/www/webtrees/"
RewriteEngine On
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
<Directory "/var/www/webtrees/">
Options FollowSymLinks MultiViews
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
+14
View File
@@ -0,0 +1,14 @@
<VirtualHost *:443>
ServerName webtrees
DocumentRoot "/var/www/webtrees/"
SSLEngine on
SSLCertificateFile /certs/webtrees.crt
SSLCertificateKeyFile /certs/webtrees.key
<Directory "/var/www/webtrees/">
Options FollowSymLinks MultiViews
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
+10
View File
@@ -0,0 +1,10 @@
<VirtualHost *:80>
ServerName webtrees
DocumentRoot "/var/www/webtrees/"
<Directory "/var/www/webtrees/">
Options FollowSymLinks MultiViews
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
+602
View File
@@ -0,0 +1,602 @@
import filecmp
import os
import shutil
import socket
import subprocess
import sys
import time
import urllib.error
from dataclasses import dataclass
from enum import Enum
from typing import Any, List, Literal, Optional, TypeVar, Union, overload
from urllib import request
from urllib.parse import urlencode
class NoRedirect(request.HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
class DBType(Enum):
mysql = "mysql"
pgsql = "pgsql"
sqlite = "sqlite"
@dataclass
class EnvVars:
prettyurls: bool
https: bool
httpsredirect: bool
sslcertfile: str
sslcertkeyfile: str
lang: str
baseurl: Optional[str]
dbtype: DBType
dbhost: Optional[str]
dbport: str
dbuser: str
dbpass: Optional[str]
dbname: str
tblpfx: str
wtuser: Optional[str]
wtname: Optional[str]
wtpass: Optional[str]
wtemail: Optional[str]
# https://github.com/fisharebest/webtrees/blob/f9a3af650116d75f1a87f454cabff5e9047e43f3/app/Http/Middleware/UseDatabase.php#L71-L82
dbkey: Optional[str]
dbcert: Optional[str]
dbca: Optional[str]
dbverify: bool
# php settings
phpmemorylimit: str
phpmaxexecutiontime: str
phppostmaxsize: str
phpuploadmaxfilesize: str
# user/group ID
puid: str
pgid: str
def truish(value: Optional[str]) -> bool:
"""
Check if a value is close enough to true
"""
if value is None:
return False
return value.lower().strip() in ["true", "yes", "1"]
def print2(msg: Any) -> None:
"""
Print a message to stderr.
"""
print(f"[NV_INIT] {msg}", file=sys.stderr)
T = TypeVar("T")
@overload
def get_environment_variable(
key: str, default: None = None, alternates: Optional[List[str]] = None
) -> Optional[str]: ...
@overload
def get_environment_variable(
key: str, default: T = None, alternates: Optional[List[str]] = None
) -> T: ...
def get_environment_variable(
key: str, default: Optional[T] = None, alternates: Optional[List[str]] = None
) -> Union[Optional[str], T]:
"""
Try to find the value of an environment variable.
"""
key = key.upper()
# try to find variable in env
if key in os.environ:
value = os.environ[key]
print2(f"{key} found in environment variables")
return value
# try to find file version of variable
file_key = f"{key}_FILE"
if file_key in os.environ:
# file name does not exist
if not os.path.isfile(os.environ[file_key]):
print(f"WARNING: {file_key} is not a file: {os.environ[file_key]}")
return None
# read data from file
with open(os.environ[file_key], "r") as f:
value = f.read().strip()
print2(f"{file_key} found in environment variables")
return value
# try to find alternate variable
if alternates is not None:
for a in alternates:
a_value = get_environment_variable(a)
if a_value is not None:
return a_value
# return default value
print2(f"{key} NOT found in environment variables, using default: {default}")
return default
ENV = EnvVars(
prettyurls=truish(get_environment_variable("PRETTY_URLS")),
https=truish(get_environment_variable("HTTPS", alternates=["SSL"])),
httpsredirect=truish(
get_environment_variable("HTTPS_REDIRECT", alternates=["SSL_REDIRECT"])
),
sslcertfile=get_environment_variable("SSL_CERT_FILE", "/certs/webtrees.crt"),
sslcertkeyfile=get_environment_variable("SSL_CERT_KEY_FILE", "/certs/webtrees.key"),
baseurl=get_environment_variable("BASE_URL"),
lang=get_environment_variable("LANG", "en-US"),
dbtype=DBType[get_environment_variable("DB_TYPE", "mysql")],
dbhost=get_environment_variable("DB_HOST"),
dbport=get_environment_variable("DB_PORT", "3306"),
dbuser=get_environment_variable(
"DB_USER",
"webtrees",
alternates=["MYSQL_USER", "MARIADB_USER", "POSTGRES_USER"],
),
dbpass=get_environment_variable(
"DB_PASS",
alternates=["MYSQL_PASSWORD", "MARIADB_PASSWORD", "POSTGRES_PASSWORD"],
),
dbname=get_environment_variable(
"DB_NAME",
default="webtrees",
alternates=["MYSQL_DATABASE", "MARIADB_DATABASE", "POSTGRES_DB"],
),
tblpfx=get_environment_variable("DB_PREFIX", "wt_"),
wtuser=get_environment_variable("WT_USER"),
wtname=get_environment_variable("WT_NAME"),
wtpass=get_environment_variable("WT_PASS"),
wtemail=get_environment_variable("WT_EMAIL"),
dbkey=get_environment_variable("DB_KEY"),
dbcert=get_environment_variable("DB_CERT"),
dbca=get_environment_variable("DB_CA"),
dbverify=truish(get_environment_variable("DB_VERIFY")),
phpmemorylimit=get_environment_variable("PHP_MEMORY_LIMIT", "1024M"),
phpmaxexecutiontime=get_environment_variable("PHP_MAX_EXECUTION_TIME", "90"),
phppostmaxsize=get_environment_variable("PHP_POST_MAX_SIZE", "50M"),
phpuploadmaxfilesize=get_environment_variable("PHP_UPLOAD_MAX_FILE_SIZE", "50M"),
puid=get_environment_variable("PUID", "33"), # www-data user
pgid=get_environment_variable("PGID", "33"),
)
ROOT = "/var/www/webtrees"
DATA_DIR = os.path.join(ROOT, "data")
CONFIG_FILE = os.path.join(DATA_DIR, "config.ini.php")
PHP_INI_FILE = "/usr/local/etc/php/php.ini"
os.chdir(ROOT)
def retry_urlopen(url: str, data: bytes) -> None:
"""
Retry a request until a postiive repsonse code is reached. Raises error if it fails.
"""
opener = request.build_opener(NoRedirect)
request.install_opener(opener)
for try_ in range(10):
try:
# make request
print2(f"Attempt {try_} for {url}")
resp = request.urlopen(url, data)
except urllib.error.HTTPError as e:
# capture error as well
resp = e
print2(f"Recieved HTTP {resp.status} response")
# check status code
# 302 is also accpetable in case the user selected something other than port 80
if resp.status in (200, 302):
return
# backoff
time.sleep(try_)
raise RuntimeError(f"Could not send a request to {url}")
def add_line_to_file(filename: str, newline: str) -> None:
"""
Add a new line to a file. If an existing line is found with the same
starting string, it will be replaced.
"""
newline += "\n"
# read file
with open(filename, "r") as fp:
lines = fp.readlines()
key = newline.split("=")[0]
# replace matching line
found = False
for i, line in enumerate(lines):
if line.startswith(key):
if line == newline:
return
lines[i] = newline
found = True
break
if not found:
lines.append(newline)
# write new contents
with open(filename, "w") as fp:
fp.writelines(lines)
def set_config_value(key: str, value: Optional[str]) -> None:
"""
In the config file, make sure the given key is set to the given value.
"""
if value is None:
return
print2(f"Setting value for {key} in config")
if not os.path.isfile(CONFIG_FILE):
print2(f"WARNING: {CONFIG_FILE} does not exist")
return
add_line_to_file(CONFIG_FILE, f'{key}="{value}"')
def set_php_ini_value(key: str, value: str) -> None:
"""
In the php.ini file, make sure the given key is set to the given value.
"""
print2(f"Setting value for {key} in php.ini")
add_line_to_file(PHP_INI_FILE, f"{key} = {value}")
def enable_apache_site(
enable_sites: List[Literal["webtrees", "webtrees-redir", "webtrees-ssl"]],
) -> None:
"""
Enable an Apache site.
"""
# update ssl apache config with cert path from env
ssl_site_file = "/etc/apache2/sites-available/webtrees-ssl.conf"
# make paths absolute
if not os.path.isabs(ENV.sslcertfile):
ENV.sslcertfile = os.path.join(DATA_DIR, ENV.sslcertfile)
if not os.path.isabs(ENV.sslcertkeyfile):
ENV.sslcertkeyfile = os.path.join(DATA_DIR, ENV.sslcertkeyfile)
# update file
with open(ssl_site_file, "r") as fp:
ssl_site_file_lines = fp.readlines()
new_ssl_site_file_lines = []
for line in ssl_site_file_lines:
if line.strip().startswith("SSLCertificateFile"):
line = line.replace(line.split()[1], ENV.sslcertfile)
elif line.strip().startswith("SSLCertificateKeyFile"):
line = line.replace(line.split()[1], ENV.sslcertkeyfile)
new_ssl_site_file_lines.append(line)
with open(ssl_site_file, "w") as fp:
fp.writelines(new_ssl_site_file_lines)
all_sites = ["webtrees", "webtrees-redir", "webtrees-ssl"]
# perl complains about locale to stderr, so disable that
# disable the other sites
for s in all_sites:
if s not in enable_sites:
print2(f"Disabling site {s}")
subprocess.check_call(
["a2dissite", s], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
)
# enable the desired sites
for s in enable_sites:
print2(f"Enabling site {s}")
subprocess.check_call(
["a2ensite", s], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
)
def perms() -> None:
"""
Set up folder permissions
"""
print2("Setting up folder permissions for uploads")
# https://github.com/linuxserver/docker-baseimage-alpine/blob/bef0f4cee208396c92c0fdd1426613de02698301/root/etc/s6-overlay/s6-rc.d/init-adduser/run#L4-L9
subprocess.check_call(["groupmod", "-o", "-g", ENV.pgid, "www-data"])
subprocess.check_call(["usermod", "-o", "-u", ENV.puid, "www-data"])
subprocess.check_call(["chown", "-R", "www-data:www-data", DATA_DIR])
if os.path.isfile(CONFIG_FILE):
subprocess.check_call(["chmod", "700", CONFIG_FILE])
def php_ini() -> None:
"""
Update PHP .ini file
"""
print2("Updating php.ini")
if not os.path.isfile(PHP_INI_FILE):
print2("Creating php.ini")
os.makedirs(os.path.dirname(PHP_INI_FILE), exist_ok=True)
with open(PHP_INI_FILE, "w") as fp:
fp.writelines(["[PHP]\n", "\n"])
set_php_ini_value("memory_limit", ENV.phpmemorylimit)
set_php_ini_value("max_execution_time", ENV.phpmaxexecutiontime)
set_php_ini_value("post_max_size", ENV.phppostmaxsize)
set_php_ini_value("upload_max_filesize", ENV.phpuploadmaxfilesize)
# https://webtrees.net/admin/performance/
set_php_ini_value("opcache.enable", "1")
set_php_ini_value(
"opcache.revalidate_freq", "60"
) # re check changed files every 60 seconds
set_php_ini_value("opcache.revalidate_path", "0")
def check_db_variables() -> bool:
"""
Check if all required database variables are present
"""
try:
assert ENV.dbtype is not None
assert ENV.dbname is not None
assert ENV.tblpfx is not None
if ENV.dbtype == DBType.sqlite:
ENV.dbhost = ""
ENV.dbport = ""
ENV.dbuser = ""
ENV.dbpass = ""
assert ENV.dbhost is not None
assert ENV.dbport is not None
assert ENV.dbuser is not None
assert ENV.dbpass is not None
except AssertionError:
print2("WARNING: Not all database variables are set")
return False
return True
def setup_wizard() -> None:
"""
Run the setup wizard
"""
if os.path.isfile(CONFIG_FILE):
return
print2("Attempting to automate setup wizard")
# make sure all the variables we need are not set to None
if not check_db_variables():
return
if any(
v is None
for v in [ENV.baseurl, ENV.wtname, ENV.wtuser, ENV.wtpass, ENV.wtemail]
):
print2("WARNING: Not all required variables were found for setup wizard")
return
assert ENV.baseurl is not None
if not ENV.baseurl.startswith("http"):
print2(
"WARNING: BASE_URL does not start with 'http'. This is likely not what you want."
)
print2("Automating setup wizard")
print2("Starting Apache in background")
# set us up to a known HTTP state
enable_apache_site(["webtrees"])
# run apache in the background
apache_proc = subprocess.Popen(["apache2-foreground"], stderr=subprocess.DEVNULL)
if ENV.dbtype in [DBType.mysql, DBType.pgsql]:
# for typing, check_db_variables already does this
assert ENV.dbhost is not None
# try to resolve the host
# most common error is wrong hostname
try:
socket.gethostbyname(ENV.dbhost)
except socket.gaierror:
print2(f"ERROR: Could not resolve database host '{ENV.dbhost}'")
print2(
"ERROR: You likely have the DBHOST environment variable set incorrectly."
)
print2("ERROR: Exiting.")
# stop apache
apache_proc.terminate()
# die
sys.exit(1)
# wait until database is ready
if ENV.dbtype == DBType.mysql:
# https://dev.mysql.com/doc/refman/8.0/en/mysqladmin.html#option_mysqladmin_user
# don't miss the capital P
cmd = ["mysqladmin", "ping", "-h", ENV.dbhost, "-P", ENV.dbport, "--silent"]
name = "MySQL"
elif ENV.dbtype == DBType.pgsql:
# https://www.postgresql.org/docs/current/app-pg-isready.html
cmd = ["pg_isready", "-h", ENV.dbhost, "-p", ENV.dbport, "--quiet"]
name = "PostgreSQL"
while subprocess.run(cmd).returncode != 0: # type: ignore
print2(f"Waiting for {name} server {ENV.dbhost}:{ENV.dbport} to be ready") # type: ignore
time.sleep(1)
else:
# sqlite
# let Apache start up
time.sleep(2)
# send it
url = "http://127.0.0.1:80/"
print2(f"Sending setup wizard request to {url}")
retry_urlopen(
url,
urlencode(
{
"lang": ENV.lang,
"tblpfx": ENV.tblpfx,
"baseurl": ENV.baseurl,
"dbtype": ENV.dbtype.value,
"dbhost": ENV.dbhost,
"dbport": ENV.dbport,
"dbuser": ENV.dbuser,
"dbpass": ENV.dbpass,
"dbname": ENV.dbname,
"wtname": ENV.wtname,
"wtuser": ENV.wtuser,
"wtpass": ENV.wtpass,
"wtemail": ENV.wtemail,
"step": "6",
}
).encode("ascii"),
)
print2("Stopping Apache")
apache_proc.terminate()
def update_config_file() -> None:
"""
Update the config file with items set via environment variables
"""
print2("Updating config file")
if not os.path.isfile(CONFIG_FILE):
print2(f"Config file not found at {CONFIG_FILE}. Nothing to update.")
return
# update independent values
set_config_value("rewrite_urls", str(int(ENV.prettyurls)))
set_config_value("base_url", ENV.baseurl)
# update database values as a group
if check_db_variables():
set_config_value("dbtype", ENV.dbtype.value)
set_config_value("dbhost", ENV.dbhost)
set_config_value("dbport", ENV.dbport)
set_config_value("dbuser", ENV.dbuser)
set_config_value("dbpass", ENV.dbpass)
set_config_value("dbname", ENV.dbname)
set_config_value("tblpfx", ENV.tblpfx)
# update databases verification values
if ENV.dbtype == DBType.mysql and all(
v is not None for v in [ENV.dbkey, ENV.dbcert, ENV.dbca]
):
set_config_value("dbkey", ENV.dbkey)
set_config_value("dbcert", ENV.dbcert)
set_config_value("dbca", ENV.dbca)
set_config_value("dbverify", str(int(ENV.dbverify)))
def https() -> None:
"""
Configure enabled Apache sites
"""
print2("Configuring HTTPS")
# no https
if not ENV.https:
print2("Removing HTTPS")
enable_apache_site(["webtrees"])
# https with redirect
elif ENV.httpsredirect:
print2("Adding HTTPS, with HTTPS redirect")
enable_apache_site(["webtrees-ssl", "webtrees-redir"])
# https no redirect
else:
print2("Adding HTTPS, removing HTTPS redirect")
enable_apache_site(["webtrees", "webtrees-ssl"])
def htaccess() -> None:
"""
Recreate .htaccess file if it ever deletes itself in the /data/ directory
"""
src_htaccess_file = os.path.join("/bak", "webtrees", "data", ".htaccess")
htaccess_file = os.path.join(DATA_DIR, ".htaccess")
if not os.path.isfile(src_htaccess_file):
# skip if the backup file does not exist
print2(
f"WARNING: {src_htaccess_file} does not exist. Skipping {htaccess_file} integrity check"
)
return
# https://github.com/NathanVaughn/webtrees-docker/issues/188
# In the event the htaccess file in the data directory is different
# from what it's supposed to be, replace it.
if os.path.isfile(htaccess_file) and filecmp.cmp(src_htaccess_file, htaccess_file):
print2(f"{htaccess_file} integrity check succeeded")
return
print2(f"WARNING: {htaccess_file} does not exist or does not match master copy")
shutil.copyfile(src_htaccess_file, htaccess_file)
print2(f"Updated {htaccess_file}")
def main() -> None:
# first, set up permissions
perms()
# create php config
php_ini()
# run the setup wizard if the config file doesn't exist
setup_wizard()
# update the config file
update_config_file()
# configure https
https()
# make sure .htaccess exists
htaccess()
# set up permissions again
perms()
print2("Starting Apache")
subprocess.run(["apache2-foreground"], stderr=subprocess.DEVNULL)
if __name__ == "__main__":
main()
+12
View File
@@ -0,0 +1,12 @@
#!/bin/bash
# https://github.com/fisharebest/webtrees/blob/7f1eef18821463166c14403c3d0e0aa3ba7a4204/app/Http/Middleware/BadBotBlocker.php#L335
# https://github.com/fisharebest/webtrees/blob/7f1eef18821463166c14403c3d0e0aa3ba7a4204/app/Http/Middleware/BadBotBlocker.php#L313-L318
# https://github.com/NathanVaughn/webtrees-docker/issues/164
if ls /etc/apache2/sites-enabled | grep -q 'ssl';
then
curl --verbose --silent --insecure --fail --cookie-jar /tmp/cookie.txt --cookie "x=y" --cookie /tmp/cookie.txt --user-agent "Chrome/" https://127.0.0.1:443/ || exit 1
else
curl --verbose --silent --fail --cookie-jar /tmp/cookie.txt --cookie "x=y" --cookie /tmp/cookie.txt --user-agent "Chrome/" http://127.0.0.1:80/ || exit 1
fi
+39
View File
@@ -0,0 +1,39 @@
diff --git "a/.\\UpgradeService.php" "b/.\\UpgradeServicePatched.php"
index b69bea0..c3e682c 100644
--- "a/.\\UpgradeService.php"
+++ "b/.\\UpgradeServicePatched.php"
@@ -339,34 +339,6 @@ class UpgradeService
*/
private function fetchLatestVersion(bool $force): string
{
- $last_update_timestamp = (int) Site::getPreference('LATEST_WT_VERSION_TIMESTAMP');
-
- $current_timestamp = time();
-
- if ($force || $last_update_timestamp < $current_timestamp - self::CHECK_FOR_UPDATE_INTERVAL) {
- try {
- $client = new Client([
- 'timeout' => self::HTTP_TIMEOUT,
- ]);
-
- $response = $client->get(self::UPDATE_URL, [
- 'query' => $this->serverParameters(),
- ]);
-
- if ($response->getStatusCode() === StatusCodeInterface::STATUS_OK) {
- Site::setPreference('LATEST_WT_VERSION', $response->getBody()->getContents());
- Site::setPreference('LATEST_WT_VERSION_TIMESTAMP', (string) $current_timestamp);
- Site::setPreference('LATEST_WT_VERSION_ERROR', '');
- } else {
- Site::setPreference('LATEST_WT_VERSION_ERROR', 'HTTP' . $response->getStatusCode());
- }
- } catch (GuzzleException $ex) {
- // Can't connect to the server?
- // Use the existing information about latest versions.
- Site::setPreference('LATEST_WT_VERSION_ERROR', $ex->getMessage());
- }
- }
-
return Site::getPreference('LATEST_WT_VERSION');
}
+39
View File
@@ -0,0 +1,39 @@
diff --git "a/.\\UpgradeService.php" "b/.\\UpgradeServicePatched.php"
index 6bb3504..a630dfb 100644
--- "a/.\\UpgradeService.php"
+++ "b/.\\UpgradeServicePatched.php"
@@ -337,34 +337,6 @@ class UpgradeService
*/
private function fetchLatestVersion(bool $force): string
{
- $last_update_timestamp = (int) Site::getPreference('LATEST_WT_VERSION_TIMESTAMP');
-
- $current_timestamp = time();
-
- if ($force || $last_update_timestamp < $current_timestamp - self::CHECK_FOR_UPDATE_INTERVAL) {
- Site::setPreference('LATEST_WT_VERSION_TIMESTAMP', (string) $current_timestamp);
-
- try {
- $client = new Client([
- 'timeout' => self::HTTP_TIMEOUT,
- ]);
-
- $response = $client->get(self::UPDATE_URL, [
- 'query' => $this->serverParameters(),
- ]);
-
- if ($response->getStatusCode() === StatusCodeInterface::STATUS_OK) {
- Site::setPreference('LATEST_WT_VERSION', $response->getBody()->getContents());
- Site::setPreference('LATEST_WT_VERSION_ERROR', '');
- } else {
- Site::setPreference('LATEST_WT_VERSION_ERROR', 'HTTP' . $response->getStatusCode());
- }
- } catch (GuzzleException $ex) {
- // Can't connect to the server?
- // Use the existing information about latest versions.
- Site::setPreference('LATEST_WT_VERSION_ERROR', $ex->getMessage());
- }
- }
return Site::getPreference('LATEST_WT_VERSION');
}