Release gohoarder 0.0.1

This commit is contained in:
github-actions[bot]
2026-01-02 23:23:54 +00:00
parent 15cc66024a
commit b163660f05
18 changed files with 2137 additions and 1 deletions
+22
View File
@@ -0,0 +1,22 @@
apiVersion: v2
name: gohoarder
description: A universal package cache proxy supporting npm, PyPI, and Go modules with security scanning
type: application
version: 0.0.1
appVersion: "0.0.1"
keywords:
- package-manager
- cache
- proxy
- npm
- pypi
- go-modules
- security
- vulnerability-scanning
home: https://github.com/lukaszraczylo/gohoarder
sources:
- https://github.com/lukaszraczylo/gohoarder
maintainers:
- name: Lukasz Raczylo
email: lukasz@raczylo.com
icon: https://raw.githubusercontent.com/lukaszraczylo/gohoarder/main/docs/logo.png
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Lukasz Raczylo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+499
View File
@@ -0,0 +1,499 @@
# GoHoarder Helm Chart
A universal package cache proxy supporting npm, PyPI, and Go modules with integrated security scanning.
## Features
- **Multi-Registry Support**: Proxy for npm, PyPI, and Go modules
- **Security Scanning**: Integrated vulnerability scanning with multiple scanners
- **Flexible Storage**: Support for filesystem, S3, and SMB storage backends
- **Metadata Storage**: SQLite or PostgreSQL for metadata
- **Auto-Configuration**: Generates configuration from Helm values
- **Production Ready**: Includes health checks, resource limits, and security contexts
## Prerequisites
- Kubernetes 1.19+
- Helm 3.0+
- PV provisioner support in the underlying infrastructure (for persistent storage)
## Installation
### Add Helm Repository
```bash
helm repo add gohoarder https://lukaszraczylo.github.io/gohoarder
helm repo update
```
### Install Chart
```bash
# Install with default values
helm install gohoarder gohoarder/gohoarder
# Install with custom values
helm install gohoarder gohoarder/gohoarder -f values.yaml
# Install in a specific namespace
helm install gohoarder gohoarder/gohoarder -n gohoarder --create-namespace
```
## Quick Start Examples
### Minimal Installation
```bash
helm install gohoarder gohoarder/gohoarder \
--set global.domain=example.com \
--set ingress.enabled=true
```
### With Security Scanning
```bash
helm install gohoarder gohoarder/gohoarder \
--set security.enabled=true \
--set security.scanners.trivy.enabled=true \
--set security.scanners.osv.enabled=true
```
### With S3 Storage
```bash
helm install gohoarder gohoarder/gohoarder \
--set storage.backend=s3 \
--set storage.s3.bucket=my-bucket \
--set storage.s3.region=us-east-1 \
--set storage.s3.accessKeyId=AKIAIOSFODNN7EXAMPLE \
--set storage.s3.secretAccessKey=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
```
### With Private Container Registry
If using images from a private registry, create an image pull secret and reference it:
```bash
# Create a Docker registry secret
kubectl create secret docker-registry ghcr-secret \
--docker-server=ghcr.io \
--docker-username=<your-username> \
--docker-password=<your-token> \
--docker-email=<your-email> \
-n gohoarder
# Install with the secret
helm install gohoarder gohoarder/gohoarder \
--set global.imagePullSecrets[0].name=ghcr-secret \
-n gohoarder
```
Or using a values file to reference existing secrets:
```yaml
global:
imagePullSecrets:
- name: ghcr-secret
- name: dockerhub-secret # Multiple secrets supported
```
**Auto-create secrets** (chart will create them for you):
```yaml
imageCredentials:
ghcr-secret:
registry: ghcr.io
username: myusername
password: mytoken
email: myemail@example.com
global:
imagePullSecrets:
- name: ghcr-secret
```
> **Note**: Storing credentials in values files is less secure than creating secrets manually. Consider using external secret management solutions like Sealed Secrets or External Secrets Operator for production.
## Configuration Methods
GoHoarder supports two configuration methods that can be used together:
### 1. ConfigMap (Default)
The chart automatically generates a `config.yaml` from Helm values and mounts it as a ConfigMap. This is the default approach and works out of the box.
### 2. Environment Variables
You can override any configuration using environment variables with the format `GOHOARDER_<CONFIG_KEY>` where dots are replaced with underscores.
**Example using values file:**
```yaml
server:
env:
- name: GOHOARDER_STORAGE_BACKEND
value: "s3"
- name: GOHOARDER_STORAGE_S3_BUCKET
value: "my-bucket"
# Reference secrets for sensitive data
- name: GOHOARDER_STORAGE_S3_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: aws-credentials
key: secret-access-key
- name: GOHOARDER_METADATA_POSTGRESQL_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secret
key: password
```
**Example using command line:**
```bash
helm install gohoarder gohoarder/gohoarder \
--set server.env[0].name=GOHOARDER_STORAGE_BACKEND \
--set server.env[0].value=s3 \
--set server.env[1].name=GOHOARDER_LOGGING_LEVEL \
--set server.env[1].value=debug
```
**Benefits of environment variables:**
- Better integration with Kubernetes secrets
- Override specific values without modifying ConfigMap
- Support for secret references (no plain-text passwords)
- Compatible with external secret management (External Secrets Operator, Sealed Secrets)
**Common environment variable mappings:**
| Config Path | Environment Variable |
|-------------|---------------------|
| `storage.backend` | `GOHOARDER_STORAGE_BACKEND` |
| `storage.s3.bucket` | `GOHOARDER_STORAGE_S3_BUCKET` |
| `storage.s3.region` | `GOHOARDER_STORAGE_S3_REGION` |
| `storage.s3.access_key_id` | `GOHOARDER_STORAGE_S3_ACCESS_KEY_ID` |
| `storage.s3.secret_access_key` | `GOHOARDER_STORAGE_S3_SECRET_ACCESS_KEY` |
| `metadata.backend` | `GOHOARDER_METADATA_BACKEND` |
| `metadata.postgresql.host` | `GOHOARDER_METADATA_POSTGRESQL_HOST` |
| `metadata.postgresql.password` | `GOHOARDER_METADATA_POSTGRESQL_PASSWORD` |
| `security.enabled` | `GOHOARDER_SECURITY_ENABLED` |
| `security.scanners.trivy.enabled` | `GOHOARDER_SECURITY_SCANNERS_TRIVY_ENABLED` |
| `logging.level` | `GOHOARDER_LOGGING_LEVEL` |
| `logging.format` | `GOHOARDER_LOGGING_FORMAT` |
## Configuration Reference
The following table lists the configurable parameters and their default values.
### Global Parameters
| Parameter | Description | Default |
|-----------|-------------|---------|
| `nameOverride` | Override the name of the chart | `""` |
| `fullnameOverride` | Override the full name of the chart | `""` |
| `global.domain` | Base domain for the deployment | `gohoarder.local` |
| `global.imagePullSecrets` | Image pull secrets (reference existing) | `[]` |
| `imageCredentials` | Auto-create image pull secrets from credentials | `{}` |
### Replica Count
| Parameter | Description | Default |
|-----------|-------------|---------|
| `replicaCount.server` | Number of server replicas | `1` |
| `replicaCount.frontend` | Number of frontend replicas | `1` |
| `replicaCount.scanner` | Number of scanner replicas | `1` |
### Image Configuration
| Parameter | Description | Default |
|-----------|-------------|---------|
| `image.server.repository` | Server image repository | `ghcr.io/lukaszraczylo/gohoarder-server` |
| `image.server.tag` | Server image tag | `latest` |
| `image.server.pullPolicy` | Server image pull policy | `IfNotPresent` |
| `image.frontend.repository` | Frontend image repository | `ghcr.io/lukaszraczylo/gohoarder-frontend` |
| `image.frontend.tag` | Frontend image tag | `latest` |
| `image.frontend.pullPolicy` | Frontend image pull policy | `IfNotPresent` |
| `image.scanner.repository` | Scanner image repository | `ghcr.io/lukaszraczylo/gohoarder-scanner` |
| `image.scanner.tag` | Scanner image tag | `latest` |
| `image.scanner.pullPolicy` | Scanner image pull policy | `IfNotPresent` |
### Environment Variables
| Parameter | Description | Default |
|-----------|-------------|---------|
| `server.env` | Additional environment variables for server | `[]` |
| `frontend.env` | Additional environment variables for frontend | `[]` |
| `scanner.env` | Additional environment variables for scanner | `[]` |
### Storage Configuration
| Parameter | Description | Default |
|-----------|-------------|---------|
| `storage.backend` | Storage backend (filesystem, s3, smb) | `filesystem` |
| `storage.filesystem.storageClass` | Storage class for PVC | `""` |
| `storage.filesystem.size` | Storage size | `100Gi` |
| `storage.filesystem.useHostPath` | Use hostPath instead of PVC | `false` |
| `storage.filesystem.hostPath` | Host path for storage | `/var/lib/gohoarder` |
| `storage.s3.endpoint` | S3 endpoint | `s3.amazonaws.com` |
| `storage.s3.bucket` | S3 bucket name | `gohoarder-cache` |
| `storage.s3.region` | S3 region | `us-east-1` |
### Metadata Configuration
| Parameter | Description | Default |
|-----------|-------------|---------|
| `metadata.backend` | Metadata backend (sqlite, postgresql) | `sqlite` |
| `metadata.sqlite.persistence.enabled` | Enable persistence for SQLite | `true` |
| `metadata.sqlite.persistence.size` | SQLite storage size | `10Gi` |
| `metadata.postgresql.host` | PostgreSQL host | `localhost` |
| `metadata.postgresql.database` | PostgreSQL database | `gohoarder` |
### Security Configuration
| Parameter | Description | Default |
|-----------|-------------|---------|
| `security.enabled` | Enable security scanning | `false` |
| `security.blockOnSeverity` | Block packages on severity | `high` |
| `security.scanners.trivy.enabled` | Enable Trivy scanner | `false` |
| `security.scanners.osv.enabled` | Enable OSV scanner | `false` |
| `security.scanners.grype.enabled` | Enable Grype scanner | `false` |
### Authentication
| Parameter | Description | Default |
|-----------|-------------|---------|
| `auth.enabled` | Enable authentication | `true` |
| `auth.adminApiKey` | Admin API key (auto-generated if empty) | `""` |
| `auth.existingSecret` | Use existing secret for admin key | `""` |
### Ingress
| Parameter | Description | Default |
|-----------|-------------|---------|
| `ingress.enabled` | Enable ingress | `false` |
| `ingress.className` | Ingress class name | `nginx` |
| `ingress.frontend.enabled` | Enable frontend ingress | `true` |
| `ingress.frontend.host` | Frontend hostname | `gohoarder.local` |
| `ingress.frontend.tls.enabled` | Enable TLS for frontend | `false` |
## High Availability & Scaling
### Running Multiple Server Replicas
GoHoarder can run with multiple server replicas for high availability and load distribution, but the configuration must be set correctly to avoid data inconsistency.
#### ✅ Compatible Configurations (Safe for Multiple Replicas)
**Storage:**
-**S3** - Fully compatible, recommended for production HA setups
-**SMB** - Compatible, shared network storage
-**Filesystem with RWX** - Compatible when using ReadWriteMany storage classes
- ✅ Examples: Longhorn RWX, NFS, CephFS, GlusterFS, Azure Files
- ✅ Uses atomic rename operations for safe concurrent writes
- ✅ Packages are static/immutable - perfect for shared storage
- ❌ Not compatible with local storage or ReadWriteOnce (RWO) PVCs
**Metadata:**
-**PostgreSQL** - Fully compatible, handles concurrent writes, recommended for HA
- ⚠️ **SQLite** - Limited compatibility:
- Uses WAL mode which supports concurrent reads
- Multiple writers can cause lock contention
- Works but may have performance issues under high concurrency
- Only if using shared storage (NFS, etc.)
#### 📋 Recommended HA Configurations
**Option 1: Cloud Storage (S3)**
Best for cloud deployments, object storage:
```yaml
replicaCount:
server: 3
storage:
backend: s3
s3:
endpoint: s3.amazonaws.com
region: us-east-1
bucket: gohoarder-cache
metadata:
backend: postgresql
postgresql:
host: postgres.database.svc.cluster.local
database: gohoarder
podDisruptionBudget:
enabled: true
minAvailable: 1
```
**Option 2: Shared Filesystem (Longhorn/NFS)**
Best for on-premises or self-hosted Kubernetes:
```yaml
replicaCount:
server: 3
storage:
backend: filesystem
filesystem:
# Use RWX storage class (Longhorn, NFS, CephFS, etc.)
storageClass: "longhorn" # or "nfs-client", "cephfs", etc.
size: "500Gi"
accessMode: "ReadWriteMany" # RWX - Critical for multiple replicas!
metadata:
backend: postgresql # Or SQLite with RWX storage
postgresql:
host: postgres.database.svc.cluster.local
database: gohoarder
podDisruptionBudget:
enabled: true
minAvailable: 1
```
**Why Filesystem with RWX Works:**
- Packages are immutable once cached (static files)
- Filesystem backend uses atomic `rename()` operations
- Race condition safe: If two replicas cache same package, one wins
- Performance: Local filesystem often faster than object storage for reads
#### ⚠️ What Won't Work with Multiple Replicas
**Filesystem storage with local volumes:**
```yaml
# ❌ DON'T DO THIS with multiple replicas
storage:
backend: filesystem
filesystem:
useHostPath: true # Each replica gets different storage
```
**SQLite with local storage:**
```yaml
# ⚠️ AVOID with multiple replicas
metadata:
backend: sqlite
sqlite:
persistence:
enabled: true # Each replica gets its own database
```
#### 🔄 How It Works
**Request Deduplication:**
- Single replica: Uses `singleflight` to prevent duplicate upstream fetches
- Multiple replicas: Each replica may fetch the same package independently
- **Mitigation**: Package metadata in shared database prevents duplicate downloads once one replica completes
**Cache Consistency:**
- Storage backend (S3/SMB) ensures all replicas see the same cached packages
- Metadata database ensures consistent package information across replicas
- First replica to cache a package wins, others will use the cached version
**Session Affinity:**
- Not required - GoHoarder is stateless
- Load balancer can distribute requests randomly
**Scanner Replicas:**
- Scanner can run as a single replica or multiple
- If multiple scanners enabled, they share work through the metadata database
- Package scans are deduplicated via database state
#### 🔬 Technical Details: Concurrent Write Safety
**Filesystem Backend with RWX Storage:**
The filesystem storage backend uses a **temp-file + atomic rename** pattern:
```go
1. Write package to: /cache/npm/package@1.0.0.tmp
2. Calculate checksums (MD5, SHA256)
3. Atomic rename: .tmp /cache/npm/package@1.0.0
```
**Why this is safe for concurrent writes:**
- `os.Rename()` is atomic on POSIX filesystems
- If two replicas cache the same package simultaneously:
- Both write to separate `.tmp` files
- Both attempt atomic rename
- One succeeds, one gets "file exists" error
- Result: Same file content, no corruption
**Package immutability:**
- Packages are versioned and immutable (npm/pypi/go semantics)
- Same package@version always has identical content
- Concurrent writes produce identical results
- No risk of partial/corrupted files
**Quota tracking:**
- Per-process mutex (minor inaccuracy across replicas)
- Conservative: May undercount slightly
- Not critical for operation
## Uninstallation
```bash
helm uninstall gohoarder -n gohoarder
```
## Upgrading
```bash
helm upgrade gohoarder gohoarder/gohoarder -f values.yaml
```
## Package Manager Configuration
After installation, configure your package managers to use GoHoarder:
### NPM
```bash
npm config set registry http://<gohoarder-url>/npm/
```
### Go
```bash
export GOPROXY=http://<gohoarder-url>/go,direct
```
### PyPI
```bash
pip config set global.index-url http://<gohoarder-url>/pypi/simple
```
## Troubleshooting
### Check Pod Status
```bash
kubectl get pods -n gohoarder
kubectl logs -n gohoarder <pod-name>
```
### Verify Configuration
```bash
kubectl get configmap -n gohoarder <release-name>-gohoarder-config -o yaml
```
### Get Admin API Key
```bash
kubectl get secret -n gohoarder <release-name>-gohoarder-auth -o jsonpath='{.data.admin-api-key}' | base64 -d
```
## Contributing
Contributions are welcome! Please visit [GitHub](https://github.com/lukaszraczylo/gohoarder) for more information.
## License
See the [LICENSE](https://github.com/lukaszraczylo/gohoarder/blob/main/LICENSE) file.
+70
View File
@@ -0,0 +1,70 @@
** GoHoarder has been installed! **
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- if .Values.ingress.frontend.enabled }}
http{{ if .Values.ingress.frontend.tls.enabled }}s{{ end }}://{{ .Values.ingress.frontend.host | default (printf "%s.%s" "gohoarder" .Values.global.domain) }}
{{- end }}
{{- else if contains "NodePort" .Values.frontend.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "gohoarder.fullname" . }}-frontend)
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.frontend.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "gohoarder.fullname" . }}-frontend'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "gohoarder.fullname" . }}-frontend --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.frontend.service.port }}
{{- else if contains "ClusterIP" .Values.frontend.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "gohoarder.name" . }},app.kubernetes.io/instance={{ .Release.Name }},app.kubernetes.io/component=frontend" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
{{- end }}
2. Admin API Key:
{{- if .Values.auth.enabled }}
{{- if .Values.auth.existingSecret }}
The admin API key is stored in the existing secret: {{ .Values.auth.existingSecret }}
To retrieve it:
kubectl get secret {{ .Values.auth.existingSecret }} -n {{ .Release.Namespace }} -o jsonpath='{.data.{{ .Values.auth.secretKey }}}' | base64 -d
{{- else if .Values.auth.adminApiKey }}
The admin API key you provided: {{ .Values.auth.adminApiKey }}
{{- else }}
A random admin API key has been generated. To retrieve it:
kubectl get secret {{ include "gohoarder.fullname" . }}-auth -n {{ .Release.Namespace }} -o jsonpath='{.data.{{ .Values.auth.secretKey }}}' | base64 -d
{{- end }}
{{- else }}
Authentication is disabled.
{{- end }}
3. Configuration:
- Storage backend: {{ .Values.storage.backend }}
- Metadata backend: {{ .Values.metadata.backend }}
- Security scanning: {{ if .Values.security.enabled }}enabled{{ else }}disabled{{ end }}
{{- if .Values.security.enabled }}
- Active scanners:
{{- range $scanner, $config := .Values.security.scanners }}
{{- if $config.enabled }}
* {{ $scanner }}
{{- end }}
{{- end }}
{{- end }}
4. Package Proxies:
Configure your package managers to use GoHoarder:
NPM:
npm config set registry http://{{ include "gohoarder.fullname" . }}-server.{{ .Release.Namespace }}.svc.cluster.local/npm/
Go:
export GOPROXY=http://{{ include "gohoarder.fullname" . }}-server.{{ .Release.Namespace }}.svc.cluster.local/go,direct
PyPI:
pip config set global.index-url http://{{ include "gohoarder.fullname" . }}-server.{{ .Release.Namespace }}.svc.cluster.local/pypi/simple
5. Health Checks:
- Server health: http://{{ include "gohoarder.fullname" . }}-server.{{ .Release.Namespace }}.svc.cluster.local/health
- Server ready: http://{{ include "gohoarder.fullname" . }}-server.{{ .Release.Namespace }}.svc.cluster.local/health/ready
For more information, visit: https://github.com/lukaszraczylo/gohoarder
+174
View File
@@ -0,0 +1,174 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "gohoarder.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
*/}}
{{- define "gohoarder.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "gohoarder.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "gohoarder.labels" -}}
helm.sh/chart: {{ include "gohoarder.chart" . }}
{{ include "gohoarder.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "gohoarder.selectorLabels" -}}
app.kubernetes.io/name: {{ include "gohoarder.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Server labels
*/}}
{{- define "gohoarder.server.labels" -}}
{{ include "gohoarder.labels" . }}
app.kubernetes.io/component: server
{{- end }}
{{/*
Server selector labels
*/}}
{{- define "gohoarder.server.selectorLabels" -}}
{{ include "gohoarder.selectorLabels" . }}
app.kubernetes.io/component: server
{{- end }}
{{/*
Frontend labels
*/}}
{{- define "gohoarder.frontend.labels" -}}
{{ include "gohoarder.labels" . }}
app.kubernetes.io/component: frontend
{{- end }}
{{/*
Frontend selector labels
*/}}
{{- define "gohoarder.frontend.selectorLabels" -}}
{{ include "gohoarder.selectorLabels" . }}
app.kubernetes.io/component: frontend
{{- end }}
{{/*
Scanner labels
*/}}
{{- define "gohoarder.scanner.labels" -}}
{{ include "gohoarder.labels" . }}
app.kubernetes.io/component: scanner
{{- end }}
{{/*
Scanner selector labels
*/}}
{{- define "gohoarder.scanner.selectorLabels" -}}
{{ include "gohoarder.selectorLabels" . }}
app.kubernetes.io/component: scanner
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "gohoarder.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "gohoarder.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
{{/*
Generate admin API key
*/}}
{{- define "gohoarder.adminApiKey" -}}
{{- if .Values.auth.adminApiKey }}
{{- .Values.auth.adminApiKey }}
{{- else }}
{{- randAlphaNum 32 }}
{{- end }}
{{- end }}
{{/*
Storage volume configuration
*/}}
{{- define "gohoarder.storageVolume" -}}
{{- if eq .Values.storage.backend "filesystem" }}
{{- if .Values.storage.filesystem.useHostPath }}
- name: storage
hostPath:
path: {{ .Values.storage.filesystem.hostPath }}
type: DirectoryOrCreate
{{- else if .Values.storage.filesystem.existingClaim }}
- name: storage
persistentVolumeClaim:
claimName: {{ .Values.storage.filesystem.existingClaim }}
{{- else }}
- name: storage
persistentVolumeClaim:
claimName: {{ include "gohoarder.fullname" . }}-storage
{{- end }}
{{- else }}
- name: storage
emptyDir: {}
{{- end }}
{{- end }}
{{/*
Metadata volume configuration
*/}}
{{- define "gohoarder.metadataVolume" -}}
{{- if and (eq .Values.metadata.backend "sqlite") .Values.metadata.sqlite.persistence.enabled }}
{{- if .Values.metadata.sqlite.persistence.existingClaim }}
- name: metadata
persistentVolumeClaim:
claimName: {{ .Values.metadata.sqlite.persistence.existingClaim }}
{{- else }}
- name: metadata
persistentVolumeClaim:
claimName: {{ include "gohoarder.fullname" . }}-metadata
{{- end }}
{{- else }}
- name: metadata
emptyDir: {}
{{- end }}
{{- end }}
{{/*
Trivy cache volume configuration
*/}}
{{- define "gohoarder.trivyCacheVolume" -}}
{{- if .Values.security.scanners.trivy.enabled }}
- name: trivy-cache
emptyDir: {}
{{- end }}
{{- end }}
+168
View File
@@ -0,0 +1,168 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "gohoarder.fullname" . }}-config
labels:
{{- include "gohoarder.labels" . | nindent 4 }}
data:
config.yaml: |
server:
host: {{ .Values.server.host | quote }}
port: {{ .Values.server.port }}
read_timeout: {{ .Values.server.readTimeout | quote }}
write_timeout: {{ .Values.server.writeTimeout | quote }}
idle_timeout: {{ .Values.server.idleTimeout | quote }}
tls:
enabled: false
storage:
backend: {{ .Values.storage.backend | quote }}
{{- if eq .Values.storage.backend "filesystem" }}
path: "/var/cache/gohoarder"
filesystem:
base_path: "/var/cache/gohoarder"
{{- else if eq .Values.storage.backend "s3" }}
s3:
endpoint: {{ .Values.storage.s3.endpoint | quote }}
region: {{ .Values.storage.s3.region | quote }}
bucket: {{ .Values.storage.s3.bucket | quote }}
{{- if .Values.storage.s3.existingSecret }}
access_key_id: "${S3_ACCESS_KEY_ID}"
secret_access_key: "${S3_SECRET_ACCESS_KEY}"
{{- else }}
access_key_id: {{ .Values.storage.s3.accessKeyId | quote }}
secret_access_key: {{ .Values.storage.s3.secretAccessKey | quote }}
{{- end }}
use_ssl: {{ .Values.storage.s3.useSSL }}
{{- else if eq .Values.storage.backend "smb" }}
smb:
host: {{ .Values.storage.smb.host | quote }}
share: {{ .Values.storage.smb.share | quote }}
{{- if .Values.storage.smb.existingSecret }}
username: "${SMB_USERNAME}"
password: "${SMB_PASSWORD}"
{{- else }}
username: {{ .Values.storage.smb.username | quote }}
password: {{ .Values.storage.smb.password | quote }}
{{- end }}
domain: {{ .Values.storage.smb.domain | quote }}
{{- end }}
metadata:
backend: {{ .Values.metadata.backend | quote }}
{{- if eq .Values.metadata.backend "sqlite" }}
connection: "file:/var/lib/gohoarder/metadata/gohoarder.db?cache=shared&mode=rwc"
sqlite:
path: "/var/lib/gohoarder/metadata/gohoarder.db"
wal_mode: {{ .Values.metadata.sqlite.walMode }}
{{- else if eq .Values.metadata.backend "postgresql" }}
postgresql:
host: {{ .Values.metadata.postgresql.host | quote }}
port: {{ .Values.metadata.postgresql.port }}
database: {{ .Values.metadata.postgresql.database | quote }}
{{- if .Values.metadata.postgresql.existingSecret }}
user: "${POSTGRES_USER}"
password: "${POSTGRES_PASSWORD}"
{{- else }}
user: {{ .Values.metadata.postgresql.username | quote }}
password: {{ .Values.metadata.postgresql.password | quote }}
{{- end }}
ssl_mode: {{ .Values.metadata.postgresql.sslMode | quote }}
{{- end }}
cache:
default_ttl: {{ .Values.cache.defaultTTL | quote }}
cleanup_interval: {{ .Values.cache.cleanupInterval | quote }}
max_size_bytes: {{ .Values.cache.maxSizeBytes }}
per_project_quota: {{ .Values.cache.perProjectQuota }}
ttl_overrides:
{{- range $key, $value := .Values.cache.ttlOverrides }}
{{ $key }}: {{ $value | quote }}
{{- end }}
security:
enabled: {{ .Values.security.enabled }}
block_on_severity: {{ .Values.security.blockOnSeverity | quote }}
scan_on_download: {{ .Values.security.scanOnDownload }}
rescan_interval: {{ .Values.security.rescanInterval | quote }}
update_db_on_startup: {{ .Values.security.updateDbOnStartup }}
block_thresholds:
critical: {{ .Values.security.blockThresholds.critical }}
high: {{ .Values.security.blockThresholds.high }}
medium: {{ .Values.security.blockThresholds.medium }}
low: {{ .Values.security.blockThresholds.low }}
scanners:
trivy:
enabled: {{ .Values.security.scanners.trivy.enabled }}
timeout: {{ .Values.security.scanners.trivy.timeout | quote }}
cache_db: {{ .Values.security.scanners.trivy.cacheDb | quote }}
osv:
enabled: {{ .Values.security.scanners.osv.enabled }}
api_url: {{ .Values.security.scanners.osv.apiUrl | quote }}
timeout: {{ .Values.security.scanners.osv.timeout | quote }}
grype:
enabled: {{ .Values.security.scanners.grype.enabled }}
timeout: {{ .Values.security.scanners.grype.timeout | quote }}
govulncheck:
enabled: {{ .Values.security.scanners.govulncheck.enabled }}
timeout: {{ .Values.security.scanners.govulncheck.timeout | quote }}
npm_audit:
enabled: {{ .Values.security.scanners.npmAudit.enabled }}
timeout: {{ .Values.security.scanners.npmAudit.timeout | quote }}
pip_audit:
enabled: {{ .Values.security.scanners.pipAudit.enabled }}
timeout: {{ .Values.security.scanners.pipAudit.timeout | quote }}
ghsa:
enabled: {{ .Values.security.scanners.ghsa.enabled }}
timeout: {{ .Values.security.scanners.ghsa.timeout | quote }}
{{- if or .Values.security.scanners.ghsa.token .Values.security.scanners.ghsa.existingSecret }}
token: "${GHSA_TOKEN}"
{{- end }}
static:
enabled: {{ .Values.security.scanners.static.enabled }}
max_package_size: {{ .Values.security.scanners.static.maxPackageSize }}
check_checksums: {{ .Values.security.scanners.static.checkChecksums }}
block_suspicious: {{ .Values.security.scanners.static.blockSuspicious }}
auth:
enabled: {{ .Values.auth.enabled }}
key_expiration: {{ .Values.auth.keyExpiration | quote }}
bcrypt_cost: {{ .Values.auth.bcryptCost }}
audit_log: {{ .Values.auth.auditLog }}
network:
connect_timeout: {{ .Values.network.connectTimeout | quote }}
read_timeout: {{ .Values.network.readTimeout | quote }}
write_timeout: {{ .Values.network.writeTimeout | quote }}
max_idle_conns: {{ .Values.network.maxIdleConns }}
max_conns_per_host: {{ .Values.network.maxConnsPerHost }}
rate_limit:
per_api_key: {{ .Values.network.rateLimit.perApiKey }}
per_ip: {{ .Values.network.rateLimit.perIp }}
burst_size: {{ .Values.network.rateLimit.burstSize }}
circuit_breaker:
threshold: {{ .Values.network.circuitBreaker.threshold }}
timeout: {{ .Values.network.circuitBreaker.timeout | quote }}
reset_interval: {{ .Values.network.circuitBreaker.resetInterval | quote }}
retry:
max_attempts: {{ .Values.network.retry.maxAttempts }}
initial_backoff: {{ .Values.network.retry.initialBackoff | quote }}
max_backoff: {{ .Values.network.retry.maxBackoff | quote }}
logging:
level: {{ .Values.logging.level | quote }}
format: {{ .Values.logging.format | quote }}
handlers:
go:
enabled: {{ .Values.handlers.go.enabled }}
upstream_proxy: {{ .Values.handlers.go.upstreamProxy | quote }}
checksum_db: {{ .Values.handlers.go.checksumDb | quote }}
verify_checksums: {{ .Values.handlers.go.verifyChecksums }}
npm:
enabled: {{ .Values.handlers.npm.enabled }}
upstream_registry: {{ .Values.handlers.npm.upstreamRegistry | quote }}
pypi:
enabled: {{ .Values.handlers.pypi.enabled }}
upstream_url: {{ .Values.handlers.pypi.upstreamUrl | quote }}
simple_api_url: {{ .Values.handlers.pypi.simpleApiUrl | quote }}
@@ -0,0 +1,85 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "gohoarder.fullname" . }}-frontend
labels:
{{- include "gohoarder.frontend.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount.frontend }}
{{- end }}
selector:
matchLabels:
{{- include "gohoarder.frontend.selectorLabels" . | nindent 6 }}
template:
metadata:
annotations:
{{- with .Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "gohoarder.frontend.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.global.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "gohoarder.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: frontend
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: false
image: "{{ .Values.image.frontend.repository }}:{{ .Values.image.frontend.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.frontend.pullPolicy }}
ports:
- name: http
containerPort: 80
protocol: TCP
env:
- name: API_BASE_URL
value: {{ .Values.frontend.backendUrl | default (printf "http://%s-server:%d" (include "gohoarder.fullname" .) (.Values.server.service.port | int)) | quote }}
- name: APP_VERSION
value: {{ .Chart.AppVersion | quote }}
- name: APP_NAME
value: "GoHoarder"
{{- with .Values.frontend.env }}
{{- toYaml . | nindent 8 }}
{{- end }}
livenessProbe:
{{- toYaml .Values.frontend.livenessProbe | nindent 12 }}
readinessProbe:
{{- toYaml .Values.frontend.readinessProbe | nindent 12 }}
resources:
{{- toYaml .Values.frontend.resources | nindent 12 }}
volumeMounts:
- name: tmp
mountPath: /tmp
- name: nginx-cache
mountPath: /var/cache/nginx
- name: nginx-run
mountPath: /var/run
volumes:
- name: tmp
emptyDir: {}
- name: nginx-cache
emptyDir: {}
- name: nginx-run
emptyDir: {}
{{- with .Values.frontend.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.frontend.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.frontend.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
@@ -0,0 +1,114 @@
{{- if .Values.security.enabled }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "gohoarder.fullname" . }}-scanner
labels:
{{- include "gohoarder.scanner.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount.scanner }}
selector:
matchLabels:
{{- include "gohoarder.scanner.selectorLabels" . | nindent 6 }}
template:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}
{{- with .Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "gohoarder.scanner.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.global.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "gohoarder.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
initContainers:
- name: init-permissions
image: busybox:latest
command: ['sh', '-c']
args:
- |
mkdir -p /var/cache/gohoarder /var/lib/gohoarder/metadata /tmp/gohoarder
{{- if .Values.security.scanners.trivy.enabled }}
mkdir -p {{ .Values.security.scanners.trivy.cacheDb }}
chown -R 1000:1000 {{ .Values.security.scanners.trivy.cacheDb }}
{{- end }}
chown -R 1000:1000 /var/cache/gohoarder /var/lib/gohoarder /tmp/gohoarder
chmod 750 /var/cache/gohoarder /var/lib/gohoarder
volumeMounts:
{{- include "gohoarder.storageVolume" . | nindent 8 }}
{{- include "gohoarder.metadataVolume" . | nindent 8 }}
{{- include "gohoarder.trivyCacheVolume" . | nindent 8 }}
- name: tmp
mountPath: /tmp/gohoarder
securityContext:
runAsUser: 0
containers:
- name: scanner
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.scanner.repository }}:{{ .Values.image.scanner.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.scanner.pullPolicy }}
env:
- name: CONFIG_FILE
value: /etc/gohoarder/config.yaml
{{- if and .Values.security.scanners.ghsa.enabled .Values.security.scanners.ghsa.existingSecret }}
- name: GHSA_TOKEN
valueFrom:
secretKeyRef:
name: {{ .Values.security.scanners.ghsa.existingSecret }}
key: token
{{- else if and .Values.security.scanners.ghsa.enabled .Values.security.scanners.ghsa.token }}
- name: GHSA_TOKEN
valueFrom:
secretKeyRef:
name: {{ include "gohoarder.fullname" . }}-ghsa
key: token
{{- end }}
{{- with .Values.scanner.env }}
{{- toYaml . | nindent 8 }}
{{- end }}
resources:
{{- toYaml .Values.scanner.resources | nindent 12 }}
volumeMounts:
- name: config
mountPath: /etc/gohoarder
readOnly: true
- name: storage
mountPath: /var/cache/gohoarder
- name: metadata
mountPath: /var/lib/gohoarder/metadata
{{- if .Values.security.scanners.trivy.enabled }}
- name: trivy-cache
mountPath: {{ .Values.security.scanners.trivy.cacheDb }}
{{- end }}
- name: tmp
mountPath: /tmp
volumes:
- name: config
configMap:
name: {{ include "gohoarder.fullname" . }}-config
{{- include "gohoarder.storageVolume" . | nindent 6 }}
{{- include "gohoarder.metadataVolume" . | nindent 6 }}
{{- include "gohoarder.trivyCacheVolume" . | nindent 6 }}
- name: tmp
emptyDir: {}
{{- with .Values.scanner.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.scanner.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.scanner.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- end }}
@@ -0,0 +1,194 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "gohoarder.fullname" . }}-server
labels:
{{- include "gohoarder.server.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount.server }}
{{- end }}
selector:
matchLabels:
{{- include "gohoarder.server.selectorLabels" . | nindent 6 }}
template:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}
{{- with .Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "gohoarder.server.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.global.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "gohoarder.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
initContainers:
- name: init-permissions
image: busybox:latest
command: ['sh', '-c']
args:
- |
mkdir -p /var/cache/gohoarder /var/lib/gohoarder/metadata /tmp/gohoarder
chown -R 1000:1000 /var/cache/gohoarder /var/lib/gohoarder /tmp/gohoarder
chmod 750 /var/cache/gohoarder /var/lib/gohoarder
volumeMounts:
{{- include "gohoarder.storageVolume" . | nindent 8 }}
{{- include "gohoarder.metadataVolume" . | nindent 8 }}
- name: tmp
mountPath: /tmp/gohoarder
securityContext:
runAsUser: 0
containers:
- name: server
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.server.repository }}:{{ .Values.image.server.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.server.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.server.port }}
protocol: TCP
env:
- name: CONFIG_FILE
value: /etc/gohoarder/config.yaml
{{- if and .Values.auth.enabled .Values.auth.existingSecret }}
- name: ADMIN_API_KEY
valueFrom:
secretKeyRef:
name: {{ .Values.auth.existingSecret }}
key: {{ .Values.auth.secretKey }}
{{- else if .Values.auth.enabled }}
- name: ADMIN_API_KEY
valueFrom:
secretKeyRef:
name: {{ include "gohoarder.fullname" . }}-auth
key: {{ .Values.auth.secretKey }}
{{- end }}
{{- if and (eq .Values.storage.backend "s3") .Values.storage.s3.existingSecret }}
- name: S3_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: {{ .Values.storage.s3.existingSecret }}
key: access-key-id
- name: S3_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: {{ .Values.storage.s3.existingSecret }}
key: secret-access-key
{{- else if and (eq .Values.storage.backend "s3") .Values.storage.s3.accessKeyId }}
- name: S3_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: {{ include "gohoarder.fullname" . }}-s3
key: access-key-id
- name: S3_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: {{ include "gohoarder.fullname" . }}-s3
key: secret-access-key
{{- end }}
{{- if and (eq .Values.storage.backend "smb") .Values.storage.smb.existingSecret }}
- name: SMB_USERNAME
valueFrom:
secretKeyRef:
name: {{ .Values.storage.smb.existingSecret }}
key: username
- name: SMB_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Values.storage.smb.existingSecret }}
key: password
{{- else if and (eq .Values.storage.backend "smb") .Values.storage.smb.username }}
- name: SMB_USERNAME
valueFrom:
secretKeyRef:
name: {{ include "gohoarder.fullname" . }}-smb
key: username
- name: SMB_PASSWORD
valueFrom:
secretKeyRef:
name: {{ include "gohoarder.fullname" . }}-smb
key: password
{{- end }}
{{- if and (eq .Values.metadata.backend "postgresql") .Values.metadata.postgresql.existingSecret }}
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: {{ .Values.metadata.postgresql.existingSecret }}
key: username
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Values.metadata.postgresql.existingSecret }}
key: password
{{- else if and (eq .Values.metadata.backend "postgresql") .Values.metadata.postgresql.username }}
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: {{ include "gohoarder.fullname" . }}-postgresql
key: username
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: {{ include "gohoarder.fullname" . }}-postgresql
key: password
{{- end }}
{{- if and .Values.security.scanners.ghsa.enabled .Values.security.scanners.ghsa.existingSecret }}
- name: GHSA_TOKEN
valueFrom:
secretKeyRef:
name: {{ .Values.security.scanners.ghsa.existingSecret }}
key: token
{{- else if and .Values.security.scanners.ghsa.enabled .Values.security.scanners.ghsa.token }}
- name: GHSA_TOKEN
valueFrom:
secretKeyRef:
name: {{ include "gohoarder.fullname" . }}-ghsa
key: token
{{- end }}
{{- with .Values.server.env }}
{{- toYaml . | nindent 8 }}
{{- end }}
livenessProbe:
{{- toYaml .Values.server.livenessProbe | nindent 12 }}
readinessProbe:
{{- toYaml .Values.server.readinessProbe | nindent 12 }}
resources:
{{- toYaml .Values.server.resources | nindent 12 }}
volumeMounts:
- name: config
mountPath: /etc/gohoarder
readOnly: true
- name: storage
mountPath: /var/cache/gohoarder
- name: metadata
mountPath: /var/lib/gohoarder/metadata
- name: tmp
mountPath: /tmp
volumes:
- name: config
configMap:
name: {{ include "gohoarder.fullname" . }}-config
{{- include "gohoarder.storageVolume" . | nindent 6 }}
{{- include "gohoarder.metadataVolume" . | nindent 6 }}
- name: tmp
emptyDir: {}
{{- with .Values.server.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.server.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.server.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
@@ -0,0 +1,14 @@
{{- if .Values.imageCredentials }}
{{- range $name, $config := .Values.imageCredentials }}
---
apiVersion: v1
kind: Secret
metadata:
name: {{ $name }}
labels:
{{- include "gohoarder.labels" $ | nindent 4 }}
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: {{ printf "{\"auths\":{\"%s\":{\"username\":\"%s\",\"password\":\"%s\",\"email\":\"%s\",\"auth\":\"%s\"}}}" $config.registry $config.username $config.password $config.email (printf "%s:%s" $config.username $config.password | b64enc) | b64enc }}
{{- end }}
{{- end }}
+118
View File
@@ -0,0 +1,118 @@
{{- if .Values.ingress.enabled -}}
{{- if .Values.ingress.frontend.enabled -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "gohoarder.fullname" . }}-frontend
labels:
{{- include "gohoarder.frontend.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.className }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
{{- if .Values.ingress.frontend.tls.enabled }}
tls:
- hosts:
- {{ .Values.ingress.frontend.host | default (printf "%s.%s" "gohoarder" .Values.global.domain) | quote }}
secretName: {{ .Values.ingress.frontend.tls.secretName }}
{{- end }}
rules:
- host: {{ .Values.ingress.frontend.host | default (printf "%s.%s" "gohoarder" .Values.global.domain) | quote }}
http:
paths:
- path: /npm
pathType: Prefix
backend:
service:
name: {{ include "gohoarder.fullname" . }}-server
port:
number: {{ .Values.server.service.port }}
- path: /pypi
pathType: Prefix
backend:
service:
name: {{ include "gohoarder.fullname" . }}-server
port:
number: {{ .Values.server.service.port }}
- path: /go
pathType: Prefix
backend:
service:
name: {{ include "gohoarder.fullname" . }}-server
port:
number: {{ .Values.server.service.port }}
- path: /api
pathType: Prefix
backend:
service:
name: {{ include "gohoarder.fullname" . }}-server
port:
number: {{ .Values.server.service.port }}
- path: /ws
pathType: Prefix
backend:
service:
name: {{ include "gohoarder.fullname" . }}-server
port:
number: {{ .Values.server.service.port }}
- path: /health
pathType: Prefix
backend:
service:
name: {{ include "gohoarder.fullname" . }}-server
port:
number: {{ .Values.server.service.port }}
- path: /metrics
pathType: Prefix
backend:
service:
name: {{ include "gohoarder.fullname" . }}-server
port:
number: {{ .Values.server.service.port }}
- path: /
pathType: Prefix
backend:
service:
name: {{ include "gohoarder.fullname" . }}-frontend
port:
number: {{ .Values.frontend.service.port }}
{{- end }}
---
{{- if .Values.ingress.api.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "gohoarder.fullname" . }}-api
labels:
{{- include "gohoarder.server.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.className }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
{{- if .Values.ingress.api.tls.enabled }}
tls:
- hosts:
- {{ .Values.ingress.api.host | default (printf "api.%s.%s" "gohoarder" .Values.global.domain) | quote }}
secretName: {{ .Values.ingress.api.tls.secretName }}
{{- end }}
rules:
- host: {{ .Values.ingress.api.host | default (printf "api.%s.%s" "gohoarder" .Values.global.domain) | quote }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ include "gohoarder.fullname" . }}-server
port:
number: {{ .Values.server.service.port }}
{{- end }}
{{- end }}
+37
View File
@@ -0,0 +1,37 @@
{{- if and (eq .Values.storage.backend "filesystem") (not .Values.storage.filesystem.useHostPath) (not .Values.storage.filesystem.existingClaim) }}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ include "gohoarder.fullname" . }}-storage
labels:
{{- include "gohoarder.labels" . | nindent 4 }}
app.kubernetes.io/component: storage
spec:
accessModes:
- {{ .Values.storage.filesystem.accessMode }}
{{- if .Values.storage.filesystem.storageClass }}
storageClassName: {{ .Values.storage.filesystem.storageClass | quote }}
{{- end }}
resources:
requests:
storage: {{ .Values.storage.filesystem.size | quote }}
{{- end }}
---
{{- if and (eq .Values.metadata.backend "sqlite") .Values.metadata.sqlite.persistence.enabled (not .Values.metadata.sqlite.persistence.existingClaim) }}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ include "gohoarder.fullname" . }}-metadata
labels:
{{- include "gohoarder.labels" . | nindent 4 }}
app.kubernetes.io/component: metadata
spec:
accessModes:
- {{ .Values.metadata.sqlite.persistence.accessMode }}
{{- if .Values.metadata.sqlite.persistence.storageClass }}
storageClassName: {{ .Values.metadata.sqlite.persistence.storageClass | quote }}
{{- end }}
resources:
requests:
storage: {{ .Values.metadata.sqlite.persistence.size | quote }}
{{- end }}
+66
View File
@@ -0,0 +1,66 @@
{{- if and .Values.auth.enabled (not .Values.auth.existingSecret) }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "gohoarder.fullname" . }}-auth
labels:
{{- include "gohoarder.labels" . | nindent 4 }}
type: Opaque
data:
{{- if .Values.auth.adminApiKey }}
{{ .Values.auth.secretKey }}: {{ .Values.auth.adminApiKey | b64enc | quote }}
{{- else }}
{{ .Values.auth.secretKey }}: {{ include "gohoarder.adminApiKey" . | b64enc | quote }}
{{- end }}
{{- end }}
---
{{- if and (eq .Values.storage.backend "s3") (not .Values.storage.s3.existingSecret) .Values.storage.s3.accessKeyId }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "gohoarder.fullname" . }}-s3
labels:
{{- include "gohoarder.labels" . | nindent 4 }}
type: Opaque
data:
access-key-id: {{ .Values.storage.s3.accessKeyId | b64enc | quote }}
secret-access-key: {{ .Values.storage.s3.secretAccessKey | b64enc | quote }}
{{- end }}
---
{{- if and (eq .Values.storage.backend "smb") (not .Values.storage.smb.existingSecret) .Values.storage.smb.username }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "gohoarder.fullname" . }}-smb
labels:
{{- include "gohoarder.labels" . | nindent 4 }}
type: Opaque
data:
username: {{ .Values.storage.smb.username | b64enc | quote }}
password: {{ .Values.storage.smb.password | b64enc | quote }}
{{- end }}
---
{{- if and (eq .Values.metadata.backend "postgresql") (not .Values.metadata.postgresql.existingSecret) .Values.metadata.postgresql.username }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "gohoarder.fullname" . }}-postgresql
labels:
{{- include "gohoarder.labels" . | nindent 4 }}
type: Opaque
data:
username: {{ .Values.metadata.postgresql.username | b64enc | quote }}
password: {{ .Values.metadata.postgresql.password | b64enc | quote }}
{{- end }}
---
{{- if and .Values.security.scanners.ghsa.enabled (not .Values.security.scanners.ghsa.existingSecret) .Values.security.scanners.ghsa.token }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "gohoarder.fullname" . }}-ghsa
labels:
{{- include "gohoarder.labels" . | nindent 4 }}
type: Opaque
data:
token: {{ .Values.security.scanners.ghsa.token | b64enc | quote }}
{{- end }}
+39
View File
@@ -0,0 +1,39 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "gohoarder.fullname" . }}-server
labels:
{{- include "gohoarder.server.labels" . | nindent 4 }}
{{- with .Values.server.service.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
type: {{ .Values.server.service.type }}
ports:
- port: {{ .Values.server.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "gohoarder.server.selectorLabels" . | nindent 4 }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ include "gohoarder.fullname" . }}-frontend
labels:
{{- include "gohoarder.frontend.labels" . | nindent 4 }}
{{- with .Values.frontend.service.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
type: {{ .Values.frontend.service.type }}
ports:
- port: {{ .Values.frontend.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "gohoarder.frontend.selectorLabels" . | nindent 4 }}
@@ -0,0 +1,12 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "gohoarder.serviceAccountName" . }}
labels:
{{- include "gohoarder.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
+475
View File
@@ -0,0 +1,475 @@
# Default values for gohoarder
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
# Override the name of the chart
nameOverride: ""
# Override the full name of the chart
fullnameOverride: ""
# Global configuration
global:
# Base domain for the deployment
domain: "gohoarder.local"
# Image pull secrets for private registries
# Reference existing secrets by name:
# imagePullSecrets:
# - name: ghcr-secret
# - name: dockerhub-secret
imagePullSecrets: []
# Auto-create image pull secrets from credentials (optional)
# If you want the chart to create the secrets for you, use this instead:
# imageCredentials:
# ghcr-secret:
# registry: ghcr.io
# username: myusername
# password: mytoken
# email: myemail@example.com
# dockerhub-secret:
# registry: https://index.docker.io/v1/
# username: myusername
# password: mytoken
# email: myemail@example.com
# Then reference them in global.imagePullSecrets:
# - name: ghcr-secret
imageCredentials: {}
# Deployment replicas
# NOTE: When running multiple server replicas (>1):
# - Use S3 or SMB for storage.backend (not filesystem with local storage)
# - Use PostgreSQL for metadata.backend (SQLite has limited concurrency)
# - See "High Availability & Scaling" section in README
replicaCount:
server: 1
frontend: 1
scanner: 1
# Image configuration
image:
server:
repository: ghcr.io/lukaszraczylo/gohoarder-server
pullPolicy: IfNotPresent
tag: "0.0.1"
frontend:
repository: ghcr.io/lukaszraczylo/gohoarder-frontend
pullPolicy: IfNotPresent
tag: "0.0.1"
scanner:
repository: ghcr.io/lukaszraczylo/gohoarder-scanner
pullPolicy: IfNotPresent
tag: "0.0.1"
# Service Account
serviceAccount:
create: true
annotations: {}
name: ""
# Pod annotations
podAnnotations: {}
# Pod security context
podSecurityContext:
fsGroup: 1000
runAsNonRoot: true
runAsUser: 1000
# Container security context
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
# Server configuration
server:
host: "0.0.0.0"
port: 8080
readTimeout: "5m"
writeTimeout: "5m"
idleTimeout: "2m"
# Additional environment variables for server container
# Use this to override config via environment variables
# Format: GOHOARDER_<CONFIG_KEY> (dots replaced with underscores)
# Examples:
# GOHOARDER_STORAGE_BACKEND: s3
# GOHOARDER_METADATA_BACKEND: postgresql
# env:
# - name: GOHOARDER_STORAGE_BACKEND
# value: "s3"
# - name: GOHOARDER_STORAGE_S3_BUCKET
# value: "my-bucket"
# - name: GOHOARDER_METADATA_POSTGRESQL_PASSWORD
# valueFrom:
# secretKeyRef:
# name: postgres-secret
# key: password
env: []
# Service configuration
service:
type: ClusterIP
port: 80
targetPort: 8080
annotations: {}
# Resource limits
resources:
limits:
cpu: 2000m
memory: 2Gi
requests:
cpu: 500m
memory: 512Mi
# Liveness and readiness probes
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /health/ready
port: http
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
# Node selector
nodeSelector: {}
# Tolerations
tolerations: []
# Affinity
affinity: {}
# Frontend configuration
frontend:
# Backend URL for API calls
backendUrl: "" # Auto-configured if empty
# Additional environment variables for frontend container
# env:
# - name: API_BASE_URL
# value: "https://api.example.com"
env: []
# Service configuration
service:
type: ClusterIP
port: 80
targetPort: 80
annotations: {}
# Resource limits
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 100m
memory: 128Mi
# Liveness and readiness probes
livenessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 10
periodSeconds: 5
nodeSelector: {}
tolerations: []
affinity: {}
# Scanner configuration
scanner:
# Additional environment variables for scanner container
# env:
# - name: GOHOARDER_SECURITY_SCANNERS_TRIVY_ENABLED
# value: "true"
env: []
# Resource limits
resources:
limits:
cpu: 2000m
memory: 4Gi
requests:
cpu: 500m
memory: 1Gi
nodeSelector: {}
tolerations: []
affinity: {}
# Storage configuration
storage:
# Storage backend: filesystem, s3, smb
# For multiple server replicas:
# - S3 or SMB (recommended)
# - Filesystem with ReadWriteMany (RWX) storage class (Longhorn, NFS, CephFS)
# - NOT filesystem with ReadWriteOnce (RWO) or local storage
backend: "filesystem"
# Filesystem storage
filesystem:
# Storage class for PVC
# For multiple replicas: use RWX-capable storage class (longhorn, nfs-client, cephfs, etc.)
storageClass: ""
# Storage size
size: "100Gi"
# Access mode:
# ReadWriteOnce (RWO) - Single replica only
# ReadWriteMany (RWX) - Multiple replicas (requires RWX storage class)
accessMode: "ReadWriteOnce"
# Use hostPath instead of PVC (for single-node testing only)
useHostPath: false
hostPath: "/var/lib/gohoarder"
# Existing PVC name (if you want to use existing PVC)
existingClaim: ""
# S3 storage
s3:
endpoint: "s3.amazonaws.com"
region: "us-east-1"
bucket: "gohoarder-cache"
accessKeyId: ""
secretAccessKey: ""
# Use existing secret for S3 credentials
existingSecret: ""
useSSL: true
# SMB storage
smb:
host: ""
share: ""
username: ""
password: ""
domain: ""
# Use existing secret for SMB credentials
existingSecret: ""
# Metadata storage configuration
metadata:
# Backend: sqlite, postgresql
# For multiple server replicas: postgresql is recommended (sqlite has concurrency limitations)
backend: "sqlite"
# SQLite configuration
sqlite:
# Use PVC for SQLite database
persistence:
enabled: true
storageClass: ""
size: "10Gi"
accessMode: "ReadWriteOnce"
existingClaim: ""
walMode: true
# PostgreSQL configuration
postgresql:
# Use bundled PostgreSQL (sets up postgresql subchart)
enabled: false
host: "localhost"
port: 5432
database: "gohoarder"
username: "gohoarder"
password: ""
sslMode: "disable"
# Use existing secret for PostgreSQL credentials
existingSecret: ""
# Cache configuration
cache:
defaultTTL: "168h" # 7 days
cleanupInterval: "1h"
maxSizeBytes: 536870912000 # 500GB
perProjectQuota: 53687091200 # 50GB
ttlOverrides:
npm: "168h"
pip: "168h"
go: "168h"
# Security scanning configuration
security:
enabled: false
blockOnSeverity: "high" # none, low, medium, high, critical
scanOnDownload: true
rescanInterval: "24h"
updateDbOnStartup: false
blockThresholds:
critical: 0
high: -1
medium: -1
low: -1
scanners:
trivy:
enabled: false
timeout: "5m"
cacheDb: "/var/lib/trivy"
osv:
enabled: false
apiUrl: "https://api.osv.dev"
timeout: "30s"
grype:
enabled: false
timeout: "5m"
govulncheck:
enabled: false
timeout: "5m"
npmAudit:
enabled: false
timeout: "2m"
pipAudit:
enabled: false
timeout: "2m"
ghsa:
enabled: false
timeout: "30s"
# GitHub token for higher rate limits
token: ""
existingSecret: ""
static:
enabled: true
maxPackageSize: 2147483648 # 2GB
checkChecksums: true
blockSuspicious: false
# Authentication configuration
auth:
enabled: true
keyExpiration: "0" # Never expire
bcryptCost: 10
auditLog: true
# Admin API key - will be auto-generated if not provided
adminApiKey: ""
# Use existing secret for admin API key
existingSecret: ""
# Secret key name for admin API key
secretKey: "admin-api-key"
# Network configuration
network:
connectTimeout: "10s"
readTimeout: "5m"
writeTimeout: "5m"
maxIdleConns: 100
maxConnsPerHost: 10
rateLimit:
perApiKey: 1000
perIp: 100
burstSize: 50
circuitBreaker:
threshold: 5
timeout: "30s"
resetInterval: "60s"
retry:
maxAttempts: 3
initialBackoff: "1s"
maxBackoff: "30s"
# Logging configuration
logging:
level: "info" # debug, info, warn, error
format: "json" # json, pretty
# Package handlers configuration
handlers:
go:
enabled: true
upstreamProxy: "https://proxy.golang.org"
checksumDb: "https://sum.golang.org"
verifyChecksums: true
npm:
enabled: true
upstreamRegistry: "https://registry.npmjs.org"
pypi:
enabled: true
upstreamUrl: "https://pypi.org"
simpleApiUrl: "https://pypi.org/simple"
# Ingress configuration
ingress:
enabled: false
className: "nginx"
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/proxy-body-size: "2048m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
# Ingress for frontend
frontend:
enabled: true
host: "gohoarder.local"
tls:
enabled: false
secretName: "gohoarder-frontend-tls"
# Ingress for API (if you want separate ingress)
api:
enabled: false
host: "api.gohoarder.local"
tls:
enabled: false
secretName: "gohoarder-api-tls"
# Autoscaling configuration
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 10
targetCPUUtilizationPercentage: 80
targetMemoryUtilizationPercentage: 80
# Pod Disruption Budget
podDisruptionBudget:
enabled: false
minAvailable: 1
# Network Policy
networkPolicy:
enabled: false
# Allow external access to server
ingress:
- from:
- namespaceSelector: {}
ports:
- protocol: TCP
port: 8080
Binary file not shown.
+29 -1
View File
@@ -1,5 +1,33 @@
apiVersion: v1
entries:
gohoarder:
- apiVersion: v2
appVersion: 0.0.1
created: "2026-01-02T23:23:54.075202022Z"
description: A universal package cache proxy supporting npm, PyPI, and Go modules
with security scanning
digest: 2271641c9d1e9942a02d94fe62b1c49d0835541160bf8abe198a1293ff4b811d
home: https://github.com/lukaszraczylo/gohoarder
icon: https://raw.githubusercontent.com/lukaszraczylo/gohoarder/main/docs/logo.png
keywords:
- package-manager
- cache
- proxy
- npm
- pypi
- go-modules
- security
- vulnerability-scanning
maintainers:
- email: lukasz@raczylo.com
name: Lukasz Raczylo
name: gohoarder
sources:
- https://github.com/lukaszraczylo/gohoarder
type: application
urls:
- https://github.com/lukaszraczylo/helm-charts/releases/download/gohoarder-0.0.1/gohoarder-0.0.1.tgz
version: 0.0.1
jobs-manager:
- annotations:
artifacthub.io/changes: |
@@ -1623,4 +1651,4 @@ entries:
urls:
- https://github.com/lukaszraczylo/helm-charts/releases/download/kubemirror-0.2.8/kubemirror-0.2.8.tgz
version: 0.2.8
generated: "2025-12-27T03:40:40.629057295Z"
generated: "2026-01-02T23:23:54.07379072Z"