mirror of
https://github.com/containers/podman-compose.git
synced 2025-02-20 12:21:42 +01:00
feat(secrets): ✨ Add support for secrets
Adds support for - - (1) Declared secrets with the file location. - (2) Declared secrets with file location, mounted as a different named secret. - (3) Declared secrets with file location, mounted at arbitrary location. - (4) External secrets (type=mount), mounted as original secret name. - (5) External secrets (type=mount), mounted as original secret name, with specified uid, gid and mode.
This commit is contained in:
parent
3836094c64
commit
62aa337f17
@ -513,6 +513,74 @@ def get_mount_args(compose, cnt, volume):
|
|||||||
args = mount_desc_to_mount_args(compose, volume, srv_name, cnt['name'])
|
args = mount_desc_to_mount_args(compose, volume, srv_name, cnt['name'])
|
||||||
return ['--mount', args]
|
return ['--mount', args]
|
||||||
|
|
||||||
|
|
||||||
|
def get_secret_args(compose, cnt, secret):
|
||||||
|
secret_name = secret if is_str(secret) else secret.get('source', None)
|
||||||
|
if not secret_name or secret_name not in compose.declared_secrets.keys():
|
||||||
|
raise ValueError(
|
||||||
|
'ERROR: undeclared secret: "{}", service: "{}"'
|
||||||
|
.format(secret, cnt['_service'])
|
||||||
|
)
|
||||||
|
declared_secret = compose.declared_secrets[secret_name]
|
||||||
|
|
||||||
|
source_file = declared_secret.get('file', None)
|
||||||
|
dest_file = ''
|
||||||
|
secret_opts = ''
|
||||||
|
|
||||||
|
target = None if is_str(secret) else secret.get('target', None)
|
||||||
|
uid = None if is_str(secret) else secret.get('uid', None)
|
||||||
|
gid = None if is_str(secret) else secret.get('gid', None)
|
||||||
|
mode = None if is_str(secret) else secret.get('mode', None)
|
||||||
|
|
||||||
|
if source_file:
|
||||||
|
if not target:
|
||||||
|
dest_file = '/run/secrets/{}'.format(secret_name)
|
||||||
|
elif not target.startswith("/"):
|
||||||
|
dest_file = '/run/secrets/{}'.format(target if target else secret_name)
|
||||||
|
else:
|
||||||
|
dest_file = target
|
||||||
|
volume_ref = [
|
||||||
|
'--volume', '{}:{}:ro,rprivate,rbind'.format(source_file, dest_file)
|
||||||
|
]
|
||||||
|
if uid or gid or mode:
|
||||||
|
print(
|
||||||
|
'WARNING: Service "{}" uses secret "{}" with uid, gid, or mode.'
|
||||||
|
.format(cnt['_service'], target if target else secret_name)
|
||||||
|
+ ' These fields are not supported by this implementation of the Compose file'
|
||||||
|
)
|
||||||
|
return volume_ref
|
||||||
|
# v3.5 and up added external flag, earlier the spec
|
||||||
|
# only required a name to be specified.
|
||||||
|
# docker-compose does not support external secrets outside of swarm mode.
|
||||||
|
# However accessing these via podman is trivial
|
||||||
|
# since these commands are directly translated to
|
||||||
|
# podman-create commands, albiet we can only support a 1:1 mapping
|
||||||
|
# at the moment
|
||||||
|
if declared_secret.get('external', False) or declared_secret.get('name', None):
|
||||||
|
secret_opts += ',uid={}'.format(uid) if uid else ''
|
||||||
|
secret_opts += ',gid={}'.format(gid) if gid else ''
|
||||||
|
secret_opts += ',mode={}'.format(mode) if mode else ''
|
||||||
|
# The target option is only valid for type=env,
|
||||||
|
# which in an ideal world would work
|
||||||
|
# for type=mount as well.
|
||||||
|
# having a custom name for the external secret
|
||||||
|
# has the same problem as well
|
||||||
|
ext_name = declared_secret.get('name', None)
|
||||||
|
err_str = 'ERROR: Custom name/target reference "{}" for mounted external secret "{}" is not supported'
|
||||||
|
if ext_name and ext_name != secret_name:
|
||||||
|
raise ValueError(err_str.format(secret_name, ext_name))
|
||||||
|
elif target and target != secret_name:
|
||||||
|
raise ValueError(err_str.format(target, secret_name))
|
||||||
|
elif target:
|
||||||
|
print('WARNING: Service "{}" uses target: "{}" for secret: "{}".'
|
||||||
|
.format(cnt['_service'], target, secret_name)
|
||||||
|
+ ' That is un-supported and a no-op and is ignored.')
|
||||||
|
return [ '--secret', '{}{}'.format(secret_name, secret_opts) ]
|
||||||
|
|
||||||
|
raise ValueError('ERROR: unparseable secret: "{}", service: "{}"'
|
||||||
|
.format(secret_name, cnt['_service']))
|
||||||
|
|
||||||
|
|
||||||
def container_to_res_args(cnt, podman_args):
|
def container_to_res_args(cnt, podman_args):
|
||||||
# v2 < https://docs.docker.com/compose/compose-file/compose-file-v2/#cpu-and-other-resources
|
# v2 < https://docs.docker.com/compose/compose-file/compose-file-v2/#cpu-and-other-resources
|
||||||
cpus_limit_v2 = try_float(cnt.get('cpus', None), None)
|
cpus_limit_v2 = try_float(cnt.get('cpus', None), None)
|
||||||
@ -587,6 +655,8 @@ def container_to_args(compose, cnt, detached=True):
|
|||||||
for volume in cnt.get('volumes', []):
|
for volume in cnt.get('volumes', []):
|
||||||
# TODO: should we make it os.path.realpath(os.path.join(, i))?
|
# TODO: should we make it os.path.realpath(os.path.join(, i))?
|
||||||
podman_args.extend(get_mount_args(compose, cnt, volume))
|
podman_args.extend(get_mount_args(compose, cnt, volume))
|
||||||
|
for secret in cnt.get('secrets', []):
|
||||||
|
podman_args.extend(get_secret_args(compose, cnt, secret))
|
||||||
for i in cnt.get('extra_hosts', []):
|
for i in cnt.get('extra_hosts', []):
|
||||||
podman_args.extend(['--add-host', i])
|
podman_args.extend(['--add-host', i])
|
||||||
for i in cnt.get('expose', []):
|
for i in cnt.get('expose', []):
|
||||||
@ -850,6 +920,7 @@ class PodmanCompose:
|
|||||||
self.pods = None
|
self.pods = None
|
||||||
self.containers = None
|
self.containers = None
|
||||||
self.shared_vols = None
|
self.shared_vols = None
|
||||||
|
self.declared_secrets = None
|
||||||
self.container_names_by_service = None
|
self.container_names_by_service = None
|
||||||
self.container_by_name = None
|
self.container_by_name = None
|
||||||
self._prefer_volume_over_mount = True
|
self._prefer_volume_over_mount = True
|
||||||
@ -1006,7 +1077,7 @@ class PodmanCompose:
|
|||||||
# other top-levels:
|
# other top-levels:
|
||||||
# networks: {driver: ...}
|
# networks: {driver: ...}
|
||||||
# configs: {...}
|
# configs: {...}
|
||||||
# secrets: {...}
|
self.declared_secrets = compose.get('secrets', {})
|
||||||
given_containers = []
|
given_containers = []
|
||||||
container_names_by_service = {}
|
container_names_by_service = {}
|
||||||
for service_name, service_desc in services.items():
|
for service_name, service_desc in services.items():
|
||||||
|
18
tests/secrets/bad_external_name/docker-compose.yaml
Normal file
18
tests/secrets/bad_external_name/docker-compose.yaml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
version: "3.8"
|
||||||
|
services:
|
||||||
|
test:
|
||||||
|
image: busybox
|
||||||
|
command:
|
||||||
|
- cat
|
||||||
|
- /run/secrets/new_secret
|
||||||
|
tmpfs:
|
||||||
|
- /run
|
||||||
|
- /tmp
|
||||||
|
secrets:
|
||||||
|
- new_secret
|
||||||
|
|
||||||
|
secrets:
|
||||||
|
new_secret:
|
||||||
|
external: true
|
||||||
|
name: my_secret
|
||||||
|
|
18
tests/secrets/bad_external_target/docker-compose.yaml
Normal file
18
tests/secrets/bad_external_target/docker-compose.yaml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
version: "3.8"
|
||||||
|
services:
|
||||||
|
test:
|
||||||
|
image: busybox
|
||||||
|
command:
|
||||||
|
- cat
|
||||||
|
- /run/secrets/my_secret_2
|
||||||
|
tmpfs:
|
||||||
|
- /run
|
||||||
|
- /tmp
|
||||||
|
secrets:
|
||||||
|
- source: my_secret
|
||||||
|
target: new_secret
|
||||||
|
|
||||||
|
secrets:
|
||||||
|
my_secret:
|
||||||
|
external: true
|
||||||
|
|
42
tests/secrets/docker-compose.yaml
Normal file
42
tests/secrets/docker-compose.yaml
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
version: "3.8"
|
||||||
|
services:
|
||||||
|
test:
|
||||||
|
image: busybox
|
||||||
|
command:
|
||||||
|
- /tmp/print_secrets.sh
|
||||||
|
tmpfs:
|
||||||
|
- /run
|
||||||
|
- /tmp
|
||||||
|
volumes:
|
||||||
|
- ./print_secrets.sh:/tmp/print_secrets.sh
|
||||||
|
secrets:
|
||||||
|
- my_secret
|
||||||
|
- my_secret_2
|
||||||
|
- source: my_secret_3
|
||||||
|
target: my_secret_3
|
||||||
|
uid: '103'
|
||||||
|
gid: '103'
|
||||||
|
mode: 400
|
||||||
|
- file_secret
|
||||||
|
- source: file_secret
|
||||||
|
target: custom_name
|
||||||
|
- source: file_secret
|
||||||
|
target: /etc/custom_location
|
||||||
|
- source: file_secret
|
||||||
|
target: unused_params_warning
|
||||||
|
uid: '103'
|
||||||
|
gid: '103'
|
||||||
|
mode: 400
|
||||||
|
|
||||||
|
secrets:
|
||||||
|
my_secret:
|
||||||
|
external: true
|
||||||
|
my_secret_2:
|
||||||
|
external: true
|
||||||
|
name: my_secret_2
|
||||||
|
my_secret_3:
|
||||||
|
external: true
|
||||||
|
name: my_secret_3
|
||||||
|
file_secret:
|
||||||
|
file: ./my_secret
|
||||||
|
|
1
tests/secrets/my_secret
Normal file
1
tests/secrets/my_secret
Normal file
@ -0,0 +1 @@
|
|||||||
|
important-secret-is-important
|
6
tests/secrets/print_secrets.sh
Executable file
6
tests/secrets/print_secrets.sh
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
ls -la /run/secrets/*
|
||||||
|
ls -la /etc/custom_location
|
||||||
|
cat /run/secrets/*
|
||||||
|
cat /etc/custom_location
|
Loading…
Reference in New Issue
Block a user