Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.git
.github
.DS_Store
.idea
.vscode
*.env*
coverage.out
deployments
docs
*.md
Dockerfile.local
docker-compose.yml
80 changes: 80 additions & 0 deletions .github/workflows/build-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
name: Build and Push Container Image
# Publishes the Argus application image to ghcr.io/<owner>/argus.

on:
push:
branches: [ main ]
paths:
- 'Dockerfile'
- 'go.mod'
- 'go.sum'
- 'cmd/**'
- 'internal/**'
- 'pkg/**'
- 'configs/**'
- '.github/workflows/build-image.yml'
pull_request:
branches: [ main ]
paths:
- 'Dockerfile'
- 'go.mod'
- 'go.sum'
- 'cmd/**'
- 'internal/**'
- 'pkg/**'
- 'configs/**'
- '.github/workflows/build-image.yml'
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read
packages: write

env:
REGISTRY: ghcr.io
IMAGE_NAME: argus

jobs:
build-and-push:
name: Build & Push Image
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Convert repository owner to lowercase
id: repo_owner
run: echo "owner=$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT"

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
push: ${{ github.event_name != 'pull_request' }}
tags: |
${{ env.REGISTRY }}/${{ steps.repo_owner.outputs.owner }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
${{ env.REGISTRY }}/${{ steps.repo_owner.outputs.owner }}/${{ env.IMAGE_NAME }}:latest
labels: |
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
build-args: |
BUILD_VERSION=${{ github.sha }}
GIT_COMMIT=${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
provenance: false
3 changes: 2 additions & 1 deletion .github/workflows/helm-ci.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: Helm Chart CI
# Validates the Helm chart on PRs: lint + render. Never publishes — packaging and
# pushing to GHCR is handled by build-dev-chart.yml (dev) and release-chart.yml (release).
# pushing the chart to GHCR is handled by build-dev-chart.yml; the application
# image is handled by build-image.yml.

on:
pull_request:
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ route registration, graceful shutdown) — business logic does not belong there.
changes to `CanonicalizeRequest` are a breaking/security-relevant change and must be mirrored in
`pkg/audit/security.go`.
- Auth middleware (`internal/middleware/auth.go`) uses `crypto/subtle.ConstantTimeCompare` over a
SHA-256 pre-hash of the bearer token specifically to avoid length-based timing side channels — don't
SHA-256 pre-hash of the API key specifically to avoid length-based timing side channels — don't
replace this with a plain `==` comparison.

### Database
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
- **Cryptographic Non-Repudiation** – Server-side verification of RSA/Ed25519 signatures for incoming logs. The `computeHash` covers the entire payload (including all metadata and message bodies) to guarantee full payload integrity.
- **High-Performance Batching** – Client-side worker pool with buffered batching to minimize HTTP overhead and eliminate goroutine leaks. The server utilizes GORM's `CreateInBatches` for high-throughput ingestion.
- **Production Observability** – Built-in Prometheus metrics for ingestion rates, latencies, and security errors.
- **Secure by Default** – Fail-closed Bearer token authentication utilizing `crypto/subtle.ConstantTimeCompare` with SHA-256 pre-hashing to prevent length-based timing attacks. Strict validation of log schemas.
- **Secure by Default** – Fail-closed API key authentication utilizing `crypto/subtle.ConstantTimeCompare` with SHA-256 pre-hashing to prevent length-based timing attacks. Strict validation of log schemas.

## Quick Start: Using the Audit Interface

Expand Down Expand Up @@ -147,12 +147,12 @@ Argus exports standard Prometheus metrics at `/metrics`:

## Deployment & Helm Chart

Argus provides an official Helm chart published as an **OCI Artifact** to GitHub Container Registry (`ghcr.io/lsflk/charts/argus`), as well as local chart source at [`deployments/helm/argus`](deployments/helm/argus).
Argus provides an official Helm chart published as an **OCI Artifact** to GitHub Container Registry (`ghcr.io/lsflk/charts/argus`), as well as local chart source at [`deployments/helm/argus`](deployments/helm/argus). The application container image is published to `ghcr.io/lsflk/argus` (`:latest` and `:<git sha>`).

### Install via OCI Artifact (Recommended)
```bash
helm upgrade --install argus oci://ghcr.io/lsflk/charts/argus \
--version 0.1.0 \
--version 0.1.1 \
-n <your-namespace> \
--create-namespace \
-f custom-values.yaml
Expand All @@ -172,7 +172,7 @@ For full Helm configuration parameters, GitOps umbrella chart integration, and O

| Variable | Default | Description |
| --- | --- | --- |
| `ARGUS_AUTH_TOKEN` | - | Bearer token required for API access. |
| `ARGUS_API_KEY` | - | API key required for write operations (`X-API-Key`). |
| `DB_TYPE` | `sqlite` | `sqlite` or `postgres`. |
| `AUDIT_ENUMS_CONFIG` | `configs/enums.yaml` | Path to allowed event types/actions. |

Expand Down
2 changes: 1 addition & 1 deletion deployments/helm/argus/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ apiVersion: v2
name: argus
description: Secure, Tamper-Proof cryptographic Audit Log Service
type: application
version: 0.1.0
version: 0.1.1
appVersion: "1.0.0"
home: https://github.com/LSFLK/argus
sources:
Expand Down
19 changes: 11 additions & 8 deletions deployments/helm/argus/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ This chart provisions:

### 1. Install via OCI Artifact (Recommended)

Argus Helm charts are published as OCI artifacts to the GitHub Container Registry (`ghcr.io`).
Argus Helm charts are published as OCI artifacts to the GitHub Container Registry (`ghcr.io`).

```bash
# Install directly from OCI registry
helm upgrade --install argus oci://ghcr.io/lsflk/charts/argus \
--version 0.1.0 \
--version 0.1.1 \
--namespace <your-namespace> \
--create-namespace \
--values ./custom-values.yaml
Expand All @@ -36,7 +36,7 @@ helm upgrade --install argus oci://ghcr.io/lsflk/charts/argus \
To pull the packaged chart locally:

```bash
helm pull oci://ghcr.io/lsflk/charts/argus --version 0.1.0
helm pull oci://ghcr.io/lsflk/charts/argus --version 0.1.1
```

### 2. Standalone Deployment from Source
Expand All @@ -57,7 +57,7 @@ When referencing Argus as a dependency in your umbrella chart (`Chart.yaml`):
```yaml
dependencies:
- name: argus
version: "0.1.0"
version: "0.1.1"
repository: "oci://ghcr.io/lsflk/charts"
```

Expand All @@ -81,7 +81,8 @@ argus:
### Automated (CI/CD)

The Helm chart automation follows a standard GitOps setup:
- **Dev Chart (`.github/workflows/build-dev-chart.yml`)**: On pushes to `main` with chart changes (or manual dispatch), packages and publishes a dev chart (`0.0.0-dev.<run_number>`) to `oci://ghcr.io/lsflk/charts`.
- **Application image (`.github/workflows/build-image.yml`)**: Builds and pushes `ghcr.io/lsflk/argus` (`:<git sha>` and `:latest`) on pushes to `main`. PRs that touch Go code or the Dockerfile build the image without pushing. After the first publish, set the GHCR package visibility to public under https://github.com/orgs/LSFLK/packages so clusters can pull without an imagePullSecret.
- **Dev Chart (`.github/workflows/build-dev-chart.yml`)**: On pushes to `main` with chart changes (or manual dispatch), packages and publishes a dev chart (`0.0.0-dev.<run_number>`) to `oci://ghcr.io/lsflk/charts`. After the image push completes, publish the stable chart by dispatching this workflow with `version=0.1.1`.
- **Chart CI (`.github/workflows/helm-ci.yml`)**: Lints the chart and verifies template rendering on pull requests.

### Manual Packaging and Push
Expand All @@ -96,7 +97,7 @@ helm package deployments/helm/argus -d .cr-release-packages/
echo "$CR_PAT" | helm registry login ghcr.io -u <username> --password-stdin

# 3. Push OCI artifact
helm push .cr-release-packages/argus-0.1.0.tgz oci://ghcr.io/lsflk/charts
helm push .cr-release-packages/argus-0.1.1.tgz oci://ghcr.io/lsflk/charts
```

---
Expand All @@ -107,7 +108,8 @@ helm push .cr-release-packages/argus-0.1.0.tgz oci://ghcr.io/lsflk/charts
| --- | --- | --- |
| `replicaCount` | Number of pod replicas | `2` |
| `image.repository` | Container image repository | `ghcr.io/lsflk/argus` |
| `image.tag` | Container image tag | `f21da85558410c19b6a96275b6e0eef2a788fb4b` |
| `image.tag` | Container image tag (`:<git sha>` is also published) | `latest` |
| `image.pullPolicy` | Image pull policy (`Always` when `tag` is `latest`) | `IfNotPresent` |
| `service.type` | Kubernetes service type | `ClusterIP` |
| `service.port` | Service port | `3001` |
| `env.ENVIRONMENT` | Deployment environment | `production` |
Expand All @@ -117,5 +119,6 @@ helm push .cr-release-packages/argus-0.1.0.tgz oci://ghcr.io/lsflk/charts
| `env.DB_NAME` | Database name | `audit_db` |
| `env.REQUIRE_SIGNATURES` | Enable signature verification | `"true"` |
| `env.S3_COMPLIANCE_BUCKET` | S3 WORM compliance bucket name | `"audit-compliance-logs-staging"` |
| `auth.existingSecret` | Existing Kubernetes secret containing `DB_PASSWORD` | `""` |
| `auth.existingSecret` | Existing Kubernetes secret containing `password` and `api-key` | `""` |
| `auth.apiKey` | API key for authentication (`ARGUS_API_KEY`) | `""` |
| `auth.externalSecrets.enabled` | Enable ExternalSecrets Operator (ESO) | `false` |
6 changes: 3 additions & 3 deletions deployments/helm/argus/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
imagePullPolicy: {{ if eq .Values.image.tag "latest" }}Always{{ else }}{{ .Values.image.pullPolicy }}{{ end }}
env:
- name: DB_PASSWORD
valueFrom:
Expand All @@ -38,11 +38,11 @@ spec:
name: {{ .Values.auth.existingSecret | default (printf "%s-credentials" (include "argus.fullname" .)) }}
key: AWS_SECRET_ACCESS_KEY
optional: true
- name: ARGUS_AUTH_TOKEN
- name: ARGUS_API_KEY
valueFrom:
secretKeyRef:
name: {{ .Values.auth.existingSecret | default (printf "%s-credentials" (include "argus.fullname" .)) }}
key: ARGUS_AUTH_TOKEN
key: {{ if .Values.auth.existingSecret }}api-key{{ else }}ARGUS_API_KEY{{ end }}
optional: true
{{- range $key, $val := .Values.env }}
- name: {{ $key }}
Expand Down
4 changes: 2 additions & 2 deletions deployments/helm/argus/templates/external-secrets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ spec:
remoteRef:
key: {{ .Values.auth.externalSecrets.remoteAwsKey | quote }}
property: "aws_secret_access_key"
- secretKey: ARGUS_AUTH_TOKEN
- secretKey: ARGUS_API_KEY
remoteRef:
key: {{ .Values.auth.externalSecrets.remoteDbKey | quote }}
property: "argus_auth_token"
property: "argus_api_key"
{{- end }}
2 changes: 1 addition & 1 deletion deployments/helm/argus/templates/secrets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ metadata:
type: Opaque
stringData:
DB_PASSWORD: {{ required "A database password is required (.Values.auth.password)" .Values.auth.password | quote }}
ARGUS_AUTH_TOKEN: {{ .Values.auth.token | default "" | quote }}
ARGUS_API_KEY: {{ .Values.auth.apiKey | default .Values.auth.token | default "" | quote }}
# Optional plain-text credentials for S3 development/testing
AWS_ACCESS_KEY_ID: ""
AWS_SECRET_ACCESS_KEY: ""
Expand Down
6 changes: 5 additions & 1 deletion deployments/helm/argus/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ replicaCount: 2

image:
repository: ghcr.io/lsflk/argus
tag: f21da85558410c19b6a96275b6e0eef2a788fb4b
# :latest is mutable; the chart uses pullPolicy Always for this tag.
# Pin a git SHA (and IfNotPresent) in production.
tag: latest
pullPolicy: IfNotPresent

service:
Expand Down Expand Up @@ -50,6 +52,8 @@ auth:
username: "postgres"
# Set plain password here if externalSecrets are disabled and existingSecret is empty
password: ""
# Static API key injected as ARGUS_API_KEY. auth.token is still read as a fallback.
apiKey: ""
existingSecret: ""

# --- External Secrets Operator (ESO) ---
Expand Down
4 changes: 3 additions & 1 deletion deployments/helm/values-example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ replicaCount: 2

image:
repository: ghcr.io/lsflk/argus
tag: "1.0.0"
# Published tags are :latest and :<git sha>. 1.0.0 is not published.
tag: latest
pullPolicy: IfNotPresent

service:
Expand Down Expand Up @@ -49,6 +50,7 @@ env:
auth:
username: "postgres"
password: "example-db-password"
apiKey: "example-api-key"
existingSecret: ""
externalSecrets:
enabled: false
Expand Down
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ services:
- DB_NAME=audit_db
- DB_SSLMODE=disable
- PORT=3001
- ARGUS_API_KEY=dev-api-key
depends_on:
- postgres
restart: unless-stopped
Expand Down
17 changes: 13 additions & 4 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ Complete API reference for integrating Argus into your microservices architectur
- **Development**: `http://localhost:3001`
- **Production**: `https://argus.yourdomain.com` or `http://argus-service:3001` (internal)

## Authentication

Write and read API endpoints require a static API key (`ARGUS_API_KEY`) via the `X-API-Key` header. `/health`, `/metrics`, and `/version` are unauthenticated.

```bash
curl -H "X-API-Key: <your-api-key>" ...
```

## Endpoints Overview

| Method | Endpoint | Description |
Expand Down Expand Up @@ -46,6 +54,7 @@ Complete API reference for integrating Argus into your microservices architectur
```bash
curl -X POST http://localhost:3001/api/audit-logs \
-H "Content-Type: application/json" \
-H "X-API-Key: <your-api-key>" \
-d '{
"traceId": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2024-01-20T10:00:00Z",
Expand Down Expand Up @@ -111,16 +120,16 @@ curl -X POST http://localhost:3001/api/audit-logs \

```bash
# Get all audit logs (paginated)
curl http://localhost:3001/api/audit-logs
curl -H "X-API-Key: <your-api-key>" http://localhost:3001/api/audit-logs

# Filter by trace ID
curl http://localhost:3001/api/audit-logs?traceId=550e8400-e29b-41d4-a716-446655440000
curl -H "X-API-Key: <your-api-key>" http://localhost:3001/api/audit-logs?traceId=550e8400-e29b-41d4-a716-446655440000

# Filter by event type
curl http://localhost:3001/api/audit-logs?eventType=MANAGEMENT_EVENT
curl -H "X-API-Key: <your-api-key>" http://localhost:3001/api/audit-logs?eventType=MANAGEMENT_EVENT

# Multiple filters with pagination
curl http://localhost:3001/api/audit-logs?eventType=MANAGEMENT_EVENT&status=SUCCESS&limit=20&offset=0
curl -H "X-API-Key: <your-api-key>" "http://localhost:3001/api/audit-logs?eventType=MANAGEMENT_EVENT&status=SUCCESS&limit=20&offset=0"
```

**Success Response: 200 OK**
Expand Down
2 changes: 1 addition & 1 deletion internal/config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ In addition to the `enums.yaml` file, Argus relies on several environment variab

| Variable | Required | Default | Description |
| --- | --- | --- | --- |
| `ARGUS_AUTH_TOKEN` | **Yes** | - | A high-entropy Bearer token required for all API write operations. Argus fails closed if this is missing. |
| `ARGUS_API_KEY` | **Yes** | - | A high-entropy API key required for all API write operations (`X-API-Key`). Argus fails closed if this is missing. `ARGUS_AUTH_TOKEN` is still accepted as an env fallback. |
| `ENVIRONMENT` | No | `development` | Setting to `production` enables stricter logging and security defaults. |
| `DB_TYPE` | No | `sqlite` | Database engine to use (`sqlite` or `postgres`). |
| `AUDIT_ENUMS_CONFIG` | No | `configs/enums.yaml` | Override path for the Event Type configuration file. |
Loading
Loading