Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added valImage to monitor image vulnerabilities #1175

Closed
wants to merge 3 commits into from
Closed
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
30 changes: 30 additions & 0 deletions images/validate-image/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Copyright 2019 The Knative Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

FROM golang:1.12.1
Copy link
Contributor

Choose a reason for hiding this comment

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

FROM golang:latest?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Didn't know this was an option. Will try this out :)

RUN apt-get update

ENV TEMP_REPO_DIR /go/src/knative.dev/test-infra
ENV TOOL_NAME validate-image

# Temporarily add test-infra to the image to build custom tools
ADD ./ $TEMP_REPO_DIR

RUN make -C $TEMP_REPO_DIR/tools/$TOOL_NAME/
RUN cp $TEMP_REPO_DIR/tools/$TOOL_NAME/$TOOL_NAME /$TOOL_NAME

# Remove test-infra from the container
RUN rm -fr $TEMP_REPO_DIR

ENTRYPOINT ["/validate-image"]
16 changes: 16 additions & 0 deletions images/validate-image/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright 2019 The Knative Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

IMAGE_NAME = validate-image
include ../../shared/Makefile.simple-image
16 changes: 16 additions & 0 deletions tools/validate-image/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright 2019 The Knative Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

all:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build .
72 changes: 72 additions & 0 deletions tools/validate-image/gke_deployment/validate_service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Copyright 2019 The Knative Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

apiVersion: v1
kind: Namespace
metadata:
name: validate-image
---
apiVersion: v1
kind: Service
metadata:
name: validate-image-service
namespace: validate-image
labels:
app: validate-image
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: http-server
selector:
app: validate-image
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: validate-image-app
namespace: validate-image
labels:
app: validate-image
spec:
template:
metadata:
labels:
app: validate-image
spec:
containers:
- name: validate-image-app
image: gcr.io/knative-tests/test-infra/validate-image:latest
command: ["/validate-image"]
env:
- name: GOOGLE_APPLICATION_CREDENTIALS
value: /secrets/google-app-credential/knative-monitoring-credential.json
imagePullPolicy: Always
ports:
- name: http-server
containerPort: 8080
volumeMounts:
- name: sender-email-credentials
mountPath: /secrets/sender-email
readOnly: true
- name: google-app-credentials
mountPath: /secrets/google-app-credential/
readOnly: true
volumes:
- name: sender-email-credentials
secret:
secretName: sender-email-credentials
- name: google-app-credentials
secret:
secretName: google-app-credentials
62 changes: 62 additions & 0 deletions tools/validate-image/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright 2019 The Knative Authors

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

// http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"flag"
"fmt"
"log"
"net/http"
"os"

"knative.dev/test-infra/tools/monitoring/mail"
)

func main() {
mailAddrSF := flag.String("sender-email", "/secrets/sender-email/mail", "Alert sender email address file")
mailPassSF := flag.String("sender-password", "/secrets/sender-email/password", "Alert sender email password file")
flag.Parse()

mailConfig, err := mail.NewMailConfig(*mailAddrSF, *mailPassSF)
if err != nil {
log.Fatal(err)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
log.Fatal(err)
log.Fatalf("Failed to create mail config: %v", err)

}

imageClient, err := NewValidateImageClient(mailConfig)
if err != nil {
log.Fatalf("Failed to create ValidateImageClient. Error: %v\n", err)
}
imageClient.Run()

// use PORT environment variable, or default to 8080
port := "8080"
if fromEnv := os.Getenv("PORT"); fromEnv != "" {
port = fromEnv
}

// register hello function to handle all requests
server := http.NewServeMux()
server.HandleFunc("/validate-image", validateImage)

// start the web server on port and accept requests
log.Printf("Server listening on port %s", port)
log.Fatal(http.ListenAndServe(":"+port, server))
}

func validateImage(w http.ResponseWriter, r *http.Request) {
log.Printf("Serving request: %s", r.URL.Path)

Copy link
Contributor

Choose a reason for hiding this comment

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

remove blank line

fmt.Fprintf(w, "Last validate-image alert sent: %v\n", lastSent)
}
111 changes: 111 additions & 0 deletions tools/validate-image/valImage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright 2019 The Knative Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"context"
"encoding/json"
"fmt"
"log"
"time"

"cloud.google.com/go/pubsub"
"knative.dev/test-infra/tools/monitoring/mail"
"knative.dev/test-infra/tools/monitoring/subscriber"
)

var (
// monitoringSubs is a list of subscriptions to subscribe for image vulnerabilities
monitoringSubs = [...]string{
"sub-container-analysis-notes-v1beta1",
"sub-container-analysis-occurrences-v1beta1",
}
recipients = []string{"[email protected]"}

// alertFreq is the minimum wait time before sending another image vulnerability alert
alertFreq = 24 * time.Hour

// Cache the last alert time in memory to prevent multiple image
// vulnerability alerts sent in a short duration of time.
lastSent = time.Time{}
)

// Client holds resources for monitoring image vulnerabilities
type Client struct {
subClients []*subscriber.Client
mailClient *mail.Config
}

// NewValidateImageClient initialize all the resources for monitoring image vulnerabilities
func NewValidateImageClient(mconfig *mail.Config) (*Client, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Add unit test

var subClients = make([]*subscriber.Client, 0)

for _, sub := range monitoringSubs {
log.Printf("Appending sub: %v\n", sub)
subc, err := subscriber.NewSubscriberClient(sub)
if err != nil {
return nil, err
}
subClients = append(subClients, subc)
log.Printf("subclients: %v\n", subClients)
}

return &Client{
subClients: subClients,
mailClient: mconfig,
Copy link
Contributor

Choose a reason for hiding this comment

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

we should validate mConfig != nil

}, nil
}

// Run start a background process that listens to the message
func (c *Client) Run() {
log.Println("Starting image vulnerabilities monitoring")
for _, sub := range c.subClients {
c.listen(sub)
}
}

func (c *Client) listen(subClient *subscriber.Client) {
go func() {
err := subClient.Receive(context.Background(), func(ctx context.Context, msg *pubsub.Message) {
log.Printf("Message: %v\n", string(msg.Data))
log.Printf("Pubsub Message: %v\n", msg)
Copy link
Contributor

Choose a reason for hiding this comment

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

whats the point of logging both? This will log the data as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The one logged with string(msg.Data) prints data in a human-readable format, while printing the message directly from msg prints the message as base64encoded string.

I'd like to keep the pubsub message for debugging purposes in case there's additional attributes that gets added to the image vulnerabilities message.

# log.Printf("Message: %v\n", string(msg.Data))
Message Data: {"name":"projects/joyceyu-test/occurrences/ce3b5a33-05ed-40c2-80f2-821d38426171","kind":"DISCOVERY","notificationTime":"2019-09-12T18:04:36.630196Z"}

# log.Printf("Pubsub Message: %v\n", msg)
Pubsub Message: {
        "ID": "719306958870977",
        "Data": "eyJuYW1lIjoicHJvamVjdHMvam95Y2V5dS10ZXN0L29jY3VycmVuY2VzL2NlM2I1YTMzLTA1ZWQtNDBjMi04MGYyLTgyMWQzODQyNjE3MSIsImtpbmQiOiJESVNDT1ZFUlkiLCJub3RpZmljYXRpb25UaW1lIjoiMjAxOS0wOS0xMlQxODowNDozNi42MzAxOTZaIn0=",
        "Attributes": null,
        "PublishTime": "2019-09-12T18:04:37.058Z"
}


if time.Now().Sub(lastSent) > alertFreq {
err := c.mailClient.Send(recipients, "Image Vulnerabilities Detected", toMailContent(msg))
Copy link
Contributor

Choose a reason for hiding this comment

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

combine if
if err := ...; err != nil {}

if err != nil {
log.Printf("Failed to send alert message %v\n", err)
} else {
lastSent = time.Now()
}
} else {
log.Println("Message not sent because an alert is sent recently.")
}
msg.Ack()
})
if err != nil {
log.Printf("Failed to receive messages due to: %v\n", err)
}
}()
}

func toMailContent(msg *pubsub.Message) string {
c := fmt.Sprintf("Message Data: %v\n", string(msg.Data))
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are we logging message and then the data from the message as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

From the alert receiver's perspective, the pubsub message is probably not useful. I'll update the mail content to only print out the human readable message.

if b, err := json.MarshalIndent(msg, "", "\t"); err == nil {
c += fmt.Sprintf("\nPubsub Message: %v\n", string(b))
}
c += fmt.Sprintf("\nRaw Message: %+v\n", msg)
log.Printf("Mail Content:\n %v\n", c)
return c
}