Skip to content

Commit

Permalink
feat: Provide helm chart
Browse files Browse the repository at this point in the history
  • Loading branch information
PSanetra committed Aug 8, 2024
1 parent cf6f9b1 commit 54db926
Show file tree
Hide file tree
Showing 34 changed files with 2,908 additions and 59 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
- v*

jobs:
test:
push_images:
name: Test and Push Images
runs-on: ubuntu-latest
strategy:
Expand Down Expand Up @@ -39,7 +39,7 @@ jobs:
git-ref: "${{ github.ref }}"
nginx-tag: "${{ steps.target_nginx_tag.outputs.tag }}"
matrix-nginx: "${{ matrix.nginx }}"
docker-repository: "${{ secrets.DOCKER_REPOSITORY }}"
docker-repository: "${{ vars.DOCKER_REPOSITORY }}"
- uses: docker/setup-qemu-action@v2
name: Set up QEMU
- uses: docker/setup-buildx-action@v2
Expand Down
44 changes: 44 additions & 0 deletions .github/workflows/publish-helm-chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Test and Push Helm Chart

on:
push:
tags:
- 'v*'

jobs:
test-and-push:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v3

- name: Get Latest Semantic Version
id: get_latest_version
uses: PSanetra/git-semver-actions/latest@v1

- name: Set up Helm
uses: azure/setup-helm@v3

- name: Install Helm Unittest Plugin
run: helm plugin install https://github.com/helm-unittest/helm-unittest

- name: Run Helm Lint
run: helm lint chart --strict

- name: Run Helm Unit Tests
run: helm unittest chart --strict

- name: Package Helm Chart
run: helm package chart -u --version ${{ steps.get_latest_version.outputs.version }} --destination .

- name: Install yq
run: sudo apt update && sudo apt install -y yq

- name: Login to Docker Hub
run: echo '${{ secrets.DOCKER_PASSWORD }}' | helm registry login ${{ vars.DOCKER_REPOSITORY }} --username '${{ secrets.DOCKER_USERNAME }}' --password-stdin

- name: Push Helm Chart to OCI Registry
run: |
helm push $(yq -r '.name' ./chart/Chart.yaml | tr -d '\n')-${{ steps.get_latest_version.outputs.version }}.tgz oci://${{ vars.DOCKER_REPOSITORY }}
helm push $(yq -r '.name' ./chart/Chart.yaml | tr -d '\n')-${{ steps.get_latest_version.outputs.version }}.tgz oci://${{ vars.DOCKER_REPOSITORY }}:latest
6 changes: 3 additions & 3 deletions .github/workflows/update.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ jobs:
id: target_image_name
run: |
IMAGE_TAG="${{ matrix.spa_server_major_version }}-nginx-${{ steps.target_nginx_tag.outputs.tag }}"
IMAGE_NAME="${{ secrets.DOCKER_REPOSITORY }}:${IMAGE_TAG}"
IMAGE_NAME="${{ vars.DOCKER_REPOSITORY }}:${IMAGE_TAG}"
echo "::set-output name=image_name::${IMAGE_NAME}"
echo "IMAGE_NAME=${IMAGE_NAME}"
TAGS="${{ secrets.DOCKER_REPOSITORY }}:${IMAGE_TAG}"
TAGS="${{ vars.DOCKER_REPOSITORY }}:${IMAGE_TAG}"
if [ "latest" = "${{ matrix.spa_server_major_version }}" ] && [ "stable" = "${{ matrix.nginx }}" ]; then
TAGS="${TAGS},${{ secrets.DOCKER_REPOSITORY }}:${{ matrix.spa_server_major_version }},${{ secrets.DOCKER_REPOSITORY }}:latest"
TAGS="${TAGS},${{ vars.DOCKER_REPOSITORY }}:${{ matrix.spa_server_major_version }},${{ vars.DOCKER_REPOSITORY }}:latest"
fi
echo "::set-output name=tags::${TAGS}"
echo "TAGS=${TAGS}"
Expand Down
110 changes: 56 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
# Single Page Application Server

![Update Docker Images](https://github.com/codecentric/single-page-application-server/workflows/Update%20Docker%20Images/badge.svg)

This image can be used as a base image for single page applications. It is itself based on Nginx.
This container image provides a base for serving Single Page Applications (SPAs), leveraging Nginx as its web server.

For a fitting Helm Chart see [chart/README.md](https://github.com/codecentric/single-page-application-server/blob/master/chart/README.md).

## Tags

The following tags will be updated automatically with the latest nginx base image on a weekly basis:
The following tags are updated automatically on a weekly basis with the latest Nginx base image:

* `latest` (alias for `1-nginx-stable-alpine`)
* `1` (alias for `1-nginx-stable-alpine`)
Expand All @@ -14,54 +17,73 @@ The following tags will be updated automatically with the latest nginx base imag
* `latest-nginx-mainline-alpine` (alias for `1-nginx-mainline-alpine`)
* `1-nginx-mainline-alpine`

There will also be tags for specific versions.
Additional tags for specific Nginx versions are also available.

## Examples

Examples for usage with Angular and React are located in the `examples` directory.

## General Features

* Any non-existing routes return the root `index.html`
* Does NOT apply to resource routes with filename extensions such as: `js | css | ico | pdf | flv | jpg | jpeg | png | gif | swf`
* Configure application dynamically at container startup
* Configure base element
* Support environment specific configuration depending on the requested host (port and domain)
* Hashed resources are cached indefinitely by the browser without revalidating
* Resource name needs to look like `my-script.3f8a240b.js` or `my-script.3f8a240b.chunk.js`
* Hash needs to consist of at least 8 characters
* Applies to resources with filename extensions such as: `js | css | ico | pdf | flv | jpg | jpeg | png | gif | swf`
* HTTP 2 is enabled by default for HTTPS connections
- **SPA Routes Handling**: Routes not matching static files will serve `index.html`, with exceptions for resources like `.js`, `.css`, etc.
- **Dynamic Configuration**: Configure applications at container startup.
- **Environment-Specific Config**: Customize settings based on port and domain.
- **Resource Caching**: Hashed resources are cached indefinitely. Resources must include a hash of at least 8 characters.
- **HTTP/2**: Enabled by default for HTTPS connections.
- **Helm Chart**: A general [Helm chart](https://github.com/codecentric/single-page-application-server/blob/master/chart/README.md) is available for applications using this image.

## Security Features

* Restrictive Content Security Policy by default
* Server API endpoints can be whitelisted automatically
* Referrer is disabled by default
* Content Type Sniffing is disabled by default
* HTTPS is enforced via HSTS by default if enabled
* HTTPS uses [recommended OWASP protocols and cipher suites](https://cheatsheetseries.owasp.org/cheatsheets/TLS_Cipher_String_Cheat_Sheet.html)
* Container runs as non-root user
* Nevertheless the server can still bind to port 80 and 443
* Serving source map files (*.js.map, *.css.map) is disabled by default
- **Content Security Policy**: Restrictive by default, with automatic whitelisting for server API endpoints.
- **Referrer Policy**: Disabled by default to prevent leakage.
- **Content Type Sniffing**: Disabled by default.
- **HTTPS**: Enforced via HSTS if enabled; uses [recommended OWASP protocols and cipher suites.](https://cheatsheetseries.owasp.org/cheatsheets/TLS_Cipher_String_Cheat_Sheet.html)
- **Non-Root User**: The container runs as a non-root user but can bind to ports 80 and 443.
- **Source Maps**: Disabled by default.
- **Read-only root filesystem**: [Supported at container runtime](#read-only-root-filesystem-support)

## Configuration

### App Directory

Copy your SPA resources to `/app/`. All resources in this directory will be served by the Nginx server.
Place your SPA resources in `/app/`. All files in this directory will be served by Nginx.

### YAML Configuration

The application container is configured via YAML files at startup time.
Configure the application through YAML files at startup:

If you need to configure some default settings for your application image, you can add a default configuration file to `/config/default.yaml`.
1. **Default Configuration**: Add a default configuration file to `/config/default.yaml`. Usually added during `docker build`.
2. **Runtime Configuration**: Mount a runtime configuration file at `/config/config.yaml`. This file will override default settings.

To configure settings at runtime, you can mount your runtime configuration file at `/config/config.yaml`. Every specified setting in this file will override the default setting.
#### Example Configuration

It is also possible to merge multiple configuration files by specifying the `CONFIG_FILES` environment variable like `CONFIG_FILES="file:///config/config1.yaml|file:///config/config2.yaml"`. The configuration options, specified in the first configuration file in that variable, will have priority over the options in later declared files.
```yaml
default:
spa_config:
appTitle: "My Application"
endpoints:
api: "https://api.example.com"
```
You can also define host-specific configurations:
```yaml
default:
spa_config:
appTitle: "My Default Title"
endpoints:
api: "https://api.example.com"
special_host:
server_names:
- "special.example.com"
spa_config:
appTitle: "My Domain-specific Title"
```
#### Configuration Reference
The following configuration shows the default values of this base image for every available setting:
```yaml
default:
# Specifies to which host names this configuration should apply.
Expand Down Expand Up @@ -132,40 +154,20 @@ default:
style-src: "'self'"
```
Aside to the `default` configuration block, you can also define other blocks, which might define configuration settings for special hosts. The non-default blocks will inherit the configured settings of the default-block if they are not explicitly redeclared.

Example:
```yaml
default:
spa_config:
appTitle: "My Default Application"
endpoints:
globalApi: "https://api.example.com"
special_host:
server_names:
- "special.example.com"
spa_config:
appTitle: "My Special Application"
```

With this configuration the application would have the app title "My Special Application", when it is accessed via the host `special.example.com`, while the endpoints would stay the same in every instance of the application.

## Read-only Root Filesystem Support
It is recommended to use a read-only root filesystem when running containers. However, the following directories must remain writable when using this base image:
For security, use a read-only root filesystem. Ensure the following directories are writable:
* `/config/.out`
* This base image generates files in this directory at startup.
* `/tmp`
* Nginx uses this directory to manage cached files and the nginx.pid file. For more information, see [nginxinc/docker-nginx-unprivileged#troubleshooting-tips](https://github.com/nginxinc/docker-nginx-unprivileged/tree/af6e325d35e6833af9cdda8493866b88649e8aaf?tab=readme-ov-file#troubleshooting-tips).
* `/config/.out`: Used for file generation.
* `/tmp`: Used by Nginx for cached files and `nginx.pid`.

It is possible to mount these directories as writable volumes. When using Kubernetes, one solution is to mount `emptyDir` volumes at these mount points.
When using Kubernetes, consider mounting these directories as writable volumes with `emptyDir`.

## Development

Configuration files are dynamically generated via [gomplate templates](https://docs.gomplate.ca/).

Tests are written in Java using [Testcontainers](https://www.testcontainers.org/).
* **Configuration Generation**: Uses [gomplate templates](https://docs.gomplate.ca/).
* **Image Tests**: Written in Java using [Testcontainers](https://www.testcontainers.org/).
* **Helm Chart Tests**: Uses the [helm-unittest](https://github.com/helm-unittest/helm-unittest) Helm plugin.

## License

Expand Down
26 changes: 26 additions & 0 deletions chart/.helmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/
# Testing
ci
tests
5 changes: 5 additions & 0 deletions chart/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
apiVersion: v2
name: single-page-application-server
description: A Helm chart for deployment of applications which are using the codecentric/single-page-application-server base image.
type: application
version: 0.0.0
Loading

0 comments on commit 54db926

Please sign in to comment.