Skip to content

Commit

Permalink
Merge pull request #3420 from deads2k/secret-sa-check
Browse files Browse the repository at this point in the history
Merged by openshift-bot
  • Loading branch information
OpenShift Bot committed Jul 14, 2015
2 parents e56fd7b + b6bc63a commit 2f8ff89
Show file tree
Hide file tree
Showing 13 changed files with 555 additions and 17 deletions.
6 changes: 5 additions & 1 deletion pkg/api/graph/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ type ExistenceChecker interface {
Found() bool
}

type ResourceNode interface {
ResourceString() string
}

type UniqueName string

type UniqueNameFunc func(obj interface{}) UniqueName
Expand Down Expand Up @@ -158,7 +162,7 @@ func (g Graph) SyntheticNodes() []graph.Node {
sort.Sort(SortedNodeList(nodeList))
for _, node := range nodeList {
if potentiallySyntheticNode, ok := node.(ExistenceChecker); ok {
if potentiallySyntheticNode.Found() {
if !potentiallySyntheticNode.Found() {
ret = append(ret, node)
}
}
Expand Down
6 changes: 6 additions & 0 deletions pkg/api/graph/test/runtimeobject_nodebuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ func init() {
if err := RegisterEnsureNode(&kapi.Service{}, kubegraph.EnsureServiceNode); err != nil {
panic(err)
}
if err := RegisterEnsureNode(&kapi.ServiceAccount{}, kubegraph.EnsureServiceAccountNode); err != nil {
panic(err)
}
if err := RegisterEnsureNode(&kapi.Secret{}, kubegraph.EnsureSecretNode); err != nil {
panic(err)
}
if err := RegisterEnsureNode(&kapi.ReplicationController{}, kubegraph.EnsureReplicationControllerNode); err != nil {
panic(err)
}
Expand Down
45 changes: 45 additions & 0 deletions pkg/api/kubegraph/analysis/podspec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package analysis

import (
osgraph "github.com/openshift/origin/pkg/api/graph"
kubeedges "github.com/openshift/origin/pkg/api/kubegraph"
kubegraph "github.com/openshift/origin/pkg/api/kubegraph/nodes"
)

// CheckMountedSecrets checks to be sure that all the referenced secrets are mountable (by service account) and present (not synthetic)
func CheckMountedSecrets(g osgraph.Graph, podSpecNode *kubegraph.PodSpecNode) ( /*unmountable secrets*/ []*kubegraph.SecretNode /*unresolved secrets*/, []*kubegraph.SecretNode) {
saNodes := g.SuccessorNodesByNodeAndEdgeKind(podSpecNode, kubegraph.ServiceAccountNodeKind, kubeedges.ReferencedServiceAccountEdgeKind)
saMountableSecrets := []*kubegraph.SecretNode{}

if len(saNodes) > 0 {
saNode := saNodes[0].(*kubegraph.ServiceAccountNode)
for _, secretNode := range g.SuccessorNodesByNodeAndEdgeKind(saNode, kubegraph.SecretNodeKind, kubeedges.MountableSecretEdgeKind) {
saMountableSecrets = append(saMountableSecrets, secretNode.(*kubegraph.SecretNode))
}
}

unmountableSecrets := []*kubegraph.SecretNode{}
missingSecrets := []*kubegraph.SecretNode{}

for _, uncastMountedSecretNode := range g.SuccessorNodesByNodeAndEdgeKind(podSpecNode, kubegraph.SecretNodeKind, kubeedges.MountedSecretEdgeKind) {
mountedSecretNode := uncastMountedSecretNode.(*kubegraph.SecretNode)
if !mountedSecretNode.Found() {
missingSecrets = append(missingSecrets, mountedSecretNode)
}

mountable := false
for _, mountableSecretNode := range saMountableSecrets {
if mountableSecretNode == mountedSecretNode {
mountable = true
break
}
}

if !mountable {
unmountableSecrets = append(unmountableSecrets, mountedSecretNode)
continue
}
}

return unmountableSecrets, missingSecrets
}
55 changes: 55 additions & 0 deletions pkg/api/kubegraph/edge_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package kubegraph

import (
"testing"

kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"

osgraph "github.com/openshift/origin/pkg/api/graph"
kubegraph "github.com/openshift/origin/pkg/api/kubegraph/nodes"
)

func TestSecretEdges(t *testing.T) {
sa := &kapi.ServiceAccount{}
sa.Namespace = "ns"
sa.Name = "shultz"
sa.Secrets = []kapi.ObjectReference{{Name: "i-know-nothing"}, {Name: "missing"}}

secret1 := &kapi.Secret{}
secret1.Namespace = "ns"
secret1.Name = "i-know-nothing"

pod := &kapi.Pod{}
pod.Namespace = "ns"
pod.Name = "the-pod"
pod.Spec.Volumes = []kapi.Volume{{Name: "rose", VolumeSource: kapi.VolumeSource{Secret: &kapi.SecretVolumeSource{SecretName: "i-know-nothing"}}}}

g := osgraph.New()
saNode := kubegraph.EnsureServiceAccountNode(g, sa)
secretNode := kubegraph.EnsureSecretNode(g, secret1)
podNode := kubegraph.EnsurePodNode(g, pod)

AddAllMountableSecretEdges(g)
AddAllMountedSecretEdges(g)

if edge := g.EdgeBetween(saNode, secretNode); edge == nil {
t.Errorf("edge missing")
} else {
if edgeKind := g.EdgeKind(edge); edgeKind != MountableSecretEdgeKind {
t.Errorf("expected %v, got %v", MountableSecretEdgeKind, edgeKind)
}
}

podSpecNodes := g.SuccessorNodesByNodeAndEdgeKind(podNode, kubegraph.PodSpecNodeKind, osgraph.ContainsEdgeKind)
if len(podSpecNodes) != 1 {
t.Fatalf("wrong number of podspecs: %v", podSpecNodes)
}

if edge := g.EdgeBetween(podSpecNodes[0], secretNode); edge == nil {
t.Errorf("edge missing")
} else {
if edgeKind := g.EdgeKind(edge); edgeKind != MountedSecretEdgeKind {
t.Errorf("expected %v, got %v", MountedSecretEdgeKind, edgeKind)
}
}
}
97 changes: 95 additions & 2 deletions pkg/api/kubegraph/edges.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,25 @@ package kubegraph
import (
"github.com/gonum/graph"

kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"

osgraph "github.com/openshift/origin/pkg/api/graph"
kubegraph "github.com/openshift/origin/pkg/api/kubegraph/nodes"
)

const (
// ExposedThroughServiceEdgeKind is an edge that goes from a podtemplatespec or a pod to service.
// The head should make the service's selector
// ExposedThroughServiceEdgeKind goes from a PodTemplateSpec or a Pod to Service. The head should make the service's selector.
ExposedThroughServiceEdgeKind = "ExposedThroughService"
// ManagedByRCEdgeKind goes from Pod to ReplicationController when the Pod satisfies the ReplicationController's label selector
ManagedByRCEdgeKind = "ManagedByRC"
// MountedSecretEdgeKind goes from PodSpec to Secret indicating that is or will be a request to mount a volume with the Secret.
MountedSecretEdgeKind = "MountedSecret"
// MountableSecretEdgeKind goes from ServiceAccount to Secret indicating that the SA allows the Secret to be mounted
MountableSecretEdgeKind = "MountableSecret"
// ReferencedServiceAccountEdgeKind goes from PodSpec to ServiceAccount indicating that Pod is or will be running as the SA.
ReferencedServiceAccountEdgeKind = "ReferencedServiceAccount"
)

// AddExposedPodTemplateSpecEdges ensures that a directed edge exists between a service and all the PodTemplateSpecs
Expand Down Expand Up @@ -94,3 +101,89 @@ func AddAllManagedByRCPodEdges(g osgraph.MutableUniqueGraph) {
}
}
}

func AddMountedSecretEdges(g osgraph.Graph, podSpec *kubegraph.PodSpecNode) {
//pod specs are always contained. We'll get the toplevel container so that we can pull a namespace from it
containerNode := osgraph.GetTopLevelContainerNode(g, podSpec)
containerObj := g.GraphDescriber.Object(containerNode)

meta, err := kapi.ObjectMetaFor(containerObj.(runtime.Object))
if err != nil {
// this should never happen. it means that a podSpec is owned by a top level container that is not a runtime.Object
panic(err)
}

for _, volume := range podSpec.Volumes {
source := volume.VolumeSource
if source.Secret == nil {
continue
}

// pod secrets must be in the same namespace
syntheticSecret := &kapi.Secret{}
syntheticSecret.Namespace = meta.Namespace
syntheticSecret.Name = source.Secret.SecretName

secretNode := kubegraph.FindOrCreateSyntheticSecretNode(g, syntheticSecret)
g.AddEdge(podSpec, secretNode, MountedSecretEdgeKind)
}
}

func AddAllMountedSecretEdges(g osgraph.Graph) {
for _, node := range g.NodeList() {
if podSpecNode, ok := node.(*kubegraph.PodSpecNode); ok {
AddMountedSecretEdges(g, podSpecNode)
}
}
}

func AddMountableSecretEdges(g osgraph.Graph, saNode *kubegraph.ServiceAccountNode) {
for _, mountableSecret := range saNode.ServiceAccount.Secrets {
syntheticSecret := &kapi.Secret{}
syntheticSecret.Namespace = saNode.ServiceAccount.Namespace
syntheticSecret.Name = mountableSecret.Name

secretNode := kubegraph.FindOrCreateSyntheticSecretNode(g, syntheticSecret)
g.AddEdge(saNode, secretNode, MountableSecretEdgeKind)
}
}

func AddAllMountableSecretEdges(g osgraph.Graph) {
for _, node := range g.NodeList() {
if saNode, ok := node.(*kubegraph.ServiceAccountNode); ok {
AddMountableSecretEdges(g, saNode)
}
}
}

func AddRequestedServiceAccountEdges(g osgraph.Graph, podSpecNode *kubegraph.PodSpecNode) {
//pod specs are always contained. We'll get the toplevel container so that we can pull a namespace from it
containerNode := osgraph.GetTopLevelContainerNode(g, podSpecNode)
containerObj := g.GraphDescriber.Object(containerNode)

meta, err := kapi.ObjectMetaFor(containerObj.(runtime.Object))
if err != nil {
panic(err)
}

// if no SA name is present, admission will set 'default'
name := "default"
if len(podSpecNode.ServiceAccountName) > 0 {
name = podSpecNode.ServiceAccountName
}

syntheticSA := &kapi.ServiceAccount{}
syntheticSA.Namespace = meta.Namespace
syntheticSA.Name = name

saNode := kubegraph.FindOrCreateSyntheticServiceAccountNode(g, syntheticSA)
g.AddEdge(podSpecNode, saNode, ReferencedServiceAccountEdgeKind)
}

func AddAllRequestedServiceAccountEdges(g osgraph.Graph) {
for _, node := range g.NodeList() {
if podSpecNode, ok := node.(*kubegraph.PodSpecNode); ok {
AddRequestedServiceAccountEdges(g, podSpecNode)
}
}
}
36 changes: 36 additions & 0 deletions pkg/api/kubegraph/nodes/nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,42 @@ func EnsureServiceNode(g osgraph.MutableUniqueGraph, svc *kapi.Service) *Service
).(*ServiceNode)
}

func EnsureServiceAccountNode(g osgraph.MutableUniqueGraph, o *kapi.ServiceAccount) *ServiceAccountNode {
return osgraph.EnsureUnique(g,
ServiceAccountNodeName(o),
func(node osgraph.Node) graph.Node {
return &ServiceAccountNode{node, o, true}
},
).(*ServiceAccountNode)
}

func FindOrCreateSyntheticServiceAccountNode(g osgraph.MutableUniqueGraph, o *kapi.ServiceAccount) *ServiceAccountNode {
return osgraph.EnsureUnique(g,
ServiceAccountNodeName(o),
func(node osgraph.Node) graph.Node {
return &ServiceAccountNode{node, o, false}
},
).(*ServiceAccountNode)
}

func EnsureSecretNode(g osgraph.MutableUniqueGraph, o *kapi.Secret) *SecretNode {
return osgraph.EnsureUnique(g,
SecretNodeName(o),
func(node osgraph.Node) graph.Node {
return &SecretNode{node, o, true}
},
).(*SecretNode)
}

func FindOrCreateSyntheticSecretNode(g osgraph.MutableUniqueGraph, o *kapi.Secret) *SecretNode {
return osgraph.EnsureUnique(g,
SecretNodeName(o),
func(node osgraph.Node) graph.Node {
return &SecretNode{node, o, false}
},
).(*SecretNode)
}

// EnsureReplicationControllerNode adds a graph node for the ReplicationController if it does not already exist.
func EnsureReplicationControllerNode(g osgraph.MutableUniqueGraph, rc *kapi.ReplicationController) *ReplicationControllerNode {
rcNodeName := ReplicationControllerNodeName(rc)
Expand Down
Loading

0 comments on commit 2f8ff89

Please sign in to comment.