Skip to content

Commit

Permalink
Merge pull request #242 from IBM/fix/multires
Browse files Browse the repository at this point in the history
support annotation signature of multi resource yaml
  • Loading branch information
yuji-watanabe-jp authored Jan 8, 2021
2 parents faef9b8 + 23c1c99 commit fc7db4b
Show file tree
Hide file tree
Showing 10 changed files with 240 additions and 139 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ metadata:
capabilities: Basic Install
operators.operatorframework.io/builder: operator-sdk-v1.1.0
operators.operatorframework.io/project_layout: go.kubebuilder.io/v2
name: integrity-shield-operator.v0.1.2
name: integrity-shield-operator.v0.1.3
namespace: placeholder
spec:
apiservicedefinitions: {}
Expand Down Expand Up @@ -240,7 +240,7 @@ spec:
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: quay.io/open-cluster-management/integrity-shield-operator:0.1.2
image: quay.io/open-cluster-management/integrity-shield-operator:0.1.3
imagePullPolicy: Always
name: manager
resources:
Expand Down Expand Up @@ -303,5 +303,5 @@ spec:
provider:
name: IBM
url: https://github.com/ibm/integrity-enforcer
replaces: integrity-shield-operator.v0.1.1
version: 0.1.2
replaces: integrity-shield-operator.v0.1.2
version: 0.1.3
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ kind: Kustomization
images:
- name: controller
newName: quay.io/open-cluster-management/integrity-shield-operator
newTag: 0.1.2
newTag: 0.1.3
6 changes: 3 additions & 3 deletions ishield-build.conf
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ REGISTRY=quay.io/open-cluster-management
LOCAL_REGISTRY=localhost:5000
BUNDLE_REGISTRY=quay.io/open-cluster-management

ISHIELD_VERSION=0.1.2
VERSION=0.1.2
PREV_VERSION=0.1.1
ISHIELD_VERSION=0.1.3
VERSION=0.1.3
PREV_VERSION=0.1.2

ISHIELD_IMAGE=integrity-shield-server
ISHIELD_LOGGING=integrity-shield-logging
Expand Down
80 changes: 3 additions & 77 deletions shield/pkg/apis/resourcesignature/v1alpha1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,9 @@
package v1alpha1

import (
"bytes"
"compress/gzip"
"encoding/base64"
"fmt"
"strings"

mapnode "github.com/IBM/integrity-enforcer/shield/pkg/util/mapnode"
yaml "gopkg.in/yaml.v2"
k8syaml "k8s.io/apimachinery/pkg/util/yaml"
ishieldyaml "github.com/IBM/integrity-enforcer/shield/pkg/util/yaml"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
Expand Down Expand Up @@ -94,8 +88,8 @@ func (ss *ResourceSignature) FindSignature(apiVersion, kind, name, namespace str
func (ss *ResourceSignature) FindSignItem(apiVersion, kind, name, namespace string) (*SignItem, []byte, bool) {
signItem := &SignItem{}
for _, si := range ss.Spec.Data {
if matched, yamlBytes := si.match(apiVersion, kind, name, namespace); matched {
return si, yamlBytes, true
if found, singleYamlBytes := ishieldyaml.FindSingleYaml([]byte(si.Message), apiVersion, kind, name, namespace); found {
return si, singleYamlBytes, true
}
}
return signItem, nil, false
Expand Down Expand Up @@ -175,74 +169,6 @@ type ResourceInfo struct {
raw []byte // raw yaml of single resource
}

func (si *SignItem) match(apiVersion, kind, name, namespace string) (bool, []byte) {
for _, ri := range si.parseMessage() {
if ri.ApiVersion == apiVersion &&
ri.Kind == kind &&
ri.Name == name &&
(ri.Namespace == namespace || ri.Namespace == "") {
return true, ri.raw
}
}
return false, nil
}

func (si *SignItem) parseMessage() []ResourceInfo {
msg := base64decode(si.Message)
msg = decompress(msg)
r := bytes.NewReader([]byte(msg))
dec := k8syaml.NewYAMLToJSONDecoder(r)
var t interface{}
resources := []ResourceInfo{}
for dec.Decode(&t) == nil {
tB, err := yaml.Marshal(t)
if err != nil {
continue
}
n, err := mapnode.NewFromYamlBytes(tB)
if err != nil {
continue
}
apiVersion := n.GetString("apiVersion")
kind := n.GetString("kind")
name := n.GetString("metadata.name")
namespace := n.GetString("metadata.namespace")
tmp := ResourceInfo{
ApiVersion: apiVersion,
Kind: kind,
Name: name,
Namespace: namespace,
raw: tB,
}
resources = append(resources, tmp)
}
return resources
}

func base64decode(str string) string {
decBytes, err := base64.StdEncoding.DecodeString(str)
if err != nil {
return ""
}
dec := string(decBytes)
return dec
}

func decompress(str string) string {
if str == "" {
return str
}
buffer := strings.NewReader(str)
reader, err := gzip.NewReader(buffer)
if err != nil {
return str
}
output := bytes.Buffer{}
output.ReadFrom(reader)
s := string(output.Bytes())
return s
}

func convert(m map[interface{}]interface{}) map[string]interface{} {
res := map[string]interface{}{}
for k, v := range m {
Expand Down
23 changes: 23 additions & 0 deletions shield/pkg/common/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package common
import (
"encoding/json"
"reflect"
"regexp"
"strings"

"github.com/jinzhu/copier"
Expand Down Expand Up @@ -178,6 +179,17 @@ func (self *KustomizePattern) OverrideName(ref *ResourceRef) *ResourceRef {
namePrefix := string(*self.NamePrefix)
if strings.HasPrefix(name, namePrefix) {
name = strings.Replace(name, namePrefix, "", 1)
} else if strings.Contains(namePrefix, "*") {
// strings.HasPrefix can not match with wildcard, so use reqexp here
if strings.Contains(namePrefix, ".") {
namePrefix = strings.Replace(namePrefix, ".", "\\.", -1)
}
namePrefix = strings.Replace(namePrefix, "*", ".*", -1)
if ok, _ := regexp.Match(namePrefix, []byte(name)); ok {
if rep, err := regexp.Compile(namePrefix); err == nil {
name = rep.ReplaceAllString(name, "")
}
}
}
}

Expand All @@ -188,6 +200,17 @@ func (self *KustomizePattern) OverrideName(ref *ResourceRef) *ResourceRef {
revSuffix := reverse(nameSuffix)
revName = strings.Replace(revName, revSuffix, "", 1)
name = reverse(revName)
} else if strings.Contains(nameSuffix, "*") {
// strings.HasSuffix can not match with wildcard, so use reqexp here
if strings.Contains(nameSuffix, ".") {
nameSuffix = strings.Replace(nameSuffix, ".", "\\.", -1)
}
nameSuffix = strings.Replace(nameSuffix, "*", ".*", -1)
if ok, _ := regexp.Match(nameSuffix, []byte(name)); ok {
if rep, err := regexp.Compile(nameSuffix); err == nil {
name = rep.ReplaceAllString(name, "")
}
}
}
}
ref.Name = name
Expand Down
36 changes: 36 additions & 0 deletions shield/pkg/common/profile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,40 @@ func TestProfile(t *testing.T) {
t.Errorf("Request does not match; Request1: %s, Request2: %s", request1Str, request2Str)
return
}

}

func TestKustomizePattern(t *testing.T) {
reqcBytes, err := ioutil.ReadFile(reqcPath)
if err != nil {
t.Error(err)
}

var reqc *ReqContext
err = json.Unmarshal(reqcBytes, &reqc)
if err != nil {
t.Error(err)
return
}
originalName := reqc.Name
reqc.Name = "prefix." + reqc.Name

var kust *KustomizePattern
kustBytes := []byte(`{"match":[{"kind":"ConfigMap"}],"namePrefix":"*."}`)
err = json.Unmarshal(kustBytes, &kust)
if err != nil {
t.Error(err)
return
}
reqFields := reqc.Map()
if ok := kust.MatchWith(reqFields); !ok {
t.Errorf("ReqContext does not match with KustomizePattern: %s", string(kustBytes))
return
}
rawRef := reqc.ResourceRef()
kustRef := kust.OverrideName(rawRef)
if kustRef.Name != originalName {
t.Errorf("OverrideName() returns wrong result; expected: %s, actual: %s", originalName, kustRef.Name)
return
}
}
58 changes: 31 additions & 27 deletions shield/pkg/shield/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
helm "github.com/IBM/integrity-enforcer/shield/pkg/plugins/helm"
config "github.com/IBM/integrity-enforcer/shield/pkg/shield/config"
logger "github.com/IBM/integrity-enforcer/shield/pkg/util/logger"
ishieldyaml "github.com/IBM/integrity-enforcer/shield/pkg/util/yaml"
)

type SignedResourceType string
Expand Down Expand Up @@ -81,40 +82,43 @@ func (self *ConcreteSignatureEvaluator) GetResourceSignature(ref *common.Resourc

//1. pick ResourceSignature from metadata.annotation if available
if sigAnnotations.Signature != "" {
message := base64decode(sigAnnotations.Message)
message = decompress(message)
messageScope := sigAnnotations.MessageScope
mutableAttrs := sigAnnotations.MutableAttrs
matchRequired := true
scopedSignature := false
if message == "" && messageScope != "" {
message = GenerateMessageFromRawObj(reqc.RawObject, messageScope, mutableAttrs)
matchRequired = false // skip matching because the message is generated from Requested Object
scopedSignature = true // enable checking if the signature is for patch
}
signature := base64decode(sigAnnotations.Signature)
certificate := base64decode(sigAnnotations.Certificate)
signType := SignedResourceTypeResource
if sigAnnotations.SignatureType == vrsig.SignatureTypeApplyingResource {
signType = SignedResourceTypeApplyingResource
} else if sigAnnotations.SignatureType == vrsig.SignatureTypePatch {
signType = SignedResourceTypePatch
}
return &GeneralSignature{
SignType: signType,
data: map[string]string{"signature": signature, "message": message, "certificate": certificate, "scope": messageScope},
option: map[string]bool{"matchRequired": matchRequired, "scopedSignature": scopedSignature},
found, yamlBytes := ishieldyaml.FindSingleYaml([]byte(sigAnnotations.Message), ref.ApiVersion, ref.Kind, ref.Name, ref.Namespace)
if found {
message := ishieldyaml.Base64decode(sigAnnotations.Message)
message = ishieldyaml.Decompress(message)
messageScope := sigAnnotations.MessageScope
mutableAttrs := sigAnnotations.MutableAttrs
matchRequired := true
scopedSignature := false
if message == "" && messageScope != "" {
message = GenerateMessageFromRawObj(reqc.RawObject, messageScope, mutableAttrs)
matchRequired = false // skip matching because the message is generated from Requested Object
scopedSignature = true // enable checking if the signature is for patch
}
signature := ishieldyaml.Base64decode(sigAnnotations.Signature)
certificate := ishieldyaml.Base64decode(sigAnnotations.Certificate)
signType := SignedResourceTypeResource
if sigAnnotations.SignatureType == vrsig.SignatureTypeApplyingResource {
signType = SignedResourceTypeApplyingResource
} else if sigAnnotations.SignatureType == vrsig.SignatureTypePatch {
signType = SignedResourceTypePatch
}
return &GeneralSignature{
SignType: signType,
data: map[string]string{"signature": signature, "message": message, "certificate": certificate, "yamlBytes": string(yamlBytes), "scope": messageScope},
option: map[string]bool{"matchRequired": matchRequired, "scopedSignature": scopedSignature},
}
}
}

//2. pick ResourceSignature from custom resource if available
if resSigList != nil && len(resSigList.Items) > 0 {
si, yamlBytes, found := resSigList.FindSignItem(ref.ApiVersion, ref.Kind, ref.Name, ref.Namespace)
if found {
signature := base64decode(si.Signature)
certificate := base64decode(si.Certificate)
message := base64decode(si.Message)
message = decompress(message)
signature := ishieldyaml.Base64decode(si.Signature)
certificate := ishieldyaml.Base64decode(si.Certificate)
message := ishieldyaml.Base64decode(si.Message)
message = ishieldyaml.Decompress(message)
mutableAttrs := si.MutableAttrs
matchRequired := true
scopedSignature := false
Expand Down
27 changes: 0 additions & 27 deletions shield/pkg/shield/signVerifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@
package shield

import (
"bytes"
"compress/gzip"
"encoding/base64"
"encoding/json"
"fmt"
"strings"
Expand Down Expand Up @@ -441,30 +438,6 @@ func GenerateMessageFromRawObj(rawObj []byte, filter, mutableAttrs string) strin
return message
}

func base64decode(str string) string {
decBytes, err := base64.StdEncoding.DecodeString(str)
if err != nil {
return ""
}
dec := string(decBytes)
return dec
}

func decompress(str string) string {
if str == "" {
return str
}
buffer := strings.NewReader(str)
reader, err := gzip.NewReader(buffer)
if err != nil {
return str
}
output := bytes.Buffer{}
_, _ = output.ReadFrom(reader)
s := string(output.Bytes())
return s
}

func makeAllowDiffPatterns(reqc *common.ReqContext, kustomizeList []*common.KustomizePattern) []*mapnode.DiffPattern {
ref := reqc.ResourceRef()
name := reqc.Name
Expand Down
Loading

0 comments on commit fc7db4b

Please sign in to comment.