1
0
mirror of https://github.com/projectatomic/atomic.git synced 2026-02-05 18:45:01 +01:00

signing bootstrap workflow

Closes: #599
Approved by: rhatdan
This commit is contained in:
Aaron Weitekamp
2016-09-08 17:12:27 -04:00
committed by Atomic Bot
parent 9350770c33
commit aa9db7bd13
3 changed files with 131 additions and 4 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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]