diff options
author | Christian Breunig <christian@breunig.cc> | 2023-04-05 17:52:00 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-05 17:52:00 +0200 |
commit | 0b0f991a86461ed725762010cf263fb2f0eaa16a (patch) | |
tree | c2dbbe85008b9b1a946fea78ae0142f034a480e7 | |
parent | e890a70d134fc63507ec396f9b7d4290df1cc0cb (diff) | |
parent | df58e083979a40df8c1a1391b82b2e4d856225dd (diff) | |
download | vyos-1x-0b0f991a86461ed725762010cf263fb2f0eaa16a.tar.gz vyos-1x-0b0f991a86461ed725762010cf263fb2f0eaa16a.zip |
Merge pull request #1928 from c-po/t4959-backport
T4959: Add container registry authentication config for containers (backport)
-rw-r--r-- | data/templates/container/registries.conf.j2 | 6 | ||||
-rw-r--r-- | interface-definitions/container.xml.in | 11 | ||||
-rw-r--r-- | interface-definitions/include/generic-password.xml.i | 15 | ||||
-rw-r--r-- | interface-definitions/include/generic-username.xml.i | 15 | ||||
-rw-r--r-- | schema/interface_definition.rnc | 2 | ||||
-rw-r--r-- | schema/interface_definition.rng | 15 | ||||
-rwxr-xr-x | src/conf_mode/container.py | 38 |
7 files changed, 89 insertions, 13 deletions
diff --git a/data/templates/container/registries.conf.j2 b/data/templates/container/registries.conf.j2 index c583e0ad5..0e3ad6ba6 100644 --- a/data/templates/container/registries.conf.j2 +++ b/data/templates/container/registries.conf.j2 @@ -23,5 +23,9 @@ # unqualified-search-registries = ["example.com"] {% if registry is defined and registry is not none %} -unqualified-search-registries = {{ registry }} +{% set registry_list = [] %} +{% for r, r_options in registry.items() if r_options.disable is not defined %} +{% set _ = registry_list.append(r) %} +{% endfor %} +unqualified-search-registries = {{ registry_list }} {% endif %} diff --git a/interface-definitions/container.xml.in b/interface-definitions/container.xml.in index 91fb4dba0..f092ad9bb 100644 --- a/interface-definitions/container.xml.in +++ b/interface-definitions/container.xml.in @@ -368,13 +368,16 @@ </leafNode> </children> </tagNode> - <leafNode name="registry"> + <tagNode name="registry"> <properties> - <help>Registry Name (default: docker.io, quay.io)</help> - <multi/> + <help>Registry Name</help> </properties> <defaultValue>docker.io quay.io</defaultValue> - </leafNode> + <children> + #include <include/interface/authentication.xml.i> + #include <include/generic-disable-node.xml.i> + </children> + </tagNode> </children> </node> </interfaceDefinition> diff --git a/interface-definitions/include/generic-password.xml.i b/interface-definitions/include/generic-password.xml.i new file mode 100644 index 000000000..76d5f12d8 --- /dev/null +++ b/interface-definitions/include/generic-password.xml.i @@ -0,0 +1,15 @@ +<!-- include start from generic-password.xml.i --> +<leafNode name="password"> + <properties> + <help>Password used for authentication</help> + <valueHelp> + <format>txt</format> + <description>Password</description> + </valueHelp> + <constraint> + <regex>[[:ascii:]]{1,128}</regex> + </constraint> + <constraintErrorMessage>Password is limited to ASCII characters only, with a total length of 128</constraintErrorMessage> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/generic-username.xml.i b/interface-definitions/include/generic-username.xml.i new file mode 100644 index 000000000..678f30ddf --- /dev/null +++ b/interface-definitions/include/generic-username.xml.i @@ -0,0 +1,15 @@ +<!-- include start from generic-username.xml.i --> +<leafNode name="username"> + <properties> + <help>Username used for authentication</help> + <valueHelp> + <format>txt</format> + <description>Username</description> + </valueHelp> + <constraint> + <regex>[[:ascii:]]{1,128}</regex> + </constraint> + <constraintErrorMessage>Username is limited to ASCII characters only, with a total length of 128</constraintErrorMessage> + </properties> +</leafNode> +<!-- include end --> diff --git a/schema/interface_definition.rnc b/schema/interface_definition.rnc index 192a70024..dd9c6abcf 100644 --- a/schema/interface_definition.rnc +++ b/schema/interface_definition.rnc @@ -43,7 +43,7 @@ node = element node tagNode = element tagNode { (ownerAttr? & nodeNameAttr), - (properties? & children ) + (defaultValue? & properties? & children ) } # Leaf nodes are terminal configuration nodes that can't have children, diff --git a/schema/interface_definition.rng b/schema/interface_definition.rng index 1ed18f456..282664ce8 100644 --- a/schema/interface_definition.rng +++ b/schema/interface_definition.rng @@ -2,19 +2,19 @@ <grammar xmlns="http://relaxng.org/ns/structure/1.0"> <!-- interface_definition.rnc: VyConf reference tree XML grammar - + Copyright (C) 2014. 2017 VyOS maintainers and contributors <maintainers@vyos.net> - + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - + This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 @@ -72,6 +72,9 @@ </interleave> <interleave> <optional> + <ref name="defaultValue"/> + </optional> + <optional> <ref name="properties"/> </optional> <ref name="children"/> @@ -127,7 +130,7 @@ Nodes may have properties For simplicity, any property is allowed in any node, but whether they are used or not is implementation-defined - + Leaf nodes may differ in number of values that can be associated with them. By default, a leaf node can have only one value. @@ -135,7 +138,7 @@ "valueless" means it can have no values at all. "hidden" means node visibility can be toggled, eg 'dangerous' commands, "secret" allows a node to hide its value from unprivileged users. - + "priority" is used to influence node processing order for nodes with exact same dependencies and in compatibility modes. --> diff --git a/src/conf_mode/container.py b/src/conf_mode/container.py index 50c3424d2..2d7f03e7f 100755 --- a/src/conf_mode/container.py +++ b/src/conf_mode/container.py @@ -18,7 +18,6 @@ import os from ipaddress import ip_address from ipaddress import ip_network -from time import sleep from json import dumps as json_write from vyos.base import Warning @@ -30,6 +29,7 @@ from vyos.util import call from vyos.util import cmd from vyos.util import dict_search from vyos.util import run +from vyos.util import rc_cmd from vyos.util import write_file from vyos.template import inc_ip from vyos.template import is_ipv4 @@ -70,6 +70,9 @@ def get_config(config=None): # container base default values can not be merged here - remove and add them later if 'name' in default_values: del default_values['name'] + # registry will be handled below + if 'registry' in default_values: + del default_values['registry'] container = dict_merge(default_values, container) # Merge per-container default values @@ -106,6 +109,15 @@ def get_config(config=None): container['name'][name]['volume'][volume] = dict_merge( default_values_volume, container['name'][name]['volume'][volume]) + # registry is a tagNode with default values - merge the list from + # default_values['registry'] into the tagNode variables + if 'registry' not in container: + container.update({'registry' : {}}) + default_values = defaults(base) + for registry in default_values['registry'].split(): + tmp = {registry : {}} + container['registry'] = dict_merge(tmp, container['registry']) + # Delete container network, delete containers tmp = node_changed(conf, base + ['network']) if tmp: container.update({'network_remove' : tmp}) @@ -237,6 +249,13 @@ def verify(container): if 'network' in container_config and network in container_config['network']: raise ConfigError(f'Can not remove network "{network}", used by container "{container}"!') + if 'registry' in container: + for registry, registry_config in container['registry'].items(): + if 'authentication' in registry_config: + if ('user' not in registry_config and 'password' in registry_config) or \ + ('user' in registry_config and 'password' not in registry_config): + raise ConfigError('If registry username or password is defined, so must be the other!') + return None def generate_run_arguments(name, container_config): @@ -366,6 +385,23 @@ def generate(container): write_file(f'/etc/cni/net.d/{network}.conflist', json_write(tmp, indent=2)) + if 'registry' in container: + cmd = f'podman logout --all' + rc, out = rc_cmd(cmd) + if rc != 0: + raise ConfigError(out) + + for registry, registry_config in container['registry'].items(): + if 'disable' in registry_config: + continue + if 'authentication' in registry_config: + username = registry_config['authentication']['user'] + password = registry_config['authentication']['password'] + cmd = f'podman login --username {username} --password {password} {registry}' + rc, out = rc_cmd(cmd) + if rc != 0: + raise ConfigError(out) + render(config_containers_registry, 'container/registries.conf.j2', container) render(config_containers_storage, 'container/storage.conf.j2', container) |