@@ -18,6 +18,7 @@ import (
18
18
"log"
19
19
"net/http"
20
20
"os"
21
+ "os/exec"
21
22
"strings"
22
23
"time"
23
24
)
@@ -155,6 +156,10 @@ func main() {
155
156
select {}
156
157
}
157
158
159
+ type Admitter struct {
160
+ Request * admissionv1.AdmissionRequest
161
+ }
162
+
158
163
func parseRequest (r * http.Request ) (* admissionv1.AdmissionReview , error ) {
159
164
if r .Header .Get ("Content-Type" ) != "application/json" {
160
165
return nil , fmt .Errorf ("Content-Type: %q should be %q" ,
@@ -177,10 +182,37 @@ func parseRequest(r *http.Request) (*admissionv1.AdmissionReview, error) {
177
182
return & a , nil
178
183
}
179
184
180
- type Admitter struct {
181
- Request * admissionv1.AdmissionRequest
185
+ func scanImageWithTrivy (image string ) (bool , string , error ) {
186
+ cmd := exec .Command ("trivy" , "image" , "--quiet" , "--severity" , "HIGH,CRITICAL" , "--format" , "json" , image )
187
+ out , err := cmd .Output ()
188
+ if err != nil {
189
+ return false , "" , fmt .Errorf ("trivy scan failed for %s: %v" , image , err )
190
+ }
191
+ var result map [string ]interface {}
192
+ if err := json .Unmarshal (out , & result ); err != nil {
193
+ return false , "" , fmt .Errorf ("failed to parse trivy output: %v" , err )
194
+ }
195
+ // Check if vulnerabilities found
196
+ vulns := []string {}
197
+ if results , ok := result ["Results" ].([]interface {}); ok {
198
+ for _ , r := range results {
199
+ rmap := r .(map [string ]interface {})
200
+ if vlist , ok := rmap ["Vulnerabilities" ].([]interface {}); ok {
201
+ for _ , v := range vlist {
202
+ vmap := v .(map [string ]interface {})
203
+ severity := vmap ["Severity" ].(string )
204
+ if severity == "HIGH" || severity == "CRITICAL" {
205
+ vulns = append (vulns , vmap ["VulnerabilityID" ].(string ))
206
+ }
207
+ }
208
+ }
209
+ }
210
+ }
211
+ if len (vulns ) > 0 {
212
+ return false , strings .Join (vulns , "," ), nil
213
+ }
214
+ return true , "" , nil
182
215
}
183
-
184
216
func ValidateDeployment (w http.ResponseWriter , r * http.Request ) {
185
217
log .Println ("Received /validate request" )
186
218
in , err := parseRequest (r )
@@ -190,27 +222,47 @@ func ValidateDeployment(w http.ResponseWriter, r *http.Request) {
190
222
return
191
223
}
192
224
193
- //adm := Admitter{
194
- // Request: in.Request,
195
- //}
196
225
var dep appsv1.Deployment
197
226
if err := json .Unmarshal (in .Request .Object .Raw , & dep ); err != nil {
198
227
log .Printf ("Failed to unmarshal deployment: %v" , err )
199
228
http .Error (w , fmt .Sprintf ("could not unmarshal deployment: %v" , err ), http .StatusBadRequest )
200
229
return
201
230
}
202
231
images := []string {}
232
+ denied := false
233
+ var reasons []string
234
+ // InitContainers
235
+ for _ , c := range dep .Spec .Template .Spec .InitContainers {
236
+ images = append (images , c .Image )
237
+ }
238
+ // Containers
203
239
for _ , c := range dep .Spec .Template .Spec .Containers {
204
240
images = append (images , c .Image )
205
241
}
242
+ for _ , image := range images {
243
+ ok , vulns , err := scanImageWithTrivy (image )
244
+ if err != nil {
245
+ log .Printf ("Error scanning image %s: %v" , image , err )
246
+ continue
247
+ }
248
+ if ! ok {
249
+ denied = true
250
+ reasons = append (reasons , fmt .Sprintf ("%s (CVE: %s)" , image , vulns ))
251
+ }
252
+ }
253
+ message := "Images allowed"
254
+ if denied {
255
+ message = fmt .Sprintf ("Denied images due to CVEs: %v" , reasons )
256
+ }
257
+
206
258
log .Printf ("Validating Deployment: %s, Images: %v" , dep .Name , images )
207
259
response := admissionv1.AdmissionReview {
208
260
TypeMeta : in .TypeMeta ,
209
261
Response : & admissionv1.AdmissionResponse {
210
262
UID : in .Request .UID ,
211
- Allowed : false ,
263
+ Allowed : ! denied ,
212
264
Result : & metav1.Status {
213
- Message : fmt . Sprintf ( "Denied images: %v" , images ) ,
265
+ Message : message ,
214
266
},
215
267
},
216
268
}
0 commit comments