Beispiel #1
0
    def login(self, registry, docker_secret_path):
        """
        login to docker registry

        :param registry: registry name
        :param docker_secret_path: path to docker config directory
        """
        logger.info("logging in: registry '%s', secret path '%s'", registry, docker_secret_path)
        # Docker-py needs username
        dockercfg = Dockercfg(docker_secret_path)
        credentials = dockercfg.get_credentials(registry)
        unpacked_auth = dockercfg.unpack_auth_b64(registry)
        username = credentials.get('username')
        if unpacked_auth:
            username = unpacked_auth.username
        if not username:
            raise RuntimeError("Failed to extract a username from '%s'" % dockercfg)

        logger.info("found username %s for registry %s", username, registry)

        response = self.d.login(registry=registry, username=username,
                                dockercfg_path=dockercfg.json_secret_path)
        if not response:
            raise RuntimeError("Failed to login to '%s' with config '%s'" % (registry, dockercfg))
        if u'Status' in response and response[u'Status'] == u'Login Succeeded':
            logger.info("login succeeded")
        else:
            if not(isinstance(response, dict) and 'password' in response.keys()):
                # for some reason docker-py returns the contents of the dockercfg - we shouldn't
                # be displaying that
                logger.debug("response: %r", response)
Beispiel #2
0
    def login(self, registry, docker_secret_path):
        """
        login to docker registry

        :param registry: registry name
        :param docker_secret_path: path to docker config directory
        """
        logger.info("logging in: registry '%s', secret path '%s'", registry,
                    docker_secret_path)
        # Docker-py needs username
        dockercfg = Dockercfg(docker_secret_path)
        username = dockercfg.get_credentials(registry)['username']
        logger.info("found username %s for registry %s", username, registry)

        response = self.d.login(registry=registry,
                                username=username,
                                dockercfg_path=dockercfg.json_secret_path)
        if not response:
            raise RuntimeError("Failed to login to '%s' with config '%s'" %
                               (registry, dockercfg))
        if u'Status' in response and response[u'Status'] == u'Login Succeeded':
            logger.info("login succeeded")
        else:
            if not (isinstance(response, dict)
                    and 'password' in response.keys()):
                # for some reason docker-py returns the contents of the dockercfg - we shouldn't
                # be displaying that
                logger.debug("response: %r", response)
Beispiel #3
0
def test_dockercfg(tmpdir, in_config, lookup, expected):
    temp_dir = mkdtemp(dir=str(tmpdir))
    with open(os.path.join(temp_dir, '.dockercfg'), 'w+') as dockerconfig:
        dockerconfig.write(json.dumps({
            in_config: {
                'username': '******', 'password': '******'
            }
        }))
    creds = Dockercfg(temp_dir).get_credentials(lookup)
    found = creds.get('username') == 'john.doe' and creds.get('password') == 'letmein'

    assert found == expected
Beispiel #4
0
def test_dockercfg(tmpdir, in_config, lookup, expected):
    temp_dir = mkdtemp(dir=str(tmpdir))
    with open(os.path.join(temp_dir, '.dockercfg'), 'w+') as dockerconfig:
        dockerconfig.write(json.dumps({
            in_config: {
                'username': '******', 'password': '******'
            }
        }))
    creds = Dockercfg(temp_dir).get_credentials(lookup)
    found = creds.get('username') == 'john.doe' and creds.get('password') == 'letmein'

    assert found == expected
Beispiel #5
0
    def push_with_skopeo(self, image: Dict[str, str], registry_image: ImageName, insecure: bool,
                         docker_push_secret: str) -> None:
        cmd = ['skopeo', 'copy']
        if docker_push_secret is not None:
            dockercfg = Dockercfg(docker_push_secret)
            cmd.append('--authfile=' + dockercfg.json_secret_path)

        if insecure:
            cmd.append('--dest-tls-verify=false')

        if image['type'] == IMAGE_TYPE_OCI:
            # ref_name is added by 'flatpak_create_oci'
            # we have to be careful when changing the source container image type
            # since assumption here is that source container image will always be 'docker-archive'
            source_img = 'oci:{path}:{ref_name}'.format(**image)
            cmd.append('--format=v2s2')
        elif image['type'] == IMAGE_TYPE_DOCKER_ARCHIVE:
            source_img = 'docker-archive://{path}'.format(**image)
        else:
            raise RuntimeError("Attempt to push unsupported image type %s with skopeo" %
                               image['type'])

        dest_img = 'docker://' + registry_image.to_str()

        cmd += [source_img, dest_img]

        try:
            retries.run_cmd(cmd)
        except subprocess.CalledProcessError as e:
            self.log.error("push failed with output:\n%s", e.output)
            raise
    def get_dockercfg_credentials(self, docker_registry):
        """
        Read the .dockercfg file and return an empty dict, or else a dict
        with keys 'basic_auth_username' and 'basic_auth_password'.
        """
        if not self.registry_secret_path:
            return {}

        dockercfg = Dockercfg(self.registry_secret_path)
        registry_creds = dockercfg.get_credentials(docker_registry)
        if 'username' not in registry_creds:
            return {}

        return {
            'basic_auth_username': registry_creds['username'],
            'basic_auth_password': registry_creds['password'],
        }
    def get_dockercfg_credentials(self, docker_registry):
        """
        Read the .dockercfg file and return an empty dict, or else a dict
        with keys 'basic_auth_username' and 'basic_auth_password'.
        """
        if not self.registry_secret_path:
            return {}

        dockercfg = Dockercfg(self.registry_secret_path)
        registry_creds = dockercfg.get_credentials(docker_registry)
        if 'username' not in registry_creds:
            return {}

        return {
            'basic_auth_username': registry_creds['username'],
            'basic_auth_password': registry_creds['password'],
        }
    def push_with_skopeo(self, registry_image, insecure, docker_push_secret):
        # If the last image has type OCI_TAR, then hunt back and find the
        # the untarred version, since skopeo only supports OCI's as an
        # untarred directory
        image = [
            x for x in self.workflow.exported_image_sequence
            if x['type'] != IMAGE_TYPE_OCI_TAR
        ][-1]

        cmd = ['skopeo', 'copy']
        if docker_push_secret is not None:
            dockercfg = Dockercfg(docker_push_secret)
            credentials = dockercfg.get_credentials(registry_image.registry)
            username = credentials['username']
            password = credentials['password']

            cmd.append('--dest-creds=' + username + ':' + password)

        if insecure:
            cmd.append('--dest-tls-verify=false')

        if image['type'] == IMAGE_TYPE_OCI:
            source_img = 'oci:{path}:{ref_name}'.format(**image)
        elif image['type'] == IMAGE_TYPE_DOCKER_ARCHIVE:
            source_img = 'docker-archive://{path}'.format(**image)
        else:
            raise RuntimeError(
                "Attempt to push unsupported image type %s with skopeo",
                image['type'])

        dest_img = 'docker://' + registry_image.to_str()

        # Make sure we don't log the credentials
        cmd += [source_img, dest_img]
        log_cmd = [
            re.sub(r'^--dest-creds=.*', '--dest-creds=<HIDDEN>', arg)
            for arg in cmd
        ]

        self.log.info("Calling: %s", ' '.join(log_cmd))
        try:
            subprocess.check_output(cmd, stderr=subprocess.STDOUT)
        except subprocess.CalledProcessError as e:
            self.log.error("push failed with output:\n%s", e.output)
            e.cmd = log_cmd  # hide credentials
            raise
    def push_with_skopeo(self, registry_image, insecure, docker_push_secret):
        # If the last image has type OCI_TAR, then hunt back and find the
        # the untarred version, since skopeo only supports OCI's as an
        # untarred directory
        image = [x for x in self.workflow.exported_image_sequence if
                 x['type'] != IMAGE_TYPE_OCI_TAR][-1]

        cmd = ['skopeo', 'copy']
        if docker_push_secret is not None:
            dockercfg = Dockercfg(docker_push_secret)
            credentials = dockercfg.get_credentials(registry_image.registry)
            username = credentials['username']
            password = credentials['password']

            cmd.append('--dest-creds=' + username + ':' + password)

        if insecure:
            cmd.append('--dest-tls-verify=false')

        if image['type'] == IMAGE_TYPE_OCI:
            source_img = 'oci:{path}:{ref_name}'.format(**image)
        elif image['type'] == IMAGE_TYPE_DOCKER_ARCHIVE:
            source_img = 'docker-archive://{path}'.format(**image)
        else:
            raise RuntimeError("Attempt to push unsupported image type %s with skopeo",
                               image['type'])

        dest_img = 'docker://' + registry_image.to_str()

        # Make sure we don't log the credentials
        cmd += [source_img, dest_img]
        log_cmd = [re.sub(r'^--dest-creds=.*', '--dest-creds=<HIDDEN>', arg)
                   for arg in cmd]

        self.log.info("Calling: %s", ' '.join(log_cmd))
        try:
            subprocess.check_output(cmd, stderr=subprocess.STDOUT)
        except subprocess.CalledProcessError as e:
            self.log.error("push failed with output:\n%s", e.output)
            e.cmd = log_cmd  # hide credentials
            raise
    def run(self):
        for registry, registry_conf in self.registries.items():
            if not registry.startswith('http://') and not registry.startswith('https://'):
                registry = 'https://' + registry

            registry_noschema = urlparse(registry).netloc

            auth = None
            secret_path = registry_conf.get('secret')
            if secret_path:
                self.log.debug("registry %s secret %s", registry_noschema, secret_path)
                dockercfg = Dockercfg(secret_path).get_credentials(registry_noschema)
                try:
                    username = dockercfg['username']
                    password = dockercfg['password']
                except KeyError:
                    self.log.error("credentials for registry %s not found in %s",
                                   registry_noschema, secret_path)
                else:
                    self.log.debug("found user %s for registry %s", username, registry_noschema)
                    auth = requests.auth.HTTPBasicAuth(username, password)

            for push_conf_registry in self.workflow.push_conf.docker_registries:
                if push_conf_registry.uri == registry_noschema:
                    break
            else:
                self.log.warning("requested deleting image from %s but we haven't pushed there",
                                 registry_noschema)
                continue

            for tag, digest in push_conf_registry.digests.items():
                repo = tag.split(':')[0]
                url = registry + "/v2/" + repo + "/manifests/" + digest
                insecure = push_conf_registry.insecure
                response = requests.delete(url, verify=not insecure, auth=auth)

                if response.status_code == requests.codes.ACCEPTED:
                    self.log.info("deleted manifest %s/%s@%s", registry_noschema, repo, digest)
                elif response.status_code == requests.codes.NOT_FOUND:
                    self.log.warning("cannot delete %s/%s@%s: not found",
                                     registry_noschema, repo, digest)
                elif response.status_code == requests.codes.METHOD_NOT_ALLOWED:
                    self.log.warning("cannot delete %s/%s@%s: image deletion disabled on registry",
                                     registry_noschema, repo, digest)
                else:
                    msg = "failed to delete %s/%s@%s: %s" % (registry_noschema, repo, digest,
                                                             response.reason)
                    self.log.error("%s\n%s", msg, response.text)
                    raise PluginFailedException(msg)
    def setup_secret(self, registry, secret_path):
        auth = None

        if secret_path:
            self.log.debug("registry %s secret %s", registry, secret_path)
            dockercfg = Dockercfg(secret_path).get_credentials(registry)
            try:
                username = dockercfg['username']
                password = dockercfg['password']
            except KeyError:
                self.log.error("credentials for registry %s not found in %s",
                               registry, secret_path)
            else:
                self.log.debug("found user %s for registry %s", username, registry)
                auth = requests.auth.HTTPBasicAuth(username, password)

        return auth
Beispiel #12
0
    def push_with_skopeo(self,
                         registry_image,
                         insecure,
                         docker_push_secret,
                         source_oci_image_path=None):
        cmd = ['skopeo', 'copy']
        if docker_push_secret is not None:
            dockercfg = Dockercfg(docker_push_secret)
            cmd.append('--authfile=' + dockercfg.json_secret_path)

        if insecure:
            cmd.append('--dest-tls-verify=false')

        if not source_oci_image_path:
            # If the last image has type OCI_TAR, then hunt back and find the
            # the untarred version, since skopeo only supports OCI's as an
            # untarred directory
            image = [
                x for x in self.workflow.exported_image_sequence
                if x['type'] != IMAGE_TYPE_OCI_TAR
            ][-1]

            if image['type'] == IMAGE_TYPE_OCI:
                source_img = 'oci:{path}:{ref_name}'.format(**image)
            elif image['type'] == IMAGE_TYPE_DOCKER_ARCHIVE:
                source_img = 'docker-archive://{path}'.format(**image)
            else:
                raise RuntimeError(
                    "Attempt to push unsupported image type %s with skopeo" %
                    image['type'])
        else:
            source_img = 'oci:{}'.format(source_oci_image_path)

        dest_img = 'docker://' + registry_image.to_str()

        cmd += [source_img, dest_img]

        self.log.info("Calling: %s", ' '.join(cmd))
        try:
            subprocess.check_output(cmd, stderr=subprocess.STDOUT)
        except subprocess.CalledProcessError as e:
            self.log.error("push failed with output:\n%s", e.output)
            raise
Beispiel #13
0
    def get_worker_manifest(self, worker_data):
        worker_digests = worker_data['digests']

        msg = "worker_registries {0}".format(self.worker_registries)
        self.log.debug(msg)

        for registry, registry_conf in self.registries.items():
            if registry_conf.get('version') == 'v1':
                continue

            if not registry.startswith('http://') and not registry.startswith(
                    'https://'):
                registry = 'https://' + registry

            registry_noschema = urlparse(registry).netloc
            self.log.debug("evaluating registry %s", registry_noschema)

            insecure = registry_conf.get('insecure', False)
            auth = None
            secret_path = registry_conf.get('secret')
            if secret_path:
                self.log.debug("registry %s secret %s", registry_noschema,
                               secret_path)
                dockercfg = Dockercfg(secret_path).get_credentials(
                    registry_noschema)
                try:
                    username = dockercfg['username']
                    password = dockercfg['password']
                except KeyError:
                    self.log.error(
                        "credentials for registry %s not found in %s",
                        registry_noschema, secret_path)
                else:
                    self.log.debug("found user %s for registry %s", username,
                                   registry_noschema)
                    auth = requests.auth.HTTPBasicAuth(username, password)

            if registry_noschema in self.worker_registries:
                self.log.debug("getting manifests from %s", registry_noschema)
                digest = worker_digests[0]['digest']
                repo = worker_digests[0]['repository']

                # get a v2 schemav2 response for now
                v2schema2 = 'application/vnd.docker.distribution.manifest.v2+json'
                headers = {'accept': v2schema2}
                kwargs = {
                    'verify': not insecure,
                    'headers': headers,
                    'auth': auth
                }

                url = '{0}/v2/{1}/manifests/{2}'.format(registry, repo, digest)
                self.log.debug("attempting get from %s", url)
                response = requests.get(url, **kwargs)

                if response.json()['schemaVersion'] == '1':
                    msg = 'invalid schema from {0}'.format(url)
                    raise PluginFailedException(msg)

                image_manifest = response.content
                headers = {'Content-Type': v2schema2}
                kwargs = {
                    'verify': not insecure,
                    'headers': headers,
                    'auth': auth
                }

                push_conf_registry = self.workflow.push_conf.add_docker_registry(
                    registry, insecure=insecure)
                for image in self.workflow.tag_conf.images:
                    url = '{0}/v2/{1}/manifests/{2}'.format(
                        registry, repo, image.tag)
                    self.log.debug("for image_tag %s, putting at %s",
                                   image.tag, url)
                    response = requests.put(url, data=image_manifest, **kwargs)

                    if not response.ok:
                        msg = "PUT failed: {0},\n manifest was: {1}".format(
                            response.json(), image_manifest)
                        self.log.error(msg)
                    response.raise_for_status()

                    # add a tag for any plugins running later that expect it
                    push_conf_registry.digests[image.tag] = digest
                break