-
Notifications
You must be signed in to change notification settings - Fork 236
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
Implemented validation logic for the webhook #593
Changes from 1 commit
b407e51
52fe5fe
db0978c
800c1a7
35dfd47
7ce9d1f
463cad3
bd32624
9d5c525
935b60b
dfd25e8
17dba08
37d0eac
8453b3e
858f0f4
b82fc7c
b400a42
1d2651d
853f485
9106582
90b685d
b11ca32
5ca4e0a
e2ec2ea
f527a8c
9594c8e
95c29d4
ba66ba4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,15 @@ limitations under the License. | |
package v1alpha2 | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"io/ioutil" | ||
"net/http" | ||
"time" | ||
|
||
"github.com/jenkinsci/kubernetes-operator/pkg/plugins" | ||
|
||
"golang.org/x/mod/semver" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
ctrl "sigs.k8s.io/controller-runtime" | ||
logf "sigs.k8s.io/controller-runtime/pkg/log" | ||
|
@@ -41,21 +50,142 @@ var _ webhook.Validator = &Jenkins{} | |
|
||
// ValidateCreate implements webhook.Validator so a webhook will be registered for the type | ||
func (in *Jenkins) ValidateCreate() error { | ||
jenkinslog.Info("validate create", "name", in.Name) | ||
if in.Spec.ValidateSecurityWarnings { | ||
jenkinslog.Info("validate create", "name", in.Name) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are these logs useful in any way? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah for debugging purposes I was checking if the function is being called and get a look the the reconciller object. |
||
return Validate(*in) | ||
} | ||
|
||
// TODO(user): fill in your validation logic upon object creation. | ||
return nil | ||
} | ||
|
||
// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type | ||
func (in *Jenkins) ValidateUpdate(old runtime.Object) error { | ||
jenkinslog.Info("validate update", "name", in.Name) | ||
if in.Spec.ValidateSecurityWarnings { | ||
jenkinslog.Info("validate update", "name", in.Name) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are these logs useful in any way? |
||
return Validate(*in) | ||
} | ||
|
||
// TODO(user): fill in your validation logic upon object update. | ||
return nil | ||
} | ||
|
||
func (in *Jenkins) ValidateDelete() error { | ||
// TODO(user): fill in your validation logic upon object deletion. | ||
return nil | ||
} | ||
|
||
type Warnings struct { | ||
Warnings []Warning `json:"securityWarnings"` | ||
} | ||
|
||
type Warning struct { | ||
Versions []Version `json:"versions"` | ||
ID string `json:"id"` | ||
Message string `json:"message"` | ||
URL string `json:"url"` | ||
Active bool `json:"active"` | ||
} | ||
type Version struct { | ||
FirstVersion string `json:"firstVersion"` | ||
LastVersion string `json:"lastVersion"` | ||
} | ||
|
||
const APIURL string = "https://plugins.jenkins.io/api/plugin/" | ||
|
||
func MakeSemanticVersion(version string) string { | ||
version = "v" + version | ||
return semver.Canonical(version) | ||
} | ||
|
||
func CompareVersions(firstVersion string, lastVersion string, pluginVersion string) bool { | ||
firstSemVer := MakeSemanticVersion(firstVersion) | ||
lastSemVer := MakeSemanticVersion(lastVersion) | ||
pluginSemVer := MakeSemanticVersion(pluginVersion) | ||
if semver.Compare(pluginSemVer, firstSemVer) == -1 || semver.Compare(pluginSemVer, lastSemVer) == 1 { | ||
return false | ||
} | ||
return true | ||
} | ||
|
||
func CheckSecurityWarnings(pluginName string, pluginVersion string) (bool, error) { | ||
jenkinslog.Info("checking security warnings", "plugin: ", pluginName) | ||
pluginURL := APIURL + pluginName | ||
client := &http.Client{ | ||
Timeout: time.Second * 30, | ||
} | ||
request, err := http.NewRequest("GET", pluginURL, nil) | ||
if err != nil { | ||
return false, err | ||
} | ||
request.Header.Add("Accept", "application/json") | ||
request.Header.Add("Content-Type", "application/json") | ||
response, err := client.Do(request) | ||
if err != nil { | ||
return false, err | ||
} | ||
defer response.Body.Close() | ||
bodyBytes, err := ioutil.ReadAll(response.Body) | ||
if err != nil { | ||
return false, err | ||
} | ||
securityWarnings := Warnings{} | ||
|
||
jsonErr := json.Unmarshal(bodyBytes, &securityWarnings) | ||
if jsonErr != nil { | ||
return false, err | ||
} | ||
|
||
jenkinslog.Info("Validate()", "warnings", securityWarnings) | ||
|
||
for _, warning := range securityWarnings.Warnings { | ||
for _, version := range warning.Versions { | ||
firstVersion := version.FirstVersion | ||
lastVersion := version.LastVersion | ||
if len(firstVersion) == 0 { | ||
firstVersion = "0" // setting default value in case of empty string | ||
} | ||
if len(lastVersion) == 0 { | ||
lastVersion = pluginVersion // setting default value in case of empty string | ||
} | ||
|
||
if CompareVersions(firstVersion, lastVersion, pluginVersion) { | ||
jenkinslog.Info("security Vulnerabilities detected", "message", warning.Message, "Check security Advisory", warning.URL) | ||
return true, nil | ||
} | ||
} | ||
} | ||
|
||
return false, nil | ||
} | ||
|
||
func Validate(r Jenkins) error { | ||
basePlugins := plugins.BasePlugins() | ||
var warnings string = "" | ||
|
||
for _, plugin := range basePlugins { | ||
name := plugin.Name | ||
version := plugin.Version | ||
hasWarnings, err := CheckSecurityWarnings(name, version) | ||
if err != nil { | ||
return err | ||
} | ||
if hasWarnings { | ||
warnings += "Security Vulnerabilities detected in base plugin:" + name | ||
} | ||
} | ||
|
||
for _, plugin := range r.Spec.Master.Plugins { | ||
name := plugin.Name | ||
version := plugin.Version | ||
hasWarnings, err := CheckSecurityWarnings(name, version) | ||
if err != nil { | ||
return err | ||
} | ||
if hasWarnings { | ||
warnings += "Security Vulnerabilities detected in the user defined plugin: " + name | ||
} | ||
} | ||
if len(warnings) > 0 { | ||
return errors.New(warnings) | ||
} | ||
|
||
return nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also please add a TODO to refactor the logic for deploying cert manager in the helm charts later :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah sure.