forked from extern/podman-compose
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'])
|
||||
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):
|
||||
# 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)
|
||||
@ -587,6 +655,8 @@ def container_to_args(compose, cnt, detached=True):
|
||||
for volume in cnt.get('volumes', []):
|
||||
# TODO: should we make it os.path.realpath(os.path.join(, i))?
|
||||
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', []):
|
||||
podman_args.extend(['--add-host', i])
|
||||
for i in cnt.get('expose', []):
|
||||
@ -850,6 +920,7 @@ class PodmanCompose:
|
||||
self.pods = None
|
||||
self.containers = None
|
||||
self.shared_vols = None
|
||||
self.declared_secrets = None
|
||||
self.container_names_by_service = None
|
||||
self.container_by_name = None
|
||||
self._prefer_volume_over_mount = True
|
||||
@ -1006,7 +1077,7 @@ class PodmanCompose:
|
||||
# other top-levels:
|
||||
# networks: {driver: ...}
|
||||
# configs: {...}
|
||||
# secrets: {...}
|
||||
self.declared_secrets = compose.get('secrets', {})
|
||||
given_containers = []
|
||||
container_names_by_service = {}
|
||||
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