Initial import of demo-secure
This commit is contained in:
commit
352d79e5fc
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
demo-secure
|
||||
17
Dockerfile
Normal file
17
Dockerfile
Normal file
@ -0,0 +1,17 @@
|
||||
FROM registry.access.redhat.com/ubi9/go-toolset:1.22 AS build
|
||||
WORKDIR /opt/app-root/src
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
COPY . .
|
||||
ARG GIT_COMMIT=unknown
|
||||
ENV CGO_ENABLED=0
|
||||
RUN go build -trimpath -ldflags "-s -w -X main.commit=${GIT_COMMIT}" -o /tmp/demo-secure .
|
||||
|
||||
FROM registry.access.redhat.com/ubi9-minimal:latest
|
||||
LABEL org.opencontainers.image.title="demo-secure" \
|
||||
org.opencontainers.image.description="RHADS supply-chain demo service" \
|
||||
org.opencontainers.image.source="http://nas1.internal.hilltopcampground.net:23232/openshift-lab/bootstrap"
|
||||
COPY --from=build /tmp/demo-secure /usr/local/bin/demo-secure
|
||||
USER 1001
|
||||
EXPOSE 8080
|
||||
ENTRYPOINT ["/usr/local/bin/demo-secure"]
|
||||
26
README.md
Normal file
26
README.md
Normal file
@ -0,0 +1,26 @@
|
||||
# demo-secure
|
||||
|
||||
Minimal Go HTTP service used by the RHADS supply-chain demo.
|
||||
|
||||
## The deliberate CVE
|
||||
|
||||
`go.mod` pins `github.com/dgrijalva/jwt-go v3.2.0+incompatible`, which has a
|
||||
documented vulnerability (CVE-2020-26160 — `alg=none` not rejected). The
|
||||
`/verify` handler actually calls into this library, so SBOM scanners pick the
|
||||
dependency up as in-use and RHTPA flags it.
|
||||
|
||||
## Fixing the CVE (the "patch" act)
|
||||
|
||||
1. `go get github.com/golang-jwt/jwt/v5`
|
||||
2. `go mod tidy`
|
||||
3. Update the import in `main.go` from `github.com/dgrijalva/jwt-go` to
|
||||
`github.com/golang-jwt/jwt/v5`
|
||||
4. Commit and push — the Tekton pipeline rebuilds, re-signs, ArgoCD redeploys,
|
||||
RHTPA dashboard flips from red to green.
|
||||
|
||||
## Endpoints
|
||||
|
||||
- `GET /` — landing page
|
||||
- `GET /version` — JSON with Go version + dep list (proves which JWT lib is shipped)
|
||||
- `GET /healthz` — liveness probe target
|
||||
- `POST /verify` — `Authorization: Bearer <jwt>` → returns claims if valid
|
||||
27
catalog-info.yaml
Normal file
27
catalog-info.yaml
Normal file
@ -0,0 +1,27 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: demo-secure
|
||||
description: Minimal Go service demonstrating the RHADS supply chain (build → sign → scan → patch).
|
||||
annotations:
|
||||
backstage.io/kubernetes-id: demo-secure
|
||||
backstage.io/kubernetes-namespace: demo-secure
|
||||
backstage.io/techdocs-ref: dir:.
|
||||
janus-idp.io/tekton: demo-secure
|
||||
argocd/app-name: demo-secure
|
||||
links:
|
||||
- url: https://demo-secure-demo-secure.apps.lab.hibachi.ninja
|
||||
title: Live service
|
||||
spec:
|
||||
type: service
|
||||
lifecycle: experimental
|
||||
owner: platform
|
||||
system: rhads-demo
|
||||
---
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: System
|
||||
metadata:
|
||||
name: rhads-demo
|
||||
description: End-to-end RHADS supply-chain demo (RHDH + RHTAS + RHTPA).
|
||||
spec:
|
||||
owner: platform
|
||||
5
go.mod
Normal file
5
go.mod
Normal file
@ -0,0 +1,5 @@
|
||||
module github.com/openshift-lab/demo-secure
|
||||
|
||||
go 1.22
|
||||
|
||||
require github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
2
go.sum
Normal file
2
go.sum
Normal file
@ -0,0 +1,2 @@
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
102
main.go
Normal file
102
main.go
Normal file
@ -0,0 +1,102 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
|
||||
jwt "github.com/dgrijalva/jwt-go"
|
||||
)
|
||||
|
||||
func signingKey() []byte {
|
||||
if k := os.Getenv("JWT_KEY"); k != "" {
|
||||
return []byte(k)
|
||||
}
|
||||
return []byte("demo-only-not-for-real-use")
|
||||
}
|
||||
|
||||
type versionInfo struct {
|
||||
Service string `json:"service"`
|
||||
Commit string `json:"commit"`
|
||||
GoVersion string `json:"go_version"`
|
||||
Deps map[string]string `json:"deps"`
|
||||
JWTLibrary string `json:"jwt_library"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/", index)
|
||||
mux.HandleFunc("/version", version)
|
||||
mux.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprintln(w, "ok")
|
||||
})
|
||||
mux.HandleFunc("/verify", verifyToken)
|
||||
|
||||
port := os.Getenv("PORT")
|
||||
if port == "" {
|
||||
port = "8080"
|
||||
}
|
||||
log.Printf("demo-secure listening on :%s", port)
|
||||
if err := http.ListenAndServe(":"+port, mux); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func index(w http.ResponseWriter, _ *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
fmt.Fprintf(w, `<!doctype html>
|
||||
<html><head><title>demo-secure</title></head>
|
||||
<body style="font-family:sans-serif;max-width:40em;margin:3em auto">
|
||||
<h1>demo-secure</h1>
|
||||
<p>A minimal Go service used to demonstrate the RHADS supply chain:
|
||||
RHDH onboarding → RHTAS signing → RHTPA scanning → patch.</p>
|
||||
<ul>
|
||||
<li><a href="/version">/version</a> — dependency info</li>
|
||||
<li><a href="/healthz">/healthz</a> — liveness</li>
|
||||
<li><code>POST /verify</code> with a Bearer token — exercises the JWT library</li>
|
||||
</ul>
|
||||
</body></html>`)
|
||||
}
|
||||
|
||||
func version(w http.ResponseWriter, _ *http.Request) {
|
||||
info := versionInfo{
|
||||
Service: "demo-secure",
|
||||
Commit: os.Getenv("GIT_COMMIT"),
|
||||
Deps: map[string]string{},
|
||||
}
|
||||
if bi, ok := debug.ReadBuildInfo(); ok {
|
||||
info.GoVersion = bi.GoVersion
|
||||
for _, d := range bi.Deps {
|
||||
info.Deps[d.Path] = d.Version
|
||||
if d.Path == "github.com/dgrijalva/jwt-go" || d.Path == "github.com/golang-jwt/jwt/v5" {
|
||||
info.JWTLibrary = fmt.Sprintf("%s@%s", d.Path, d.Version)
|
||||
}
|
||||
}
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(info)
|
||||
}
|
||||
|
||||
func verifyToken(w http.ResponseWriter, r *http.Request) {
|
||||
tok := r.Header.Get("Authorization")
|
||||
if len(tok) > 7 && tok[:7] == "Bearer " {
|
||||
tok = tok[7:]
|
||||
}
|
||||
if tok == "" {
|
||||
http.Error(w, "missing bearer token", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
parsed, err := jwt.Parse(tok, func(t *jwt.Token) (interface{}, error) {
|
||||
return signingKey(), nil
|
||||
})
|
||||
if err != nil || !parsed.Valid {
|
||||
http.Error(w, "invalid token", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(parsed.Claims)
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user