Skip to content
Draft
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
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,26 @@ quadlets::quadlet { "centos.network":
}
```

### Manage podman secrets with puppet
The expected format for the title is 'username:secretname'. This allows manage the same secret for multiple
users. To manage secrets for non root users, they need to have an active session (either enable linger or being logged in !).
```puppet
# a secret for the root user:
quadlets_secret{ 'root:secretname':
secret => 'donotforget!',
}
# a secret for the xyz user, with labels:
quadlets_secret{ 'xyz:secretname':
secret => 'ensuretorember',
labels => { 'label1' => 'one', 'label2' => 'two' },
}

# ensure to remove a secret:
quadlets_secret{ 'root:secretname':
ensure => 'absent'
}
```

### Hiera Representation Of User setup and Quadlet deployment

```yaml
Expand Down
85 changes: 85 additions & 0 deletions REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
* [`quadlets::quadlet`](#quadlets--quadlet): Generate and manage podman quadlet definitions (podman > 4.4.0)
* [`quadlets::user`](#quadlets--user): Generate and manage podman quadlet user

### Resource types

* [`quadlets_secret`](#quadlets_secret): Type for managing podman secrets (per user)

### Data types

* [`Quadlets::Auth`](#Quadlets--Auth): custom datatype to specify username and password
Expand Down Expand Up @@ -70,6 +74,7 @@ The following parameters are available in the `quadlets` class:
* [`purge_quadlet_dir`](#-quadlets--purge_quadlet_dir)
* [`quadlets_hash`](#-quadlets--quadlets_hash)
* [`users_hash`](#-quadlets--users_hash)
* [`secrets_hash`](#-quadlets--secrets_hash)

##### <a name="-quadlets--manage_package"></a>`manage_package`

Expand Down Expand Up @@ -195,6 +200,14 @@ a `Hash` of `quadlets::users` to deploy

Default value: `{}`

##### <a name="-quadlets--secrets_hash"></a>`secrets_hash`

Data type: `Stdlib::CreateResources`

a `Hash` of `quadlets_secrets` to deploy

Default value: `{}`

## Defined types

### <a name="quadlets--quadlet"></a>`quadlets::quadlet`
Expand Down Expand Up @@ -636,6 +649,78 @@ Define additional parameters to be used to create the user.

Default value: `{}`

## Resource types

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

Type for managing podman secrets (per user)

#### Examples

##### Define a secret mysecret for user blah

```puppet
quadlets_secret{ 'blah:mysecret':
secret => '***secret***',
}
```

#### Properties

The following properties are available in the `quadlets_secret` type.

##### `ensure`

Valid values: `present`, `absent`

The basic property that the resource should be in.

Default value: `present`

##### `labels`

secret labels to set

Default value: `{}`

##### `secret`

the secret himself

#### Parameters

The following parameters are available in the `quadlets_secret` type.

* [`doptions`](#-quadlets_secret--doptions)
* [`driver`](#-quadlets_secret--driver)
* [`name`](#-quadlets_secret--name)
* [`provider`](#-quadlets_secret--provider)

##### <a name="-quadlets_secret--doptions"></a>`doptions`

driver options used for secret creation

Default value: `{}`

##### <a name="-quadlets_secret--driver"></a>`driver`

driver to be used for secret creation

Default value: `file`

##### <a name="-quadlets_secret--name"></a>`name`

Valid values: `%r{^\S+:\S+}`

namevar

combination of user:secretname of the secret to administrate

##### <a name="-quadlets_secret--provider"></a>`provider`

The specific backend to use for this `quadlets_secret` resource. You will seldom need to specify this --- Puppet will
usually discover the appropriate provider for your platform.

## Data types

### <a name="Quadlets--Auth"></a>`Quadlets::Auth`
Expand Down
105 changes: 105 additions & 0 deletions lib/puppet/provider/quadlets_secret/podman.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# frozen_string_literal: true

#
# This file contains a provider for the resource type `libvirt_nwfilter`,
#

require 'json'

Puppet::Type.type(:quadlets_secret).provide(:podman) do
desc "@summary provider for the resource type `quadlets_secret`,
which manages quadlet secrets
using the podman command."

commands podman: 'podman'

def should_user
@should_user || @should_user = resource[:name].split(':')[0]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we uninventing composite namevars here?

end

def should_secretname
@should_secretname || @should_secretname = resource[:name].split(':')[1]
end

def hash_to_array(inhash)
res = []
if inhash
inhash.keys.sort.each do |k|
res << "#{k}=#{inhash[k]}"
end
end
res
end

def run_podman(args, env = {})
user_info = Etc.getpwnam(should_user)
runenv = {
cwd: user_info.dir,
failonfail: true,
uid: user_info.uid,
gid: user_info.gid,
combine: false,
custom_environment: env.merge({ 'HOME' => user_info.dir, 'XDG_RUNTIME_DIR' => "/run/user/#{user_info.uid}" })
}
# test if user is logged in or lingering:
raise Puppet::Error, "user #{should_user} is not logged in, consider enable linger." unless File.directory?("/run/user/#{user_info.uid}")

execute([command('podman')] + args, runenv)
end

def create_secret(replace)
args = ['secret', 'create', should_secretname, '--env', 'psecret']

if replace
args << "-d=#{@result['Spec']['Driver']['Name']}"
hash_to_array(@result['Spec']['Driver']['Options']).each do |d|
args << "--driver-opts=#{d}"
end
args << '--replace'
else
args << "-d=#{@resource[:driver]}"
hash_to_array(resource[:doptions]).each do |d|
args << "--driver-opts=#{d}"
end
end

resource[:labels]&.split('|')&.each do |l|
args << "-l=#{l}"
end

run_podman(args, { 'psecret' => @resource[:secret] })
end

def create
create_secret(false)
end

def destroy
run_podman(['secret', 'rm', should_secretname])
end

def labels=(_label)
create_secret(true)
end

def labels
hash_to_array(@result['Spec']['Labels']).join('|') if @result
end

def secret=(_secret)
create_secret(true)
end

def secret
@result['SecretData'] if @result
end

def exists?
begin
res = run_podman(['secret', 'inspect', should_secretname, '--showsecret'])
rescue Puppet::ExecutionFailure
return false
end
@result = JSON.parse(res)[0]
end
end
81 changes: 81 additions & 0 deletions lib/puppet/type/quadlets_secret.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# frozen_string_literal: true

Puppet::Type.newtype(:quadlets_secret) do
desc <<~DESC
Type for managing podman secrets (per user)

@example Define a secret mysecret for user blah
quadlets_secret{ 'blah:mysecret':
secret => '***secret***',
}
DESC

ensurable

newparam(:name, namevar: true) do
desc 'combination of user:secretname of the secret to administrate'

newvalues(%r{^\S+:\S+})
end

newparam(:driver) do
desc 'driver to be used for secret creation'
defaultto 'file'
end

newparam(:doptions) do
desc 'driver options used for secret creation'
defaultto({})

validate do |value|
raise ArgumentError, 'needs to be a Hash' unless value.is_a?(Hash)
end
end

newproperty(:labels) do
desc 'secret labels to set'
defaultto({})

validate do |value|
raise ArgumentError, 'needs to be a Hash' unless value.is_a?(Hash)
end

munge do |value|
res = []
value.keys.sort.each do |k|
res << "#{k}=#{value[k]}"
end
return res.join('|')
end
end

newproperty(:secret) do
desc 'the secret himself'

validate do |value|
raise ArgumentError, 'needs to be a string' unless value.is_a?(String)
end

def should_to_s(_value)
'*****'
end

def is_to_s(_value)
'*****'
end
end

autorequire(:class) { 'quadlets' }

autorequire(:user) do
[self[:name].split(':')[0]]
end

autorequire(:loginctl_user) do
[self[:name].split(':')[0]]
end

autorequire(:package) do
['podman']
end
end
10 changes: 10 additions & 0 deletions manifests/init.pp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
# @param quadlets_hash a `Hash` of `quadlets::quadlet` to deploy
# @param users_hash a `Hash` of `quadlets::users` to deploy
#
# @param secrets_hash
# a `Hash` of `quadlets_secrets` to deploy
#
# @example Set up Podman for quadlets
# include quadlets
#
Expand All @@ -46,6 +49,7 @@
Boolean $purge_quadlet_dir = false,
Stdlib::CreateResources $quadlets_hash = {},
Stdlib::CreateResources $users_hash = {},
Stdlib::CreateResources $secrets_hash = {},
) {
$quadlet_dir = '/etc/containers/systemd'
$quadlet_system_user_dir = '/etc/containers/systemd/users'
Expand All @@ -68,4 +72,10 @@
* => $_v,
}
}

$secrets_hash.each |$_n, $_v| {
quadlets_secret { $_n:
* => $_v,
}
}
}
Loading
Loading