Skip to content
Merged
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
338 changes: 316 additions & 22 deletions integrity-shield-operator/go.sum

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions observer/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/IBM/integrity-shield/observer v0.0.0-00010101000000-000000000000
github.com/IBM/integrity-shield/shield v0.0.0-00010101000000-000000000000
github.com/pkg/errors v0.9.1
github.com/sigstore/k8s-manifest-sigstore v0.0.0-20210820081408-1767e96c5fe2
github.com/sigstore/k8s-manifest-sigstore v0.0.0-20210909071548-2120192e4ff7
github.com/sirupsen/logrus v1.8.1
k8s.io/api v0.21.3
k8s.io/apimachinery v0.21.3
Expand All @@ -17,7 +17,6 @@ replace (
github.com/IBM/integrity-shield/observer => ./
github.com/IBM/integrity-shield/shield => ../shield
github.com/IBM/integrity-shield/webhook/admission-controller => ../webhook/admission-controller
github.com/sigstore/k8s-manifest-sigstore => github.com/hirokuni-kitahara/k8s-manifest-sigstore v0.0.0-20210901055134-ae30242ab9d1
k8s.io/kubectl => k8s.io/kubectl v0.21.2

)
Expand Down
315 changes: 292 additions & 23 deletions observer/go.sum

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions shield/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ require (
github.com/ghodss/yaml v1.0.0
github.com/jinzhu/copier v0.3.2
github.com/pkg/errors v0.9.1
github.com/sigstore/k8s-manifest-sigstore v0.0.0-20210820081408-1767e96c5fe2
github.com/sigstore/cosign v1.1.0
github.com/sigstore/k8s-manifest-sigstore v0.0.0-20210909071548-2120192e4ff7
github.com/sirupsen/logrus v1.8.1
k8s.io/api v0.21.3
k8s.io/apimachinery v0.21.3
Expand All @@ -17,7 +18,6 @@ require (

replace (
github.com/IBM/integrity-shield/shield => ./
github.com/sigstore/k8s-manifest-sigstore => github.com/hirokuni-kitahara/k8s-manifest-sigstore v0.0.0-20210901055134-ae30242ab9d1
k8s.io/kubectl => k8s.io/kubectl v0.21.2
)

Expand Down
310 changes: 290 additions & 20 deletions shield/go.sum

Large diffs are not rendered by default.

37 changes: 37 additions & 0 deletions shield/pkg/config/parameter.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,25 @@ type KeyConfig struct {
KeySecretNamespace string `json:"keySecretNamespace,omitempty"`
}

type ImageRef string
type ImageRefList []ImageRef

func (l ImageRefList) Match(imageRef string) bool {
if len(l) == 0 {
return true
}
for _, r := range l {
if r.Match(imageRef) {
return true
}
}
return false
}

func (r ImageRef) Match(imageRef string) bool {
return k8smnfutil.MatchPattern(string(r), imageRef)
}

type ObjectUserBindingList []ObjectUserBinding

type ObjectUserBinding struct {
Expand All @@ -58,6 +77,9 @@ type ObjectUserBinding struct {
}

type ImageProfile struct {
KeyConfigs []KeyConfig `json:"keyConfigs,omitempty"`
Match ImageRefList `json:"match,omitempty"`
Exclude ImageRefList `json:"exclude,omitempty"`
}

func (p *ParameterObject) DeepCopyInto(p2 *ParameterObject) {
Expand All @@ -84,3 +106,18 @@ func (l ObjectUserBindingList) Match(obj unstructured.Unstructured, username str
}
return false
}

// if any profile condition is defined, image profile returns enabled = true
func (p ImageProfile) Enabled() bool {
return len(p.Match) > 0 || len(p.Exclude) > 0
}

// returns if this profile matches the specified image ref or not
func (p ImageProfile) MatchWith(imageRef string) bool {
matched := p.Match.Match(imageRef)
excluded := false
if len(p.Exclude) > 0 {
excluded = p.Exclude.Match(imageRef)
}
return matched && !excluded
}
110 changes: 110 additions & 0 deletions shield/pkg/image/image.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
//
// Copyright 2020 IBM Corporation
//
// 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 image

import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"time"

"github.com/ghodss/yaml"
"github.com/pkg/errors"

ishieldconfig "github.com/IBM/integrity-shield/shield/pkg/config"
cosigncli "github.com/sigstore/cosign/cmd/cosign/cli"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

type ImageVerifyResult struct {
Object unstructured.Unstructured `json:"object"`
ImageRef string `json:"imageRef"`
Verified bool `json:"verified"`
InScope bool `json:"inScope"`
Signer string `json:"signer"`
SignedTime *time.Time `json:"signedTime"`
FailReason string `json:"failReason"`
}

type ImageVerifyOption struct {
KeyPath string
}

// verify all images in a container of the specified resource
func VerifyImageInManifest(resource unstructured.Unstructured, profile ishieldconfig.ImageProfile) (bool, error) {
yamlBytes, err := yaml.Marshal(resource.Object)
if err != nil {
return false, errors.Wrap(err, "failed to yaml.Marshal() the resource")
}
tmpDir, err := ioutil.TempDir("", "verify-image")
if err != nil {
return false, fmt.Errorf("failed to create temp dir: %s", err.Error())
}
defer os.RemoveAll(tmpDir)

manifestPath := filepath.Join(tmpDir, "manifest.yaml")
err = ioutil.WriteFile(manifestPath, yamlBytes, 0644)
if err != nil {
return false, fmt.Errorf("failed to create temp manifest file: %s", err.Error())
}

keyPathList := []string{}
for _, keyConfig := range profile.KeyConfigs {
if keyConfig.KeySecretName != "" {
keyPath, err := ishieldconfig.LoadKeySecret(keyConfig.KeySecretNamespace, keyConfig.KeySecretName)
if err != nil {
return false, errors.Wrap(err, "failed to load a key secret for image verification")
}
keyPathList = append(keyPathList, keyPath)
}
}
if len(keyPathList) == 0 {
keyPathList = []string{""} // for keyless verification
}

allImagesVerified := false
failReason := ""
// overallFailReason := ""
for _, keyPath := range keyPathList {
cmd := cosigncli.VerifyManifestCommand{VerifyCommand: cosigncli.VerifyCommand{}}
if keyPath != "" {
cmd.KeyRef = keyPath
}

var verifiedWithThisKey bool
// currently cosigncli.VerifyManifestCommand.Exec() does not return detail information like image names and their signer names
// TODO: create an issue in sigstore/cosign for this function to return some additional information
iErr := cmd.Exec(context.Background(), []string{manifestPath})
if iErr == nil {
verifiedWithThisKey = true
} else {
failReason = iErr.Error()
}
if verifiedWithThisKey {
allImagesVerified = true
break
}
}
var retErr error
if !allImagesVerified {
retErr = errors.New(failReason)
}

return allImagesVerified, retErr
}
27 changes: 19 additions & 8 deletions shield/pkg/shield/request_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

k8smnfconfig "github.com/IBM/integrity-shield/shield/pkg/config"
ishieldimage "github.com/IBM/integrity-shield/shield/pkg/image"
"github.com/sigstore/k8s-manifest-sigstore/pkg/k8smanifest"
"github.com/sigstore/k8s-manifest-sigstore/pkg/util/kubeutil"
"github.com/sigstore/k8s-manifest-sigstore/pkg/util/mapnode"
Expand Down Expand Up @@ -217,19 +218,29 @@ func RequestHandler(req admission.Request, paramObj *k8smnfconfig.ParameterObjec
allow = true
message = "not protected"
}
// image verify result

// image verify
imageAllow := true
imageMessage := ""
if len(result.ImageVerifyResults) != 0 {
for _, res := range result.ImageVerifyResults {
if res.InScope && !res.Verified {
imageAllow = false
var imageVerifyResults []ishieldimage.ImageVerifyResult
if paramObj.ImageProfile.Enabled() {
_, err = ishieldimage.VerifyImageInManifest(resource, paramObj.ImageProfile)
if err != nil {
log.Errorf("failed to verify images: %s", err.Error())
imageAllow = false
imageMessage = "Image signature verification is required, but failed to verify signature: " + err.Error()

} else {
for _, res := range imageVerifyResults {
if res.InScope && !res.Verified {
imageAllow = false
imageMessage = "Image signature verification is required, but failed to verify signature: " + res.FailReason
break
}
}
}
}
if !imageAllow {
imageMessage = "Image signature verification is required, but failed to verify signature."
}

if allow && !imageAllow {
message = imageMessage
allow = false
Expand Down
3 changes: 1 addition & 2 deletions webhook/admission-controller/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ require (
github.com/ghodss/yaml v1.0.0
github.com/jinzhu/copier v0.3.2
github.com/pkg/errors v0.9.1
github.com/sigstore/k8s-manifest-sigstore v0.0.0-20210820081408-1767e96c5fe2
github.com/sigstore/k8s-manifest-sigstore v0.0.0-20210909071548-2120192e4ff7
github.com/sirupsen/logrus v1.8.1
k8s.io/api v0.21.3
k8s.io/apimachinery v0.21.3
Expand All @@ -18,7 +18,6 @@ require (
replace (
github.com/IBM/integrity-shield/shield => ../../shield
github.com/IBM/integrity-shield/webhook/admission-controller => ./
github.com/sigstore/k8s-manifest-sigstore => github.com/hirokuni-kitahara/k8s-manifest-sigstore v0.0.0-20210901055134-ae30242ab9d1
k8s.io/kubectl => k8s.io/kubectl v0.21.2

)
Loading