188 lines
12 KiB
Markdown
188 lines
12 KiB
Markdown
# Ansible role: Wikideck
|
||
|
||
Deploy the Wikideck production container to an existing Kubernetes cluster using
|
||
`kubernetes.core.k8s`. The role creates a single-replica Deployment, a persistent
|
||
volume claim, a dedicated ServiceAccount, a ClusterIP Service, and a NetworkPolicy.
|
||
An HTTPS Ingress is optional and disabled by default.
|
||
|
||
**Wikideck is a prototype, not a hardened public service.** This role does not add
|
||
application authentication, distributed consensus, a database, or wallet recovery.
|
||
Use test wallets and restrict access. Mining happens in the browser; the server
|
||
coordinates the chain and verifies proofs.
|
||
|
||
## Requirements
|
||
|
||
- A Linux/macOS/WSL controller with Python 3.10+ and Ansible Core 2.16–2.19
|
||
(the selected Ansible release may require a newer Python).
|
||
- Install controller packages from [requirements.txt](requirements.txt) and the
|
||
collection from [requirements.yml](requirements.yml).
|
||
- A supported Kubernetes cluster with `apps/v1` and `networking.k8s.io/v1` APIs,
|
||
a storage provisioner or an existing PVC, and a NetworkPolicy-capable CNI.
|
||
- Kubeconfig access to create/update/read the resources in the target namespace,
|
||
delete the role's Ingress/NetworkPolicy when disabled, and optionally create a
|
||
Namespace. No cluster-admin access or in-cluster workload RBAC is needed.
|
||
- **A published image built from Wikideck's production Dockerfile.** There is no
|
||
assumed official registry image. Set `wikideck_image` to your actual tagged or
|
||
digest-pinned image. The role does not build/push images or install a cluster.
|
||
- For ingress: an ingress controller, DNS, and a trusted TLS Secret in the app
|
||
namespace. Alternatively, configure cert-manager annotations and an existing
|
||
issuer. The role does not install cert-manager or create an issuer.
|
||
|
||
Install dependencies on the controller with `pip install -r requirements.txt`
|
||
and `ansible-galaxy collection install -r requirements.yml`. Use an isolated Python
|
||
environment. Windows controllers must run Ansible through WSL or Linux containers.
|
||
|
||
## Usage
|
||
|
||
Install this checkout into your playbook's role search path as `roles/wikideck`,
|
||
or install it using an Ansible Galaxy Git role requirement named `wikideck`.
|
||
[examples/playbook.yml](examples/playbook.yml) shows an HTTPS deployment with
|
||
ingress-nginx and cert-manager. Replace the image, hostname, class, issuer, and
|
||
controller namespace with real values before running it.
|
||
|
||
```yaml
|
||
- name: Deploy a cluster-internal Wikideck instance
|
||
hosts: localhost
|
||
connection: local
|
||
gather_facts: false
|
||
roles:
|
||
- role: wikideck
|
||
vars:
|
||
wikideck_image: registry.example.com/games/wikideck:1.0.0
|
||
```
|
||
|
||
Run the playbook on the controller, using its normal kubeconfig discovery or
|
||
`wikideck_kubeconfig` / `wikideck_kube_context` overrides. Do not target every
|
||
Kubernetes node: the role operates on the Kubernetes API, not over node SSH.
|
||
|
||
For a private local preview, forward the Service with
|
||
`kubectl -n wikideck port-forward service/wikideck 3001:80` and open
|
||
<http://127.0.0.1:3001>. Keep the forwarding bind address at its loopback default.
|
||
Wikideck already allows this local origin. Different hosts, ports, schemes, and
|
||
browser profiles use different browser wallets.
|
||
|
||
For remote browsers, enable ingress and set `wikideck_public_origin` to exactly
|
||
`https://` followed by `wikideck_ingress_host` (no slash or path). HTTPS is needed
|
||
for browser wallet cryptography. Configure HTTP-to-HTTPS redirect at your ingress
|
||
controller; the example includes the ingress-nginx annotation. Ingress creation
|
||
does not wait for DNS, certificate issuance, or an externally reachable endpoint.
|
||
|
||
## Variables
|
||
|
||
All defaults are in [defaults/main.yml](defaults/main.yml).
|
||
|
||
| Variable | Default | Description |
|
||
| --- | --- | --- |
|
||
| `wikideck_image` | empty, required | Published image with explicit tag or SHA-256 digest; prefer a verified digest. |
|
||
| `wikideck_name` | `wikideck` | Resource name and instance label; at most 50 characters. |
|
||
| `wikideck_namespace` | `wikideck` | Namespace for all namespaced resources. |
|
||
| `wikideck_create_namespace` | `true` | Create the namespace; disable for a pre-provisioned namespace. |
|
||
| `wikideck_kubeconfig` / `wikideck_kube_context` | `null` | Override controller authentication/context. API TLS verification remains enabled. |
|
||
| `wikideck_wait` / `wikideck_wait_timeout` | `true` / `300` | Wait for Deployment readiness, timeout in seconds. |
|
||
| `wikideck_image_pull_policy` | `IfNotPresent` | `Always`, `IfNotPresent`, or `Never`. |
|
||
| `wikideck_image_pull_secrets` | `[]` | Names of existing registry Secrets in this namespace. |
|
||
| `wikideck_offline` | `false` | Starter articles without live Wikipedia requests. |
|
||
| `wikideck_public_origin` | empty | Additional allowed HTTPS browser origin; required with ingress. |
|
||
| `wikideck_ingress_enabled` | `false` | Create TLS ingress; disabling removes the role's named ingress. |
|
||
| `wikideck_ingress_host` | empty | DNS hostname, without scheme, port, or path. |
|
||
| `wikideck_ingress_class_name` | empty | Omit to use the cluster default IngressClass. |
|
||
| `wikideck_ingress_annotations` | `{}` | Controller/cert-manager-specific configuration. |
|
||
| `wikideck_tls_secret_name` | `{{ wikideck_name }}-tls` | Existing or externally provisioned TLS Secret. |
|
||
| `wikideck_existing_claim` | empty | Use an existing PVC instead of creating one. |
|
||
| `wikideck_storage_size` | `1Gi` | Requested PVC capacity; resizing requires StorageClass support. |
|
||
| `wikideck_storage_class` | `null` | `null`: cluster default; empty string: no class; otherwise a class name. |
|
||
| `wikideck_storage_access_modes` | `[ReadWriteOnce]` | `ReadWriteOnce` or `ReadWriteOncePod`; never `ReadWriteMany`. |
|
||
| `wikideck_resources` | see defaults | CPU/memory requests `100m`/`128Mi`, limits `1`/`512Mi`. |
|
||
| `wikideck_node_selector` / `wikideck_tolerations` / `wikideck_affinity` | `{}` / `[]` / `{}` | Pod placement settings. |
|
||
| `wikideck_network_policy_enabled` | `true` | Manage network isolation; disabling deletes this role's policy. |
|
||
| `wikideck_network_policy_ingress_from` | same-namespace pods | List of NetworkPolicy peers allowed to reach TCP 3001; empty list denies all ingress. |
|
||
| `wikideck_network_policy_dns_to` | `kube-system` namespace | DNS peers allowed on UDP/TCP 53; adapt for custom/NodeLocal DNS. |
|
||
| `wikideck_network_policy_https_to` | public IPv4/IPv6 ranges, private/link-local excluded | TCP 443 egress peers; omitted in offline mode. |
|
||
|
||
## Storage and safe updates
|
||
|
||
- Exactly **one replica** and **Recreate** updates are deliberate and cannot be
|
||
changed through role variables. Updates briefly interrupt service. Do not add
|
||
an HPA, manually scale, or attach the same chain PVC to another writer.
|
||
- `ReadWriteOnce` alone does not enforce a single pod on the same node. Prefer
|
||
`ReadWriteOncePod` if supported by your CSI driver. Do not force-delete an old
|
||
pod on a partitioned node until it has been fenced; Recreate is not a
|
||
distributed storage lock.
|
||
- `/app/data` contains the chain and signed transfer journal. The process runs
|
||
as UID/GID 1000 with `fsGroup: 1000`. Your storage driver must honor ownership
|
||
settings, or the existing volume must already be writable by this identity.
|
||
No privileged/root ownership-fixing init container is used.
|
||
- The PVC is created before the Deployment without waiting, supporting
|
||
`WaitForFirstConsumer` storage classes. Existing claims are not modified.
|
||
- The role never deletes PVCs or Namespaces, and does not assign ownerReferences
|
||
that would garbage-collect storage with the Deployment. Renaming resources or
|
||
switching claims does not migrate data or remove previous resources.
|
||
- Back up the chain before upgrades. Stop the Deployment for a consistent file
|
||
backup or use your CSI provider's documented snapshot procedure. Encrypt disks
|
||
and backups, define retention, and test restores. Deleting a namespace or PVC
|
||
can still destroy data depending on the underlying reclaim policy.
|
||
- Chain verification runs at startup. For large chains, review resources,
|
||
startup probe budget (300 seconds), and readiness wait timeout. JSON storage
|
||
is unbounded; this role cannot solve Wikideck's persistence scalability limits.
|
||
|
||
## Security and networking
|
||
|
||
The workload uses a read-only root filesystem, drops all Linux capabilities,
|
||
disallows privilege escalation, uses runtime-default seccomp, and has no mounted
|
||
service account token or RBAC grants. Only persistent data and a 32 MiB memory
|
||
temporary directory are writable. All three HTTP probes target `/healthz` on port
|
||
3001; Kubernetes does not use the image's Docker HEALTHCHECK. A 30-second grace
|
||
period accommodates the application's 25-second shutdown deadline.
|
||
|
||
NetworkPolicy defaults allow same-namespace clients, cluster DNS in `kube-system`,
|
||
and public HTTPS for Wikipedia. With ingress, explicitly allow the ingress
|
||
controller's namespace/pods as shown in the example. Peer entries are additive
|
||
OR alternatives: put a namespaceSelector and podSelector in the **same entry** to
|
||
restrict pods within that namespace. Empty peer lists deny that traffic; empty
|
||
peer objects are rejected to avoid accidentally allowing every source/destination.
|
||
|
||
Standard NetworkPolicy cannot allow Wikipedia by hostname; the default public
|
||
TCP 443 egress is broader than a domain allowlist. Use CNI FQDN policies or an
|
||
egress gateway if required. Policies are additive with any other policies in the
|
||
namespace and have no effect without an enforcing CNI. NodeLocal DNS, host-network
|
||
ingress controllers, service meshes, and private proxies may need peer changes.
|
||
|
||
TLS terminates at ingress; backend HTTP is private but **not encrypted by this
|
||
role**. Use a properly configured service mesh if in-cluster encryption is
|
||
required. TLS keys and registry credentials must be provisioned separately, not
|
||
committed to playbooks. Use trusted, rotated certificates and retain Kubernetes
|
||
API certificate verification. The role does not embed secrets or implement its
|
||
own cryptography; image signature verification and encrypted storage are cluster
|
||
operator responsibilities. Scan and rebuild the application image regularly.
|
||
|
||
Wikideck does not currently configure Express `trust proxy`. Clients behind one
|
||
proxy can therefore share an IP-based application rate limit. Review the
|
||
application before public exposure; do not try to fix this by blindly trusting
|
||
forwarded headers. The role adds no authentication, WAF, or abuse protection.
|
||
|
||
## Validation
|
||
|
||
[tests/render.yml](tests/render.yml) runs entirely offline: it validates inputs,
|
||
renders/parses every template across default, ingress, existing-claim, storage
|
||
class, offline, deny-all, and digest-image variants, and checks wiring/security
|
||
invariants. It also checks that unsafe/invalid settings are rejected. Run it with
|
||
`ansible-playbook -i tests/inventory tests/render.yml`.
|
||
|
||
[tests/test.yml](tests/test.yml) is a real-cluster smoke test using the local
|
||
checkout. Syntax-check it with `ansible-playbook -i tests/inventory tests/test.yml --syntax-check`.
|
||
To deploy to a disposable test namespace, run it with
|
||
`ansible-playbook -i tests/inventory tests/test.yml -e wikideck_image=YOUR_IMAGE -e wikideck_namespace=wikideck-test`.
|
||
Run it a second time to verify idempotence. Check Pod readiness, `/healthz`, PVC
|
||
retention across updates, DNS/HTTPS egress, TLS, and browser mining/transfer flows
|
||
on your chosen cluster. Offline tests do not prove CSI/CNI/controller behavior.
|
||
|
||
Ansible check mode (`--check --diff`) is supported by the Kubernetes module, but
|
||
still needs API access; creating a new namespace in check mode may require a
|
||
separate namespace provisioning pass. No uninstall or automatic data deletion is
|
||
provided.
|
||
|
||
## License
|
||
|
||
MIT-0. See [LICENSE](LICENSE). The application and Wikipedia/Wikimedia content
|
||
retain their own upstream licenses and attribution requirements.
|