Skip to content
Draft
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ __pycache__/
.cursorrules
.cursor/
.vscode/
.venv/
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ These components are commented out in `values-hub.yaml` by default. Uncomment th
* [Red Hat Quay](https://docs.redhat.com/en/documentation/red_hat_quay) _(externalized chart)_
* Enables a private OCI image registry within the environment
* [Multicloud Object Gateway (NooBaa MCG)](https://docs.redhat.com/en/documentation/red_hat_openshift_data_foundation/latest/html/managing_hybrid_and_multicloud_resources/about-the-multicloud-object-gateway)
* Provides S3-compatible object storage for Quay and RHTPA
* Provides S3-compatible object storage for Quay, RHTPA, and Network Observability
* [Network Observability](https://docs.redhat.com/en/documentation/openshift_container_platform/latest/html/network_observability)
* eBPF flow collection and console Network Traffic views; requires storage/NooBaa
* [Red Hat Trusted Artifact Signer (RHTAS)](https://docs.redhat.com/en/documentation/red_hat_trusted_artifact_signer/1.3)
* Provides cryptographic signing and verification of software artifacts and container images
* [Red Hat Trusted Profile Analyzer (RHTPA)](https://docs.redhat.com/en/documentation/red_hat_trusted_profile_analyzer)
Expand Down
14 changes: 14 additions & 0 deletions charts/netobserv/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: v2
name: netobserv
description: OpenShift Network Observability (NooBaa-backed LokiStack + FlowCollector) for ZTVP
type: application
version: 0.1.0
appVersion: "1.12"
keywords:
- network-observability
- zero-trust
maintainers:
- name: Validated Patterns Team
home: https://validatedpatterns.io/patterns/layered-zero-trust/
sources:
- https://github.com/validatedpatterns/layered-zero-trust
32 changes: 32 additions & 0 deletions charts/netobserv/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{{- define "netobserv.name" -}}
netobserv
{{- end }}

{{- define "netobserv.labels" -}}
app.kubernetes.io/name: {{ include "netobserv.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

{{/*
Resolve LokiStack PVC storageClassName:
explicit value, else the cluster default StorageClass, else fail.
*/}}
{{- define "netobserv.lokiStorageClassName" -}}
{{- $sc := .Values.loki.storageClassName | default "" -}}
{{- if $sc -}}
{{- $sc -}}
{{- else -}}
{{- $default := "" -}}
{{- range (lookup "storage.k8s.io/v1" "StorageClass" "" "").items | default list -}}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tested this in OCP 4.22 and using Helm lookup to find the default StorageClass works with helm install but fails in ArgoCD because ArgoCD's Helm renderer doesn't support lookup. I had to set loki.storageClassName explicitly via an ArgoCD helm parameter override.

If I'm not wrong, when storageClassName is omitted from the LokiStack CR, the Loki Operator uses the cluster's default StorageClass automatically (https://docs.redhat.com/en/documentation/red_hat_openshift_logging/6.5/html/installing_logging/verifying-cluster-prerequisites).

You can update the lokistack.yaml to make storageClassName conditional (see my next comment).

{{- $annotations := .metadata.annotations | default dict -}}
{{- if eq (index $annotations "storageclass.kubernetes.io/is-default-class" | default "") "true" -}}
{{- $default = .metadata.name -}}
{{- end -}}
{{- end -}}
{{- if not $default -}}
{{- fail "loki.storageClassName is empty and no default StorageClass was found; set loki.storageClassName in charts/netobserv values" -}}
{{- end -}}
{{- $default -}}
{{- end -}}
{{- end }}
27 changes: 27 additions & 0 deletions charts/netobserv/templates/flowcollector.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{{- if .Values.enabled }}
apiVersion: flows.netobserv.io/v1beta2
kind: FlowCollector
metadata:
name: {{ .Values.flowcollector.name }}
labels:
{{- include "netobserv.labels" . | nindent 4 }}
annotations:
argocd.argoproj.io/sync-wave: "42"
argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true
spec:
namespace: {{ .Values.flowcollector.namespace }}
deploymentModel: {{ .Values.flowcollector.deploymentModel }}
agent:
type: {{ .Values.flowcollector.agent.type }}
ebpf:
sampling: {{ .Values.flowcollector.agent.sampling }}
privileged: {{ .Values.flowcollector.agent.privileged }}
loki:
enable: {{ .Values.flowcollector.loki.enable }}
mode: {{ .Values.flowcollector.loki.mode }}
lokiStack:
name: {{ .Values.loki.name }}
namespace: {{ .Values.loki.namespace }}
consolePlugin:
enable: {{ .Values.flowcollector.consolePlugin.enable }}
{{- end }}
164 changes: 164 additions & 0 deletions charts/netobserv/templates/loki-s3-credentials-job.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
{{- if and .Values.enabled .Values.loki.enabled }}
apiVersion: v1
kind: ServiceAccount
metadata:
name: netobserv-loki-s3-setup
namespace: {{ .Values.loki.namespace }}
labels:
{{- include "netobserv.labels" . | nindent 4 }}
annotations:
argocd.argoproj.io/sync-wave: "37"
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: netobserv-loki-s3-setup
namespace: {{ .Values.loki.namespace }}
labels:
{{- include "netobserv.labels" . | nindent 4 }}
annotations:
argocd.argoproj.io/sync-wave: "37"
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "create", "update", "patch"]
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list"]
- apiGroups: ["objectbucket.io"]
resources: ["objectbucketclaims"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: netobserv-loki-s3-setup
namespace: {{ .Values.loki.namespace }}
labels:
{{- include "netobserv.labels" . | nindent 4 }}
annotations:
argocd.argoproj.io/sync-wave: "37"
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: netobserv-loki-s3-setup
subjects:
- kind: ServiceAccount
name: netobserv-loki-s3-setup
namespace: {{ .Values.loki.namespace }}
---
apiVersion: batch/v1
kind: Job
metadata:
name: netobserv-loki-s3-credentials
namespace: {{ .Values.loki.namespace }}
labels:
{{- include "netobserv.labels" . | nindent 4 }}
annotations:
argocd.argoproj.io/sync-wave: "39"
argocd.argoproj.io/hook: Sync
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
spec:
ttlSecondsAfterFinished: 600
backoffLimit: {{ .Values.credentialsJob.backoffLimit }}
template:
metadata:
labels:
{{- include "netobserv.labels" . | nindent 8 }}
spec:
serviceAccountName: netobserv-loki-s3-setup
automountServiceAccountToken: true
restartPolicy: OnFailure
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: remap-obc-secret
image: {{ .Values.credentialsJob.image }}
imagePullPolicy: IfNotPresent
securityContext:
allowPrivilegeEscalation: false
runAsNonRoot: true
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
resources:
{{- toYaml .Values.credentialsJob.resources | nindent 12 }}
volumeMounts:
- name: tmp
mountPath: /tmp
env:
- name: NS
value: {{ .Values.loki.namespace | quote }}
- name: OBC_NAME
value: {{ .Values.loki.objectBucketClaim.name | quote }}
- name: LOKI_SECRET
value: {{ .Values.loki.storageSecretName | quote }}
- name: REGION
value: {{ .Values.loki.region | quote }}
command:
- /bin/bash
- -ec
- |
# NooBaa OBC creates a Secret (keys) + ConfigMap (bucket/host/port)
# with the same name as the claim — not all keys live in the Secret.
echo "Checking ObjectBucketClaim ${OBC_NAME} in ${NS}..."
oc get objectbucketclaim "${OBC_NAME}" -n "${NS}"

echo "Waiting for ObjectBucketClaim ${OBC_NAME} to be Bound (timeout: 10 minutes)..."
oc wait --for=jsonpath='{.status.phase}'=Bound \
"objectbucketclaim/${OBC_NAME}" -n "${NS}" --timeout=600s || {
echo "ERROR: ObjectBucketClaim failed to reach Bound state within timeout" >&2
oc describe objectbucketclaim "${OBC_NAME}" -n "${NS}" >&2 || true
exit 1
}

echo "Waiting for OBC Secret and ConfigMap ${OBC_NAME}..."
found=0
for i in $(seq 1 60); do

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm missing error handling if resources are unavailable after 5 minutes (60 attempts). With the current logic, the script would continue executing, but the oc get secret requests would fail.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed

if oc get secret "${OBC_NAME}" -n "${NS}" >/dev/null 2>&1 \
&& oc get configmap "${OBC_NAME}" -n "${NS}" >/dev/null 2>&1; then
found=1
break
fi
sleep 5
done
if [[ "${found}" -ne 1 ]]; then
echo "ERROR: OBC Secret and/or ConfigMap ${OBC_NAME} not available after 5 minutes" >&2
oc describe objectbucketclaim "${OBC_NAME}" -n "${NS}" >&2 || true
exit 1
fi

ACCESS=$(oc get secret "${OBC_NAME}" -n "${NS}" -o jsonpath='{.data.AWS_ACCESS_KEY_ID}' | base64 -d)
SECRET=$(oc get secret "${OBC_NAME}" -n "${NS}" -o jsonpath='{.data.AWS_SECRET_ACCESS_KEY}' | base64 -d)
BUCKET=$(oc get configmap "${OBC_NAME}" -n "${NS}" -o jsonpath='{.data.BUCKET_NAME}')
HOST=$(oc get configmap "${OBC_NAME}" -n "${NS}" -o jsonpath='{.data.BUCKET_HOST}')
PORT=$(oc get configmap "${OBC_NAME}" -n "${NS}" -o jsonpath='{.data.BUCKET_PORT}')

if [[ -z "${ACCESS}" || -z "${SECRET}" || -z "${BUCKET}" || -z "${HOST}" ]]; then
echo "OBC Secret/ConfigMap is missing required keys" >&2
exit 1
fi

if [[ -n "${PORT}" && "${PORT}" != "443" ]]; then
ENDPOINT="https://${HOST}:${PORT}"
else
ENDPOINT="https://${HOST}"
fi

oc create secret generic "${LOKI_SECRET}" -n "${NS}" \
--from-literal=access_key_id="${ACCESS}" \
--from-literal=access_key_secret="${SECRET}" \
--from-literal=bucketnames="${BUCKET}" \
--from-literal=endpoint="${ENDPOINT}" \
--from-literal=region="${REGION}" \
--dry-run=client -o yaml | oc apply -f -
echo "Created/updated ${LOKI_SECRET} for LokiStack (endpoint=${ENDPOINT})"
volumes:
- name: tmp
emptyDir: {}
{{- end }}
24 changes: 24 additions & 0 deletions charts/netobserv/templates/lokistack.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{{- if and .Values.enabled .Values.loki.enabled }}
apiVersion: loki.grafana.com/v1
kind: LokiStack
metadata:
name: {{ .Values.loki.name }}
namespace: {{ .Values.loki.namespace }}
labels:
{{- include "netobserv.labels" . | nindent 4 }}
annotations:
argocd.argoproj.io/sync-wave: "41"
argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true
spec:
size: {{ .Values.loki.size }}
storage:
schemas:
- version: v13
effectiveDate: "2022-06-01"
secret:
name: {{ .Values.loki.storageSecretName }}
type: s3
storageClassName: {{ include "netobserv.lokiStorageClassName" . }}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can make it conditional to address my earlier comment:

    {{- with (include "netobserv.lokiStorageClassName" .) }}
    storageClassName: {{ . }}
    {{- end }}

tenants:
mode: openshift-network
{{- end }}
15 changes: 15 additions & 0 deletions charts/netobserv/templates/object-bucket-claim.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{{- if and .Values.enabled .Values.loki.enabled }}
apiVersion: objectbucket.io/v1alpha1
kind: ObjectBucketClaim
metadata:
name: {{ .Values.loki.objectBucketClaim.name }}
namespace: {{ .Values.loki.namespace }}
labels:
{{- include "netobserv.labels" . | nindent 4 }}
annotations:
argocd.argoproj.io/sync-wave: "36"
argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true
spec:
generateBucketName: {{ .Values.loki.objectBucketClaim.bucketName }}
storageClassName: {{ .Values.loki.objectBucketClaim.storageClass }}
{{- end }}
41 changes: 41 additions & 0 deletions charts/netobserv/values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
enabled: true

loki:
enabled: true
namespace: netobserv-loki
name: loki
size: 1x.extra-small
# Empty: use the cluster default StorageClass (or set explicitly)
storageClassName: ""
storageSecretName: loki-s3
objectBucketClaim:
name: netobserv-loki-obc
bucketName: netobserv-loki
storageClass: openshift-storage.noobaa.io
# NooBaa MCG default region used in remapped secret
region: us-east-1

flowcollector:
name: cluster
namespace: netobserv
deploymentModel: Direct
agent:
type: eBPF
sampling: 50
privileged: false
loki:
enable: true
mode: LokiStack
consolePlugin:
enable: true

credentialsJob:
image: registry.redhat.io/openshift4/ose-cli-rhel9:latest
backoffLimit: 5
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
23 changes: 23 additions & 0 deletions docs/SYNC-WAVE-INVENTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,16 @@ Every sync-wave in the repository, in order. **App** = hub-level Argo CD Applica
| 26 | └ openshift-storage | ns | Namespace + OperatorGroup |
| 26 | └ rhtpa-operator | ns | Namespace + OperatorGroup |
| 26 | └ external-secrets-operator | ns | Namespace + OperatorGroup |
| 26 | └ openshift-operators-redhat | ns | Namespace + OperatorGroup (NetObserv / Loki) |
| 26 | └ openshift-netobserv-operator | ns | Namespace + OperatorGroup |
| 26 | └ netobserv-loki | ns | Namespace |
| 26 | └ netobserv | ns | Namespace |
| 27 | └ odf | sub | ODF operator install |
| 27 | └ rhtpa-operator | sub | RHTPA operator install |
| 27 | └ external-secrets | sub | External Secrets Operator install |
| 27 | └ loki-operator | sub | Loki Operator install (NetObserv) |
| 28 | └ quay-operator | sub | Quay operator install |
| 28 | └ netobserv-operator | sub | Network Observability Operator |
| 29 | └ rhtas-operator | sub | RHTAS operator install |
| 30 | zero-trust-workload-identity-manager | **App** | |
| 31 | └ rhtpa-operator | chart | ingress-ca-job (SA, Role, RoleBinding, ConfigMap, Job) |
Expand Down Expand Up @@ -59,6 +65,12 @@ Every sync-wave in the repository, in order. **App** = hub-level Argo CD Applica
| 41 | acs-central | **App** | |
| 41 | quay-registry | **App** | |
| 41 | trusted-profile-analyzer | **App** | |
| 42 | netobserv | **App** | Network Observability (after NooBaa) |
| 36 | └ netobserv | chart | object-bucket-claim |
| 37 | └ netobserv | chart | loki-s3 credentials SA/Role/RoleBinding |
| 39 | └ netobserv | chart | loki-s3-credentials Job (hook) |
| 41 | └ netobserv | chart | LokiStack |
| 42 | └ netobserv | chart | FlowCollector |
| 41 | └ rhtpa-operator | chart | postgresql-statefulset, postgresql-service |
| 41 | └ keycloak | chart | keycloak-realm-import |
| 41 | └ quay-registry | chart | quay-registry (QuayRegistry CR) |
Expand Down Expand Up @@ -111,6 +123,7 @@ Every sync-wave in the repository, in order. **App** = hub-level Argo CD Applica
| rh-keycloak | — | 35 | After ZTWIM for SPIFFE IdP (newly added) |
| noobaa-mcg | 5 | 36 | Deploy after core services |
| qtodo | — | 38 | After Keycloak, Vault (newly added) |
| netobserv | — | 42 | After NooBaa |
| acs-central | 10 | 41 | — |
| quay-registry | 10 | 41 | Deploy after NooBaa |
| trusted-profile-analyzer | 10 | 41 | Chart resources (OBC, DB, etc.) |
Expand Down Expand Up @@ -171,6 +184,16 @@ Charts marked **(external)** have been externalized to standalone repositories m
| noobaa-system.yaml | 2 | 33 |
| bucket-class.yaml | 3 | 34 |

### netobserv (`charts/netobserv/templates/`) — App wave: 42

| Resource | Old | Current |
| --- | ---: | ---: |
| object-bucket-claim.yaml | — | 36 |
| loki-s3-credentials-job.yaml (SA, Role, RoleBinding) | — | 37 |
| loki-s3-credentials-job.yaml (Job hook) | — | 39 |
| lokistack.yaml | — | 41 |
| flowcollector.yaml | — | 42 |

### keycloak — **(external)** `rhbk-chart` v0.0.4 — App wave: 35

| Resource | Old | Current |
Expand Down
Loading
Loading