mirror of
https://github.com/jenkinsci/kubernetes-operator.git
synced 2026-10-18 23:40:31 +02:00
Implemented validation logic for the webhook
- Created a single Validate() function to validate both updating and creating Jenkins CR. - Implemented the Validate function to fetch warnings from the API and do security check if being enabled. - Updated the helm charts and helm-e2e target to run the helm tests.
This commit is contained in:
@@ -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)
|
||||
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)
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user