fix(api-gateway): rewrite APISIX package and deploy to production
The 09-01 package referenced a nonexistent Cloudron etcd addon and an unpinned base image. Rewritten: digest-pinned apisix 3.18.0 with an embedded single-node etcd, loopback-only Admin API, and read-only rootfs support. Deployed live at apigw.knownelement.com (healthz 200). Umbrella: https://projects.knownelement.com/issues/632
This commit is contained in:
@@ -1,21 +1,34 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## [3.12.0] - 2025-01-24
|
## [1.0.2] - 2026-09-07
|
||||||
|
|
||||||
### Added
|
### Fixed
|
||||||
- Initial Cloudron package for APISIX
|
- Read-only rootfs: relocate the whole APISIX runtime tree (CLI, deps,
|
||||||
- Official Apache APISIX Docker image wrapper
|
config.yaml, nginx.conf, logs, sockets) to /app/data/apisix - Cloudron 8
|
||||||
- Automatic etcd configuration via Cloudron addon
|
mounts app root filesystems read-only
|
||||||
- Admin API key configuration
|
- Patched the CLI's hardcoded apisix_home in the copied tree so nginx.conf
|
||||||
- Health check endpoint
|
generation lands on the writable volume
|
||||||
- Documentation with usage examples
|
|
||||||
- Architecture diagram
|
|
||||||
|
|
||||||
### Features
|
### Changed
|
||||||
- Multi-protocol support (HTTP/HTTPS, TCP/UDP, Dubbo, MQTT, gRPC, WebSocket)
|
- Upgraded APISIX 3.12.0 -> 3.18.0-debian, pinned by tag AND digest
|
||||||
- Dynamic configuration without restarts
|
- etcd copied from quay.io/coreos/etcd v3.5.33 (digest-pinned)
|
||||||
- Load balancing with multiple strategies
|
|
||||||
- Rich plugin ecosystem (100+ plugins)
|
## [1.0.1] - 2026-09-07
|
||||||
- Security features (IP restrictions, JWT, API Key auth)
|
|
||||||
- Traffic management (rate limiting, circuit breaking, canary releases)
|
### Fixed
|
||||||
- AI Gateway capabilities for LLM proxying
|
- Run as root: Cloudron does not chown /app/data to arbitrary non-root
|
||||||
|
image users, so the apisix user could not persist state
|
||||||
|
|
||||||
|
## [1.0.0] - 2026-09-07
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- REWRITE of the 2026-09-01 package, which referenced a nonexistent
|
||||||
|
Cloudron "etcd addon" and an unpinned apache/apisix:latest
|
||||||
|
- Embedded single-node etcd (no platform addon dependency); Admin API on
|
||||||
|
127.0.0.1:9180 with a persisted random key in /app/data/.admin_key
|
||||||
|
- healthCheckPath "/" (Cloudron treats 4xx as alive); /healthz route
|
||||||
|
seeded for external monitoring
|
||||||
|
- Manifest: semver version, localstorage-only addons, 1 GiB memory limit
|
||||||
|
|
||||||
|
### Deployed
|
||||||
|
- Production Cloudron at https://apigw.knownelement.com (2026-09-07)
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"version": 1,
|
"version": "1.0.2",
|
||||||
"manifestVersion": 2,
|
"manifestVersion": 2,
|
||||||
"type": "app",
|
|
||||||
"id": "io.cloudron.apisix",
|
"id": "io.cloudron.apisix",
|
||||||
"title": "APISIX",
|
"title": "APISIX",
|
||||||
"description": "Apache APISIX is a dynamic, real-time, high-performance API Gateway. Provides rich traffic management features like load balancing, dynamic upstream, canary release, circuit breaking, authentication, observability and more.",
|
"description": "Apache APISIX is a dynamic, real-time, high-performance API Gateway. Provides rich traffic management features like load balancing, dynamic upstream, canary release, circuit breaking, authentication, observability and more.",
|
||||||
@@ -9,33 +8,15 @@
|
|||||||
"website": "https://apisix.apache.org",
|
"website": "https://apisix.apache.org",
|
||||||
"contactEmail": "cloudron@tsys.dev",
|
"contactEmail": "cloudron@tsys.dev",
|
||||||
"tagline": "Cloud-native, high-performance API gateway",
|
"tagline": "Cloud-native, high-performance API gateway",
|
||||||
"version": "3.12-latest",
|
"healthCheckPath": "/",
|
||||||
"healthCheckPath": "/apisix/admin/routes",
|
"httpPort": 9080,
|
||||||
"httpPort": 9180,
|
"memoryLimit": 1073741824,
|
||||||
"memoryLimit": 1024,
|
|
||||||
"addons": {
|
"addons": {
|
||||||
"localstorage": true,
|
"localstorage": {}
|
||||||
"etcd": {
|
|
||||||
"version": "3.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tcpPorts": {
|
|
||||||
"ADMIN_PORT": {
|
|
||||||
"description": "APISIX Admin API port",
|
|
||||||
"defaultValue": 9180
|
|
||||||
},
|
|
||||||
"HTTP_PORT": {
|
|
||||||
"description": "APISIX HTTP proxy port",
|
|
||||||
"defaultValue": 9080
|
|
||||||
},
|
|
||||||
"HTTPS_PORT": {
|
|
||||||
"description": "APISIX HTTPS proxy port",
|
|
||||||
"defaultValue": 9443
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"mediaLinks": [
|
"mediaLinks": [
|
||||||
"https://raw.githubusercontent.com/apache/apisix/master/logos/apache-apisix.png"
|
"https://raw.githubusercontent.com/apache/apisix/master/logos/apache-apisix.png"
|
||||||
],
|
],
|
||||||
"changelog": "Initial Cloudron package for APISIX API Gateway",
|
"changelog": "APISIX 3.18.0 with embedded single-node etcd; Admin API loopback-only",
|
||||||
"icon": "file://logo.png"
|
"icon": "file://logo.png"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,43 @@
|
|||||||
FROM apache/apisix:latest
|
# APISIX Cloudron Package
|
||||||
|
#
|
||||||
|
# Apache APISIX API gateway, traditional deployment mode with an embedded
|
||||||
|
# single-node etcd. Cloudron has no etcd addon, so the etcd server ships
|
||||||
|
# inside this image (copied from the upstream release image) and stores its
|
||||||
|
# state under /app/data/etcd - that keeps the Admin API + dynamic routes
|
||||||
|
# available without any platform dependency.
|
||||||
|
#
|
||||||
|
# Upstream: https://github.com/apache/apisix
|
||||||
|
# - Official image apache/apisix:3.18.0-debian, runs as non-root apisix
|
||||||
|
# (uid 636), ENTRYPOINT /docker-entrypoint.sh CMD docker-start which
|
||||||
|
# does `apisix init` + `apisix init_etcd` then execs openresty with
|
||||||
|
# `daemon off`.
|
||||||
|
#
|
||||||
|
# Authentication: APISIX is a gateway (no user concept). The Admin API
|
||||||
|
# listens on 127.0.0.1:9180 ONLY (never exposed) and is protected by a
|
||||||
|
# persisted random admin key in /app/data/.admin_key. Operators reach it
|
||||||
|
# via `cloudron exec` into the app. Data-plane auth (key-auth, OIDC
|
||||||
|
# relay, etc.) is route-level and configured per API by the operator.
|
||||||
|
#
|
||||||
|
# Pattern: official-image wrapper. Image pinned by tag AND digest.
|
||||||
|
FROM apache/apisix:3.18.0-debian@sha256:84e6b5e787e9f889ebff88161cb9a16599bafcffa236c6b54c7f779a0655940d
|
||||||
|
|
||||||
# Copy start script (already executable from host)
|
# etcd + etcdctl from the upstream release image (multi-arch manifest
|
||||||
COPY start.sh /start.sh
|
# digest; build resolves the amd64 binary). etcdctl is kept as an ops tool
|
||||||
|
# for `cloudron exec` debugging of the config store.
|
||||||
|
COPY --from=quay.io/coreos/etcd@sha256:d367cba7801b29d2f7481bb56802894658ec8a647834509118c81f77f721381b /usr/local/bin/etcd /usr/local/bin/etcd
|
||||||
|
COPY --from=quay.io/coreos/etcd@sha256:d367cba7801b29d2f7481bb56802894658ec8a647834509118c81f77f721381b /usr/local/bin/etcdctl /usr/local/bin/etcdctl
|
||||||
|
|
||||||
# Start APISIX
|
# start.sh waits for etcd, regenerates config.yaml from the Cloudron env
|
||||||
CMD ["/start.sh"]
|
# contract and execs openresty in the foreground. Made executable on the
|
||||||
|
# host, not at build time (Cloudron builds hit permission errors on RUN chmod).
|
||||||
|
COPY start.sh /app/start.sh
|
||||||
|
|
||||||
|
# Cloudron proxies HTTPS -> this port (manifest httpPort / healthCheckPath "/").
|
||||||
|
EXPOSE 9080
|
||||||
|
|
||||||
|
# Cloudron does not chown /app/data to arbitrary non-root image users, so the
|
||||||
|
# container runs as root (the Cloudron app convention) and start.sh keeps the
|
||||||
|
# Admin API loopback-only + key-protected.
|
||||||
|
USER root
|
||||||
|
|
||||||
|
ENTRYPOINT ["/bin/bash", "/app/start.sh"]
|
||||||
|
|||||||
@@ -1,188 +1,74 @@
|
|||||||
# APISIX Cloudron Package
|
# APISIX Cloudron Package
|
||||||
|
|
||||||
|
**DEPLOYED**: `https://apigw.knownelement.com` (production Cloudron, 2026-09-07)
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
Apache APISIX is a dynamic, real-time, high-performance API Gateway. It provides rich traffic management features such as load balancing, dynamic upstream, canary release, circuit breaking, authentication, observability and more.
|
Apache APISIX is a dynamic, real-time, high-performance API Gateway. It
|
||||||
|
provides rich traffic management features such as load balancing, dynamic
|
||||||
|
upstream, canary release, circuit breaking, authentication, observability
|
||||||
|
and more.
|
||||||
|
|
||||||
## Features
|
## Package design
|
||||||
|
|
||||||
### Core Capabilities
|
- **Base image**: `apache/apisix:3.18.0-debian` (pinned by tag AND digest).
|
||||||
- **Dynamic Configuration**: Hot updates and hot plugins without restarts
|
- **Embedded etcd**: Cloudron has no etcd addon, so a single-node etcd
|
||||||
- **Multi-Protocol Support**: HTTP/HTTPS, TCP/UDP, Dubbo, MQTT, gRPC, WebSocket
|
(copied from `quay.io/coreos/etcd` v3.5.33, digest-pinned) runs inside
|
||||||
- **Load Balancing**: Multiple load balancing strategies
|
the container, state under `/app/data/etcd`. This keeps the Admin API
|
||||||
- **Security**: IP restrictions, JWT authentication, API Key authentication
|
and dynamic routes available with zero platform dependencies.
|
||||||
- **Traffic Management**: Rate limiting, circuit breaking, canary releases
|
- **Read-only rootfs ready**: Cloudron 8 mounts app root filesystems
|
||||||
- **Observability**: Prometheus metrics, distributed tracing
|
read-only. `start.sh` relocates the APISIX runtime tree (CLI, deps,
|
||||||
- **AI Gateway**: Support for LLM proxying and AI workloads
|
generated `config.yaml`, `nginx.conf`, logs, sockets) to
|
||||||
|
`/app/data/apisix` via `APISIX_PREFIX` and a path-patched CLI copy.
|
||||||
|
- **Ports**: only the proxy port (9080) is served; Cloudron terminates
|
||||||
|
TLS and proxies `https://apigw.knownelement.com` to it. In-container
|
||||||
|
TLS is disabled.
|
||||||
|
|
||||||
### Gateway Features
|
## Authentication / admin access
|
||||||
- Proxy Rewrite (host, URI, schema, method, headers)
|
|
||||||
- Upstream Health Checks
|
|
||||||
- Request/Response Transformation
|
|
||||||
- CORS Support
|
|
||||||
- Web Application Firewall (WAF) via plugins
|
|
||||||
- OpenID Connect integration
|
|
||||||
|
|
||||||
## Configuration
|
The **Admin API listens on 127.0.0.1:9180 only** - it is never published.
|
||||||
|
It is protected by a random 64-hex-char key persisted at
|
||||||
|
`/app/data/.admin_key` (rotating it invalidates automation clients).
|
||||||
|
|
||||||
### Etcd Connection
|
Operate the gateway from the host (or any admin shell):
|
||||||
APISIX requires an etcd instance for configuration storage. The Cloudron package automatically configures this connection using Cloudron's etcd addon.
|
|
||||||
|
|
||||||
### Environment Variables
|
|
||||||
- `CLOUDRON_ETCD_HOST`: Etcd host (automatically set by Cloudron)
|
|
||||||
- `CLOUDRON_ETCD_PORT`: Etcd port (automatically set by Cloudron)
|
|
||||||
- `ADMIN_KEY`: Admin API key (default: admin-key-secret-change-me, **change this in production**)
|
|
||||||
|
|
||||||
### Ports
|
|
||||||
- **9180**: Admin API port (for configuration via REST API)
|
|
||||||
- **9080**: HTTP proxy port (client requests)
|
|
||||||
- **9443**: HTTPS proxy port (client requests with SSL)
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
### 1. Configure Routes via Admin API
|
|
||||||
APISIX provides a RESTful Admin API for configuration:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# List all routes
|
# read the admin key
|
||||||
curl http://localhost:9180/apisix/admin/routes/ \
|
cloudron exec -a apigw.knownelement.com cat /app/data/.admin_key
|
||||||
-H 'X-API-KEY: admin-key-secret-change-me'
|
|
||||||
|
|
||||||
# Create a new route
|
# example: list routes (from inside the app container)
|
||||||
curl http://localhost:9180/apisix/admin/routes/1 \
|
cloudron exec -a apigw.knownelement.com curl \
|
||||||
-H 'X-API-KEY: admin-key-secret-change-me' \
|
-H "X-API-Key: $KEY" http://127.0.0.1:9180/apisix/admin/routes
|
||||||
-X PUT -d '{
|
|
||||||
"uri": "/hello",
|
|
||||||
"upstream": {
|
|
||||||
"type": "roundrobin",
|
|
||||||
"nodes": {
|
|
||||||
"httpbin.org:80": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}'
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Test the Gateway
|
Data-plane auth (key-auth, jwt-auth, OIDC relay, IP restriction, ...) is
|
||||||
```bash
|
per-route and configured by the operator through the Admin API - the
|
||||||
# Test via HTTP
|
gateway itself has no user concept.
|
||||||
curl http://localhost:9080/hello
|
|
||||||
|
|
||||||
# Test via HTTPS (after configuring SSL)
|
## Operator notes
|
||||||
curl -k https://localhost:9443/hello
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Add SSL Certificates
|
- **Routes persist** in the embedded etcd across restarts and Cloudron
|
||||||
```bash
|
backups (state is under `/app/data`).
|
||||||
curl http://localhost:9180/apisix/admin/ssls/1 \
|
- **Health**: Cloudron's healthcheck hits `/` (a 404 counts as alive);
|
||||||
-H 'X-API-KEY: admin-key-secret-change-me' \
|
`/healthz` returns 200 for external monitoring.
|
||||||
-X PUT -d '{
|
- **Real client IPs** are restored from the Cloudron proxy's
|
||||||
"cert": "...",
|
`X-Forwarded-For` (RFC1918 trusted).
|
||||||
"key": "...",
|
- **etcd tuning**: auto-compaction every hour keeps the embedded store
|
||||||
"snis": ["example.com"]
|
small; single-node by design (gateway config is reproducible from
|
||||||
}'
|
infrastructure-as-code, not a durability concern).
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Configure Plugins
|
## Files
|
||||||
APISIX supports 100+ plugins for various capabilities:
|
|
||||||
|
|
||||||
```bash
|
| File | Purpose |
|
||||||
# Enable rate limiting
|
|------|---------|
|
||||||
curl http://localhost:9180/apisix/admin/routes/1 \
|
| `Dockerfile` | Pinned base image + etcd/etcdctl binaries + start.sh |
|
||||||
-H 'X-API-KEY: admin-key-secret-change-me' \
|
| `start.sh` | etcd lifecycle, config generation, health route seed |
|
||||||
-X PATCH -d '{
|
| `CloudronManifest.json` | Package manifest (localstorage only) |
|
||||||
"plugins": {
|
| `sbom.cyclonedx.json` | SBOM [#834] (syft, pinned container) |
|
||||||
"limit-count": {
|
|
||||||
"count": 10,
|
|
||||||
"time_window": 60,
|
|
||||||
"rejected_code": 429
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Security
|
## History
|
||||||
|
|
||||||
### Change Default Admin Key
|
The original package (2026-09-01) used a nonexistent Cloudron "etcd
|
||||||
The default admin key is `admin-key-secret-change-me`. **Change this immediately after installation**:
|
addon" and an unpinned `apache/apisix:latest` - it could never have
|
||||||
|
installed. Rewritten 2026-09-07 (embedded etcd, digest pins, read-only
|
||||||
```bash
|
rootfs support) and deployed to production; see JOURNAL.md §20.
|
||||||
# Get current admin key from Cloudron environment
|
|
||||||
# Update in /usr/local/apisix/conf/config.yaml
|
|
||||||
# Restart APISIX
|
|
||||||
```
|
|
||||||
|
|
||||||
### Use HTTPS in Production
|
|
||||||
Always use HTTPS (port 9443) for production deployments. Configure SSL certificates via the Admin API.
|
|
||||||
|
|
||||||
### IP Restrictions
|
|
||||||
Configure IP restrictions to limit who can access the Admin API:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl http://localhost:9180/apisix/admin/routes/1 \
|
|
||||||
-H 'X-API-KEY: admin-key-secret-change-me' \
|
|
||||||
-X PATCH -d '{
|
|
||||||
"plugins": {
|
|
||||||
"ip-restriction": {
|
|
||||||
"whitelist": ["192.168.1.0/24"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────┐
|
|
||||||
│ Client │
|
|
||||||
└──────┬──────┘
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
┌──────────────┐
|
|
||||||
│ APISIX │
|
|
||||||
│ (Gateway) │
|
|
||||||
└──────┬──────┘
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
┌──────────────┐
|
|
||||||
│ Upstream │
|
|
||||||
│ Services │
|
|
||||||
└──────────────┘
|
|
||||||
|
|
||||||
┌──────────────┐
|
|
||||||
│ Etcd │
|
|
||||||
│ (Config DB) │
|
|
||||||
└──────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
For more information on configuring APISIX:
|
|
||||||
- [Official Documentation](https://apisix.apache.org/docs/)
|
|
||||||
- [Admin API Reference](https://apisix.apache.org/docs/apisix/admin-api/)
|
|
||||||
- [Plugin Configuration](https://apisix.apache.org/docs/apisix/plugins/)
|
|
||||||
- [Best Practices](https://apisix.apache.org/docs/general/faq)
|
|
||||||
- [AI Gateway Guide](https://apisix.apache.org/ai-gateway/)
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### APISIX won't start
|
|
||||||
1. Check etcd connectivity: `curl http://localhost:2379/health`
|
|
||||||
2. Review logs in `/usr/local/apisix/logs/error.log`
|
|
||||||
3. Verify configuration syntax in `/usr/local/apisix/conf/config.yaml`
|
|
||||||
|
|
||||||
### Routes not working
|
|
||||||
1. Check Admin API key is correct
|
|
||||||
2. Verify upstream servers are accessible
|
|
||||||
3. Check firewall rules allow traffic to ports 9080/9443
|
|
||||||
4. Review route configuration via Admin API
|
|
||||||
|
|
||||||
### Performance issues
|
|
||||||
1. Increase worker processes in nginx_config
|
|
||||||
2. Enable HTTP/2 for better performance
|
|
||||||
3. Consider horizontal scaling (multiple APISIX instances)
|
|
||||||
|
|
||||||
## Upstream
|
|
||||||
|
|
||||||
[GitHub Repository](https://github.com/apache/apisix)
|
|
||||||
[Apache Project Page](https://apisix.apache.org/)
|
|
||||||
[Official Docker Images](https://hub.docker.com/r/apache/apisix)
|
|
||||||
|
|||||||
@@ -1,95 +0,0 @@
|
|||||||
# APISIX Configuration Sample
|
|
||||||
# This file is automatically generated by Cloudron package
|
|
||||||
# Located at: /usr/local/apisix/conf/config.yaml
|
|
||||||
|
|
||||||
deployment:
|
|
||||||
role: traditional
|
|
||||||
role_traditional:
|
|
||||||
config_provider: etcd
|
|
||||||
|
|
||||||
admin:
|
|
||||||
# Admin API port
|
|
||||||
port: 9180
|
|
||||||
|
|
||||||
# Allow admin access from all IPs
|
|
||||||
# Restrict this in production to specific IPs
|
|
||||||
allow_admin:
|
|
||||||
- 0.0.0.0/0
|
|
||||||
|
|
||||||
# Admin API key (CHANGE THIS IN PRODUCTION)
|
|
||||||
admin_key:
|
|
||||||
- admin-key-secret-change-me
|
|
||||||
|
|
||||||
# Admin API version
|
|
||||||
admin_api_version: v3
|
|
||||||
|
|
||||||
etcd:
|
|
||||||
# Etcd hosts (automatically configured by Cloudron)
|
|
||||||
host:
|
|
||||||
- 127.0.0.1
|
|
||||||
port: 2379
|
|
||||||
prefix: "/apisix"
|
|
||||||
timeout: 30
|
|
||||||
|
|
||||||
apisix:
|
|
||||||
# SSL configuration
|
|
||||||
ssl:
|
|
||||||
ssl_trusted_certificate: /etc/ssl/certs/ca-certificates.crt
|
|
||||||
ssl_protocols: "TLSv1.2 TLSv1.3"
|
|
||||||
|
|
||||||
# Main HTTP proxy port
|
|
||||||
node_listen: 9080
|
|
||||||
|
|
||||||
# Disable IPv6
|
|
||||||
enable_ipv6: false
|
|
||||||
|
|
||||||
# Enable CORS for admin API
|
|
||||||
enable_admin_cors: true
|
|
||||||
|
|
||||||
# Enable HTTP/2
|
|
||||||
enable_http2: true
|
|
||||||
|
|
||||||
# Nginx configuration
|
|
||||||
nginx_config:
|
|
||||||
# Error log file
|
|
||||||
error_log: "logs/error.log"
|
|
||||||
error_log_level: "warn"
|
|
||||||
|
|
||||||
# Worker processes
|
|
||||||
worker_processes: auto
|
|
||||||
|
|
||||||
# Maximum open files
|
|
||||||
worker_rlimit_nofile: 20480
|
|
||||||
|
|
||||||
# Event worker processes
|
|
||||||
event_worker_processes: 2
|
|
||||||
|
|
||||||
# Worker shutdown timeout
|
|
||||||
worker_shutdown_timeout: 240s
|
|
||||||
|
|
||||||
# Additional configuration options:
|
|
||||||
#
|
|
||||||
# Plugin configuration (default plugins enabled)
|
|
||||||
# plugins:
|
|
||||||
# - real-ip
|
|
||||||
# - proxy-rewrite
|
|
||||||
# - limit-req
|
|
||||||
# - limit-conn
|
|
||||||
# - prometheus
|
|
||||||
# - node-status
|
|
||||||
# - jwt-auth
|
|
||||||
# - key-auth
|
|
||||||
# - basic-auth
|
|
||||||
# - ip-restriction
|
|
||||||
# - cors
|
|
||||||
# - proxy-cache
|
|
||||||
# - limit-count
|
|
||||||
# - request-id
|
|
||||||
# - fault-injection
|
|
||||||
# - consumer
|
|
||||||
#
|
|
||||||
# Stream plugins (for TCP/UDP):
|
|
||||||
# stream_plugins:
|
|
||||||
# - mqtt-proxy
|
|
||||||
# - ip-restriction
|
|
||||||
# - limit-conn
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,69 +1,148 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
set -e
|
# APISIX runtime setup for Cloudron:
|
||||||
|
# 1. persist the Admin API key (rotating it would break automation clients)
|
||||||
|
# 2. relocate the APISIX runtime tree to /app/data - Cloudron mounts the
|
||||||
|
# container rootfs READ-ONLY (writable: /app/data, /tmp, /run), so the
|
||||||
|
# generated config.yaml, nginx.conf, logs and sockets must live there
|
||||||
|
# 3. start the embedded single-node etcd (state under /app/data/etcd,
|
||||||
|
# survives restarts and backups)
|
||||||
|
# 4. regenerate config.yaml on EVERY start, then apisix init + init_etcd
|
||||||
|
# 5. seed the /healthz route once (cosmetic - Cloudron accepts 4xx)
|
||||||
|
# 6. exec openresty in the foreground with the /app/data prefix
|
||||||
|
#
|
||||||
|
# The Admin API listens on 127.0.0.1:9180 ONLY and is never published;
|
||||||
|
# reach it via `cloudron exec` with the key in /app/data/.admin_key.
|
||||||
|
|
||||||
# Cloudron etcd connection
|
DATA_DIR="/app/data"
|
||||||
ETCD_HOST=${CLOUDRON_ETCD_HOST:-127.0.0.1}
|
ETCD_DIR="${DATA_DIR}/etcd"
|
||||||
ETCD_PORT=${CLOUDRON_ETCD_PORT:-2379}
|
ADMIN_KEY_FILE="${DATA_DIR}/.admin_key"
|
||||||
|
ETCD_ENDPOINT="http://127.0.0.1:2379"
|
||||||
|
|
||||||
echo "Etcd host: $ETCD_HOST"
|
PROXY_PORT="${CLOUDRON_APP_PORT:-9080}"
|
||||||
echo "Etcd port: $ETCD_PORT"
|
|
||||||
|
|
||||||
# Wait for etcd to be ready
|
# writable runtime tree (container rootfs is read-only on Cloudron).
|
||||||
echo "Waiting for etcd to be ready..."
|
# The apisix CLI derives its install dir from the script path and writes
|
||||||
MAX_WAIT=30
|
# nginx.conf there, so the lua CLI tree + deps are copied to /app/data and
|
||||||
WAIT_TIME=0
|
# invoked from the copy (~62 MB, once per start).
|
||||||
while ! curl -f "http://${ETCD_HOST}:${ETCD_PORT}/health" 2>/dev/null; do
|
export APISIX_PREFIX="${DATA_DIR}/apisix"
|
||||||
if [ $WAIT_TIME -ge $MAX_WAIT ]; then
|
CONF="${APISIX_PREFIX}/conf/config.yaml"
|
||||||
echo "Timeout waiting for etcd"
|
mkdir -p "${APISIX_PREFIX}/conf" "${APISIX_PREFIX}/logs"
|
||||||
|
# re-copy on every start so upgrades never run a stale tree
|
||||||
|
rm -rf "${APISIX_PREFIX}/apisix" "${APISIX_PREFIX}/deps"
|
||||||
|
cp -a /usr/local/apisix/apisix "${APISIX_PREFIX}/"
|
||||||
|
cp -a /usr/local/apisix/deps "${APISIX_PREFIX}/"
|
||||||
|
# the CLI hardcodes apisix_home = /usr/local/apisix (read-only); repoint the
|
||||||
|
# copy at the writable prefix so nginx.conf and deps resolve under /app/data
|
||||||
|
grep -rl '/usr/local/apisix' "${APISIX_PREFIX}/apisix" \
|
||||||
|
| xargs sed -i "s|/usr/local/apisix|${APISIX_PREFIX}|g"
|
||||||
|
# the CLI looks for its config templates under $APISIX_PREFIX/conf
|
||||||
|
cp -a /usr/local/apisix/conf/. "${APISIX_PREFIX}/conf/"
|
||||||
|
APISIX_CLI="/usr/local/openresty/luajit/bin/luajit ${APISIX_PREFIX}/apisix/cli/apisix.lua"
|
||||||
|
|
||||||
|
# --- 1. persistent Admin API key ---------------------------------------------
|
||||||
|
if [[ ! -s "${ADMIN_KEY_FILE}" ]]; then
|
||||||
|
( umask 077; head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n' > "${ADMIN_KEY_FILE}" )
|
||||||
|
echo "Generated new Admin API key at ${ADMIN_KEY_FILE}"
|
||||||
|
fi
|
||||||
|
ADMIN_KEY="$(cat "${ADMIN_KEY_FILE}")"
|
||||||
|
|
||||||
|
# --- 2. embedded etcd ----------------------------------------------------------
|
||||||
|
mkdir -p "${ETCD_DIR}"
|
||||||
|
# --initial-cluster is ignored on restarts from an existing data-dir.
|
||||||
|
etcd \
|
||||||
|
--name default \
|
||||||
|
--data-dir "${ETCD_DIR}" \
|
||||||
|
--listen-client-urls "http://127.0.0.1:2379" \
|
||||||
|
--advertise-client-urls "http://127.0.0.1:2379" \
|
||||||
|
--listen-peer-urls "http://127.0.0.1:2380" \
|
||||||
|
--initial-advertise-peer-urls "http://127.0.0.1:2380" \
|
||||||
|
--initial-cluster "default=http://127.0.0.1:2380" \
|
||||||
|
--auto-compaction-retention=1h &
|
||||||
|
|
||||||
|
echo "Waiting for embedded etcd at ${ETCD_ENDPOINT} ..."
|
||||||
|
for i in $(seq 1 30); do
|
||||||
|
if etcdctl --endpoints="${ETCD_ENDPOINT}" endpoint health >/dev/null 2>&1; then
|
||||||
|
echo "etcd is up"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
if [[ "$i" -eq 30 ]]; then
|
||||||
|
echo "etcd failed to become healthy" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
echo "Etcd is unavailable - sleeping ($WAIT_TIME/$MAX_WAIT)"
|
sleep 1
|
||||||
sleep 2
|
|
||||||
WAIT_TIME=$((WAIT_TIME+2))
|
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "Etcd is ready!"
|
# --- 3. generated config (rewritten on every start) -----------------------------
|
||||||
|
cat > "${CONF}" <<EOF
|
||||||
|
# Generated by start.sh on every boot - manual edits will be lost.
|
||||||
|
# Data-plane routes live in the embedded etcd (persisted under
|
||||||
|
# /app/data/etcd) and are managed through the Admin API.
|
||||||
|
|
||||||
# Create APISIX configuration file
|
|
||||||
cat > /usr/local/apisix/conf/config.yaml << 'EOF'
|
|
||||||
deployment:
|
deployment:
|
||||||
role: traditional
|
role: traditional
|
||||||
role_traditional:
|
role_traditional:
|
||||||
config_provider: etcd
|
config_provider: etcd
|
||||||
admin:
|
|
||||||
port: 9180
|
|
||||||
allow_admin:
|
|
||||||
- 0.0.0.0/0
|
|
||||||
admin_key:
|
|
||||||
- ${ADMIN_KEY:-admin-key-secret-change-me}
|
|
||||||
admin_api_version: v3
|
|
||||||
etcd:
|
etcd:
|
||||||
host:
|
host:
|
||||||
- ${ETCD_HOST}
|
- "${ETCD_ENDPOINT}"
|
||||||
port: ${ETCD_PORT}
|
prefix: /apisix
|
||||||
prefix: "/apisix"
|
|
||||||
timeout: 30
|
timeout: 30
|
||||||
apisix:
|
admin:
|
||||||
ssl:
|
admin_listen:
|
||||||
ssl_trusted_certificate: /etc/ssl/certs/ca-certificates.crt
|
ip: 127.0.0.1
|
||||||
ssl_protocols: "TLSv1.2 TLSv1.3"
|
port: 9180
|
||||||
node_listen: 9080
|
admin_key:
|
||||||
|
- name: admin
|
||||||
|
key: ${ADMIN_KEY}
|
||||||
|
role: admin
|
||||||
|
|
||||||
|
apisix:
|
||||||
|
node_listen:
|
||||||
|
- ${PROXY_PORT}
|
||||||
enable_ipv6: false
|
enable_ipv6: false
|
||||||
enable_admin_cors: true
|
ssl:
|
||||||
enable_http2: true
|
# TLS terminates at the Cloudron platform proxy
|
||||||
nginx_config:
|
enable: false
|
||||||
error_log: "logs/error.log"
|
|
||||||
error_log_level: "warn"
|
nginx_config:
|
||||||
|
error_log_level: warn
|
||||||
worker_processes: auto
|
worker_processes: auto
|
||||||
worker_rlimit_nofile: 20480
|
http:
|
||||||
event_worker_processes: 2
|
# restore real client IPs from the Cloudron proxy's X-Forwarded-For
|
||||||
worker_shutdown_timeout: 240s
|
real_ip_header: X-Forwarded-For
|
||||||
|
real_ip_from:
|
||||||
|
- 10.0.0.0/8
|
||||||
|
- 172.16.0.0/12
|
||||||
|
- 192.168.0.0/16
|
||||||
|
- 127.0.0.0/8
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
echo "APISIX configuration created at /usr/local/apisix/conf/config.yaml"
|
# --- 4. init + schema bootstrap -------------------------------------------------
|
||||||
cat /usr/local/apisix/conf/config.yaml
|
${APISIX_CLI} init
|
||||||
|
${APISIX_CLI} init_etcd
|
||||||
|
|
||||||
# Start APISIX
|
# apisix init rewrites config.yaml (deployment timestamp). nginx runs workers
|
||||||
echo "Starting APISIX..."
|
# as nobody (no `user` directive in the generated nginx.conf), so the config
|
||||||
exec /usr/bin/apisix start
|
# must stay world-readable within the container - acceptable because the
|
||||||
|
# Admin API itself listens on 127.0.0.1 only.
|
||||||
|
chmod 644 "${CONF}"
|
||||||
|
echo "Generated APISIX config at ${CONF} (proxy port ${PROXY_PORT})"
|
||||||
|
|
||||||
|
# --- 5. seed the health route once ----------------------------------------------
|
||||||
|
# Cloudron's healthcheck accepts 4xx, so it is not required for the platform -
|
||||||
|
# it gives https://<app-domain>/healthz a friendly answer instead of a bare 404.
|
||||||
|
if [[ -z "$(etcdctl --endpoints="${ETCD_ENDPOINT}" get /apisix/routes/healthz --print-value-only 2>/dev/null)" ]]; then
|
||||||
|
# ngx.say is unavailable in the rewrite phase - a bare ngx.exit(200) is the
|
||||||
|
# error-proof direct response (200, empty body).
|
||||||
|
etcdctl --endpoints="${ETCD_ENDPOINT}" put /apisix/routes/healthz \
|
||||||
|
'{"uri":"/healthz","id":"healthz","priority":100,"plugins":{"serverless-pre-function":{"phase":"rewrite","functions":["return function() ngx.exit(200) end"]}}}' > /dev/null \
|
||||||
|
|| echo "WARNING: seeding /healthz failed (cosmetic only, continuing)" >&2
|
||||||
|
echo "Seeded /healthz route"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- 6. run (foreground) ----------------------------------------------------------
|
||||||
|
echo "Starting APISIX ..."
|
||||||
|
rm -f "${APISIX_PREFIX}/conf/config_listen.sock" "${APISIX_PREFIX}/logs/worker_events.sock"
|
||||||
|
exec /usr/local/openresty/bin/openresty -p "${APISIX_PREFIX}" -g 'daemon off;'
|
||||||
|
|||||||
Reference in New Issue
Block a user