Skip to content

Commit

Permalink
Merge branch 'traylenator-secret'
Browse files Browse the repository at this point in the history
  • Loading branch information
southalc committed Nov 4, 2023
2 parents 879f17f + 6a444ea commit fb3ce01
Show file tree
Hide file tree
Showing 5 changed files with 355 additions and 1 deletion.
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ address of '127.0.0.1'. Be aware when running rootless containers that publishe
host firewall. Use another module like [firewalld](https://forge.puppet.com/modules/puppet/firewalld) to open ports on the
host and the inbound traffic will reach the rootless container.

The defined types 'pod', 'image', 'volume', and 'container' are essentially wrappers around the respective podman "create"
The defined types 'pod', 'image', 'volume', 'secret' and 'container' are essentially wrappers around the respective podman "create"
commands (`podman <type> create`). The defined types support all flags for the command, but require them to be expressed
using the long form (`--env` instead of `-e`). Flags that don't require values should set the value to undef (use `~` or
`null` in YAML). Flags that are used more than once should be expressed as an array. The Jenkins example configuration
Expand Down Expand Up @@ -94,6 +94,7 @@ here is using hiera lookup for class assignments. The example will perform the
* Use `loginctl` to `enable-linger` on the 'jenkins' user so the user's containers can run as a systemd user service
* Creates volume `jenkins` owned by user `jenkins`
* Creates container `jenkins` from the defined image source owned by user `jenkins`
* Creates secret `db_pass` with secret version and gives it to jenkins container as an environment variable.
* Sets container flags to label the container, publish ports, and attach the previously created `jenkins` volume
* Set service flags for the systemd service to timeout at 60 seconds
* A systemd service `podman-<container_name>` is created, enabled, and started that runs as a user service
Expand Down Expand Up @@ -148,6 +149,16 @@ podman::volumes:
jenkins:
user: jenkins
lookup_options:
podman::secret::secret:
convert_to: "Sensitive"
podman::secret:
db_pass:
user: jenkins
secret: very
label:
- version=20230615
podman::containers:
jenkins:
user: jenkins
Expand All @@ -159,6 +170,8 @@ podman::containers:
- '8080:8080'
- '50000:50000'
volume: 'jenkins:/var/jenkins_home'
secret:
- 'db_pass,type=env,target=DB_PASS'
service_flags:
timeout: '60'
require:
Expand Down
99 changes: 99 additions & 0 deletions REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
* [`podman::network`](#podmannetwork): Create a podman network with defined flags
* [`podman::pod`](#podmanpod): Create a podman pod with defined flags
* [`podman::rootless`](#podmanrootless): Enable a given user to run rootless podman containers as a systemd user service.
* [`podman::secret`](#podmansecret): Manage a podman secret. Create and remove secrets, it cannot replace.
* [`podman::subgid`](#podmansubgid): Define an entry in the `/etc/subgid` file.
* [`podman::subuid`](#podmansubuid): Manage entries in `/etc/subuid`
* [`podman::volume`](#podmanvolume): Create a podman volume with defined flags
Expand Down Expand Up @@ -673,6 +674,104 @@ Default value: `''`

Enable a given user to run rootless podman containers as a systemd user service.

### <a name="podmansecret"></a>`podman::secret`

Manage a podman secret. Create and remove secrets, it cannot replace.

#### Examples

##### Set a secret with a version from puppet directly

```puppet
podman::secret{'db_password':
secret => Sensitive('NeverGuess'),
flags => {
label => [
'version=20230615',
]
}
}
```

##### Set a secret from a file

```puppet
podman::secret{'db_password':
path => '/etc/passwd',
}
```

##### Set a secret from a deferred function call.

```puppet
podman::secret{'ora_password':
secret => Sensitive(Deferred('secret_lookup',['ora_password'])),
flags => {
labels => ['version=20230615'],
}
user => 'rootless user',
}
```

#### Parameters

The following parameters are available in the `podman::secret` defined type:

* [`ensure`](#ensure)
* [`path`](#path)
* [`secret`](#secret)
* [`flags`](#flags)
* [`user`](#user)

##### <a name="ensure"></a>`ensure`

Data type: `Enum['present','absent']`

State of the resource must be either 'present' or 'absent'.

Default value: `'present'`

##### <a name="path"></a>`path`

Data type: `Optional[Stdlib::Unixpath]`

Load secret from an existing file path
The secret and path parameters are mutually exclusive.

Default value: ``undef``

##### <a name="secret"></a>`secret`

Data type: `Optional[Sensitive[String]]`

A secret to be stored - can be set as a Deferred function. If the secret is
changed the secret will **NOT** be modified. Best to set a secret version
as a label.
The secret and path parameters are mutually exclusive.

Default value: ``undef``

##### <a name="flags"></a>`flags`

Data type: `Hash`

All flags for the 'podman secret create' command are supported as part of the
'flags' hash, using only the long form of the flag name. The value for any
defined flag in the 'flags' hash must be entered as a string.
If the flags for a secret are modified the secret will be recreated.

Default value: `{}`

##### <a name="user"></a>`user`

Data type: `Optional[String[1]]`

Optional user for running rootless containers. When using this parameter,
the user must also be defined as a Puppet resource and must include the
'uid', 'gid', and 'home'

Default value: ``undef``

### <a name="podmansubgid"></a>`podman::subgid`

Define an entry in the `/etc/subgid` file.
Expand Down
146 changes: 146 additions & 0 deletions manifests/secret.pp
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
# @summary
# Manage a podman secret. Create and remove secrets, it cannot replace.
#
# @param ensure
# State of the resource must be either 'present' or 'absent'.
#
# @param path
# Load secret from an existing file path
# The secret and path parameters are mutually exclusive.
#
# @param secret
# A secret to be stored - can be set as a Deferred function. If the secret is
# changed the secret will **NOT** be modified. Best to set a secret version
# as a label.
# The secret and path parameters are mutually exclusive.
#
# @param flags
# All flags for the 'podman secret create' command are supported as part of the
# 'flags' hash, using only the long form of the flag name. The value for any
# defined flag in the 'flags' hash must be entered as a string.
# If the flags for a secret are modified the secret will be recreated.
#
# @param user
# Optional user for running rootless containers. When using this parameter,
# the user must also be defined as a Puppet resource and must include the
# 'uid', 'gid', and 'home'
#
# @example Set a secret with a version from puppet directly
# podman::secret{'db_password':
# secret => Sensitive('NeverGuess'),
# flags => {
# label => [
# 'version=20230615',
# ]
# }
# }
#
# @example Set a secret from a file
# podman::secret{'db_password':
# path => '/etc/passwd',
# }
#
# @example Set a secret from a deferred function call.
# podman::secret{'ora_password':
# secret => Sensitive(Deferred('secret_lookup',['ora_password'])),
# flags => {
# labels => ['version=20230615'],
# }
# user => 'rootless user',
# }
#
define podman::secret (
Enum['present','absent'] $ensure = 'present',
Optional[Sensitive[String]] $secret = undef,
Optional[Stdlib::Unixpath] $path = undef,
Optional[String[1]] $user = undef,
Hash $flags = {},
) {
require podman::install

# Do not encode and store the secret
$flags_base64 = base64('encode',String($flags.delete('secret')),'strict')

# Add the default name and a custom label using the base64 encoded flags
if 'label' in $flags {
$label = $flags['label'] + "puppet_resource_flags=${flags_base64}"
$no_label = $flags.delete('label')
} else {
$label = "puppet_resource_flags=${flags_base64}"
$no_label = $flags
}

# If a secret name is not set, use the Puppet resource name
$merged_flags = merge({ label => $label }, $no_label )

# Convert $flags hash to command arguments
$_flags = $merged_flags.reduce('') |$mem, $flag| {
if $flag[1] =~ String {
"${mem} --${flag[0]} '${flag[1]}'"
} elsif $flag[1] =~ Undef {
"${mem} --${flag[0]}"
} else {
$dup = $flag[1].reduce('') |$mem2, $value| {
"${mem2} --${flag[0]} '${value}'"
}
"${mem} ${dup}"
}
}

if $user {
ensure_resource('podman::rootless', $user, {})

# Set execution environment for the rootless user
$exec_defaults = {
path => '/sbin:/usr/sbin:/bin:/usr/bin',
environment => [
"HOME=${User[$user]['home']}",
"XDG_RUNTIME_DIR=/run/user/${User[$user]['uid']}",
],
cwd => User[$user]['home'],
provider => 'shell',
user => $user,
require => [
Podman::Rootless[$user],
Service['podman systemd-logind'],
],
}
} else {
$exec_defaults = {
path => '/sbin:/usr/sbin:/bin:/usr/bin',
provider => 'shell',
}
}

if $secret and $path {
fail('Only one of the parameters path or secret to podman::secret must be set')
} elsif $secret {
$_command = Sensitive(stdlib::deferrable_epp('podman/set_secret_from_stdin.epp', {
'secret' => $secret ,
'title' => $title,
'flags' => $_flags,
}))
} elsif $path {
$_command = "podman secret create${_flags} ${title} ${path}"
} else {
fail('One of the parameters path or secret to podman::secret must be set')
}

case $ensure {
'present': {
Exec { "create_secret_${title}":
command => $_command,
unless => "test \"$(podman secret inspect ${title} --format ''{{.Spec.Labels.puppet_resource_flags}}'')\" = \"${flags_base64}\"",
* => $exec_defaults,
}
}
default: {
Exec { "create_secret_${title}":
command => $_command,
unless => "podman secret rm ${title}",
onlyif => "podman secret inspect ${title}",
* => $exec_defaults,
}
}
}
}
90 changes: 90 additions & 0 deletions spec/defines/secret_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
require 'spec_helper'

describe 'podman::secret' do
let(:title) { 'root_password' }
let(:params) do
{
ensure: 'present',
}
end
let(:pre_condition) do
'user {"testuser":
ensure => "present",
home => "/home/testuser",
uid => 5000,
gid => 5000,
managehome => true,
}
file {"/home/testuser":
ensure => "directory",
}
class {"podman":
podman_pkg => "podman",
skopeo_pkg => "skopeo",
buildah_pkg => "buildah",
buildah_pkg_ensure => "installed",
podman_docker_pkg_ensure => "installed",
podman_docker_pkg => "podman-docker",
manage_subuid => true,
file_header => " FILE MANAGED BY PUPPET",
match_subuid_subgid => true,
subid => {
testuser => {
subuid => 5000000,
count => 1000,
},
},
nodocker => "file",
}'
end

on_supported_os.each do |os, os_facts|
context "on #{os}" do
let(:facts) { os_facts }

it { is_expected.to compile.and_raise_error(%r{One of the parameters path or secret}) }
context 'with secret and path parameter set' do
let(:params) do
super().merge(secret: sensitive('tiptop'), path: '/bin/fail')
end

it { is_expected.to compile.and_raise_error(%r{Only one of the parameters path or secret}) }
end
context 'with secret parameter set' do
let(:params) do
super().merge(secret: sensitive('tiptop'))
end

it { is_expected.to compile }
it { is_expected.to contain_exec('create_secret_root_password') }
it {
is_expected.to contain_exec('create_secret_root_password')
.with_command(sensitive("printf 'tiptop' | podman secret create --label 'puppet_resource_flags=e30=' root_password -\n"))
.with_unless("test \"\$(podman secret inspect root_password --format ''{{.Spec.Labels.puppet_resource_flags}}'')\" = \"e30=\"")
}
end
context 'with path parameter set' do
let(:params) do
super().merge(path: '/tmp/my_root')
end

it { is_expected.to compile }
it {
is_expected.to contain_exec('create_secret_root_password')
.with_command("podman secret create --label 'puppet_resource_flags=e30=' root_password /tmp/my_root")
}
context 'with a label set' do
let(:params) do
super().merge(flags: { label: ['trust=this'] })
end

it {
is_expected.to contain_exec('create_secret_root_password')
.with_command("podman secret create --label 'trust=this' --label 'puppet_resource_flags=eydsYWJlbCcgPT4gWyd0cnVzdD10aGlzJ119' root_password /tmp/my_root")
.with_unless("test \"\$(podman secret inspect root_password --format ''{{.Spec.Labels.puppet_resource_flags}}'')\" = \"eydsYWJlbCcgPT4gWyd0cnVzdD10aGlzJ119\"")
}
end
end
end
end
end
Loading

0 comments on commit fb3ce01

Please sign in to comment.