mirror of
https://github.com/projectatomic/atomic.git
synced 2026-02-05 18:45:01 +01:00
committed by
Atomic Bot
parent
9350770c33
commit
aa9db7bd13
@@ -2,7 +2,7 @@ try:
|
||||
from . import Atomic
|
||||
except ImportError:
|
||||
from atomic import Atomic # pylint: disable=relative-import
|
||||
from .util import skopeo_copy, get_atomic_config, skopeo_inspect, decompose, write_out
|
||||
from .util import skopeo_copy, get_atomic_config, get_atomic_config_item, skopeo_inspect, decompose, write_out, write_registry_config, install_pubkey, update_trust_policy
|
||||
|
||||
ATOMIC_CONFIG = get_atomic_config()
|
||||
|
||||
@@ -27,6 +27,9 @@ class Pull(Atomic):
|
||||
tag = tag if tag != "" else "latest"
|
||||
fq_name = skopeo_inspect("docker://{}".format(self.args.image))['Name']
|
||||
image = "docker-daemon:{}:{}".format(fq_name, tag)
|
||||
if get_atomic_config_item(['discover_sigstores'], get_atomic_config()):
|
||||
if not self.discover_sigstore():
|
||||
write_out("There was a problem configuring the trust policy")
|
||||
skopeo_copy("docker://{}".format(self.args.image), image, debug=self.args.debug)
|
||||
|
||||
def pull_image(self):
|
||||
@@ -41,3 +44,72 @@ class Pull(Atomic):
|
||||
write_out("Image %s is being pulled to %s ..." % (self.args.image, self.args.backend))
|
||||
handler()
|
||||
|
||||
def discover_sigstore(self):
|
||||
"""
|
||||
Check for registry/repo/sigstore metadata image
|
||||
prompt user for trust on first use workflow
|
||||
:return: True if sigstore discovered and configured
|
||||
"""
|
||||
(registry, repo, _) = decompose(self.args.image)
|
||||
# FIXME: this should be handled in util.decompose
|
||||
_repo, _image = repo.split('/')
|
||||
# TODO: check local /etc/containers/registries.d config here
|
||||
repo_sigstore_labels = self._get_sigstore_image_metadata(registry, _repo)
|
||||
if self._validate_sigstore_labels(repo_sigstore_labels):
|
||||
if self._prompt_trust(repo_sigstore_labels):
|
||||
discover_config = False
|
||||
trust_scope = "%s/%s" % (registry, _repo)
|
||||
discover_config = write_registry_config(trust_scope)
|
||||
pubkey_path = install_pubkey(repo_sigstore_labels['pubkey-id'], repo_sigstore_labels['pubkey-url'])
|
||||
discover_config = update_trust_policy(trust_scope, pubkey_path, repo_sigstore_labels['sigstore-url'])
|
||||
return discover_config
|
||||
return False
|
||||
|
||||
def _get_sigstore_image_metadata(self, registry, repo):
|
||||
"""
|
||||
Get sigstore metadata image
|
||||
:param registry: registry string
|
||||
:param repo: repo string
|
||||
:return True on success
|
||||
"""
|
||||
_img = get_atomic_config_item(['sigstore_metadata_image'], get_atomic_config())
|
||||
sigstoreimage = '/'.join([registry, repo, _img])
|
||||
data = skopeo_inspect("docker://" + sigstoreimage, args=None, fail_silent=True)
|
||||
if data:
|
||||
write_out("Found registry sigstore metadata image %s" % sigstoreimage)
|
||||
return data['Labels']
|
||||
else:
|
||||
return False
|
||||
|
||||
def _validate_sigstore_labels(self, labels):
|
||||
"""
|
||||
Validate sigstore metadata.
|
||||
If there's a missing key or something we don't want to perform any automatic trust policy configuration
|
||||
:param labels: unvalidated labels. Should be either dict or False
|
||||
:return: True if labels are valid
|
||||
"""
|
||||
valid = False
|
||||
if labels:
|
||||
expected_keys = ["pubkey-id", "pubkey-fingerprint", "pubkey-url", "sigstore-url"]
|
||||
for k in expected_keys:
|
||||
valid = k in labels
|
||||
return valid
|
||||
|
||||
def _prompt_trust(self, labels):
|
||||
"""
|
||||
Prompt user for trust on first use workflow
|
||||
:param labels: dict of metadata labels defining sigstore trust
|
||||
:return: True if user accepts
|
||||
"""
|
||||
write_out("ID: " + labels['pubkey-id'])
|
||||
write_out("Fingerprint: " + labels['pubkey-fingerprint'])
|
||||
write_out("Public key download URL: %s" % labels['pubkey-url'])
|
||||
confirm = None
|
||||
if self.args.assumeyes:
|
||||
confirm = "yes"
|
||||
else:
|
||||
confirm = util.input("Do you want to add trust policy for this registry? (y/N)")
|
||||
if not "y" in confirm.lower():
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
@@ -52,7 +52,9 @@ def check_if_python2():
|
||||
input, is_python2 = check_if_python2() # pylint: disable=redefined-builtin
|
||||
|
||||
def decompose(compound_name):
|
||||
# '[reg/]repo[:tag]' -> (reg, repo, tag)
|
||||
# TODO: this doesn't behave when the registry is omitted or using hub "library" images
|
||||
# we should really decompose into reg, repo, image and tag components
|
||||
# 'reg/repo/image[:tag]' -> (reg, repo, image, tag)
|
||||
reg, repo, tag = '', compound_name, ''
|
||||
if '/' in repo:
|
||||
reg, repo = repo.split('/', 1)
|
||||
@@ -245,14 +247,15 @@ def urllib3_disable_warnings():
|
||||
if hasattr(urllib3, 'disable_warnings'):
|
||||
urllib3.disable_warnings()
|
||||
|
||||
def skopeo_inspect(image, args=None, return_json=True, newline=False):
|
||||
def skopeo_inspect(image, args=None, return_json=True, newline=False, quiet=False):
|
||||
if not args:
|
||||
args=[]
|
||||
|
||||
# Performs remote inspection of an image on a registry
|
||||
# :param image: fully qualified name
|
||||
# :param args: additional parameters to pass to Skopeo
|
||||
# :return: Returns json formatted data
|
||||
# :param fail_silent: return false if failed
|
||||
# :return: Returns json formatted data or false
|
||||
|
||||
# Adding in --verify-tls=false to deal with the change in skopeo
|
||||
# policy. The prior inspections were also false. We need to define
|
||||
@@ -266,6 +269,8 @@ def skopeo_inspect(image, args=None, return_json=True, newline=False):
|
||||
except OSError:
|
||||
raise ValueError("skopeo must be installed to perform remote inspections")
|
||||
if results.return_code is not 0:
|
||||
if quiet:
|
||||
return False
|
||||
raise ValueError(results)
|
||||
else:
|
||||
if return_json:
|
||||
@@ -368,6 +373,53 @@ def get_atomic_config(atomic_config=None):
|
||||
with open(atomic_config, 'r') as conf_file:
|
||||
return yaml_load(conf_file)
|
||||
|
||||
def write_registry_config(scope):
|
||||
"""
|
||||
Write registry sigstore configuration file
|
||||
:param scope: registry string
|
||||
:return: True on success
|
||||
"""
|
||||
# FIXME: pending agreement on registry sigstore layout
|
||||
registry_dir = get_atomic_config_item(['registry_sigstore_dir'], get_atomic_config())
|
||||
write_out("TODO: Writing trust config for %s to %s" % (scope, registry_dir))
|
||||
return False
|
||||
|
||||
def install_pubkey(key_name, key_url):
|
||||
"""
|
||||
Installs public key to system config directory
|
||||
:param key_name: id of key used as filename
|
||||
:param key_url: download URI of public key
|
||||
:return: pubkey path string or False
|
||||
"""
|
||||
pubkeys_dir = get_atomic_config_item(['pubkeys_dir'], get_atomic_config())
|
||||
pubkey_file = "%s/%s" % (pubkeys_dir, key_name)
|
||||
if not os.path.exists(pubkeys_dir):
|
||||
os.mkdir(pubkeys_dir)
|
||||
if os.path.exists(pubkey_file):
|
||||
write_out("Public key %s already installed at %s" % (key_name, pubkey_file))
|
||||
else:
|
||||
r = requests.get(key_url)
|
||||
if r.status_code == 200:
|
||||
with open(pubkey_file, 'w') as pubfile:
|
||||
pubfile.write(r.content)
|
||||
write_out("Installed public key %s" % pubkey_file)
|
||||
else:
|
||||
write_out("WARNING: Could not download public key using URL %s." % key_url)
|
||||
write_out("Download the public key manually and install as %s" % pubkey_file)
|
||||
return pubkey_file
|
||||
|
||||
def update_trust_policy(trust_scope, pubkey_path, sigstore_url):
|
||||
"""
|
||||
Add trust policy for the specified registry scope
|
||||
:param trust_scope: registry/repository scope
|
||||
:param pubkey_path: absolute public key path
|
||||
:param sigstore_url: url of sigstore
|
||||
:return: True if success
|
||||
"""
|
||||
# FIXME: pending feedback on manage policy
|
||||
write_out("TODO: Adding trust policy: %s %s %s" % (trust_scope, pubkey_path, sigstore_url))
|
||||
return False
|
||||
|
||||
def add_opt(sub):
|
||||
sub.add_argument("--opt1", dest="opt1",help=argparse.SUPPRESS)
|
||||
sub.add_argument("--opt2", dest="opt2",help=argparse.SUPPRESS)
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
default_scanner:
|
||||
default_docker: docker
|
||||
registry_confdir: /etc/containers/registries.d/
|
||||
discover_sigstores: true
|
||||
sigstore_metadata_image: sigstore
|
||||
pubkeys_dir: /etc/pki/containers
|
||||
|
||||
|
||||
# Default storage backend [ostree, docker]
|
||||
|
||||
Reference in New Issue
Block a user