Skip to content

Commit

Permalink
Merge pull request #14567 from cdoern/secrets
Browse files Browse the repository at this point in the history
Implement kubernetes secret handling for podman play kube
  • Loading branch information
openshift-merge-robot authored Jul 22, 2022
2 parents 5e43fb1 + 438fef1 commit d1f432d
Show file tree
Hide file tree
Showing 14 changed files with 308 additions and 32 deletions.
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ require (
github.com/containernetworking/cni v1.1.1
github.com/containernetworking/plugins v1.1.1
github.com/containers/buildah v1.26.1-0.20220716095526-d31d27c357ab
github.com/containers/common v0.48.1-0.20220718075257-ecddf87b3840
github.com/containers/common v0.48.1-0.20220720145307-26032247af78
github.com/containers/conmon v2.0.20+incompatible
github.com/containers/image/v5 v5.21.2-0.20220721072459-bf19265865b7
github.com/containers/ocicrypt v1.1.5
github.com/containers/psgo v1.7.2
github.com/containers/storage v1.41.1-0.20220714115232-fc9b0ff5272a
github.com/containers/storage v1.41.1-0.20220718173332-b10c469fda0a
github.com/coreos/go-systemd/v22 v22.3.2
github.com/coreos/stream-metadata-go v0.0.0-20210225230131-70edb9eb47b3
github.com/cyphar/filepath-securejoin v0.2.3
Expand Down
7 changes: 4 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -393,8 +393,8 @@ github.com/containernetworking/plugins v1.1.1/go.mod h1:Sr5TH/eBsGLXK/h71HeLfX19
github.com/containers/buildah v1.26.1-0.20220716095526-d31d27c357ab h1:NeI0DOkTf3Tn4OpdjMhMubAfTPs2oCO5jUY5wnpv4qk=
github.com/containers/buildah v1.26.1-0.20220716095526-d31d27c357ab/go.mod h1:iVtQtU6a+pbETBqIzg0oAWW3gTR1ItrAihJpLFFppmA=
github.com/containers/common v0.48.1-0.20220715075726-2ac10faca05a/go.mod h1:1dA7JPGoSi83kjf5H4NIrGANyLOULyvFqV1bwvYFEek=
github.com/containers/common v0.48.1-0.20220718075257-ecddf87b3840 h1:+5C5tnWO2a6M9QqKNUibMIn56LAuIDtuyb/xT6ZGU/4=
github.com/containers/common v0.48.1-0.20220718075257-ecddf87b3840/go.mod h1:1dA7JPGoSi83kjf5H4NIrGANyLOULyvFqV1bwvYFEek=
github.com/containers/common v0.48.1-0.20220720145307-26032247af78 h1:Z25E8uA264l7dgk0h+1pfV5Y3ZhrFY/0eR6BUQ5BwkY=
github.com/containers/common v0.48.1-0.20220720145307-26032247af78/go.mod h1:w9q2iCLu6pIB9qu0S9oZWM2Qo4lOWrIdDB+2SjLljqA=
github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg=
github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I=
github.com/containers/image/v5 v5.21.2-0.20220712113758-29aec5f7bbbf/go.mod h1:0+N0ZM9mgMmoZZc6uNcgnEsbX85Ne7b29cIW5lqWwVU=
Expand All @@ -416,8 +416,9 @@ github.com/containers/storage v1.37.0/go.mod h1:kqeJeS0b7DO2ZT1nVWs0XufrmPFbgV3c
github.com/containers/storage v1.38.0/go.mod h1:lBzt28gAk5ADZuRtwdndRJyqX22vnRaXmlF+7ktfMYc=
github.com/containers/storage v1.41.0/go.mod h1:Pb0l5Sm/89kolX3o2KolKQ5cCHk5vPNpJrhNaLcdS5s=
github.com/containers/storage v1.41.1-0.20220712184034-d26be7b27860/go.mod h1:uu6HCcijN30xRxW1ZuZRngwFGOlH5NpBWYiNBnDQNRw=
github.com/containers/storage v1.41.1-0.20220714115232-fc9b0ff5272a h1:+arJAP0v8kEy5fKRPIELjarjpwUHhB7SyRE0uFXlyKY=
github.com/containers/storage v1.41.1-0.20220714115232-fc9b0ff5272a/go.mod h1:4DfR+cPpkXKhJnnyydD3z82DXrnTBT63y1k0QWtM2i4=
github.com/containers/storage v1.41.1-0.20220718173332-b10c469fda0a h1:+2n/MLjSlLQwxcROpJLAOvN86SvOMB5IxOahq4iitok=
github.com/containers/storage v1.41.1-0.20220718173332-b10c469fda0a/go.mod h1:jfYNR16uI3eCaWU2Bdn9sHgET7W8JB0OPiswQamG1Cs=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
Expand Down
5 changes: 5 additions & 0 deletions pkg/domain/entities/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ type PlayKubeReport struct {
// Volumes - volumes created by play kube.
Volumes []PlayKubeVolume
PlayKubeTeardown
Secrets []PlaySecret
}

type KubePlayReport = PlayKubeReport
Expand All @@ -100,3 +101,7 @@ type PlayKubeTeardown struct {
StopReport []*PodStopReport
RmReport []*PodRmReport
}

type PlaySecret struct {
CreateReport *SecretCreateReport
}
80 changes: 73 additions & 7 deletions pkg/domain/infra/abi/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,15 @@ func (ic *ContainerEngine) createServiceContainer(ctx context.Context, name stri
return ctr, nil
}

// Creates the name for a service container based on the provided content of a
// K8s yaml file.
func serviceContainerName(content []byte) string {
// Creates the name for a k8s entity based on the provided content of a
// K8s yaml file and a given suffix.
func k8sName(content []byte, suffix string) string {
// The name of the service container is the first 12
// characters of the yaml file's hash followed by the
// '-service' suffix to guarantee a predictable and
// discoverable name.
hash := digest.FromBytes(content).Encoded()
return hash[0:12] + "-service"
return hash[0:12] + "-" + suffix
}

func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options entities.PlayKubeOptions) (_ *entities.PlayKubeReport, finalErr error) {
Expand Down Expand Up @@ -132,7 +132,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options
// TODO: create constants for the various "kinds" of yaml files.
var serviceContainer *libpod.Container
if options.ServiceContainer && (kind == "Pod" || kind == "Deployment") {
ctr, err := ic.createServiceContainer(ctx, serviceContainerName(content), options)
ctr, err := ic.createServiceContainer(ctx, k8sName(content, "service"), options)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -213,6 +213,19 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, options
return nil, fmt.Errorf("unable to read YAML as Kube ConfigMap: %w", err)
}
configMaps = append(configMaps, configMap)
case "Secret":
var secret v1.Secret

if err := yaml.Unmarshal(document, &secret); err != nil {
return nil, fmt.Errorf("unable to read YAML as kube secret: %w", err)
}

r, err := ic.playKubeSecret(&secret)
if err != nil {
return nil, err
}
report.Secrets = append(report.Secrets, entities.PlaySecret{CreateReport: r})
validKinds++
default:
logrus.Infof("Kube kind %s not supported", kind)
continue
Expand Down Expand Up @@ -380,15 +393,15 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
configMaps = append(configMaps, cm)
}

volumes, err := kube.InitializeVolumes(podYAML.Spec.Volumes, configMaps)
volumes, err := kube.InitializeVolumes(podYAML.Spec.Volumes, configMaps, secretsManager)
if err != nil {
return nil, err
}

// Go through the volumes and create a podman volume for all volumes that have been
// defined by a configmap
for _, v := range volumes {
if v.Type == kube.KubeVolumeTypeConfigMap && !v.Optional {
if (v.Type == kube.KubeVolumeTypeConfigMap || v.Type == kube.KubeVolumeTypeSecret) && !v.Optional {
vol, err := ic.Libpod.NewVolume(ctx, libpod.WithVolumeName(v.Source))
if err != nil {
if errors.Is(err, define.ErrVolumeExists) {
Expand Down Expand Up @@ -583,6 +596,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
UserNSIsHost: p.Userns.IsHost(),
Volumes: volumes,
}

specGen, err := kube.ToSpecGen(ctx, &specgenOpts)
if err != nil {
return nil, err
Expand Down Expand Up @@ -968,3 +982,55 @@ func (ic *ContainerEngine) PlayKubeDown(ctx context.Context, body io.Reader, _ e

return reports, nil
}

// playKubeSecret allows users to create and store a kubernetes secret as a podman secret
func (ic *ContainerEngine) playKubeSecret(secret *v1.Secret) (*entities.SecretCreateReport, error) {
r := &entities.SecretCreateReport{}

// Create the secret manager before hand
secretsManager, err := ic.Libpod.SecretsManager()
if err != nil {
return nil, err
}

data, err := yaml.Marshal(secret)
if err != nil {
return nil, err
}

secretsPath := ic.Libpod.GetSecretsStorageDir()
opts := make(map[string]string)
opts["path"] = filepath.Join(secretsPath, "filedriver")
// maybe k8sName(data)...
// using this does not allow the user to use the name given to the secret
// but keeping secret.Name as the ID can lead to a collision.

s, err := secretsManager.Lookup(secret.Name)
if err == nil {
if val, ok := s.Metadata["immutable"]; ok {
if val == "true" {
return nil, fmt.Errorf("cannot remove colliding secret as it is set to immutable")
}
}
_, err = secretsManager.Delete(s.Name)
if err != nil {
return nil, err
}
}

// now we have either removed the old secret w/ the same name or
// the name was not taken. Either way, we can now store.

meta := make(map[string]string)
if secret.Immutable != nil && *secret.Immutable {
meta["immutable"] = "true"
}
secretID, err := secretsManager.Store(secret.Name, data, "file", opts, meta)
if err != nil {
return nil, err
}

r.ID = secretID

return r, nil
}
2 changes: 1 addition & 1 deletion pkg/domain/infra/abi/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func (ic *ContainerEngine) SecretCreate(ctx context.Context, name string, reader
}
}

secretID, err := manager.Store(name, data, options.Driver, options.DriverOpts)
secretID, err := manager.Store(name, data, options.Driver, options.DriverOpts, nil)
if err != nil {
return nil, err
}
Expand Down
1 change: 1 addition & 0 deletions pkg/k8s.io/api/core/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type VolumeSource struct {
// ConfigMap represents a configMap that should populate this volume
// +optional
ConfigMap *ConfigMapVolumeSource `json:"configMap,omitempty"`
Secret *SecretVolumeSource
}

// PersistentVolumeClaimVolumeSource references the user's PVC in the same namespace.
Expand Down
10 changes: 10 additions & 0 deletions pkg/specgen/generate/kube/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,16 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener
Type: "b",
}
s.Devices = append(s.Devices, device)
case KubeVolumeTypeSecret:
// in podman play kube we need to add these secrets as volumes rather than as
// specgen.Secrets. Adding them as volumes allows for all key: value pairs to be mounted
secretVolume := specgen.NamedVolume{
Dest: volume.MountPath,
Name: volumeSource.Source,
Options: options,
}

s.Volumes = append(s.Volumes, &secretVolume)
default:
return nil, errors.New("unsupported volume source type")
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/specgen/generate/kube/play_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func createSecrets(t *testing.T, d string) *secrets.SecretsManager {
data, err := json.Marshal(s.Data)
assert.NoError(t, err)

_, err = secretsManager.Store(s.ObjectMeta.Name, data, driver, driverOpts)
_, err = secretsManager.Store(s.ObjectMeta.Name, data, driver, driverOpts, nil)
assert.NoError(t, err)
}

Expand Down
56 changes: 53 additions & 3 deletions pkg/specgen/generate/kube/volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@ import (
"os"

"github.com/containers/common/pkg/parse"
"github.com/containers/common/pkg/secrets"
"github.com/containers/podman/v4/libpod"
v1 "github.com/containers/podman/v4/pkg/k8s.io/api/core/v1"
metav1 "github.com/containers/podman/v4/pkg/k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/sirupsen/logrus"
"gopkg.in/yaml.v3"
)

const (
Expand All @@ -27,6 +31,7 @@ const (
KubeVolumeTypeConfigMap
KubeVolumeTypeBlockDevice
KubeVolumeTypeCharDevice
KubeVolumeTypeSecret
)

//nolint:revive
Expand Down Expand Up @@ -125,6 +130,49 @@ func VolumeFromHostPath(hostPath *v1.HostPathVolumeSource) (*KubeVolume, error)
}, nil
}

// VolumeFromSecret creates a new kube volume from a kube secret.
func VolumeFromSecret(secretSource *v1.SecretVolumeSource, secretsManager *secrets.SecretsManager) (*KubeVolume, error) {
// returns a byte array of a kube secret data, meaning this needs to go into a string map
_, secretByte, err := secretsManager.LookupSecretData(secretSource.SecretName)
if err != nil {
return nil, err
}

// unmarshaling directly into a v1.secret creates type mismatch errors
// use a more friendly, string only secret struct.
type KubeSecret struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ObjectMeta `json:"metadata,omitempty"`
// +optional
Immutable *bool `json:"immutable,omitempty"`
Data map[string]string `json:"data,omitempty"`
// +optional
StringData map[string]string `json:"stringData,omitempty"`
// +optional
Type string `json:"type,omitempty"`
}

data := &KubeSecret{}

err = yaml.Unmarshal(secretByte, data)
if err != nil {
return nil, err
}

kv := &KubeVolume{}
kv.Type = KubeVolumeTypeSecret
kv.Source = secretSource.SecretName
kv.Optional = *secretSource.Optional
kv.Items = make(map[string]string)

// add key: value pairs to the items array
for key, entry := range data.Data {
kv.Items[key] = entry
}
return kv, nil
}

// Create a KubeVolume from a PersistentVolumeClaimVolumeSource
func VolumeFromPersistentVolumeClaim(claim *v1.PersistentVolumeClaimVolumeSource) (*KubeVolume, error) {
return &KubeVolume{
Expand Down Expand Up @@ -172,25 +220,27 @@ func VolumeFromConfigMap(configMapVolumeSource *v1.ConfigMapVolumeSource, config
}

// Create a KubeVolume from one of the supported VolumeSource
func VolumeFromSource(volumeSource v1.VolumeSource, configMaps []v1.ConfigMap) (*KubeVolume, error) {
func VolumeFromSource(volumeSource v1.VolumeSource, configMaps []v1.ConfigMap, secretsManager *secrets.SecretsManager) (*KubeVolume, error) {
switch {
case volumeSource.HostPath != nil:
return VolumeFromHostPath(volumeSource.HostPath)
case volumeSource.PersistentVolumeClaim != nil:
return VolumeFromPersistentVolumeClaim(volumeSource.PersistentVolumeClaim)
case volumeSource.ConfigMap != nil:
return VolumeFromConfigMap(volumeSource.ConfigMap, configMaps)
case volumeSource.Secret != nil:
return VolumeFromSecret(volumeSource.Secret, secretsManager)
default:
return nil, errors.New("HostPath, ConfigMap, and PersistentVolumeClaim are currently the only supported VolumeSource")
}
}

// Create a map of volume name to KubeVolume
func InitializeVolumes(specVolumes []v1.Volume, configMaps []v1.ConfigMap) (map[string]*KubeVolume, error) {
func InitializeVolumes(specVolumes []v1.Volume, configMaps []v1.ConfigMap, secretsManager *secrets.SecretsManager) (map[string]*KubeVolume, error) {
volumes := make(map[string]*KubeVolume)

for _, specVolume := range specVolumes {
volume, err := VolumeFromSource(specVolume.VolumeSource, configMaps)
volume, err := VolumeFromSource(specVolume.VolumeSource, configMaps, secretsManager)
if err != nil {
return nil, fmt.Errorf("failed to create volume %q: %w", specVolume.Name, err)
}
Expand Down
Loading

0 comments on commit d1f432d

Please sign in to comment.