Add playground backend and proxy
This commit is contained in:
1 parent
bac31b44c1
commit
70be3c37e5
43 files changed
+3140
-4
No files matched your search
@@ -0,0 +1,5 @@
|
||||
.git
|
||||
.gitignore
|
||||
certs/*.crt
|
||||
certs/*.key
|
||||
*.md
|
||||
@@ -0,0 +1,49 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
FROM nginx:1.27-alpine
|
||||
|
||||
# Install openssl for auto-generating self-signed dev certificates and curl for healthchecks
|
||||
RUN apk add --no-cache openssl curl bash
|
||||
|
||||
# Create directory structure
|
||||
RUN mkdir -p /etc/nginx/conf.d \
|
||||
/etc/nginx/includes \
|
||||
/etc/nginx/certs \
|
||||
/usr/share/nginx/html
|
||||
|
||||
# Copy main Nginx configuration
|
||||
COPY conf/nginx.conf /etc/nginx/nginx.conf
|
||||
|
||||
# Clear base image default configs and copy custom configs
|
||||
RUN rm -rf /etc/nginx/conf.d/*
|
||||
COPY conf/conf.d/ /etc/nginx/conf.d/
|
||||
COPY conf/includes/ /etc/nginx/includes/
|
||||
|
||||
# Copy custom error templates and fallback static files
|
||||
COPY html/ /usr/share/nginx/html/
|
||||
|
||||
# Copy entrypoint script
|
||||
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
||||
RUN chmod +x /docker-entrypoint.sh
|
||||
|
||||
# Expose standard HTTP and HTTPS ports
|
||||
EXPOSE 80 443
|
||||
|
||||
# Healthcheck to ensure Nginx is responding
|
||||
HEALTHCHECK --interval=15s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD curl -k -f https://localhost/healthz || curl -f http://localhost:80/healthz || exit 1
|
||||
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
@@ -0,0 +1,127 @@
|
||||
# 🌐 Artemis Ingress Reverse Proxy Container
|
||||
|
||||
Production-ready, high-performance Nginx Reverse Proxy container designed for running Artemis on a **Container-Optimized OS (COS)** VM in Google Cloud.
|
||||
|
||||
---
|
||||
|
||||
## 📌 Architecture & Routing Matrix
|
||||
|
||||
The proxy acts as the single TLS/SSL termination gateway on standard ports (`80` $\rightarrow$ `443`), dynamically routing traffic to persistent platform services and on-demand ephemeral Artemis session containers without needing Nginx reload or restart:
|
||||
|
||||
| Path Prefix | Destination Upstream | Protocol | Purpose |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `GET /healthz` | Nginx Internal | HTTP/HTTPS | Ingress health check probe |
|
||||
| `/` | `http://backend:8000/` | HTTP/1.1 | Static frontend Single Page Application (React/Vue/Angular) |
|
||||
| `/api/*` | `http://backend:8000/api/*` | HTTP/1.1 / WS | Management API, OTP auth verification, session lifecycle |
|
||||
| `/session/{id}/artemis/stream/*` | `http://artemis-session-{id}:8080/stream/*` | **SSE / WebSocket** | Real-time agent thought stream (zero buffering, 1h timeout) |
|
||||
| `/session/{id}/artemis/*` | `http://artemis-session-{id}:8080/*` | HTTP/1.1 / WS | Artemis REST API and task execution endpoints |
|
||||
| `/session/{id}/cuttlefish/*` | `http://host.docker.internal:8443/*` | **WebRTC / WS** | Cuttlefish WebRTC signaling & interactive device controls |
|
||||
|
||||
---
|
||||
|
||||
## 📁 File Structure
|
||||
|
||||
```
|
||||
playground/nginx_container/
|
||||
├── Dockerfile # Nginx alpine image with openssl and health checks
|
||||
├── docker-entrypoint.sh # Auto-generates dev certificates & tests nginx syntax
|
||||
├── docker-compose.yml # Compose specification with artemis-net bridge network
|
||||
├── .dockerignore
|
||||
├── README.md # Detailed operational documentation
|
||||
├── certs/
|
||||
│ ├── generate_dev_certs.sh # Manual SSL certificate generation helper
|
||||
│ └── .gitkeep
|
||||
├── conf/
|
||||
│ ├── nginx.conf # Master Nginx configuration (JSON logging, WebSocket map)
|
||||
│ ├── conf.d/
|
||||
│ │ ├── http_redirect.conf # Port 80 redirect to Port 443 + ACME challenge
|
||||
│ │ └── artemis_ssl.conf # Port 443 SSL server block & dynamic Docker DNS routing
|
||||
│ └── includes/
|
||||
│ ├── proxy_headers.conf # Standard forwarding headers (Host, X-Real-IP, X-Forwarded-*)
|
||||
│ ├── websocket_params.conf# WebSocket upgrade headers & 3600s timeouts
|
||||
│ └── sse_params.conf # SSE streaming parameters (proxy_buffering off)
|
||||
└── html/
|
||||
├── index.html # Fallback landing page
|
||||
├── 502.json # JSON error response for provisioning containers
|
||||
└── 504.json # JSON error response for timeouts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### 1. Build the Docker Image
|
||||
```bash
|
||||
docker build -t artemis-nginx-proxy:latest .
|
||||
```
|
||||
|
||||
### 2. Run with Docker Compose
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
### 3. Run Standalone on COS VM
|
||||
```bash
|
||||
# Create shared bridge network if not exists
|
||||
docker network create artemis-net || true
|
||||
|
||||
# Run Nginx Reverse Proxy Container
|
||||
docker run -d \
|
||||
--name artemis-nginx-proxy \
|
||||
--restart unless-stopped \
|
||||
--network artemis-net \
|
||||
--add-host host.docker.internal:host-gateway \
|
||||
-p 80:80 \
|
||||
-p 443:443 \
|
||||
-v $(pwd)/certs:/etc/nginx/certs:ro \
|
||||
artemis-nginx-proxy:latest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔑 Key Features & Technical Highlights
|
||||
|
||||
### 1. Dynamic Zero-Reload Upstream Resolution
|
||||
When a user launches a new session, the backend starts a new Docker container named `artemis-session-<session_id>` on the `artemis-net` network.
|
||||
|
||||
Using Docker's embedded DNS server (`127.0.0.11 valid=5s`) combined with variable-based `proxy_pass`:
|
||||
```nginx
|
||||
resolver 127.0.0.11 valid=5s ipv6=off;
|
||||
|
||||
location ~ ^/session/(?<session_id>[a-zA-Z0-9_-]+)/artemis/(?<artemis_path>.*)$ {
|
||||
set $artemis_host "artemis-session-$session_id";
|
||||
proxy_pass http://$artemis_host:8080/$artemis_path$is_args$args;
|
||||
}
|
||||
```
|
||||
Nginx resolves the container hostname at **request-time**, allowing session containers to be created and destroyed without touching or reloading Nginx configuration.
|
||||
|
||||
### 2. Real-Time Streaming (SSE & WebSocket)
|
||||
- **SSE Streams** (`/stream/*`): Uses `proxy_buffering off;`, `proxy_cache off;`, and `chunked_transfer_encoding on;` so the browser receives agent reasoning tokens with zero buffering delay.
|
||||
- **WebRTC Signaling & WebSockets**: Employs `map $http_upgrade $connection_upgrade` with 1-hour idle timeouts (`proxy_read_timeout 3600s;`).
|
||||
|
||||
### 3. Automated SSL/TLS Management
|
||||
- **Development/Staging**: The container entrypoint automatically generates a self-signed certificate if no certificates exist in `/etc/nginx/certs/`.
|
||||
- **Production**: Mount your Let's Encrypt or GCP managed certificates to `/etc/nginx/certs/tls.crt` and `/etc/nginx/certs/tls.key`.
|
||||
|
||||
### 4. Resilient Error Handling
|
||||
If a requested session container is still booting or unavailable, Nginx returns a clean JSON error response (`502.json` / `504.json`) instead of standard Nginx HTML error pages, enabling the frontend UI to gracefully poll or display a loading spinner.
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Verification & Testing
|
||||
|
||||
1. **Verify Health Endpoint**:
|
||||
```bash
|
||||
curl -k https://localhost/healthz
|
||||
# Output: OK
|
||||
```
|
||||
|
||||
2. **Test Dynamic Routing**:
|
||||
```bash
|
||||
# Launch a mock session container
|
||||
docker run -d --name artemis-session-test123 --network artemis-net hashicorp/http-echo -text="Hello from Session test123" -listen=:8080
|
||||
|
||||
# Test via Nginx proxy
|
||||
curl -k https://localhost/session/test123/artemis/
|
||||
# Output: Hello from Session test123
|
||||
```
|
||||
@@ -0,0 +1 @@
|
||||
# Keep certs directory in repository while ignoring .crt and .key files
|
||||
@@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -e
|
||||
|
||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
CERT_FILE="${DIR}/tls.crt"
|
||||
KEY_FILE="${DIR}/tls.key"
|
||||
|
||||
echo "Generating self-signed SSL certificates for development..."
|
||||
|
||||
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||
-keyout "${KEY_FILE}" \
|
||||
-out "${CERT_FILE}" \
|
||||
-subj "/CN=localhost/O=Artemis Development/C=US" \
|
||||
-addext "subjectAltName=DNS:localhost,DNS:*.localhost,IP:127.0.0.1"
|
||||
|
||||
chmod 600 "${KEY_FILE}"
|
||||
chmod 644 "${CERT_FILE}"
|
||||
|
||||
echo "Development certificates generated:"
|
||||
echo " Private Key: ${KEY_FILE}"
|
||||
echo " Certificate: ${CERT_FILE}"
|
||||
@@ -0,0 +1,118 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
server {
|
||||
listen 443 ssl default_server;
|
||||
listen [::]:443 ssl default_server;
|
||||
http2 on;
|
||||
server_name _;
|
||||
|
||||
# TLS / SSL Configuration
|
||||
ssl_certificate /etc/nginx/certs/tls.crt;
|
||||
ssl_certificate_key /etc/nginx/certs/tls.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
|
||||
ssl_prefer_server_ciphers off;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_tickets off;
|
||||
|
||||
# Security Headers
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
|
||||
# Embedded Docker DNS Resolver for dynamic container discovery
|
||||
# 127.0.0.11 is Docker's internal DNS server
|
||||
resolver 127.0.0.11 valid=5s ipv6=off;
|
||||
resolver_timeout 5s;
|
||||
|
||||
# Health check endpoint
|
||||
location = /healthz {
|
||||
access_log off;
|
||||
default_type text/plain;
|
||||
return 200 "OK\n";
|
||||
}
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# 1. Backend Core Management API (Auth, OTP, Session Lifecycle)
|
||||
# --------------------------------------------------------------------------
|
||||
location /api/ {
|
||||
set $backend_upstream "backend:8000";
|
||||
proxy_pass http://$backend_upstream;
|
||||
include /etc/nginx/includes/proxy_headers.conf;
|
||||
include /etc/nginx/includes/websocket_params.conf;
|
||||
}
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# 2. Dynamic Artemis Session Containers: Real-time Trace & Event Stream (SSE/WS)
|
||||
# Target container: artemis-session-<session_id>:8080
|
||||
# --------------------------------------------------------------------------
|
||||
location ~ ^/session/(?<session_id>[a-zA-Z0-9_-]+)/artemis/stream/(?<stream_path>.*)$ {
|
||||
set $artemis_host "artemis-session-$session_id";
|
||||
proxy_pass http://$artemis_host:8080/stream/$stream_path$is_args$args;
|
||||
include /etc/nginx/includes/sse_params.conf;
|
||||
include /etc/nginx/includes/websocket_params.conf;
|
||||
}
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# 3. Dynamic Artemis Session Containers: REST APIs & Admin Console
|
||||
# Target container: artemis-session-<session_id>:8080
|
||||
# --------------------------------------------------------------------------
|
||||
location ~ ^/session/(?<session_id>[a-zA-Z0-9_-]+)/artemis/(?<artemis_path>.*)$ {
|
||||
set $artemis_host "artemis-session-$session_id";
|
||||
proxy_pass http://$artemis_host:8080/$artemis_path$is_args$args;
|
||||
include /etc/nginx/includes/proxy_headers.conf;
|
||||
include /etc/nginx/includes/websocket_params.conf;
|
||||
}
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# 4. Cuttlefish Emulator WebRTC Signaling & Stream Proxy
|
||||
# Proxies signaling requests to the Cloud Orchestrator / CVD daemon
|
||||
# --------------------------------------------------------------------------
|
||||
location ~ ^/session/(?<session_id>[a-zA-Z0-9_-]+)/cuttlefish/(?<cf_path>.*)$ {
|
||||
# Host gateway communication or cloud-orchestrator container
|
||||
set $cuttlefish_upstream "host.docker.internal:8443";
|
||||
proxy_pass http://$cuttlefish_upstream/$cf_path$is_args$args;
|
||||
include /etc/nginx/includes/proxy_headers.conf;
|
||||
include /etc/nginx/includes/websocket_params.conf;
|
||||
}
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# 5. Static Frontend Single Page Application (Served via Backend or Static Dist)
|
||||
# --------------------------------------------------------------------------
|
||||
location / {
|
||||
set $backend_upstream "backend:8000";
|
||||
proxy_pass http://$backend_upstream;
|
||||
include /etc/nginx/includes/proxy_headers.conf;
|
||||
}
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# 6. Custom JSON Error Pages for Ephemeral Container Boot & Teardown States
|
||||
# --------------------------------------------------------------------------
|
||||
error_page 502 /502.json;
|
||||
error_page 504 /504.json;
|
||||
|
||||
location = /502.json {
|
||||
root /usr/share/nginx/html;
|
||||
internal;
|
||||
default_type application/json;
|
||||
}
|
||||
|
||||
location = /504.json {
|
||||
root /usr/share/nginx/html;
|
||||
internal;
|
||||
default_type application/json;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
server_name _;
|
||||
|
||||
# Health check endpoint over plain HTTP
|
||||
location = /healthz {
|
||||
access_log off;
|
||||
default_type text/plain;
|
||||
return 200 "OK\n";
|
||||
}
|
||||
|
||||
# ACME Challenge endpoint for automated Let's Encrypt SSL renewal
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
allow all;
|
||||
}
|
||||
|
||||
# Enforce HTTPS redirect for all incoming web traffic
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
|
||||
# Buffer tuning for standard API calls
|
||||
proxy_buffers 8 64k;
|
||||
proxy_buffer_size 128k;
|
||||
proxy_busy_buffers_size 128k;
|
||||
@@ -0,0 +1,18 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Critical directives for real-time Server-Sent Events (SSE) streaming
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
chunked_transfer_encoding on;
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
@@ -0,0 +1,92 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
worker_rlimit_nofile 65535;
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 4096;
|
||||
multi_accept on;
|
||||
use epoll;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
# High-performance event I/O settings
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
keepalive_timeout 65;
|
||||
types_hash_max_size 2048;
|
||||
server_tokens off;
|
||||
|
||||
# Client payload size (allows large artifact, image, APK uploads)
|
||||
client_max_body_size 100M;
|
||||
client_body_buffer_size 128k;
|
||||
|
||||
# Enhanced JSON-structured access logging for easy parsing & cloud monitoring
|
||||
log_format artemis_json escape=json '{'
|
||||
'"time_local":"$time_local",'
|
||||
'"remote_addr":"$remote_addr",'
|
||||
'"request_method":"$request_method",'
|
||||
'"request_uri":"$request_uri",'
|
||||
'"status":$status,'
|
||||
'"body_bytes_sent":$body_bytes_sent,'
|
||||
'"request_time":$request_time,'
|
||||
'"upstream_response_time":"$upstream_response_time",'
|
||||
'"upstream_addr":"$upstream_addr",'
|
||||
'"upstream_status":"$upstream_status",'
|
||||
'"http_referrer":"$http_referer",'
|
||||
'"http_user_agent":"$http_user_agent"'
|
||||
'}';
|
||||
|
||||
access_log /var/log/nginx/access.log artemis_json;
|
||||
|
||||
# Global proxy defaults
|
||||
proxy_http_version 1.1;
|
||||
proxy_connect_timeout 10s;
|
||||
proxy_send_timeout 3600s;
|
||||
proxy_read_timeout 3600s;
|
||||
|
||||
# Gzip compression for static assets and API payloads
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_types
|
||||
text/plain
|
||||
text/css
|
||||
text/xml
|
||||
text/javascript
|
||||
application/json
|
||||
application/javascript
|
||||
application/x-javascript
|
||||
application/xml
|
||||
application/xml+rss
|
||||
image/svg+xml;
|
||||
|
||||
# WebSocket Upgrade Header Mapping
|
||||
map $http_upgrade $connection_upgrade {
|
||||
default upgrade;
|
||||
'' close;
|
||||
}
|
||||
|
||||
# Load modular server configurations
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
nginx-proxy:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: artemis-nginx-proxy
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
# Mount certificates from host (or let entrypoint generate dev certs)
|
||||
- ./certs:/etc/nginx/certs:ro
|
||||
networks:
|
||||
- artemis-net
|
||||
extra_hosts:
|
||||
# Allows reaching Cuttlefish / Cloud Orchestrator running on the COS host
|
||||
- "host.docker.internal:host-gateway"
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-k", "-f", "https://localhost/healthz"]
|
||||
interval: 15s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
|
||||
networks:
|
||||
artemis-net:
|
||||
name: artemis-net
|
||||
driver: bridge
|
||||
@@ -0,0 +1,46 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set -e
|
||||
|
||||
CERT_DIR="/etc/nginx/certs"
|
||||
CERT_FILE="${CERT_DIR}/tls.crt"
|
||||
KEY_FILE="${CERT_DIR}/tls.key"
|
||||
|
||||
echo "[Nginx Entrypoint] Initializing Artemis Ingress Proxy..."
|
||||
|
||||
# Check if SSL certificates exist; if not, auto-generate a self-signed fallback cert
|
||||
if [ ! -f "$CERT_FILE" ] || [ ! -f "$KEY_FILE" ]; then
|
||||
echo "[Nginx Entrypoint] No SSL certificate found at ${CERT_FILE}. Generating self-signed certificate for development/staging..."
|
||||
mkdir -p "${CERT_DIR}"
|
||||
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||
-keyout "${KEY_FILE}" \
|
||||
-out "${CERT_FILE}" \
|
||||
-subj "/CN=localhost/O=Artemis/C=US" \
|
||||
-addext "subjectAltName=DNS:localhost,DNS:*.localhost,IP:127.0.0.1" \
|
||||
2>/dev/null
|
||||
chmod 600 "${KEY_FILE}"
|
||||
chmod 644 "${CERT_FILE}"
|
||||
echo "[Nginx Entrypoint] Self-signed certificate generated successfully."
|
||||
else
|
||||
echo "[Nginx Entrypoint] SSL certificate verified at ${CERT_FILE}."
|
||||
fi
|
||||
|
||||
# Test Nginx configuration validity before starting
|
||||
echo "[Nginx Entrypoint] Verifying Nginx configuration syntax..."
|
||||
nginx -t
|
||||
|
||||
echo "[Nginx Entrypoint] Starting Nginx daemon..."
|
||||
exec "$@"
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"status": 502,
|
||||
"error": "bad_gateway",
|
||||
"message": "The requested Artemis session container or Cuttlefish device is provisioning, booting, or currently unreachable. Please retry shortly.",
|
||||
"timestamp": "auto"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"status": 504,
|
||||
"error": "gateway_timeout",
|
||||
"message": "The upstream Artemis container or device service took too long to respond.",
|
||||
"timestamp": "auto"
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Artemis Reverse Proxy</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
background: #0f172a;
|
||||
color: #f8fafc;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.card {
|
||||
background: #1e293b;
|
||||
border: 1px solid #334155;
|
||||
border-radius: 16px;
|
||||
padding: 40px;
|
||||
max-width: 540px;
|
||||
width: 100%;
|
||||
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.4);
|
||||
text-align: center;
|
||||
}
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
margin-bottom: 12px;
|
||||
color: #38bdf8;
|
||||
}
|
||||
p {
|
||||
color: #94a3b8;
|
||||
font-size: 15px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 6px 14px;
|
||||
background: #064e3b;
|
||||
color: #34d399;
|
||||
border-radius: 9999px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: #10b981;
|
||||
border-radius: 50%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<div class="status">
|
||||
<span class="dot"></span> Ingress Proxy Active
|
||||
</div>
|
||||
<h1>Artemis Cloud Platform</h1>
|
||||
<p>The reverse proxy is operational and listening on ports 80 & 443. Connect your backend service to begin routing user sessions and Cuttlefish devices.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user