Ejemplo n.º 1
0
    def validate(cls, validator_context):
        """
        Validates the OAuth credentials and API endpoint for a GitLab service.
        """
        config = validator_context.config
        url_scheme_and_hostname = validator_context.url_scheme_and_hostname
        client = validator_context.http_client

        github_config = config.get("GITLAB_TRIGGER_CONFIG")
        if not github_config:
            raise ConfigValidationException(
                "Missing GitLab client id and client secret")

        endpoint = github_config.get("GITLAB_ENDPOINT")
        if endpoint:
            if endpoint.find("http://") != 0 and endpoint.find(
                    "https://") != 0:
                raise ConfigValidationException(
                    "GitLab Endpoint must start with http:// or https://")

        if not github_config.get("CLIENT_ID"):
            raise ConfigValidationException("Missing Client ID")

        if not github_config.get("CLIENT_SECRET"):
            raise ConfigValidationException("Missing Client Secret")

        oauth = GitLabOAuthService(config, "GITLAB_TRIGGER_CONFIG")
        result = oauth.validate_client_id_and_secret(client,
                                                     url_scheme_and_hostname)
        if not result:
            raise ConfigValidationException(
                "Invalid client id or client secret")
Ejemplo n.º 2
0
    def validate(cls, validator_context):
        """
        Validates the config for BitBucket.
        """
        config = validator_context.config

        trigger_config = config.get("BITBUCKET_TRIGGER_CONFIG")
        if not trigger_config:
            raise ConfigValidationException(
                "Missing client ID and client secret")

        if not trigger_config.get("CONSUMER_KEY"):
            raise ConfigValidationException("Missing Consumer Key")

        if not trigger_config.get("CONSUMER_SECRET"):
            raise ConfigValidationException("Missing Consumer Secret")

        key = trigger_config["CONSUMER_KEY"]
        secret = trigger_config["CONSUMER_SECRET"]
        callback_url = "%s/oauth1/bitbucket/callback/trigger/" % (
            validator_context.url_scheme_and_hostname.get_url())

        bitbucket_client = BitBucket(key, secret, callback_url)
        (result, _, _) = bitbucket_client.get_authorization_url()
        if not result:
            raise ConfigValidationException("Invalid consumer key or secret")
Ejemplo n.º 3
0
    def validate(cls, validator_context):
        config = validator_context.config

        if not "DEFAULT_TAG_EXPIRATION" in config:
            # Old style config
            return

        try:
            convert_to_timedelta(
                config["DEFAULT_TAG_EXPIRATION"]).total_seconds()
        except ValueError as ve:
            raise ConfigValidationException("Invalid default expiration: %s" %
                                            ve.message)

        if not config["DEFAULT_TAG_EXPIRATION"] in config.get(
                "TAG_EXPIRATION_OPTIONS", []):
            raise ConfigValidationException(
                "Default expiration must be in expiration options set")

        for ts in config.get("TAG_EXPIRATION_OPTIONS", []):
            try:
                convert_to_timedelta(ts)
            except ValueError as ve:
                raise ConfigValidationException(
                    "Invalid tag expiration option: %s" % ts)
Ejemplo n.º 4
0
    def validate(cls, validator_context):
        config = validator_context.config

        logs_model = config.get("LOGS_MODEL", "database")
        if logs_model != "elasticsearch":
            raise ConfigValidationException("LOGS_MODEL not set to Elasticsearch")

        logs_model_config = config.get("LOGS_MODEL_CONFIG", {})
        elasticsearch_config = logs_model_config.get("elasticsearch_config", {})
        if not elasticsearch_config:
            raise ConfigValidationException("Missing Elasticsearch config")

        if not "host" in elasticsearch_config:
            raise ConfigValidationException("Missing Elasticsearch hostname")

        host = elasticsearch_config["host"]
        port = str(elasticsearch_config["port"])
        index_prefix = elasticsearch_config.get("index_prefix") or INDEX_NAME_PREFIX
        auth = (elasticsearch_config.get("access_key"), elasticsearch_config.get("secret_key"))

        resp = requests.get("https://" + host + ":" + port + "/" + index_prefix + "*", auth=auth)
        if resp.status_code != 200:
            raise ConfigValidationException(
                "Unable to connect to Elasticsearch with config: %s", resp.status_code
            )
Ejemplo n.º 5
0
    def validate(cls, validator_context):
        config = validator_context.config
        client = validator_context.http_client

        login_manager = OAuthLoginManager(config, client=client)
        for service in login_manager.services:
            if not isinstance(service, OIDCLoginService):
                continue

            if service.config.get('OIDC_SERVER') is None:
                msg = 'Missing OIDC_SERVER on OIDC service %s' % service.service_id(
                )
                raise ConfigValidationException(msg)

            if service.config.get('CLIENT_ID') is None:
                msg = 'Missing CLIENT_ID on OIDC service %s' % service.service_id(
                )
                raise ConfigValidationException(msg)

            if service.config.get('CLIENT_SECRET') is None:
                msg = 'Missing CLIENT_SECRET on OIDC service %s' % service.service_id(
                )
                raise ConfigValidationException(msg)

            try:
                if not service.validate():
                    msg = 'Could not validate OIDC service %s' % service.service_id(
                    )
                    raise ConfigValidationException(msg)
            except DiscoveryFailureException as dfe:
                msg = 'Could not validate OIDC service %s: %s' % (
                    service.service_id(), dfe.message)
                raise ConfigValidationException(msg)
Ejemplo n.º 6
0
    def validate(cls, validator_context):
        """ Validates registry storage. """
        config = validator_context.config
        client = validator_context.http_client
        ip_resolver = validator_context.ip_resolver
        config_provider = validator_context.config_provider

        replication_enabled = config.get('FEATURE_STORAGE_REPLICATION', False)

        providers = _get_storage_providers(config, ip_resolver,
                                           config_provider).items()
        if not providers:
            raise ConfigValidationException('Storage configuration required')

        for name, (storage_type, driver) in providers:
            # We can skip localstorage validation, since we can't guarantee that
            # this will be the same machine Q.E. will run under
            if storage_type == TYPE_LOCAL_STORAGE:
                continue

            try:
                if replication_enabled and storage_type == 'LocalStorage':
                    raise ConfigValidationException(
                        'Locally mounted directory not supported ' +
                        'with storage replication')

                # Run validation on the driver.
                driver.validate(client)

                # Run setup on the driver if the read/write succeeded.
                driver.setup()
            except Exception as ex:
                msg = str(ex).strip().split("\n")[0]
                raise ConfigValidationException(
                    'Invalid storage configuration: %s: %s' % (name, msg))
Ejemplo n.º 7
0
    def validate(cls, validator_context):
        """
        Validates the SSL configuration (if enabled).
        """
        config = validator_context.config
        config_provider = validator_context.config_provider

        # Skip if non-SSL.
        if config.get("PREFERRED_URL_SCHEME", "http") != "https":
            return

        # Skip if externally terminated.
        if config.get("EXTERNAL_TLS_TERMINATION", False) is True:
            return

        # Verify that we have all the required SSL files.
        for filename in SSL_FILENAMES:
            if not config_provider.volume_file_exists(filename):
                raise ConfigValidationException(
                    "Missing required SSL file: %s" % filename)

        # Read the contents of the SSL certificate.
        with config_provider.get_volume_file(SSL_FILENAMES[0]) as f:
            cert_contents = f.read()

        # Validate the certificate.
        try:
            certificate = load_certificate(cert_contents)
        except CertInvalidException as cie:
            raise ConfigValidationException(
                "Could not load SSL certificate: %s" % cie)

        # Verify the certificate has not expired.
        if certificate.expired:
            raise ConfigValidationException(
                "The specified SSL certificate has expired.")

        # Verify the hostname matches the name in the certificate.
        if not certificate.matches_name(_ssl_cn(config["SERVER_HOSTNAME"])):
            msg = 'Supported names "%s" in SSL cert do not match server hostname "%s"' % (
                ", ".join(list(certificate.names)),
                _ssl_cn(config["SERVER_HOSTNAME"]),
            )
            raise ConfigValidationException(msg)

        # Verify the private key against the certificate.
        private_key_path = None
        with config_provider.get_volume_file(SSL_FILENAMES[1]) as f:
            private_key_path = f.name

        if not private_key_path:
            # Only in testing.
            return

        try:
            certificate.validate_private_key(private_key_path)
        except KeyInvalidException as kie:
            raise ConfigValidationException(
                "SSL private key failed to validate: %s" % kie)
Ejemplo n.º 8
0
    def validate(cls, validator_context):
        """ Validates the configuration for talking to a Quay Security Scanner. """
        config = validator_context.config
        client = validator_context.http_client
        feature_sec_scanner = validator_context.feature_sec_scanner
        is_testing = validator_context.is_testing

        server_hostname = validator_context.url_scheme_and_hostname.hostname
        uri_creator = validator_context.uri_creator

        if not feature_sec_scanner:
            return

        api = SecurityScannerAPI(
            config,
            None,
            server_hostname,
            client=client,
            skip_validation=True,
            uri_creator=uri_creator,
        )

        # if not is_testing:
        # Generate a temporary Quay key to use for signing the outgoing requests.
        # setup_jwt_proxy()

        # We have to wait for JWT proxy to restart with the newly generated key.
        max_tries = 5
        response = None
        last_exception = None

        while max_tries > 0:
            try:
                response = api.ping()
                last_exception = None
                if response.status_code == 200:
                    return
            except Exception as ex:
                last_exception = ex

            time.sleep(1)
            max_tries = max_tries - 1

        if last_exception is not None:
            message = str(last_exception)
            raise ConfigValidationException(
                "Could not ping security scanner: %s" % message)
        else:
            message = "Expected 200 status code, got %s: %s" % (
                response.status_code, response.text)
            raise ConfigValidationException(
                "Could not ping security scanner: %s" % message)
Ejemplo n.º 9
0
  def validate(cls, validator_context):
    config = validator_context.config

    if config.get('AUTHENTICATION_TYPE', 'Database') != 'AppToken':
      return

    # Ensure that app tokens are enabled, as they are required.
    if not config.get('FEATURE_APP_SPECIFIC_TOKENS', False):
      msg = 'Application token support must be enabled to use External Application Token auth'
      raise ConfigValidationException(msg)

    # Ensure that direct login is disabled.
    if config.get('FEATURE_DIRECT_LOGIN', True):
      msg = 'Direct login must be disabled to use External Application Token auth'
      raise ConfigValidationException(msg)
Ejemplo n.º 10
0
    def validate(cls, validator_context, public_key_path=None):
        """
        Validates the JWT authentication system.
        """
        config = validator_context.config
        http_client = validator_context.http_client
        jwt_auth_max = validator_context.jwt_auth_max
        config_provider = validator_context.config_provider

        if config.get("AUTHENTICATION_TYPE", "Database") != "JWT":
            return

        verify_endpoint = config.get("JWT_VERIFY_ENDPOINT")
        query_endpoint = config.get("JWT_QUERY_ENDPOINT", None)
        getuser_endpoint = config.get("JWT_GETUSER_ENDPOINT", None)

        issuer = config.get("JWT_AUTH_ISSUER")

        if not verify_endpoint:
            raise ConfigValidationException("Missing JWT Verification endpoint")

        if not issuer:
            raise ConfigValidationException("Missing JWT Issuer ID")

        override_config_directory = config_provider.get_config_dir_path()

        # Try to instatiate the JWT authentication mechanism. This will raise an exception if
        # the key cannot be found.
        users = ExternalJWTAuthN(
            verify_endpoint,
            query_endpoint,
            getuser_endpoint,
            issuer,
            override_config_directory,
            http_client,
            jwt_auth_max,
            public_key_path=public_key_path,
            requires_email=config.get("FEATURE_MAILING", True),
        )

        # Verify that we can reach the jwt server
        (result, err_msg) = users.ping()
        if not result:
            msg = (
                "Verification of JWT failed: %s. \n\nWe cannot reach the JWT server"
                + "OR JWT auth is misconfigured"
            ) % err_msg
            raise ConfigValidationException(msg)
Ejemplo n.º 11
0
  def validate(cls, validator_context):
    config = validator_context.config

    """ Validates the action log archiving configuration. """
    if not config.get('FEATURE_ACTION_LOG_ROTATION', False):
      return

    if not config.get('ACTION_LOG_ARCHIVE_PATH'):
      raise ConfigValidationException('Missing action log archive path')

    if not config.get('ACTION_LOG_ARCHIVE_LOCATION'):
      raise ConfigValidationException('Missing action log archive storage location')

    location = config['ACTION_LOG_ARCHIVE_LOCATION']
    storage_config = config.get('DISTRIBUTED_STORAGE_CONFIG') or {}
    if location not in storage_config:
      msg = 'Action log archive storage location `%s` not found in storage config' % location
      raise ConfigValidationException(msg)
Ejemplo n.º 12
0
    def validate(cls, validator_context):
        config = validator_context.config

        """ Validates the action log archiving configuration. """
        if not config.get("FEATURE_ACTION_LOG_ROTATION", False):
            return

        if not config.get("ACTION_LOG_ARCHIVE_PATH"):
            raise ConfigValidationException("Missing action log archive path")

        if not config.get("ACTION_LOG_ARCHIVE_LOCATION"):
            raise ConfigValidationException("Missing action log archive storage location")

        location = config["ACTION_LOG_ARCHIVE_LOCATION"]
        storage_config = config.get("DISTRIBUTED_STORAGE_CONFIG") or {}
        if location not in storage_config:
            msg = "Action log archive storage location `%s` not found in storage config" % location
            raise ConfigValidationException(msg)
Ejemplo n.º 13
0
    def validate(cls, validator_context):
        config = validator_context.config

        logs_model = config.get("LOGS_MODEL", "database")
        if logs_model != "elasticsearch":
            raise ConfigValidationException(
                "LOGS_MODEL not set to Elasticsearch")

        logs_model_config = config.get("LOGS_MODEL_CONFIG", {})
        producer = logs_model_config.get("producer", {})
        if not producer or producer != "kinesis_stream":
            raise ConfigValidationException(
                "Producer not set to 'kinesis_stream'")

        kinesis_stream_config = logs_model_config.get("kinesis_stream_config",
                                                      {})
        if not kinesis_stream_config:
            raise ConfigValidationException(
                "No kinesis_stream_config defined'")

        stream_name = kinesis_stream_config.get("stream_name")
        aws_access_key = kinesis_stream_config.get("aws_access_key")
        aws_secret_key = kinesis_stream_config.get("aws_secret_key")
        aws_region = kinesis_stream_config.get("aws_region")

        producer = boto3.client(
            "kinesis",
            use_ssl=True,
            region_name=aws_region,
            aws_access_key_id=aws_access_key,
            aws_secret_access_key=aws_secret_key,
        )

        try:
            producer.describe_stream(StreamName=stream_name)
        except (
                producer.exceptions.ClientError,
                producer.exceptions.ResourceNotFoundException,
        ) as e:
            raise ConfigValidationException(
                "Unable to connect to Kinesis with config: %s", e)
        except Exception:
            raise ConfigValidationException("Unable to connect to Kinesis")
Ejemplo n.º 14
0
    def validate(cls, validator_context):
        """ Validates connecting to redis. """
        config = validator_context.config

        redis_config = config.get("BUILDLOGS_REDIS", {})
        if not "host" in redis_config:
            raise ConfigValidationException("Missing redis hostname")

        client = redis.StrictRedis(socket_connect_timeout=5, **redis_config)
        client.ping()
Ejemplo n.º 15
0
    def validate(cls, validator_context):
        """
        Validates the Keystone authentication system.
        """
        config = validator_context.config

        if config.get("AUTHENTICATION_TYPE", "Database") != "Keystone":
            return

        auth_url = config.get("KEYSTONE_AUTH_URL")
        auth_version = int(config.get("KEYSTONE_AUTH_VERSION", 2))
        admin_username = config.get("KEYSTONE_ADMIN_USERNAME")
        admin_password = config.get("KEYSTONE_ADMIN_PASSWORD")
        admin_tenant = config.get("KEYSTONE_ADMIN_TENANT")

        if not auth_url:
            raise ConfigValidationException("Missing authentication URL")

        if not admin_username:
            raise ConfigValidationException("Missing admin username")

        if not admin_password:
            raise ConfigValidationException("Missing admin password")

        if not admin_tenant:
            raise ConfigValidationException("Missing admin tenant")

        requires_email = config.get("FEATURE_MAILING", True)
        users = get_keystone_users(auth_version, auth_url, admin_username,
                                   admin_password, admin_tenant,
                                   requires_email)

        # Verify that the superuser exists. If not, raise an exception.
        (result, err_msg) = users.at_least_one_user_exists()
        if not result:
            msg = (
                "Verification that users exist failed: %s. \n\nNo users exist "
                + "in the admin tenant/project " +
                "in the remote authentication system " +
                "OR Keystone auth is misconfigured.") % err_msg
            raise ConfigValidationException(msg)
Ejemplo n.º 16
0
    def validate(cls, validator_context):
        """ Validates connecting to the database. """
        config = validator_context.config

        try:
            validate_database_precondition(
                config["DB_URI"], config.get("DB_CONNECTION_ARGS", {}))
        except OperationalError as ex:
            if ex.args and len(ex.args) > 1:
                raise ConfigValidationException(ex.args[1])
            else:
                raise ex
Ejemplo n.º 17
0
def _get_storage_providers(config, ip_resolver, config_provider):
    storage_config = config.get("DISTRIBUTED_STORAGE_CONFIG", {})
    drivers = {}

    try:
        for name, parameters in storage_config.items():
            driver = get_storage_driver(None, None, config_provider, ip_resolver, parameters)
            drivers[name] = (parameters[0], driver)
    except TypeError:
        raise ConfigValidationException("Missing required parameter(s) for storage %s" % name)

    return drivers
Ejemplo n.º 18
0
    def validate(cls, validator_context):
        """
        Validates the Google Login client ID and secret.
        """
        config = validator_context.config
        client = validator_context.http_client
        url_scheme_and_hostname = validator_context.url_scheme_and_hostname

        google_login_config = config.get("GOOGLE_LOGIN_CONFIG")
        if not google_login_config:
            raise ConfigValidationException("Missing client ID and client secret")

        if not google_login_config.get("CLIENT_ID"):
            raise ConfigValidationException("Missing Client ID")

        if not google_login_config.get("CLIENT_SECRET"):
            raise ConfigValidationException("Missing Client Secret")

        oauth = GoogleOAuthService(config, "GOOGLE_LOGIN_CONFIG")
        result = oauth.validate_client_id_and_secret(client, url_scheme_and_hostname)
        if not result:
            raise ConfigValidationException("Invalid client id or client secret")
Ejemplo n.º 19
0
    def validate(cls, validator_context):
        """ Validates the Keystone authentication system. """
        config = validator_context.config

        if config.get('AUTHENTICATION_TYPE', 'Database') != 'Keystone':
            return

        auth_url = config.get('KEYSTONE_AUTH_URL')
        auth_version = int(config.get('KEYSTONE_AUTH_VERSION', 2))
        admin_username = config.get('KEYSTONE_ADMIN_USERNAME')
        admin_password = config.get('KEYSTONE_ADMIN_PASSWORD')
        admin_tenant = config.get('KEYSTONE_ADMIN_TENANT')

        if not auth_url:
            raise ConfigValidationException('Missing authentication URL')

        if not admin_username:
            raise ConfigValidationException('Missing admin username')

        if not admin_password:
            raise ConfigValidationException('Missing admin password')

        if not admin_tenant:
            raise ConfigValidationException('Missing admin tenant')

        requires_email = config.get('FEATURE_MAILING', True)
        users = get_keystone_users(auth_version, auth_url, admin_username,
                                   admin_password, admin_tenant,
                                   requires_email)

        # Verify that the superuser exists. If not, raise an exception.
        (result, err_msg) = users.at_least_one_user_exists()
        if not result:
            msg = (
                'Verification that users exist failed: %s. \n\nNo users exist '
                + 'in the admin tenant/project ' +
                'in the remote authentication system ' +
                'OR Keystone auth is misconfigured.') % err_msg
            raise ConfigValidationException(msg)
Ejemplo n.º 20
0
    def validate(cls, validator_context):
        config = validator_context.config
        client = validator_context.http_client

        if not config.get("FEATURE_DIRECT_LOGIN", True):
            # Make sure we have at least one OIDC enabled.
            github_login = config.get("FEATURE_GITHUB_LOGIN", False)
            google_login = config.get("FEATURE_GOOGLE_LOGIN", False)

            login_manager = OAuthLoginManager(config, client=client)
            custom_oidc = [
                s for s in login_manager.services
                if isinstance(s, OIDCLoginService)
            ]

            if not github_login and not google_login and not custom_oidc:
                msg = "Cannot disable credentials login to UI without configured OIDC service"
                raise ConfigValidationException(msg)

        if not config.get("FEATURE_USER_CREATION", True) and config.get(
                "FEATURE_INVITE_ONLY_USER_CREATION", False):
            msg = "Invite only user creation requires user creation to be enabled"
            raise ConfigValidationException(msg)
Ejemplo n.º 21
0
    def validate(cls, validator_context):
        """ Validates the GPG public+private key pair used for signing converted ACIs. """
        config = validator_context.config
        config_provider = validator_context.config_provider

        if config.get("SIGNING_ENGINE") is None:
            return

        if config["SIGNING_ENGINE"] not in SIGNING_ENGINES:
            raise ConfigValidationException("Unknown signing engine: %s" %
                                            config["SIGNING_ENGINE"])

        engine = SIGNING_ENGINES[config["SIGNING_ENGINE"]](config,
                                                           config_provider)
        engine.detached_sign(StringIO("test string"))
Ejemplo n.º 22
0
    def validate(cls, validator_context):
        """
        Validates the configuration for using BitTorrent for downloads.
        """
        config = validator_context.config
        client = validator_context.http_client

        announce_url = config.get("BITTORRENT_ANNOUNCE_URL")
        if not announce_url:
            raise ConfigValidationException("Missing announce URL")

        # Ensure that the tracker is reachable and accepts requests signed with a registry key.
        params = {
            "info_hash": sha1("test").digest(),
            "peer_id": "-QUAY00-6wfG2wk6wWLc",
            "uploaded": 0,
            "downloaded": 0,
            "left": 0,
            "numwant": 0,
            "port": 80,
        }

        torrent_config = TorrentConfiguration.for_testing(
            validator_context.instance_keys, announce_url,
            validator_context.registry_title)
        encoded_jwt = jwt_from_infohash(torrent_config, params["info_hash"])
        params["jwt"] = encoded_jwt

        resp = client.get(announce_url, timeout=5, params=params)
        logger.debug("Got tracker response: %s: %s", resp.status_code,
                     resp.text)

        if resp.status_code == 404:
            raise ConfigValidationException(
                "Announce path not found; did you forget `/announce`?")

        if resp.status_code == 500:
            raise ConfigValidationException(
                "Did not get expected response from Tracker; " +
                "please check your settings")

        if resp.status_code == 200:
            if "invalid jwt" in resp.text:
                raise ConfigValidationException(
                    "Could not authorize to Tracker; is your Tracker " +
                    "properly configured?")

            if "failure reason" in resp.text:
                raise ConfigValidationException(
                    "Could not validate signed announce request: " + resp.text)

            if "go_goroutines" in resp.text:
                raise ConfigValidationException(
                    "Could not validate signed announce request: " +
                    "provided port is used for Prometheus")
Ejemplo n.º 23
0
    def validate(cls, validator_context):
        """
        Validates the OAuth credentials and API endpoint for a Github service.
        """
        config = validator_context.config
        client = validator_context.http_client
        url_scheme_and_hostname = validator_context.url_scheme_and_hostname

        github_config = config.get(cls.config_key)
        if not github_config:
            raise ConfigValidationException(
                "Missing GitHub client id and client secret")

        endpoint = github_config.get("GITHUB_ENDPOINT")
        if not endpoint:
            raise ConfigValidationException("Missing GitHub Endpoint")

        if endpoint.find("http://") != 0 and endpoint.find("https://") != 0:
            raise ConfigValidationException(
                "Github Endpoint must start with http:// or https://")

        if not github_config.get("CLIENT_ID"):
            raise ConfigValidationException("Missing Client ID")

        if not github_config.get("CLIENT_SECRET"):
            raise ConfigValidationException("Missing Client Secret")

        if github_config.get("ORG_RESTRICT") and not github_config.get(
                "ALLOWED_ORGANIZATIONS"):
            raise ConfigValidationException(
                "Organization restriction must have at least one allowed " +
                "organization")

        oauth = GithubOAuthService(config, cls.config_key)
        result = oauth.validate_client_id_and_secret(client,
                                                     url_scheme_and_hostname)
        if not result:
            raise ConfigValidationException(
                "Invalid client id or client secret")

        if github_config.get("ALLOWED_ORGANIZATIONS"):
            for org_id in github_config.get("ALLOWED_ORGANIZATIONS"):
                if not oauth.validate_organization(org_id, client):
                    raise ConfigValidationException(
                        "Invalid organization: %s" % org_id)
Ejemplo n.º 24
0
    def validate(cls, validator_context):
        """ Validates the configuration for using BitTorrent for downloads. """
        config = validator_context.config
        client = validator_context.http_client

        announce_url = config.get('BITTORRENT_ANNOUNCE_URL')
        if not announce_url:
            raise ConfigValidationException('Missing announce URL')

        # Ensure that the tracker is reachable and accepts requests signed with a registry key.
        params = {
            'info_hash': sha1('test').digest(),
            'peer_id': '-QUAY00-6wfG2wk6wWLc',
            'uploaded': 0,
            'downloaded': 0,
            'left': 0,
            'numwant': 0,
            'port': 80,
        }

        torrent_config = TorrentConfiguration.for_testing(
            validator_context.instance_keys, announce_url,
            validator_context.registry_title)
        encoded_jwt = jwt_from_infohash(torrent_config, params['info_hash'])
        params['jwt'] = encoded_jwt

        resp = client.get(announce_url, timeout=5, params=params)
        logger.debug('Got tracker response: %s: %s', resp.status_code,
                     resp.text)

        if resp.status_code == 404:
            raise ConfigValidationException(
                'Announce path not found; did you forget `/announce`?')

        if resp.status_code == 500:
            raise ConfigValidationException(
                'Did not get expected response from Tracker; ' +
                'please check your settings')

        if resp.status_code == 200:
            if 'invalid jwt' in resp.text:
                raise ConfigValidationException(
                    'Could not authorize to Tracker; is your Tracker ' +
                    'properly configured?')

            if 'failure reason' in resp.text:
                raise ConfigValidationException(
                    'Could not validate signed announce request: ' + resp.text)

            if 'go_goroutines' in resp.text:
                raise ConfigValidationException(
                    'Could not validate signed announce request: ' +
                    'provided port is used for Prometheus')
Ejemplo n.º 25
0
 def valid(self):
     if not self._feature_repo_mirror:
         raise ConfigValidationException("REPO_MIRROR feature not enabled")
     return True
Ejemplo n.º 26
0
    def validate(cls, validator_context):
        """ Validates the LDAP connection. """
        config = validator_context.config
        config_provider = validator_context.config_provider
        init_scripts_location = validator_context.init_scripts_location

        if config.get('AUTHENTICATION_TYPE', 'Database') != 'LDAP':
            return

        # If there is a custom LDAP certificate, then reinstall the certificates for the container.
        if config_provider.volume_file_exists(LDAP_CERT_FILENAME):
            subprocess.check_call(
                [os.path.join(init_scripts_location, 'certs_install.sh')],
                env={'QUAYCONFIG': config_provider.get_config_dir_path()})

        # Note: raises ldap.INVALID_CREDENTIALS on failure
        admin_dn = config.get('LDAP_ADMIN_DN')
        admin_passwd = config.get('LDAP_ADMIN_PASSWD')

        if not admin_dn:
            raise ConfigValidationException(
                'Missing Admin DN for LDAP configuration')

        if not admin_passwd:
            raise ConfigValidationException(
                'Missing Admin Password for LDAP configuration')

        ldap_uri = config.get('LDAP_URI', 'ldap://localhost')
        if not ldap_uri.startswith('ldap://') and not ldap_uri.startswith(
                'ldaps://'):
            raise ConfigValidationException(
                'LDAP URI must start with ldap:// or ldaps://')

        allow_tls_fallback = config.get('LDAP_ALLOW_INSECURE_FALLBACK', False)

        try:
            with LDAPConnection(ldap_uri, admin_dn, admin_passwd,
                                allow_tls_fallback):
                pass
        except ldap.LDAPError as ex:
            values = ex.args[0] if ex.args else {}
            if not isinstance(values, dict):
                raise ConfigValidationException(str(ex.args))

            raise ConfigValidationException(values.get('desc',
                                                       'Unknown error'))

        base_dn = config.get('LDAP_BASE_DN')
        user_rdn = config.get('LDAP_USER_RDN', [])
        uid_attr = config.get('LDAP_UID_ATTR', 'uid')
        email_attr = config.get('LDAP_EMAIL_ATTR', 'mail')
        requires_email = config.get('FEATURE_MAILING', True)

        users = LDAPUsers(ldap_uri,
                          base_dn,
                          admin_dn,
                          admin_passwd,
                          user_rdn,
                          uid_attr,
                          email_attr,
                          allow_tls_fallback,
                          requires_email=requires_email)

        # Ensure at least one user exists to verify the connection is setup properly.
        (result, err_msg) = users.at_least_one_user_exists()
        if not result:
            msg = (
                'Verification that users exist failed: %s. \n\nNo users exist '
                + 'in the remote authentication system ' +
                'OR LDAP auth is misconfigured.') % err_msg
            raise ConfigValidationException(msg)
Ejemplo n.º 27
0
    def validate(cls, validator_context):
        """
        Validates the LDAP connection.
        """
        config = validator_context.config
        config_provider = validator_context.config_provider
        init_scripts_location = validator_context.init_scripts_location

        if config.get("AUTHENTICATION_TYPE", "Database") != "LDAP":
            return

        # If there is a custom LDAP certificate, then reinstall the certificates for the container.
        if config_provider.volume_file_exists(LDAP_CERT_FILENAME):
            subprocess.check_call(
                [os.path.join(init_scripts_location, "certs_install.sh")],
                env={"QUAYCONFIG": config_provider.get_config_dir_path()},
            )

        # Note: raises ldap.INVALID_CREDENTIALS on failure
        admin_dn = config.get("LDAP_ADMIN_DN")
        admin_passwd = config.get("LDAP_ADMIN_PASSWD")

        if not admin_dn:
            raise ConfigValidationException("Missing Admin DN for LDAP configuration")

        if not admin_passwd:
            raise ConfigValidationException("Missing Admin Password for LDAP configuration")

        ldap_uri = config.get("LDAP_URI", "ldap://localhost")
        if not ldap_uri.startswith("ldap://") and not ldap_uri.startswith("ldaps://"):
            raise ConfigValidationException("LDAP URI must start with ldap:// or ldaps://")

        allow_tls_fallback = config.get("LDAP_ALLOW_INSECURE_FALLBACK", False)

        try:
            with LDAPConnection(ldap_uri, admin_dn, admin_passwd, allow_tls_fallback):
                pass
        except ldap.LDAPError as ex:
            values = ex.args[0] if ex.args else {}
            if not isinstance(values, dict):
                raise ConfigValidationException(str(ex.args))

            raise ConfigValidationException(values.get("desc", "Unknown error"))

        base_dn = config.get("LDAP_BASE_DN")
        user_rdn = config.get("LDAP_USER_RDN", [])
        uid_attr = config.get("LDAP_UID_ATTR", "uid")
        email_attr = config.get("LDAP_EMAIL_ATTR", "mail")
        email_attr = config.get("LDAP_EMAIL_ATTR", "mail")
        ldap_user_filter = config.get("LDAP_USER_FILTER", None)

        users = LDAPUsers(
            ldap_uri,
            base_dn,
            admin_dn,
            admin_passwd,
            user_rdn,
            uid_attr,
            email_attr,
            allow_tls_fallback,
            ldap_user_filter=ldap_user_filter,
        )

        # Ensure at least one user exists to verify the connection is setup properly.
        (result, err_msg) = users.at_least_one_user_exists()
        if not result:
            msg = (
                "Verification that users exist failed: %s. \n\nNo users exist "
                + "in the remote authentication system "
                + "OR LDAP auth is misconfigured."
            ) % err_msg
            raise ConfigValidationException(msg)