Deploy phpPgAdmin with maintained public image
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
name: Validate and deploy phpPgAdmin
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
KUBECONFORM_VERSION: v0.7.0
|
||||
KUBECTL_VERSION: v1.36.3
|
||||
KUBERNETES_API: https://host.containers.internal:6443
|
||||
KUBERNETES_TLS_SERVER_NAME: 192.168.0.2
|
||||
NAMESPACE: default
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
name: Validate manifests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out source
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Validate scripts and Kubernetes resources
|
||||
run: |
|
||||
sh -n install.sh
|
||||
bash -n create-ci-kubeconfig.sh
|
||||
bash -n test.sh
|
||||
docker run --rm --interactive \
|
||||
"ghcr.io/yannh/kubeconform:${KUBECONFORM_VERSION}@sha256:85dbef6b4b312b99133decc9c6fc9495e9fc5f92293d4ff3b7e1b30f5611823c" \
|
||||
-strict -ignore-missing-schemas -summary < phppgadmin.yaml
|
||||
docker run --rm --interactive \
|
||||
"ghcr.io/yannh/kubeconform:${KUBECONFORM_VERSION}@sha256:85dbef6b4b312b99133decc9c6fc9495e9fc5f92293d4ff3b7e1b30f5611823c" \
|
||||
-strict -summary < ci-deployer.yaml
|
||||
|
||||
deploy:
|
||||
name: Deploy to Kubernetes
|
||||
if: gitea.ref == 'refs/heads/main'
|
||||
needs: validate
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out source
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install kubectl
|
||||
run: |
|
||||
curl --fail --silent --show-error --location \
|
||||
--output "${RUNNER_TEMP}/kubectl" \
|
||||
"https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl"
|
||||
curl --fail --silent --show-error --location \
|
||||
--output "${RUNNER_TEMP}/kubectl.sha256" \
|
||||
"https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl.sha256"
|
||||
printf '%s %s\n' "$(cat "${RUNNER_TEMP}/kubectl.sha256")" "${RUNNER_TEMP}/kubectl" | sha256sum --check
|
||||
chmod 0700 "${RUNNER_TEMP}/kubectl"
|
||||
|
||||
- name: Configure Kubernetes access
|
||||
env:
|
||||
KUBE_CONFIG_BASE64: ${{ secrets.KUBE_CONFIG_BASE64 }}
|
||||
run: |
|
||||
test -n "${KUBE_CONFIG_BASE64}"
|
||||
printf '%s' "${KUBE_CONFIG_BASE64}" | base64 --decode > "${RUNNER_TEMP}/kubeconfig"
|
||||
chmod 0600 "${RUNNER_TEMP}/kubeconfig"
|
||||
export KUBECONFIG="${RUNNER_TEMP}/kubeconfig"
|
||||
"${RUNNER_TEMP}/kubectl" config set-cluster cluster \
|
||||
--server="${KUBERNETES_API}" \
|
||||
--tls-server-name="${KUBERNETES_TLS_SERVER_NAME}"
|
||||
|
||||
- name: Apply and verify
|
||||
env:
|
||||
COMMIT_SHA: ${{ gitea.sha }}
|
||||
run: |
|
||||
export KUBECONFIG="${RUNNER_TEMP}/kubeconfig"
|
||||
sed \
|
||||
"s|deployment.brunner.ninja/revision: manual|deployment.brunner.ninja/revision: ${COMMIT_SHA}|" \
|
||||
phppgadmin.yaml > "${RUNNER_TEMP}/phppgadmin.yaml"
|
||||
grep -Fq "deployment.brunner.ninja/revision: ${COMMIT_SHA}" \
|
||||
"${RUNNER_TEMP}/phppgadmin.yaml"
|
||||
"${RUNNER_TEMP}/kubectl" apply \
|
||||
--dry-run=server \
|
||||
--validate=false \
|
||||
--filename "${RUNNER_TEMP}/phppgadmin.yaml"
|
||||
"${RUNNER_TEMP}/kubectl" apply \
|
||||
--validate=false \
|
||||
--filename "${RUNNER_TEMP}/phppgadmin.yaml"
|
||||
|
||||
for attempt in {1..120}; do
|
||||
IFS='|' read -r generation observed desired updated ready available unavailable image <<< "$(
|
||||
"${RUNNER_TEMP}/kubectl" --namespace "${NAMESPACE}" \
|
||||
get deployment/phppgadmin \
|
||||
--output=jsonpath='{.metadata.generation}|{.status.observedGeneration}|{.spec.replicas}|{.status.updatedReplicas}|{.status.readyReplicas}|{.status.availableReplicas}|{.status.unavailableReplicas}|{.spec.template.spec.containers[0].image}'
|
||||
)"
|
||||
|
||||
echo "Rollout ${attempt}/120: generation ${observed}/${generation}, replicas ${updated}/${desired} updated, ${ready}/${desired} ready, ${available}/${desired} available, image ${image}"
|
||||
if [[ "${observed}" == "${generation}" \
|
||||
&& "${updated}" == "${desired}" \
|
||||
&& "${ready}" == "${desired}" \
|
||||
&& "${available}" == "${desired}" \
|
||||
&& ( -z "${unavailable}" || "${unavailable}" == "0" ) ]]; then
|
||||
echo "phpPgAdmin deployment successfully rolled out"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
sleep 5
|
||||
done
|
||||
|
||||
echo "phpPgAdmin rollout did not complete within ten minutes" >&2
|
||||
exit 1
|
||||
@@ -0,0 +1,7 @@
|
||||
# Never commit generated cluster or registry credentials.
|
||||
kubeconfig
|
||||
*.kubeconfig
|
||||
.env
|
||||
|
||||
# Local build output.
|
||||
*.log
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
FROM registry.brunner.ninja/feedc0de/php-apache-postgres
|
||||
|
||||
RUN pacman -Syu --noconfirm git sudo base-devel \
|
||||
&& cd /tmp \
|
||||
&& sudo -u http git clone https://aur.archlinux.org/phppgadmin.git \
|
||||
&& cd phppgadmin \
|
||||
&& sudo -u http makepkg -s \
|
||||
&& pacman -U --noconfirm *.pkg.* \
|
||||
&& ls \
|
||||
&& cd .. \
|
||||
&& rm -Rfv phppgadmin \
|
||||
&& pacman -Rcnsu --noconfirm git sudo base-devel \
|
||||
&& pacman -Sc --noconfirm \
|
||||
&& rm -v /var/log/pacman.log /var/lib/pacman/sync/*
|
||||
|
||||
RUN sed -i 's|^DocumentRoot.*|DocumentRoot "/usr/share/webapps/phppgadmin"|' /etc/httpd/conf/httpd.conf \
|
||||
&& echo '<Directory "/usr/share/webapps/phppgadmin">' >> /etc/httpd/conf/httpd.conf \
|
||||
&& echo ' DirectoryIndex index.php' >> /etc/httpd/conf/httpd.conf \
|
||||
&& echo ' AllowOverride All' >> /etc/httpd/conf/httpd.conf \
|
||||
&& echo ' Options FollowSymlinks' >> /etc/httpd/conf/httpd.conf \
|
||||
&& echo ' Require all granted' >> /etc/httpd/conf/httpd.conf \
|
||||
&& echo '</Directory>' >> /etc/httpd/conf/httpd.conf
|
||||
@@ -1,3 +1,185 @@
|
||||
# phppgadmin-docker
|
||||
# phpPgAdmin on Kubernetes
|
||||
|
||||
Simple docker image to run latest phppgadmin (provided by arch)
|
||||
Production deployment for the brunner.ninja phpPgAdmin service. The repository
|
||||
uses a maintained public container image, a single plain Kubernetes manifest,
|
||||
Traefik ingress, health probes, zero-unavailable rolling updates, and restricted
|
||||
Gitea Actions deployment credentials.
|
||||
|
||||
## Architecture
|
||||
|
||||
- `tozd/phppgadmin` supplies phpPgAdmin, Nginx, PHP, and PostgreSQL client tools.
|
||||
- The image is pinned to an immutable multi-platform manifest digest rather
|
||||
than a mutable tag.
|
||||
- `default/phppgadmin-config` mounts the complete multi-server configuration at
|
||||
`/etc/phppgadmin/config.inc.php`.
|
||||
- A ClusterIP Service exposes TCP port 80 to Traefik.
|
||||
- `https://phppgadmin.brunner.ninja` redirects to the image's native
|
||||
`/phppgadmin/` path.
|
||||
- One replica uses `maxUnavailable: 0`, `maxSurge: 1`, and five seconds of
|
||||
minimum readiness. The old pod remains available until the replacement has
|
||||
passed its HTTP probe continuously for five seconds.
|
||||
- CI places the Git commit in a pod-template annotation. This forces a safe
|
||||
rollout when a ConfigMap-only change would otherwise leave a `subPath` mount
|
||||
unchanged in the running container.
|
||||
- Startup, readiness, and liveness probes request `/phppgadmin/` over HTTP.
|
||||
|
||||
The deployment is stateless. phpPgAdmin does not store database passwords or
|
||||
sessions on a PVC; users authenticate directly to the selected PostgreSQL
|
||||
server. The ConfigMap lists server addresses but contains no credentials.
|
||||
|
||||
## Why there is no Dockerfile
|
||||
|
||||
This repository previously built phpPgAdmin from the Arch User Repository on
|
||||
top of a private base image and pushed the result to the private Quay registry.
|
||||
That duplicated public image storage and made every repository commit rebuild
|
||||
an otherwise unchanged application.
|
||||
|
||||
The current deployment instead uses the actively maintained public
|
||||
[`tozd/phppgadmin`](https://gitlab.com/tozd/docker/phppgadmin) image. It supports
|
||||
the same configuration-file mount used here. The pinned image contains
|
||||
phpPgAdmin 7.14.7, which is also the current AUR package version at the time of
|
||||
this repository conversion.
|
||||
|
||||
The committed image is:
|
||||
|
||||
```text
|
||||
tozd/phppgadmin@sha256:4ee690768d54b0c057dbc4ed5c1baa152c1a5ef0ebd6eb12f1e310e545eb4489
|
||||
```
|
||||
|
||||
It was inspected and smoke-tested before deployment. Pinning prevents an
|
||||
upstream rebuild from silently changing the running software. To upgrade,
|
||||
inspect and test the new upstream digest, replace the image line in
|
||||
`phppgadmin.yaml`, and merge the change through the normal deployment workflow.
|
||||
|
||||
## Repository contents
|
||||
|
||||
- `phppgadmin.yaml`: ConfigMap, Deployment, Service, Ingress, and Traefik root
|
||||
redirect Middleware.
|
||||
- `install.sh`: server-side dry-run, apply, and rollout verification.
|
||||
- `test.sh`: local script and Kubernetes schema validation.
|
||||
- `ci-deployer.yaml`: restricted Gitea Actions ServiceAccount, Role, binding,
|
||||
and revocable token.
|
||||
- `create-ci-kubeconfig.sh`: one-time CI identity bootstrap.
|
||||
- `.gitea/workflows/deploy.yml`: validation on every push and pull request;
|
||||
deployment after successful validation of `main`.
|
||||
|
||||
There is no Helm chart, Kustomize overlay, Argo CD application, private image,
|
||||
or container build pipeline.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Kubernetes and kubectl.
|
||||
- An amd64 worker node. The pinned upstream image currently publishes amd64.
|
||||
- Traefik with the `traefik.io/v1alpha1` Middleware CRD and ingress class
|
||||
`traefik`.
|
||||
- The existing Traefik `websecure` entrypoint and default
|
||||
`*.brunner.ninja` TLS certificate.
|
||||
- Network connectivity from the pod to every PostgreSQL hostname listed in the
|
||||
ConfigMap.
|
||||
|
||||
Another cluster operator can reuse the manifest by changing the namespace,
|
||||
database server list, ingress hostname, ingress class, and redirect Middleware.
|
||||
|
||||
## Install manually
|
||||
|
||||
With an administrator kubeconfig active, run:
|
||||
|
||||
```sh
|
||||
./install.sh
|
||||
```
|
||||
|
||||
The script performs a server-side dry-run, applies all resources, and waits up
|
||||
to ten minutes for the Deployment. Existing resource names are preserved, so
|
||||
the Service keeps its ClusterIP and the change rolls out in place.
|
||||
|
||||
Verify both application paths:
|
||||
|
||||
```sh
|
||||
curl -I https://phppgadmin.brunner.ninja/
|
||||
curl -I https://phppgadmin.brunner.ninja/phppgadmin/
|
||||
```
|
||||
|
||||
The first should redirect permanently and the second should return the
|
||||
phpPgAdmin application.
|
||||
|
||||
## Configure PostgreSQL servers
|
||||
|
||||
Edit `config.inc.php` inside the ConfigMap in `phppgadmin.yaml`. Each server
|
||||
entry contains a display name, service DNS name, port, SSL mode, and default
|
||||
database. Do not put PostgreSQL usernames or passwords into the ConfigMap or
|
||||
Git repository.
|
||||
|
||||
The file is mounted using `subPath`, so Kubernetes does not update it inside an
|
||||
already-running pod. The CI workflow replaces the manifest's `manual` revision
|
||||
annotation with the Git commit SHA before applying it, which safely rolls the
|
||||
pod for every merged configuration change. A manual `./install.sh` changes the
|
||||
annotation back to `manual` and likewise triggers a rollout after a CI deploy.
|
||||
|
||||
## Validate locally
|
||||
|
||||
```sh
|
||||
./test.sh
|
||||
```
|
||||
|
||||
The test checks both shell scripts and validates the Kubernetes resources with
|
||||
kubeconform. The Traefik Middleware is reported as skipped because kubeconform's
|
||||
standard schema catalog does not contain cluster-specific CRDs; the Kubernetes
|
||||
API validates it during the server-side dry-run.
|
||||
|
||||
## Automatic deployment with Gitea Actions
|
||||
|
||||
The workflow validates every push and pull request. A successful push to
|
||||
`main` applies the complete `phppgadmin.yaml`, including ConfigMap, Deployment,
|
||||
Service, Ingress, and Middleware, then polls the Deployment for up to ten
|
||||
minutes.
|
||||
|
||||
No image is built or pushed. The workflow requires only one Gitea Actions
|
||||
secret:
|
||||
|
||||
- `KUBE_CONFIG_BASE64`: the restricted phpPgAdmin deployment kubeconfig,
|
||||
base64-encoded on one line.
|
||||
|
||||
### One-time CI setup
|
||||
|
||||
1. Run `./install.sh` with an administrator identity so every application
|
||||
resource already exists.
|
||||
2. Run:
|
||||
|
||||
```sh
|
||||
./create-ci-kubeconfig.sh
|
||||
```
|
||||
|
||||
3. Copy only the final base64 line into the Gitea repository Actions secret
|
||||
named `KUBE_CONFIG_BASE64`.
|
||||
4. Protect `main`, require successful validation before merging, and review
|
||||
manifest and workflow changes.
|
||||
5. Push a harmless README change and confirm the deployment job succeeds.
|
||||
|
||||
The workflow matches the existing brunner.ninja runner: Kubernetes API URL
|
||||
`https://host.containers.internal:6443`, with TLS server name `192.168.0.2`.
|
||||
Other clusters must change those values.
|
||||
|
||||
CI can get, patch, and update only these existing named objects:
|
||||
|
||||
- ConfigMap `phppgadmin-config`;
|
||||
- Deployment and Service `phppgadmin`;
|
||||
- Ingress `phppgadmin`;
|
||||
- Middleware `phppgadmin-root-redirect`.
|
||||
|
||||
It cannot create or delete resources and cannot call the Kubernetes Secrets
|
||||
API. Revoke it by deleting `default/phppgadmin-deployer-token` and the Gitea
|
||||
secret. Rerunning the wrapper creates a replacement token.
|
||||
|
||||
Kubernetes RBAC cannot constrain individual fields inside an allowed
|
||||
Deployment patch. A malicious change merged into `main` could alter the pod
|
||||
specification. Branch protection and review therefore remain part of the
|
||||
security boundary.
|
||||
|
||||
## Internet exposure
|
||||
|
||||
This instance is reachable from the public internet and presents direct
|
||||
PostgreSQL username/password authentication. Use long, unique database
|
||||
passwords, restrictive PostgreSQL roles and `pg_hba.conf` rules, TLS for remote
|
||||
database connections, and external rate limiting or access control where
|
||||
appropriate. phpPgAdmin's `extra_login_security` remains enabled in the
|
||||
committed configuration.
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
# Bootstrap this file once with an administrator identity. CI can update only
|
||||
# the existing phpPgAdmin resources and cannot call the Kubernetes Secrets API.
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: phppgadmin-deployer
|
||||
namespace: default
|
||||
automountServiceAccountToken: false
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: phppgadmin-deployer
|
||||
namespace: default
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
resourceNames:
|
||||
- phppgadmin-config
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- services
|
||||
resourceNames:
|
||||
- phppgadmin
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- apps
|
||||
resources:
|
||||
- deployments
|
||||
resourceNames:
|
||||
- phppgadmin
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- networking.k8s.io
|
||||
resources:
|
||||
- ingresses
|
||||
resourceNames:
|
||||
- phppgadmin
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- traefik.io
|
||||
resources:
|
||||
- middlewares
|
||||
resourceNames:
|
||||
- phppgadmin-root-redirect
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: phppgadmin-deployer
|
||||
namespace: default
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: phppgadmin-deployer
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: phppgadmin-deployer
|
||||
namespace: default
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: phppgadmin-deployer-token
|
||||
namespace: default
|
||||
annotations:
|
||||
kubernetes.io/service-account.name: phppgadmin-deployer
|
||||
type: kubernetes.io/service-account-token
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
project_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
|
||||
namespace=${NAMESPACE:-default}
|
||||
service_account=${SERVICE_ACCOUNT:-phppgadmin-deployer}
|
||||
secret=${TOKEN_SECRET:-phppgadmin-deployer-token}
|
||||
|
||||
kubectl apply --filename "${project_dir}/ci-deployer.yaml" >&2
|
||||
|
||||
for attempt in {1..30}; do
|
||||
if kubectl --namespace "${namespace}" get secret "${secret}" \
|
||||
--output=jsonpath='{.data.token}' 2>/dev/null | grep -q .; then
|
||||
break
|
||||
fi
|
||||
if [[ "${attempt}" == 30 ]]; then
|
||||
echo "Timed out waiting for the service-account token" >&2
|
||||
exit 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
workdir=$(mktemp --directory)
|
||||
trap 'rm -rf "${workdir}"' EXIT
|
||||
|
||||
server=$(kubectl config view --minify --output=jsonpath='{.clusters[0].cluster.server}')
|
||||
kubectl --namespace "${namespace}" get secret "${secret}" \
|
||||
--output=jsonpath='{.data.ca\.crt}' | base64 --decode > "${workdir}/ca.crt"
|
||||
token=$(kubectl --namespace "${namespace}" get secret "${secret}" \
|
||||
--output=jsonpath='{.data.token}' | base64 --decode)
|
||||
|
||||
export KUBECONFIG="${workdir}/config"
|
||||
kubectl config set-cluster cluster \
|
||||
--server="${server}" \
|
||||
--certificate-authority="${workdir}/ca.crt" \
|
||||
--embed-certs=true >/dev/null
|
||||
kubectl config set-credentials "${service_account}" --token="${token}" >/dev/null
|
||||
kubectl config set-context phppgadmin \
|
||||
--cluster=cluster \
|
||||
--user="${service_account}" \
|
||||
--namespace="${namespace}" >/dev/null
|
||||
kubectl config use-context phppgadmin >/dev/null
|
||||
|
||||
echo "Store the following single line as the Gitea Actions secret KUBE_CONFIG_BASE64." >&2
|
||||
echo "Treat it as a password; do not commit it." >&2
|
||||
base64 --wrap=0 "${KUBECONFIG}"
|
||||
printf '\n'
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
namespace=default
|
||||
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
|
||||
if ! command -v kubectl >/dev/null 2>&1; then
|
||||
echo "Required command not found: kubectl" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
kubectl apply --dry-run=server --filename "$script_dir/phppgadmin.yaml"
|
||||
kubectl apply --filename "$script_dir/phppgadmin.yaml"
|
||||
kubectl rollout status deployment/phppgadmin \
|
||||
--namespace "$namespace" \
|
||||
--timeout=10m
|
||||
|
||||
echo
|
||||
echo "phpPgAdmin is available at https://phppgadmin.brunner.ninja."
|
||||
+334
@@ -0,0 +1,334 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: phppgadmin-config
|
||||
namespace: default
|
||||
labels:
|
||||
app.kubernetes.io/name: phppgadmin
|
||||
data:
|
||||
config.inc.php: |
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Central phpPgAdmin configuration. As a user you may modify the
|
||||
* settings here for your particular configuration.
|
||||
*
|
||||
* $Id: config.inc.php-dist,v 1.55 2008/02/18 21:10:31 xzilla Exp $
|
||||
*/
|
||||
|
||||
// An example server. Create as many of these as you wish,
|
||||
// indexed from zero upwards.
|
||||
|
||||
// Display name for the server on the login screen
|
||||
$conf['servers'][0]['desc'] = 'arschrock';
|
||||
|
||||
// Hostname or IP address for server. Use '' for UNIX domain socket.
|
||||
// use 'localhost' for TCP/IP connection on this computer
|
||||
$conf['servers'][0]['host'] = 'arschrock';
|
||||
|
||||
// Database port on server (5432 is the PostgreSQL default)
|
||||
$conf['servers'][0]['port'] = 5432;
|
||||
|
||||
// Database SSL mode
|
||||
// Possible options: disable, allow, prefer, require
|
||||
// To require SSL on older servers use option: legacy
|
||||
// To ignore the SSL mode, use option: unspecified
|
||||
$conf['servers'][0]['sslmode'] = 'allow';
|
||||
|
||||
// Change the default database only if you cannot connect to template1.
|
||||
// For a PostgreSQL 8.1+ server, you can set this to 'postgres'.
|
||||
$conf['servers'][0]['defaultdb'] = 'template1';
|
||||
|
||||
// Specify the path to the database dump utilities for this server.
|
||||
// You can set these to '' if no dumper is available.
|
||||
$conf['servers'][0]['pg_dump_path'] = '/usr/bin/pg_dump';
|
||||
$conf['servers'][0]['pg_dumpall_path'] = '/usr/bin/pg_dumpall';
|
||||
|
||||
$conf['servers'][1]['desc'] = 'nextcloud-postgresql.nextcloud.svc.cluster.local';
|
||||
$conf['servers'][1]['host'] = 'nextcloud-postgresql.nextcloud.svc.cluster.local';
|
||||
$conf['servers'][1]['port'] = 5432;
|
||||
$conf['servers'][1]['sslmode'] = 'allow';
|
||||
$conf['servers'][1]['defaultdb'] = 'template1';
|
||||
|
||||
$conf['servers'][2]['desc'] = 'postgresql.default.svc.cluster.local';
|
||||
$conf['servers'][2]['host'] = 'postgresql.default.svc.cluster.local';
|
||||
$conf['servers'][2]['port'] = 5432;
|
||||
$conf['servers'][2]['sslmode'] = 'allow';
|
||||
$conf['servers'][2]['defaultdb'] = 'template1';
|
||||
|
||||
$conf['servers'][3]['desc'] = 'postgresql.authentik.svc.cluster.local';
|
||||
$conf['servers'][3]['host'] = 'postgresql.authentik.svc.cluster.local';
|
||||
$conf['servers'][3]['port'] = 5432;
|
||||
$conf['servers'][3]['sslmode'] = 'allow';
|
||||
$conf['servers'][3]['defaultdb'] = 'template1';
|
||||
|
||||
$conf['servers'][4]['desc'] = 'postgresql.gramps.svc.cluster.local';
|
||||
$conf['servers'][4]['host'] = 'postgresql.gramps.svc.cluster.local';
|
||||
$conf['servers'][4]['port'] = 5432;
|
||||
$conf['servers'][4]['sslmode'] = 'allow';
|
||||
$conf['servers'][4]['defaultdb'] = 'template1';
|
||||
|
||||
$conf['servers'][5]['desc'] = 'postgresql.matrix.svc.cluster.local';
|
||||
$conf['servers'][5]['host'] = 'postgresql.matrix.svc.cluster.local';
|
||||
$conf['servers'][5]['port'] = 5432;
|
||||
$conf['servers'][5]['sslmode'] = 'allow';
|
||||
$conf['servers'][5]['defaultdb'] = 'template1';
|
||||
|
||||
/* Groups definition */
|
||||
/* Groups allow administrators to logicaly group servers together under
|
||||
* group nodes in the left browser tree
|
||||
*
|
||||
* The group '0' description
|
||||
*/
|
||||
//$conf['srv_groups'][0]['desc'] = 'group one';
|
||||
|
||||
/* Add here servers indexes belonging to the group '0' separated by comma */
|
||||
//$conf['srv_groups'][0]['servers'] = '0,1,2';
|
||||
|
||||
/* A server can belong to multi groups. Here server 1 is referenced in both
|
||||
* 'group one' and 'group two'*/
|
||||
//$conf['srv_groups'][1]['desc'] = 'group two';
|
||||
//$conf['srv_groups'][1]['servers'] = '3,1';
|
||||
|
||||
/* A group can be nested in one or more existing groups using the 'parents'
|
||||
* parameter. Here the group 'group three' contains only one server and will
|
||||
* appear as a subgroup in both 'group one' and 'group two':
|
||||
*/
|
||||
//$conf['srv_groups'][2]['desc'] = 'group three';
|
||||
//$conf['srv_groups'][2]['servers'] = '4';
|
||||
//$conf['srv_groups'][2]['parents'] = '0,1';
|
||||
|
||||
/* Warning: Only groups with no parents appears at the root of the tree. */
|
||||
|
||||
/* You can apply specific theme depending on servers, users and databases
|
||||
* The priority order is :
|
||||
* * the theme defined for a server
|
||||
* * the theme defined for a database apply over the server one
|
||||
* * the theme defined for a user apply over the database one
|
||||
*/
|
||||
/* Example for servers */
|
||||
//$conf['servers'][0]['theme']['default'] = 'default';
|
||||
/* Example for users */
|
||||
//$conf['servers'][0]['theme']['user']['specific_user'] = 'default';
|
||||
/* Example for databases */
|
||||
//$conf['servers'][0]['theme']['db']['specific_db'] = 'default';
|
||||
|
||||
// Default language. E.g.: 'english', 'polish', etc. See lang/ directory
|
||||
// for all possibilities. If you specify 'auto' (the default) it will use
|
||||
// your browser preference.
|
||||
$conf['default_lang'] = 'auto';
|
||||
|
||||
// If extra session security is true, then PHP's session cookies will have
|
||||
// SameSite cookie flags set to prevent CSRF attacks. If you're using
|
||||
// auto-start sessions, autostarted sessions will be destroyed and
|
||||
// restarted with SameSite on. If this this solution is not acceptable for
|
||||
// your situation, you will need to either turn off auot-start sessions, or
|
||||
// turn off secure sessions. Versions of PHP below 7.3 do not have access
|
||||
// to this feature and will be vulnerable to CSRF attacks.
|
||||
$conf['extra_session_security'] = true;
|
||||
|
||||
// AutoComplete uses AJAX interaction to list foreign key values
|
||||
// on insert fields. It currently only works on single column
|
||||
// foreign keys. You can choose one of the following values:
|
||||
// 'default on' enables AutoComplete and turns it on by default.
|
||||
// 'default off' enables AutoComplete but turns it off by default.
|
||||
// 'disable' disables AutoComplete.
|
||||
$conf['autocomplete'] = 'default on';
|
||||
|
||||
// If extra login security is true, then logins via phpPgAdmin with no
|
||||
// password or certain usernames (pgsql, postgres, root, administrator)
|
||||
// will be denied. Only set this false once you have read the FAQ and
|
||||
// understand how to change PostgreSQL's pg_hba.conf to enable
|
||||
// passworded local connections.
|
||||
$conf['extra_login_security'] = true;
|
||||
|
||||
// Only show owned databases?
|
||||
// Note: This will simply hide other databases in the list - this does
|
||||
// not in any way prevent your users from seeing other database by
|
||||
// other means. (e.g. Run 'SELECT * FROM pg_database' in the SQL area.)
|
||||
$conf['owned_only'] = false;
|
||||
|
||||
// Display comments on objects? Comments are a good way of documenting
|
||||
// a database, but they do take up space in the interface.
|
||||
$conf['show_comments'] = true;
|
||||
|
||||
// Display "advanced" objects? Setting this to true will show
|
||||
// aggregates, types, operators, operator classes, conversions,
|
||||
// languages and casts in phpPgAdmin. These objects are rarely
|
||||
// administered and can clutter the interface.
|
||||
$conf['show_advanced'] = false;
|
||||
|
||||
// Display "system" objects?
|
||||
$conf['show_system'] = false;
|
||||
|
||||
// Minimum length users can set their password to.
|
||||
$conf['min_password_length'] = 1;
|
||||
|
||||
// Width of the left frame in pixels (object browser)
|
||||
$conf['left_width'] = 200;
|
||||
|
||||
// Which look & feel theme to use
|
||||
$conf['theme'] = 'default';
|
||||
|
||||
// Show OIDs when browsing tables?
|
||||
// Only supported in versions <=11
|
||||
$conf['show_oids'] = false;
|
||||
|
||||
// Max rows to show on a page when browsing record sets
|
||||
$conf['max_rows'] = 30;
|
||||
|
||||
// Max chars of each field to display by default in browse mode
|
||||
$conf['max_chars'] = 50;
|
||||
|
||||
// Send XHTML strict headers?
|
||||
$conf['use_xhtml_strict'] = false;
|
||||
|
||||
// Base URL for PostgreSQL documentation.
|
||||
// '%s', if present, will be replaced with the PostgreSQL version
|
||||
// (e.g. 8.4 )
|
||||
$conf['help_base'] = 'http://www.postgresql.org/docs/%s/interactive/';
|
||||
|
||||
// Configuration for ajax scripts
|
||||
// Time in seconds. If set to 0, refreshing data using ajax will be disabled (locks and activity pages)
|
||||
$conf['ajax_refresh'] = 3;
|
||||
|
||||
/** Plugins management
|
||||
* Add plugin names to the following array to activate them
|
||||
* Example:
|
||||
* $conf['plugins'] = array(
|
||||
* 'Example',
|
||||
* 'Slony'
|
||||
* );
|
||||
*/
|
||||
$conf['plugins'] = array();
|
||||
|
||||
/*****************************************
|
||||
* Don't modify anything below this line *
|
||||
*****************************************/
|
||||
|
||||
$conf['version'] = 19;
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: phppgadmin
|
||||
namespace: default
|
||||
labels:
|
||||
app: phppgadmin
|
||||
app.kubernetes.io/name: phppgadmin
|
||||
spec:
|
||||
replicas: 1
|
||||
revisionHistoryLimit: 3
|
||||
minReadySeconds: 5
|
||||
progressDeadlineSeconds: 600
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxUnavailable: 0
|
||||
maxSurge: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: phppgadmin
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
deployment.brunner.ninja/revision: manual
|
||||
labels:
|
||||
app: phppgadmin
|
||||
app.kubernetes.io/name: phppgadmin
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
terminationGracePeriodSeconds: 30
|
||||
containers:
|
||||
- name: phppgadmin
|
||||
# Public, maintained image pinned to the verified multi-platform
|
||||
# manifest digest published on 2026-08-08.
|
||||
image: tozd/phppgadmin@sha256:4ee690768d54b0c057dbc4ed5c1baa152c1a5ef0ebd6eb12f1e310e545eb4489
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
protocol: TCP
|
||||
startupProbe:
|
||||
httpGet:
|
||||
path: /phppgadmin/
|
||||
port: http
|
||||
periodSeconds: 2
|
||||
timeoutSeconds: 2
|
||||
failureThreshold: 60
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /phppgadmin/
|
||||
port: http
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 2
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /phppgadmin/
|
||||
port: http
|
||||
periodSeconds: 15
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 3
|
||||
volumeMounts:
|
||||
- name: phppgadmin-config
|
||||
mountPath: /etc/phppgadmin/config.inc.php
|
||||
subPath: config.inc.php
|
||||
volumes:
|
||||
- name: phppgadmin-config
|
||||
configMap:
|
||||
name: phppgadmin-config
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: phppgadmin
|
||||
namespace: default
|
||||
labels:
|
||||
app: phppgadmin
|
||||
app.kubernetes.io/name: phppgadmin
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
protocol: TCP
|
||||
targetPort: http
|
||||
selector:
|
||||
app: phppgadmin
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: phppgadmin
|
||||
namespace: default
|
||||
labels:
|
||||
app.kubernetes.io/name: phppgadmin
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
||||
traefik.ingress.kubernetes.io/router.middlewares: default-phppgadmin-root-redirect@kubernetescrd
|
||||
spec:
|
||||
ingressClassName: traefik
|
||||
rules:
|
||||
- host: phppgadmin.brunner.ninja
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: phppgadmin
|
||||
port:
|
||||
name: http
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: phppgadmin-root-redirect
|
||||
namespace: default
|
||||
labels:
|
||||
app.kubernetes.io/name: phppgadmin
|
||||
spec:
|
||||
redirectRegex:
|
||||
regex: ^https://phppgadmin\.brunner\.ninja/?$
|
||||
replacement: https://phppgadmin.brunner.ninja/phppgadmin/
|
||||
permanent: true
|
||||
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
project_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
|
||||
|
||||
sh -n "${project_dir}/install.sh"
|
||||
bash -n "${project_dir}/create-ci-kubeconfig.sh"
|
||||
|
||||
validate_file() {
|
||||
local manifest=$1
|
||||
|
||||
if command -v kubeconform >/dev/null 2>&1; then
|
||||
kubeconform -strict -ignore-missing-schemas -summary "${manifest}"
|
||||
elif command -v docker >/dev/null 2>&1; then
|
||||
docker run --rm --interactive \
|
||||
ghcr.io/yannh/kubeconform:v0.7.0@sha256:85dbef6b4b312b99133decc9c6fc9495e9fc5f92293d4ff3b7e1b30f5611823c \
|
||||
-strict -ignore-missing-schemas -summary < "${manifest}"
|
||||
else
|
||||
echo "Required command not found: install kubeconform or Docker" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
validate_file "${project_dir}/phppgadmin.yaml"
|
||||
validate_file "${project_dir}/ci-deployer.yaml"
|
||||
|
||||
echo "phpPgAdmin scripts and Kubernetes resources are valid."
|
||||
Reference in New Issue
Block a user