Skip to content
vexxhostPublic

About

OpenStack Dashboard

Resources

Stars

9 stars

Watchers

10 watching

Forks

Latest commit

 

History

615 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Sunrise

Local Development

  1. Create a client inside Keycloak which will be used for authenticating the user using Sunrise.

  2. Copy .env.dist to .env.local and configure the local cloud and identity values. Never commit .env.local.

  3. Start the development server:

    pnpm dev -p 9990
  4. Navigate to the following URL. You will be redirected to login with Keycloak:

    http://localhost:9990

Nova Console Integration

Sunrise runs the noVNC client in the browser and connects directly to nova-novncproxy. The browser therefore sends the Sunrise dashboard hostname as the WebSocket origin. Add every public Sunrise hostname to Nova's [console] allowed_origins list in each region:

[console]
allowed_origins = sunrise.example.com

Use hostnames only, without a scheme, port, path, or wildcard. Separate multiple hostnames with commas. Local development on this repository's default URL needs localhost. Reconfigure or restart nova-novncproxy after changing the list. When TLS terminates in front of Nova, the proxy must also preserve WebSocket upgrades and set X-Forwarded-Proto to the browser-facing scheme.

Local Production Container

The Compose stack builds the Next.js standalone runtime for the local host architecture and runs it as a non-root user with a read-only root filesystem. It also starts an internal Redis-compatible service and selects the server-side session backend, so the same image can be exercised with the persistence model intended for multiple Sunrise replicas. The local stack defaults to Valkey while all Sunrise configuration uses the implementation-neutral Redis protocol vocabulary.

Next.js writes ephemeral image/data cache entries under .next/cache and promotes immutable prerender seeds into .next/server/route-cache at runtime. Compose mounts both paths as bounded tmpfs volumes. Kubernetes deployments that enable readOnlyRootFilesystem must likewise mount writable pod-local emptyDir volumes at /app/.next/cache and /app/.next/server/route-cache; neither directory contains shared session state and each replica may maintain it independently.

docker compose build sunrise
docker compose up -d
docker compose ps
docker compose logs -f sunrise

Compose mounts the public development-only Server Actions key from deploy/local/next-server-actions-encryption-key while building the image. It exists only to make separate local builds compatible during rollout exercises; never use it for a published image.

A publishable image must provide an immutable deployment ID and a private, stable Server Actions encryption key. The key is consumed through a BuildKit secret, never an image build argument or runtime environment variable:

docker build \
  --secret id=next_server_actions_encryption_key,src=/secure/path/next-server-actions-key \
  --build-arg SUNRISE_SERVER_ACTIONS_REVISION=production-v1 \
  --build-arg SUNRISE_DEPLOYMENT_ID="$(git rev-parse HEAD)" \
  --build-arg SUNRISE_IMAGE_VERSION=0.1.0 \
  --tag registry.example.com/sunrise:0.1.0 .

The key file must contain canonical base64 for a 16, 24, or 32-byte key. Reuse the same key for image revisions that can overlap during a rolling deployment. SUNRISE_SERVER_ACTIONS_REVISION is a non-secret revision or fingerprint used only to invalidate the builder cache; change it whenever the secret changes. Production automation should derive it from a cryptographic hash of the key or advance an operator-managed revision. Never put the key itself in a build argument. The deployment ID is compiled into Next.js, retained in the final image, and reported by /healthz; overriding it with a different runtime value causes startup validation to fail.

Published Container Image

The Image GitHub Actions workflow builds ghcr.io/vexxhost/sunrise for linux/amd64 and linux/arm64. Pull requests build without publishing. Pushes to main publish main and immutable sha-<commit> tags; release tags matching v* publish the matching release tag and the immutable commit tag. The workflow does not publish a floating latest tag. Published manifests include OCI metadata, an SBOM, and maximum-mode provenance.

The workflow uses Docker Buildx and QEMU on GitHub-hosted runners to produce the multi-platform image. BuildKit layers are stored in the GitHub Actions cache to reduce repeated native and emulated build work; publishing requires only the repository's standard GITHUB_TOKEN package permission.

Published builds require the SUNRISE_SERVER_ACTIONS_ENCRYPTION_KEY Actions secret. Generate canonical base64 once and retain the same value while image revisions may overlap:

openssl rand -base64 32 | tr -d '\n'

Pull requests use a disposable build-only key because their images are never published. The workflow derives the non-secret cache revision from a SHA-256 fingerprint and passes the key itself only through a BuildKit secret mount. When deploying, prefer the manifest digest or immutable sha-* tag over the mutable main tag.

Override the implementation image without changing Sunrise configuration:

SUNRISE_REDIS_IMAGE=redis:8.10.1-alpine docker compose --profile replica up -d --force-recreate redis sunrise sunrise-replica

Unset SUNRISE_REDIS_IMAGE and recreate the same services to return to the default Valkey image. The Compose service and hostname remain redis in both cases.

Sunrise currently uses only the shared Redis OSS 7.2 command and RESP surface, which Valkey supports with ordinary Redis clients. Keep implementation-specific modules and newer vendor-only commands out of the session layer if this switchability is required. The local data directory is ephemeral by design; do not reuse Redis 7.4+ or Redis 8 persistence files with Valkey during a production migration.

The default local topology is intentionally plain and unauthenticated. The same Compose setup also provides three production-oriented connection exercises:

Mode Command
Password authentication SUNRISE_REDIS_PASSWORD='local-test-password' docker compose --profile replica up -d --force-recreate redis sunrise sunrise-replica
TLS docker compose -f compose.yaml -f compose.redis-tls.yaml --profile replica up -d --force-recreate redis sunrise sunrise-replica
Password authentication and TLS SUNRISE_REDIS_PASSWORD='local-test-password' docker compose -f compose.yaml -f compose.redis-tls.yaml --profile replica up -d --force-recreate redis sunrise sunrise-replica

The TLS override creates a disposable local CA and server certificate in a named volume. Remove that test material with:

docker compose -f compose.yaml -f compose.redis-tls.yaml --profile replica down -v

Production should provide SUNRISE_REDIS_URL=rediss://..., optional SUNRISE_REDIS_USERNAME, required SUNRISE_REDIS_PASSWORD, and an optional SUNRISE_REDIS_CA_FILE mounted from deployment secrets when private PKI is in use. Do not reuse the Compose-generated CA. Authentication alone does not encrypt traffic, while TLS without authentication does not authorize clients; use both, network isolation, and a narrowly scoped ACL for production.

For private Keycloak or OpenStack PKI, mount a PEM CA bundle and set NODE_EXTRA_CA_CERTS to its in-container path. Do not deploy NODE_TLS_REJECT_UNAUTHORIZED=0; Sunrise reports it as an explicit startup warning because it disables certificate validation for the whole Node.js process.

Sunrise is available at http://localhost:9990. /healthz checks only the application process; /readyz checks Redis when server-side sessions require it. The readiness probe performs short-lived Lua writes across the session and refresh namespaces and samples Redis TIME, so read-only endpoints and ACLs missing required data or shared-clock commands remove the replica from service instead of accepting traffic it cannot handle.

Run the repeatable local probe baseline after the stack becomes healthy:

pnpm measure:compose
docker image inspect sunrise:local --format '{{.Size}} bytes'
docker stats --no-stream sunrise-local-sunrise-1 sunrise-local-redis-1

Start a second independent Sunrise process against the same Redis sessions with:

docker compose --profile replica up -d sunrise-replica

Replica 2 is available at http://localhost:9991. Browser cookies are scoped to localhost, not its port, so switching between ports exercises the same opaque session through two processes without load-balancer affinity. Compose explicitly allows this secondary origin as a trusted same-host dashboard origin; production remains restricted to SUNRISE_DASHBOARD_URL unless additional exact origins are configured.

Stop and remove the local stack with docker compose down. The Compose Redis data directory is deliberately ephemeral: it models a disposable local QA dependency, not a production data policy. Production deployments must provide an authenticated, highly available Redis endpoint (preferably rediss://), shared SUNRISE_SESSION_SECRET, stable deployment namespace, and one immutable SUNRISE_DEPLOYMENT_ID embedded by the image build pipeline per image build. It is not deployment-time configuration.

Every server validates its static deployment configuration before accepting requests. Invalid dashboard or identity URLs, missing or short session secrets, malformed federation providers, unsupported service settings, unreadable CA files, invalid Redis settings, and image/runtime deployment-ID skew stop the process. Deliberately weak but locally useful settings such as plain Redis, HTTP dashboard URLs, cookie sessions, and disabled TLS verification produce sanitized startup warnings without logging credentials.

Compose gives Sunrise 30 seconds to finish in-flight requests after SIGTERM. Next.js handles the signal and drains pending work; Kubernetes should use the same signal with a configurable termination grace period instead of sending an immediate SIGKILL.

Sunrise currently connects to one stable writable Redis endpoint. That endpoint may be provided by a managed service, a Kubernetes operator, or a Sentinel deployment that exposes the elected primary through stable DNS. Do not point SUNRISE_REDIS_URL at a Sentinel port. Native Sentinel discovery and Redis Cluster sharding remain separate deployment features; neither is required for the expected session workload, and neither is enabled by this local Compose topology.

For production sessions, high availability alone is not the whole contract: the selected Redis service must provide acceptable durability for login, token rotation, and logout-revocation writes. Losing a recent session revocation has security consequences and must be covered by failover testing.

Redis is deliberately limited to security-bearing session state in this iteration. Sunrise's OpenStack requests are non-cacheable, so each replica uses Next.js's local server cache rather than maintaining a custom distributed cache and tag-invalidation protocol. Revisit shared application caching only when a measured cacheable workload justifies that separate consistency boundary.

Local Kubernetes

The Helm chart in deploy/k8s/chart runs multiple read-only, non-root Sunrise replicas against a shared Redis-compatible session backend. The disposable kind harness in deploy/k8s/kind uses the running local cluster, imports the required runtime settings from .env.local into a Kubernetes Secret, installs Reloader to restart pods when that external Secret changes, and deploys a temporary password-protected Valkey instance for integration testing.

make -C deploy/k8s/kind sunrise
make -C deploy/k8s/kind sunrise-status
make -C deploy/k8s/kind sunrise-test

The local route is https://sunrise.localtest.me. See the chart documentation for production inputs, security boundaries, and local-image overrides. Production deployments must use an external highly available Redis-compatible service; the kind Valkey manifest is deliberately ephemeral and is not part of the Helm chart.

Cloud-specific kind settings belong in the ignored deploy/k8s/kind/values.yaml. When that file is absent, the Makefile falls back to the tracked values.example.yaml; copy the example before customizing a local environment. The provider list in that file must match KEYSTONE_FEDERATION_IDENTITY_PROVIDERS in .env.local; the kind harness checks this before creating or changing cluster resources.

Set SUNRISE_SESSION_BACKEND=cookie to use the single-process development fallback. That mode is not suitable for horizontally scaled production replicas because credential rotation and logout state are held by the browser. Switching an existing deployment from cookie to redis intentionally invalidates credential-bearing cookie sessions and requires users to sign in again. This ensures every accepted authenticated session has a server-side, revocable identity before its credentials are used.

About

OpenStack Dashboard

Resources

Stars

9 stars

Watchers

10 watching

Forks

Releases

Packages

Used by

Contributors

Languages