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
26 changes: 26 additions & 0 deletions .github/workflows/lint-and-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,32 @@ jobs:
- name: Run ct lint
run: ct lint --config .github/ct.yaml --all

# `ct lint` never looks at examples/, so a shipped example could reference a
# removed key, miss a required one, or trip a template-time guard and no one
# would find out until a user copied it. `helm template` also enforces
# values.schema.json, so this covers schema drift in the examples too.
#
# Raw manifests in examples/ (Secrets, Pods) are not values overlays and are
# skipped by looking for a top-level `apiVersion:`.
- name: Render shipped examples
run: |
set -u
rc=0
while IFS= read -r f; do
if grep -q '^apiVersion:' "$f"; then
echo "skip $f (raw manifest, not a values overlay)"
continue
fi
if out=$(helm template ci-examples charts/graylog -f "$f" 2>&1); then
echo "ok $f"
else
echo "FAILED $f"
printf '%s\n' "$out" | sed 's/^/ /'
rc=1
fi
done < <(find examples -name '*.yaml' | sort)
exit $rc

helm-unittest:
runs-on: ubuntu-latest
strategy:
Expand Down
6 changes: 6 additions & 0 deletions charts/graylog/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,8 +253,14 @@ If the password is lost and you cannot set a new one through Helm (for example w
your values are managed by GitOps), patch the new password's SHA-256 into both
Secrets and restart Graylog:

These patch the Secrets' `data` field, so the value *is* base64 here — unlike
[an externally managed Secret](../../docs/graylog-secrets.md), which uses `stringData`
and takes plain text. Both `printf '%s'` calls matter: `echo` would append a newline
and the hash would not match.

```sh
PASS="your-new-password"
# macOS: shasum -a 256 instead of sha256sum, and plain `base64` (no -w flag).
SHA=$(printf '%s' "$PASS" | sha256sum | awk '{print $1}')
SHA64=$(printf '%s' "$SHA" | base64 -w0)
kubectl patch secret graylog-secrets --namespace graylog -p "{\"data\":{\"GRAYLOG_ROOT_PASSWORD_SHA2\":\"$SHA64\"}}"
Expand Down
30 changes: 22 additions & 8 deletions docs/graylog-secrets.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,32 @@ supply it.
> `datanode.config.s3ClientDefaultEndpoint`. A `GRAYLOG_S3_CLIENT_DEFAULT_*` key in this Secret
> reaches the Graylog server container and does not configure searchable snapshots.

`GEO_IP_MAXMIND_ACCOUNT_ID` and `GEO_IP_MAXMIND_LICENSE_KEY`: MaxMind credentials for the GeoIP
update sidecar. Supply both when `graylog.config.geolocation.enabled` and
`graylog.config.geolocation.sidecar.enabled` are both `true`. The sidecar reads them with a
`secretKeyRef`, so the pod does not start when a key is absent from the Secret.
> [!NOTE]
> MaxMind GeoIP credentials do not belong in this Secret either. The GeoIP update sidecar reads
> them from a Secret of its own, named by
> `graylog.config.geolocation.maxmindGeoIp.existingSecret`, with the key names
> `GEO_IP_MAXMIND_ACCOUNT_ID` and `GEO_IP_MAXMIND_LICENSE_KEY` (both overridable via
> `accountIdKey` / `licenseKeyKey`). Putting them in this Secret has no effect: with
> `global.existingSecretName` set, the sidecar never reads this Secret, and supplying the inline
> `maxmindGeoIp.accountId` / `licenseKey` values alongside an external Secret is refused at render
> time. The sidecar reads its keys with a `secretKeyRef`, so the pod does not start when one is
> absent from the GeoIP Secret. See
> [examples/graylog-geoip-secret.yaml](../examples/graylog-geoip-secret.yaml).

## Secret Example

The following is an example of a Kubernetes Secret managed externally from the Graylog Helm chart.
The example uses `stringData`, so you supply plain text and Kubernetes encodes it for you.

> [!CAUTION]
> Replace every `<...>` value before you apply this file. Keep or change `admin` as you prefer,
> and leave the optional S3 keys empty when you do not use an S3 backend. Never commit real
> credentials.
> Replace every `<...>` value before you apply this file. Keep or change `admin` as you prefer.
> Never commit real credentials.

> [!IMPORTANT]
> Because this is `stringData`, do not base64 encode the values yourself — a base64 string here is
> encoded a second time, and Graylog then reads the base64 text itself as the username, pepper or
> hash. Avoid trailing newlines for the same reason, which is why the commands above use
> `printf %s` rather than `echo`.

```yaml
apiVersion: v1
Expand All @@ -71,9 +83,11 @@ stringData:
GRAYLOG_PASSWORD_SECRET: "<96-character-random-string>"
# SHA-256 hash of the admin login password: 64 lowercase hexadecimal characters.
GRAYLOG_ROOT_PASSWORD_SHA2: "<64-character-sha256-hex-digest>"
# Optional. Only necessary for features that reach S3 with keys.
```

That is the whole Secret. S3 and MaxMind credentials are deliberately absent — see the two notes
above for where each of those belongs.

## Setting Your Secret

> [!IMPORTANT]
Expand Down
18 changes: 12 additions & 6 deletions examples/graylog-geoip-secret.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,21 @@
# accountIdKey: GEO_IP_MAXMIND_ACCOUNT_ID
# licenseKeyKey: GEO_IP_MAXMIND_LICENSE_KEY
#
# WARNING: the values below are placeholders. Replace both <...> values before you
# apply this file, and never commit real credentials.
#
# This Secret uses stringData, so both values are PLAIN TEXT and Kubernetes encodes
# them for you. Do not base64 encode them yourself, and take care not to leave a
# trailing newline (use `printf %s`, not `echo`) -- the sidecar sends these straight
# to MaxMind, which rejects a key with stray whitespace.
#
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: graylog-geoip-secret-example
data:
# MaxMind account ID.
# To get this value, run: echo -n "123456" | base64
GEO_IP_MAXMIND_ACCOUNT_ID: "[REDACTED]"
stringData:
# MaxMind account ID, as shown on your MaxMind account page.
GEO_IP_MAXMIND_ACCOUNT_ID: "<maxmind-account-id>"
# MaxMind license key.
# To get this value, run: echo -n "your-license-key" | base64
GEO_IP_MAXMIND_LICENSE_KEY: "[REDACTED]"
GEO_IP_MAXMIND_LICENSE_KEY: "<maxmind-license-key>"
39 changes: 25 additions & 14 deletions examples/graylog-secret.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@
# If you wish to generate your own graylog-secret, supply the secret name as
# global.existingSecretName: "". GRAYLOG_MONGODB_URI, GRAYLOG_PASSWORD_SECRET, and
# GRAYLOG_ROOT_PASSWORD_SHA2 are required. GRAYLOG_ROOT_USERNAME is optional and
# defaults to "admin". The two GEO_IP_MAXMIND keys are necessary only when the
# GeoIP sidecar is enabled.
# defaults to "admin".
#
# stringData takes plain text and Kubernetes encodes it for you. Do not base64 encode
# these values yourself.
# This Secret uses stringData, so every value below is PLAIN TEXT and Kubernetes
# encodes it for you. Do not base64 encode these values yourself -- a base64 string
# here gets encoded a second time, and Graylog then reads the base64 text itself as
# the username, pepper or hash. Take care not to leave a trailing newline either:
# use `printf %s` rather than `echo`, since `echo` appends one.
#
# See docs/graylog-secrets.md for what each key means.
#
apiVersion: v1
kind: Secret
Expand All @@ -19,17 +23,24 @@ metadata:
stringData:
# Mongo DB URI with valid credentials and location of the Mongo cluster.
# The URI must contain a username and a password, or Graylog does not start.
GRAYLOG_MONGODB_URI: "mongodb://<username>:<password>@<host>:27017/graylog_2"
# Graylog admin username.
# To get this value, run: echo -n admin | base64
GRAYLOG_ROOT_USERNAME: "YWRtaW4="
# Graylog admin user's password pepper.
# To get a value similar to this, run: pwgen 96 1 | base64
GRAYLOG_PASSWORD_SECRET: "a2U5b0wxZWlnaDF1bjFFZWNoYTJvaEJpZXJlZTBIZWk2S2VpNk9oZmFlNXF1b2FtYWhjaGlwaG9oOXp1OHJhbzVQaWVZaWUyYWVjaTRlZWY4ZWlDaDdBaFBoMm9vbjd1Cg=="
# Graylog admin user's password SHA2 value.
# To get this value, run: echo -n admin | shasum -a 256 | cut -d ' ' -f1 | base64
GRAYLOG_ROOT_PASSWORD_SHA2: "OGM2OTc2ZTViNTQxMDQxNWJkZTkwOGJkNGRlZTE1ZGZiMTY3YTljODczZmM0YmI4YTgxZjZmMmFiNDQ4YTkxOAo="
GRAYLOG_MONGODB_URI: "mongodb://<username>:<password>@<host>:27017/graylog?authSource=admin"
# Graylog admin username. Optional; defaults to "admin".
GRAYLOG_ROOT_USERNAME: "admin"
# The pepper Graylog applies to stored user data. Not a password, and not used to
# log in. Minimum 64 characters, and it must stay the same for the life of the
# cluster: change it and Graylog can no longer read what it already stored.
# Generate one with: pwgen 96 1
GRAYLOG_PASSWORD_SECRET: "<96-character-random-string>"
# SHA-256 hash of the admin login password -- 64 lowercase hex characters. This is
# the password you log in with, and it is NOT a hash of GRAYLOG_PASSWORD_SECRET.
# Generate it with: printf %s '<your-password>' | sha256sum | cut -d ' ' -f1
# (on macOS: printf %s '<your-password>' | shasum -a 256 | cut -d ' ' -f1)
GRAYLOG_ROOT_PASSWORD_SHA2: "<64-character-sha256-hex-digest>"
#
# MaxMind GeoIP credentials do NOT belong in this Secret. The GeoIP update sidecar
# reads them from a Secret of its own -- see examples/graylog-geoip-secret.yaml and
# graylog.config.geolocation.maxmindGeoIp.existingSecret.
#
# S3 credentials for searchable snapshots do not belong here either. They live in a
# separate chart-managed Secret named <global.existingSecretName>-datanode; set them
# through datanode.config.s3ClientDefault*. See docs/graylog-secrets.md.
28 changes: 20 additions & 8 deletions examples/values/values-example-aws.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,16 @@ graylog:
persistence:
enabled: true
storageClass: "gp3"
size: 5Gi
# The journal cap and this volume are sized together. The chart refuses to
# render a cap above 90% of the volume, because the journal shares it with
# the node-id, truststore, content packs and GeoIP databases. Raise both or
# neither. See docs/graylog-message-handling.md#sizing.
size: 20Gi
config:
messageJournal:
# Graylog's suffixes are binary, so 10gb is 10x1024^3 - comfortably inside
# 90% of the 20Gi volume above.
maxSize: "10gb"
inputs:
- name: gelf-tcp
port: 12201
Expand All @@ -27,7 +36,9 @@ datanode:
data:
enabled: true
storageClass: "gp3"
size: 5Gi
# Must comfortably exceed datanode.config.nodeSearchCacheSize (default
# 10gb), which is allocated on this same volume alongside shard data.
size: 50Gi
# Prevent Karpenter from disrupting DataNodes (stateful search data).
podAnnotations:
karpenter.sh/do-not-disrupt: "true"
Expand All @@ -42,7 +53,9 @@ ingress:
alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:us-east-1:000000000000:certificate/00000000-0000-0000-0000-000000000000
alb.ingress.kubernetes.io/group.name: some-graylog
alb.ingress.kubernetes.io/healthcheck-interval-seconds: "5"
alb.ingress.kubernetes.io/healthcheck-path: /
# Graylog's unauthenticated load-balancer status endpoint: 200 ALIVE /
# 503 DEAD. "/" answers 200 whether or not the node can actually serve.
alb.ingress.kubernetes.io/healthcheck-path: /api/system/lbstatus
alb.ingress.kubernetes.io/healthcheck-port: "9000"
alb.ingress.kubernetes.io/healthcheck-protocol: HTTP
alb.ingress.kubernetes.io/healthcheck-timeout-seconds: "2"
Expand All @@ -62,8 +75,7 @@ ingress:
paths:
- path: /
pathType: Prefix
backend:
service:
name: graylog-app
port:
name: app
# No `backend:` here on purpose. Left unset, the chart points the
# rule at the Service it actually creates (<release>-graylog-svc) on
# the configured app port. Hardcoding a name here silently produces
# an Ingress aimed at a Service that does not exist.
Loading