Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions DECISIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,15 @@ Also adds `tzdata` to Noble's distro `extraPackages` (matching Jammy). FB3 relie
**Decision:** The `update-repo` job in `publish-fork.yaml` only commits and pushes regenerated `generated/` and `README.md` when running on the fork's default branch (`github.event.repository.default_branch`). Dispatches on PR or feature branches still run `Invoke-Build Prepare` and `Invoke-Build Update-Readme` (so a template-substitution regression still fails the workflow) but skip the `git commit` / `git push`. Amends D-014 for the publish-fork case; `publish.yaml` (the official-repo publish) is unchanged.

**Rationale:** `publish-fork.yaml` passes `-Registry 'ghcr.io/<owner>'` to `Update-Readme`, which substitutes the fork's registry into the README table header. The previous unguarded auto-commit pushed that fork-specific README back to whatever branch was dispatched, including branches with open upstream PRs — directly polluting the PR diff with content that must not land upstream, and breaking GitHub's linear rebase when the upstream master had its own concurrent `README.md` changes (observed during PR #43, which required a force-pushed clean rebase to unblock). Branch-gating preserves D-014's "generated/ tracked in git" invariant on the fork's default branch while keeping PR/feature branches diff-clean against upstream. Confines the `-Registry` rewrite to the only place the fork wants it (its own showcased README on `master`).

## D-019: FB3 library provisioning runs before the Firebird installer

**Decision:** The FB3-only `libtommath.so.0` symlink and the `libncurses5`/`libtinfo5` provisioning (D-017) run inside the main `RUN` step, after the prerequisite `apt-get install` and before `./install.sh -silent`. They are no longer separate `RUN` steps after the install. Amends the placement described in D-017; the provisioning logic itself is unchanged.

**Rationale:** The FB3 installer sets the generated SYSDBA password with `gsec -add sysdba -pw <password>`, and then writes that password to `/opt/firebird/SYSDBA.password`. FB3's `gsec` (and `libfbclient`, `libSrp.so`) link against `libtommath.so.0` and `libncurses.so.5`/`libtinfo.so.5`. With the libraries provisioned only after the install, `gsec` failed with `error while loading shared libraries: libtommath.so.0`; the installer's `runSilent` wrapper printed the error and carried on. The image therefore shipped the tarball's pristine `security3.fdb` — no Srp user, no `PLG$SRP` table — alongside a `SYSDBA.password` file holding a password that was never set. Any Srp login then failed with the misleading `Install incomplete, please read the Compatibility chapter in the release notes for this version`. The defect stayed hidden because `FIREBIRD_ROOT_PASSWORD` (used in every documented example) and `FIREBIRD_USER` both go through the Srp user manager at container start, which creates the missing structures. The test suite now covers the stock, no-environment container. FB4+ is unaffected: its binaries' dependencies are satisfied by the prerequisite packages. See [issue #47](https://github.com/FirebirdSQL/firebird-docker/issues/47).

## D-021: Random SYSDBA password on first start, gated by the security database checksum

**Decision:** When `FIREBIRD_ROOT_PASSWORD` is not set, the entrypoint generates a random 20-character alphanumeric SYSDBA password, sets it with `CREATE OR ALTER USER SYSDBA ... USING PLUGIN Srp` (and `Legacy_UserManager` when `FIREBIRD_USE_LEGACY_AUTH=true`), and rewrites `/opt/firebird/SYSDBA.password` in the installer's format. This happens only while the security database still matches the SHA-256 checksum recorded at image build time (`/opt/firebird/.security.fdb.sha256`). Only the file's path is logged, not the password: container logs are often readable by more people than the container itself (log aggregators, CI output), so printing the password to stdout would re-expose it. The behaviour when `FIREBIRD_ROOT_PASSWORD` is set is unchanged.

**Rationale:** The Firebird installer generates the SYSDBA password at image build time, so every container of an image shared the same password, readable by anyone who can pull the image. The security database lives in the container's writable layer, so the password must be replaced once per container: restarts keep it, recreated containers get a new one. The checksum is what makes the change idempotent and safe. After the first start the database no longer matches, so restarts leave it alone, with no marker file needed. A security database persisted outside the container and bind-mounted in ([issue #5](https://github.com/FirebirdSQL/firebird-docker/issues/5)) doesn't match either, so its SYSDBA password is never overwritten. A plain "first start" marker would have broken that use case on every container recreation. See [issue #48](https://github.com/FirebirdSQL/firebird-docker/issues/48).
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,10 +199,18 @@ The following environment variables can be used to customize the container.

### `FIREBIRD_ROOT_PASSWORD`

Firebird installer generates a one-off password for `SYSDBA` and stores it in `/opt/firebird/SYSDBA.password`.

If `FIREBIRD_ROOT_PASSWORD` is set, `SYSDBA` password will be changed. And the file `/opt/firebird/SYSDBA.password` will be removed.

Otherwise, a random password for `SYSDBA` is generated on the container's first start and stored in `/opt/firebird/SYSDBA.password`. It is not printed to the container log. To read it:

```bash
docker exec MY_CONTAINER_NAME_OR_ID cat /opt/firebird/SYSDBA.password
```

The security database lives in the container (not in the data volume). Therefore, restarting a container keeps its password, while recreating it (e.g. `docker compose up` after an image update) generates a new one. Set `FIREBIRD_ROOT_PASSWORD` if you need a stable `SYSDBA` password.

A security database changed since the image was built (e.g. a persisted `/opt/firebird/security5.fdb` bind-mounted into the container) is left untouched.



### `FIREBIRD_USER`
Expand Down
86 changes: 38 additions & 48 deletions generated/3.0.10/bookworm/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,40 @@ RUN set -eux; \
ca-certificates \
curl; \
\
# FB 3.0 only: provide libtommath.so.0 and libncurses5/libtinfo5 BEFORE running the installer.
# The installer sets the SYSDBA password with 'gsec', which cannot start without them.
# See DECISIONS.md D-019 and https://github.com/FirebirdSQL/firebird-docker/issues/47
if [ "$FIREBIRD_MAJOR" = "3" ]; then \
# Fix libtommath -- https://github.com/FirebirdSQL/firebird/issues/5716#issuecomment-826239174
MULTIARCH=$(dpkg-architecture -qDEB_HOST_MULTIARCH 2>/dev/null || echo "$(uname -m)-linux-gnu"); \
ln -sf /usr/lib/$MULTIARCH/libtommath.so.1 /usr/lib/$MULTIARCH/libtommath.so.0; \
\
# libncurses5/libtinfo5 were dropped from apt on Debian Trixie and Ubuntu Noble; pull them from the previous release pool.
# See DECISIONS.md D-017 and https://github.com/FirebirdSQL/firebird-docker/issues/42
. /etc/os-release; \
case "$ID-$VERSION_CODENAME" in \
debian-bookworm|debian-bullseye|ubuntu-jammy) \
apt-get install -y --no-install-recommends libtinfo5 libncurses5 \
;; \
debian-trixie) \
curl -fSL -o /tmp/libtinfo5.deb http://deb.debian.org/debian/pool/main/n/ncurses/libtinfo5_6.4-4_amd64.deb; \
curl -fSL -o /tmp/libncurses5.deb http://deb.debian.org/debian/pool/main/n/ncurses/libncurses5_6.4-4_amd64.deb; \
dpkg -i /tmp/libtinfo5.deb /tmp/libncurses5.deb; \
rm -f /tmp/libtinfo5.deb /tmp/libncurses5.deb \
;; \
ubuntu-noble) \
curl -fSL -o /tmp/libtinfo5.deb http://archive.ubuntu.com/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.3_amd64.deb; \
curl -fSL -o /tmp/libncurses5.deb http://archive.ubuntu.com/ubuntu/pool/universe/n/ncurses/libncurses5_6.3-2ubuntu0.3_amd64.deb; \
dpkg -i /tmp/libtinfo5.deb /tmp/libncurses5.deb; \
rm -f /tmp/libtinfo5.deb /tmp/libncurses5.deb \
;; \
*) \
echo "FB3: no libncurses5 provisioning path for $ID-$VERSION_CODENAME" >&2; \
exit 1 \
;; \
esac; \
fi; \
\
# Download
ARCH=$(dpkg --print-architecture); \
case "$ARCH" in \
Expand Down Expand Up @@ -61,60 +95,16 @@ RUN set -eux; \
/opt/firebird/include; \
# Remove 'employee' sample database from 'databases.conf'
sed -i '/^employee/d' /opt/firebird/databases.conf; \
# Record the checksum of the security database as shipped in the image. The entrypoint replaces the SYSDBA
# password generated by the installer (the same for every container of this image) only while the security
# database still matches it. See DECISIONS.md D-021 and https://github.com/FirebirdSQL/firebird-docker/issues/48
sha256sum /opt/firebird/security${FIREBIRD_MAJOR}.fdb > /opt/firebird/.security.fdb.sha256; \
\
# Clean up temporary packages (curl, ca-certificates) and apt lists
apt-get purge -y --auto-remove curl ca-certificates; \
apt-get clean; \
rm -rf /var/lib/apt/lists/*

# Fix libtommath for FB 3.0 -- https://github.com/FirebirdSQL/firebird/issues/5716#issuecomment-826239174
RUN set -eux; \
ARCH=$(dpkg-architecture -qDEB_HOST_MULTIARCH 2>/dev/null || echo "$(uname -m)-linux-gnu"); \
[ $FIREBIRD_MAJOR -eq 3 ] && ln -sf /usr/lib/$ARCH/libtommath.so.1 /usr/lib/$ARCH/libtommath.so.0 || true

# FB 3.0 needs libncurses5/libtinfo5. On Debian Trixie and Ubuntu Noble those
# packages were dropped from apt; pull them from the previous release pool.
# See DECISIONS.md D-017 and https://github.com/FirebirdSQL/firebird-docker/issues/42
RUN set -eux; \
if [ "$FIREBIRD_MAJOR" = "3" ]; then \
. /etc/os-release; \
case "$ID-$VERSION_CODENAME" in \
debian-bookworm|debian-bullseye|ubuntu-jammy) \
apt-get update; \
apt-get install -y --no-install-recommends libtinfo5 libncurses5; \
rm -rf /var/lib/apt/lists/* \
;; \
debian-trixie) \
apt-get update; \
apt-get install -y --no-install-recommends ca-certificates curl; \
cd /tmp; \
curl -fSL -O http://deb.debian.org/debian/pool/main/n/ncurses/libtinfo5_6.4-4_amd64.deb; \
curl -fSL -O http://deb.debian.org/debian/pool/main/n/ncurses/libncurses5_6.4-4_amd64.deb; \
dpkg -i libtinfo5_6.4-4_amd64.deb libncurses5_6.4-4_amd64.deb; \
rm -f libtinfo5_6.4-4_amd64.deb libncurses5_6.4-4_amd64.deb; \
apt-get purge -y --auto-remove curl ca-certificates; \
apt-get clean; \
rm -rf /var/lib/apt/lists/* \
;; \
ubuntu-noble) \
apt-get update; \
apt-get install -y --no-install-recommends ca-certificates curl; \
cd /tmp; \
curl -fSL -O http://archive.ubuntu.com/ubuntu/pool/universe/n/ncurses/libtinfo5_6.3-2ubuntu0.2_amd64.deb; \
curl -fSL -O http://archive.ubuntu.com/ubuntu/pool/universe/n/ncurses/libncurses5_6.3-2ubuntu0.2_amd64.deb; \
dpkg -i libtinfo5_6.3-2ubuntu0.2_amd64.deb libncurses5_6.3-2ubuntu0.2_amd64.deb; \
rm -f libtinfo5_6.3-2ubuntu0.2_amd64.deb libncurses5_6.3-2ubuntu0.2_amd64.deb; \
apt-get purge -y --auto-remove curl ca-certificates; \
apt-get clean; \
rm -rf /var/lib/apt/lists/* \
;; \
*) \
echo "FB3: no libncurses5 provisioning path for $ID-$VERSION_CODENAME" >&2; \
exit 1 \
;; \
esac; \
fi

# System path
ENV PATH=/opt/firebird/bin:$PATH

Expand Down
88 changes: 71 additions & 17 deletions generated/3.0.10/bookworm/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -142,39 +142,96 @@ set_config() {
fi
}

# Changes SYSDBA password if FIREBIRD_ROOT_PASSWORD variable is set.
set_sysdba() {
read_from_file_or_env 'FIREBIRD_ROOT_PASSWORD'
if [ -n "$FIREBIRD_ROOT_PASSWORD" ]; then
echo 'Changing SYSDBA password.'
# Security database as shipped in the image, and its checksum recorded at image build time.
SECURITY_DB_CHECKSUM_FILE=/opt/firebird/.security.fdb.sha256

# usage: generate_password
# Prints a random 20-character alphanumeric password.
generate_password() {
local password
password=$(head -c 512 /dev/urandom | LC_ALL=C tr -dc 'A-Za-z0-9')
printf '%s' "${password:0:20}"
}

local escaped_password
escaped_password=$(escape_sql_string "$FIREBIRD_ROOT_PASSWORD")
# usage: change_sysdba_password PASSWORD
# Sets SYSDBA password in the security database (also for Legacy_UserManager if FIREBIRD_USE_LEGACY_AUTH is 'true').
change_sysdba_password() {
local escaped_password
escaped_password=$(escape_sql_string "$1")

# [Tabs ahead]
/opt/firebird/bin/isql -b -user SYSDBA security.db <<-EOL
# [Tabs ahead]
/opt/firebird/bin/isql -b -user SYSDBA security.db <<-EOL
CREATE OR ALTER USER SYSDBA
PASSWORD '${escaped_password}'
USING PLUGIN Srp;
EXIT;
EOL

if [ "$FIREBIRD_USE_LEGACY_AUTH" == 'true' ]; then
# [Tabs ahead]
/opt/firebird/bin/isql -b -user SYSDBA security.db <<-EOL
if [ "$FIREBIRD_USE_LEGACY_AUTH" == 'true' ]; then
# [Tabs ahead]
/opt/firebird/bin/isql -b -user SYSDBA security.db <<-EOL
CREATE OR ALTER USER SYSDBA
PASSWORD '${escaped_password}'
USING PLUGIN Legacy_UserManager;
EXIT;
EOL
fi
fi
}

# Sets SYSDBA password.
# If FIREBIRD_ROOT_PASSWORD variable is set, uses it and removes /opt/firebird/SYSDBA.password.
# Otherwise, if the security database is still the one shipped in the image, generates a random password and stores it
# in /opt/firebird/SYSDBA.password. The password generated by the Firebird installer at image build time is the same
# for every container of an image. See https://github.com/FirebirdSQL/firebird-docker/issues/48
# A security database changed since the image build (by a previous start of this container, or bind-mounted by
# the user) is left untouched.
set_sysdba() {
read_from_file_or_env 'FIREBIRD_ROOT_PASSWORD'
if [ -n "$FIREBIRD_ROOT_PASSWORD" ]; then
echo 'Changing SYSDBA password.'
change_sysdba_password "$FIREBIRD_ROOT_PASSWORD"
rm -rf /opt/firebird/SYSDBA.password
elif sha256sum --status --check "$SECURITY_DB_CHECKSUM_FILE" 2>/dev/null; then
echo 'Generating random SYSDBA password.'

local password
password=$(generate_password)
change_sysdba_password "$password"

# Stores it using the same format of Firebird installer.
# The file is read-only (0440): removes and recreates it.
rm -f /opt/firebird/SYSDBA.password
# [Tabs ahead]
(umask 0337; cat > /opt/firebird/SYSDBA.password <<-EOL
#
# Firebird generated password for user SYSDBA is:
#
ISC_USER=sysdba
ISC_PASSWORD=${password}
#
# Also set legacy variable though it can't be exported directly
#
ISC_PASSWD=${password}
#
# generated on ${HOSTNAME} at time $(date)
#
# Your password can be changed to a more suitable one using
# SQL operator ALTER USER.
#
EOL
)

# The password is not printed: container logs are often readable by more people than the container itself.
echo 'SYSDBA password stored in /opt/firebird/SYSDBA.password.'
fi
}

# Requires FIREBIRD_PASSWORD if FIREBIRD_USER is set.
# Runs before any other initialization step, so an invalid configuration fails without changing anything.
requires_user_password() {
read_from_file_or_env 'FIREBIRD_USER'
read_from_file_or_env 'FIREBIRD_PASSWORD'

if [ -n "$FIREBIRD_USER" ] && [ -z "$FIREBIRD_PASSWORD" ]; then
# [Tabs ahead]
cat >&2 <<-EOL
Expand All @@ -190,11 +247,7 @@ requires_user_password() {

# Create Firebird user.
create_user() {
read_from_file_or_env 'FIREBIRD_USER'
read_from_file_or_env 'FIREBIRD_PASSWORD'

if [ -n "$FIREBIRD_USER" ]; then
requires_user_password
echo "Creating user '$FIREBIRD_USER'..."

local quoted_user
Expand Down Expand Up @@ -345,6 +398,7 @@ run_daemon_and_wait() {
# main()
#
if [ "$1" = 'firebird' ]; then
requires_user_password
set_config
set_sysdba

Expand Down
Loading
Loading