Skip to content

Commit 6195b0c

Browse files
authored
mbp-1008: UC02 automated pipeline (#81)
Signed-off-by: Manuel Lorenzo <mlorenzofr@redhat.com>
1 parent 1ec32f5 commit 6195b0c

30 files changed

+1562
-2
lines changed

.github/workflows/superlinter.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ jobs:
4343
VALIDATE_SHELL_SHFMT: false
4444
VALIDATE_YAML: false
4545
VALIDATE_YAML_PRETTIER: false
46+
VALIDATE_TEKTON: false
4647
# VALIDATE_DOCKERFILE_HADOLINT: false
4748
# VALIDATE_MARKDOWN: false
4849
# VALIDATE_NATURAL_LANGUAGE: false
49-
# VALIDATE_TEKTON: false

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,16 @@ The following components are included in the Layered Zero Trust Pattern
2828
* Synchronizes secrets stored in HashiCorp Vault with OpenShift
2929
* [Red Hat Advanced Cluster Management (ACM)](https://docs.redhat.com/en/documentation/red_hat_advanced_cluster_management_for_kubernetes/2.14)
3030
* Provides a management control in multi-cluster scenarios
31+
* [Red Hat Quay](https://docs.redhat.com/en/documentation/red_hat_quay/3.15)
32+
** * Enables a private repository for OCI images within the environment
33+
* [Multicloud Object Gateway](https://docs.redhat.com/en/documentation/red_hat_openshift_container_storage/4.8/html/managing_hybrid_and_multicloud_resources/index)
34+
* Provides an object storage service for Openshift
35+
* [Red Hat Trusted Artifact Signer (RHTAS)](https://docs.redhat.com/en/documentation/red_hat_trusted_artifact_signer/1.3)
36+
** * Provides cryptographic signing and verification of software artifacts and container images
37+
* [Red Hat Trusted Profile Analyzer (RHTPA)](https://docs.redhat.com/es/documentation/red_hat_trusted_profile_analyzer/2.2)
38+
** * Provides the storage and management means for _Software Bill of Materials_ (SBOMs), with cross-referencing capabilities between SBOMs and CVEs/Security Advisories
39+
* [Red Hat Openshift Pipelines](https://docs.redhat.com/en/documentation/red_hat_openshift_pipelines/1.20)
40+
* Enables a native CI/CD solution in Openshift
3141

3242
## Getting Started
3343

charts/qtodo/templates/app-serviceaccount.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,7 @@ metadata:
55
app: qtodo
66
name: qtodo
77
namespace: qtodo
8+
{{- if .Values.app.images.main.registry.auth }}
9+
imagePullSecrets:
10+
- name: {{ .Values.app.images.main.registry.secretName }}
11+
{{- end }}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{{- if .Values.app.images.main.registry.auth }}
2+
---
3+
apiVersion: "external-secrets.io/v1beta1"
4+
kind: ExternalSecret
5+
metadata:
6+
name: {{ .Values.app.images.main.registry.secretName }}
7+
namespace: {{ .Release.Namespace | default .Values.global.namespace }}
8+
spec:
9+
refreshInterval: 15s
10+
secretStoreRef:
11+
name: {{ .Values.global.secretStore.name }}
12+
kind: {{ .Values.global.secretStore.kind }}
13+
target:
14+
name: {{ .Values.app.images.main.registry.secretName }}
15+
template:
16+
type: kubernetes.io/dockerconfigjson
17+
data:
18+
.dockerconfigjson: |
19+
{
20+
"auths": {
21+
"{{ .Values.app.images.main.registry.domain | default (printf "quay-registry-quay-quay-enterprise.%s" .Values.global.hubClusterDomain) }}": {
22+
"auth": "{{ `{{ printf "%s:%s" "` }}{{ .Values.app.images.main.registry.user }}{{ `" .password | b64enc }}` }}"
23+
}
24+
}
25+
}
26+
data:
27+
- secretKey: password
28+
remoteRef:
29+
key: {{ .Values.app.images.main.registry.vaultPath }}
30+
property: {{ .Values.app.images.main.registry.passwordVaultKey }}
31+
{{- end }}

charts/qtodo/values.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ app:
1414
tag: latest
1515
# Modified to Always to force a pull so we can test changes to the container image without requiring manual deletion of images or restarts of argo
1616
pullPolicy: Always
17+
registry:
18+
auth: false
19+
secretName: qtodo-registry-auth
20+
user: quay-user
21+
# domain: quay-registry-quay-quay-enterprise.apps.example.com
22+
vaultPath: secret/data/global/quay-users
23+
passwordVaultKey: password
1724
spiffeHelper:
1825
name: ghcr.io/spiffe/spiffe-helper
1926
tag: 0.10.1

charts/supply-chain/Chart.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
apiVersion: v2
2+
name: supply-chain
3+
description: A Helm chart for Supply Chain (UC02)
4+
type: application
5+
version: 0.1.0
6+
appVersion: "1.0.0"
7+
keywords:
8+
- supply-chain
9+
- uc02
10+
- zero-trust
11+
- tekton
12+
maintainers:
13+
- name: Zero Trust Validated Patterns Team
14+
email: ztvp-arch-group@redhat.com
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
#!/usr/bin/env python3
2+
3+
import http.cookiejar
4+
import json
5+
import os
6+
import ssl
7+
import sys
8+
import time
9+
import urllib.error
10+
import urllib.parse
11+
import urllib.request
12+
13+
# Configuration
14+
QUAY_HOST = os.getenv("QUAY_HOST")
15+
USERNAME = os.getenv("QUAY_ADMIN_USER", "username")
16+
EMAIL = os.getenv("QUAY_ADMIN_EMAIL", "user@example.com")
17+
PASSWORD = os.getenv("QUAY_ADMIN_PASSWORD")
18+
CA_CERT = os.getenv("CA_CERT", "/run/secrets/kubernetes.io/serviceaccount/ca.crt")
19+
20+
if not all([QUAY_HOST, PASSWORD]):
21+
print("ERROR: Missing QUAY_HOST or QUAY_ADMIN_PASSWORD env vars")
22+
sys.exit(1)
23+
24+
BASE_URL = f"https://{QUAY_HOST}"
25+
26+
27+
def log(msg):
28+
"""Log a message to the console"""
29+
print(f"[{time.strftime('%X')}] {msg}", flush=True)
30+
31+
32+
# Setup SSL
33+
ctx = ssl.create_default_context()
34+
if os.path.exists(CA_CERT):
35+
ctx.load_verify_locations(CA_CERT)
36+
log(f"Using CA certificate from {CA_CERT}")
37+
ctx.check_hostname = True
38+
ctx.verify_mode = ssl.CERT_REQUIRED
39+
else:
40+
log(f"WARNING: CA certificate file not found at {CA_CERT}")
41+
ctx.check_hostname = False
42+
ctx.verify_mode = ssl.CERT_NONE
43+
44+
# Setup Cookies (Required for CSRF)
45+
cj = http.cookiejar.CookieJar()
46+
opener = urllib.request.build_opener(
47+
urllib.request.HTTPSHandler(context=ctx),
48+
urllib.request.HTTPCookieProcessor(cj),
49+
)
50+
51+
52+
def wait_for_quay():
53+
"""Loop until Quay health endpoint returns 200"""
54+
url = f"{BASE_URL}/health/instance"
55+
while True:
56+
try:
57+
log(f"Checking Quay health at {url}...")
58+
with opener.open(url, timeout=10) as response:
59+
if response.status == 200:
60+
log("Quay is Online.")
61+
return
62+
except Exception as e:
63+
log(f"Quay unavailable ({e}). Retrying in 5s...")
64+
time.sleep(5)
65+
66+
67+
def get_csrf_token():
68+
"""Fetch CSRF token and prime the cookie jar"""
69+
url = f"{BASE_URL}/csrf_token"
70+
with opener.open(url) as response:
71+
data = json.loads(response.read().decode())
72+
token = data.get("csrf_token")
73+
return token
74+
75+
76+
def create_user():
77+
"""Perform the creation flow"""
78+
try:
79+
log("Attempting to create user...")
80+
csrf_token = get_csrf_token()
81+
82+
url = f"{BASE_URL}/api/v1/user/"
83+
payload = json.dumps(
84+
{
85+
"username": USERNAME,
86+
"email": EMAIL,
87+
"password": PASSWORD,
88+
"_csrf_token": csrf_token,
89+
}
90+
).encode("utf-8")
91+
92+
headers = {
93+
"Content-Type": "application/json",
94+
"X-CSRF-Token": csrf_token,
95+
}
96+
97+
req = urllib.request.Request(url, data=payload, headers=headers, method="POST")
98+
99+
with opener.open(req) as response:
100+
if response.status in [200, 201, 202]:
101+
log("SUCCESS: User created successfully.")
102+
return True
103+
except urllib.error.HTTPError as e:
104+
if e.code == 400:
105+
log(f"User '{USERNAME}' already exists. Exiting.")
106+
return True
107+
log(f"FAILED to create user: {e.code} {e.reason}")
108+
except Exception as e:
109+
log(f"FAILED to create user: {e}")
110+
return False
111+
112+
113+
# Main
114+
if __name__ == "__main__":
115+
log("Starting Quay User Automator")
116+
117+
wait_for_quay()
118+
119+
while True:
120+
if create_user():
121+
sys.exit(0)
122+
123+
log("Retrying user creation in 10s...")
124+
time.sleep(10)

0 commit comments

Comments
 (0)