Skip to content

Commit 71fa08c

Browse files
wip: adding addmission webhook
# Conflicts: # main.go
1 parent e1f821a commit 71fa08c

File tree

1 file changed

+15
-126
lines changed

1 file changed

+15
-126
lines changed

main.go

Lines changed: 15 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,6 @@ import (
44
"bytes"
55
"encoding/json"
66
"fmt"
7-
"log"
8-
"net/http"
9-
"os"
10-
"os/exec"
11-
"strings"
12-
"time"
13-
147
"github.com/joho/godotenv"
158
"github.com/manzil-infinity180/k8s-custom-controller/controller"
169
admissionv1 "k8s.io/api/admission/v1"
@@ -22,6 +15,11 @@ import (
2215
"k8s.io/client-go/rest"
2316
"k8s.io/client-go/tools/clientcmd"
2417
"k8s.io/client-go/tools/clientcmd/api"
18+
"log"
19+
"net/http"
20+
"os"
21+
"strings"
22+
"time"
2523
)
2624

2725
// homeDir retrieves the user's home directory
@@ -134,16 +132,7 @@ func main() {
134132
fmt.Errorf("%s", err.Error())
135133
}
136134

137-
// Start the webhook server in a goroutine
138-
go func() {
139-
http.HandleFunc("/validate", ValidateDeployment)
140-
log.Println("Starting webhook server on :8000...")
141-
// local go for certs/tls.crt and certs/tls.key
142-
err := http.ListenAndServeTLS(":8000", "/certs/tls.crt", "/certs/tls.key", nil) // k8s
143-
if err != nil {
144-
log.Fatalf("Failed to start webhook server: %v", err)
145-
}
146-
}()
135+
http.HandleFunc("/validate", ValidateDeployment)
147136

148137
ch := make(chan struct{})
149138
// factory
@@ -153,13 +142,6 @@ func main() {
153142
c.Run(ch)
154143
fmt.Println(factory)
155144
factory.Apps().V1().Deployments().Informer()
156-
157-
// Block forever
158-
select {}
159-
}
160-
161-
type Admitter struct {
162-
Request *admissionv1.AdmissionRequest
163145
}
164146

165147
func parseRequest(r *http.Request) (*admissionv1.AdmissionReview, error) {
@@ -184,138 +166,45 @@ func parseRequest(r *http.Request) (*admissionv1.AdmissionReview, error) {
184166
return &a, nil
185167
}
186168

187-
func scanImageWithTrivy(image string) (bool, string, error) {
188-
// cmd := exec.Command("trivy", "image", "--quiet", "--severity", "HIGH,CRITICAL", "--format", "json", image)
189-
// out, err := cmd.Output()
190-
cmd := exec.Command(
191-
"trivy",
192-
"image",
193-
"--scanners", "vuln",
194-
"--server", "http://trivy-server-service.default.svc:8080", // [service_name].[namespace].svc:[port] (if not port 80)
195-
"--format", "json",
196-
image,
197-
)
198-
out, err := cmd.Output()
199-
if err != nil {
200-
return false, "", fmt.Errorf("trivy scan failed for %s: %v", image, err)
201-
}
202-
var result map[string]interface{}
203-
if err := json.Unmarshal(out, &result); err != nil {
204-
return false, "", fmt.Errorf("failed to parse trivy output: %v", err)
205-
}
206-
// Check if vulnerabilities found
207-
vulns := []string{}
208-
log.Println("❗CVEs Found: ")
209-
if results, ok := result["Results"].([]interface{}); ok {
210-
for _, r := range results {
211-
rmap := r.(map[string]interface{})
212-
if vlist, ok := rmap["Vulnerabilities"].([]interface{}); ok {
213-
for _, v := range vlist {
214-
vmap := v.(map[string]interface{})
215-
severity := vmap["Severity"].(string)
216-
if severity == "HIGH" || severity == "CRITICAL" {
217-
msg := fmt.Sprintf(" - 🔥 %s\n", vmap["VulnerabilityID"].(string))
218-
//vulns = append(vulns, vmap["VulnerabilityID"].(string))
219-
vulns = append(vulns, msg)
220-
}
221-
}
222-
}
223-
}
224-
}
225-
if len(vulns) > 0 {
226-
return false, strings.Join(vulns, ","), nil
227-
}
228-
return true, "", nil
169+
type Admitter struct {
170+
Request *admissionv1.AdmissionRequest
229171
}
172+
230173
func ValidateDeployment(w http.ResponseWriter, r *http.Request) {
231-
log.Println("Received /validate request")
232174
in, err := parseRequest(r)
233175
if err != nil {
234-
log.Printf("Error parsing admission request: %v", err)
235176
http.Error(w, err.Error(), http.StatusBadRequest)
236177
return
237178
}
238179

180+
//adm := Admitter{
181+
// Request: in.Request,
182+
//}
239183
var dep appsv1.Deployment
240184
if err := json.Unmarshal(in.Request.Object.Raw, &dep); err != nil {
241-
log.Printf("Failed to unmarshal deployment: %v", err)
242185
http.Error(w, fmt.Sprintf("could not unmarshal deployment: %v", err), http.StatusBadRequest)
243186
return
244187
}
245188
images := []string{}
246-
denied := false
247-
var reasons []string
248-
BYPASS_CVE_DENIED := false
249-
// InitContainers
250-
for _, c := range dep.Spec.Template.Spec.InitContainers {
251-
for _, e := range c.Env {
252-
if e.Name == "BYPASS_CVE_DENIED" && (e.Value == "yes" || e.Value == "true") {
253-
BYPASS_CVE_DENIED = true
254-
}
255-
}
256-
images = append(images, c.Image)
257-
}
258-
// Containers
259189
for _, c := range dep.Spec.Template.Spec.Containers {
260-
for _, e := range c.Env {
261-
if e.Name == "BYPASS_CVE_DENIED" && (e.Value == "yes" || e.Value == "true") {
262-
BYPASS_CVE_DENIED = true
263-
}
264-
}
265190
images = append(images, c.Image)
266191
}
267-
for _, image := range images {
268-
log.Println("────────────────────────────────────────────────────")
269-
log.Printf("🛡️ Deployment Image Scanning Started : %s\n", image)
270-
if BYPASS_CVE_DENIED {
271-
log.Println("📦 BYPASS_CVE_DENIED: true/yes")
272-
} else {
273-
log.Println("📦 BYPASS_CVE_DENIED: default(false/no)")
274-
}
275-
ok, vulns, err := scanImageWithTrivy(image)
276-
if err != nil {
277-
log.Printf("Error scanning image %s: %v", image, err)
278-
continue
279-
}
280-
log.Println("────────────────────────────────────────────────────")
281-
if !ok {
282-
denied = true
283-
reasons = append(reasons, fmt.Sprintf("%s (CVE: %s)", image, vulns))
284-
}
285-
}
286-
message := "Images allowed"
287-
if denied {
288-
message = fmt.Sprintf("Denied images due to total CVEs across %v images: %v", len(images), reasons)
289-
log.Printf("Denied images due to CVEs: %v", reasons)
290-
}
291-
292-
// look for BYPASS_CVE env - you need to skip
293-
if BYPASS_CVE_DENIED {
294-
log.Printf("It have CVE across all the %v images, but we are skipping as BYPASS_CVE_DENIED set true", len(images))
295-
denied = false
296-
}
297-
298-
log.Printf("Validating Deployment: %s, Images: %v", dep.Name, images)
299192
response := admissionv1.AdmissionReview{
300193
TypeMeta: in.TypeMeta,
301194
Response: &admissionv1.AdmissionResponse{
302195
UID: in.Request.UID,
303-
Allowed: !denied,
196+
Allowed: false,
304197
Result: &metav1.Status{
305-
Message: message,
198+
Message: fmt.Sprintf("Denied images: %v", images),
306199
},
307200
},
308201
}
309202
w.Header().Set("Content-Type", "application/json")
310203
jout, err := json.Marshal(response)
311204
if err != nil {
312205
e := fmt.Sprintf("could not parse admission response: %v", err)
313-
log.Println(e)
314206
http.Error(w, e, http.StatusInternalServerError)
315207
return
316208
}
317-
if _, err := w.Write(jout); err != nil {
318-
log.Printf("Failed to write response: %v", err)
319-
}
320-
log.Println("Admission response sent")
209+
w.Write(jout)
321210
}

0 commit comments

Comments
 (0)