Example #1
0
def taskcluster_user_loader(auth_header):

    # Get Endpoint configuration
    if ':' in request.host:
        host, port = request.host.split(':')
    else:
        host = request.host
        port = request.environ.get('HTTP_X_FORWARDED_PORT')
        if port is None:
            port = request.scheme == 'https' and 443 or 80
    method = request.method.lower()

    # Build taskcluster payload
    payload = {
        'resource': request.path,
        'method': method,
        'host': host,
        'port': int(port),
        'authorization': auth_header,
    }

    # Auth with taskcluster
    auth = taskcluster.Auth()
    try:
        resp = auth.authenticateHawk(payload)
        if not resp.get('status') == 'auth-success':
            raise Exception('Taskcluster rejected the authentication')
    except Exception as e:
        logger.error('TC auth error: {}'.format(e))
        logger.error('TC auth details: {}'.format(payload))
        abort(401)  # Unauthorized

    return TaskclusterUser(resp)
Example #2
0
def parse_header_taskcluster(request):
    auth_header = request.headers.get("Authorization")
    if not auth_header:
        auth_header = request.headers.get("Authentication")
    if not auth_header:
        return NO_AUTH
    if not auth_header.startswith("Hawk"):
        return NO_AUTH

    # Get Endpoint configuration
    if ":" in request.host:
        host, port = request.host.split(":")
    else:
        host = request.host
        port = request.environ.get("HTTP_X_FORWARDED_PORT")
        if port is None:
            port = request.scheme == "https" and 443 or 80
    method = request.method.lower()

    # Build taskcluster payload
    payload = {"resource": request.path, "method": method, "host": host, "port": int(port), "authorization": auth_header}

    # Auth with taskcluster
    auth = taskcluster.Auth(dict(rootUrl=flask.current_app.config["TASKCLUSTER_ROOT_URL"]))
    try:
        resp = auth.authenticateHawk(payload)
        if not resp.get("status") == "auth-success":
            raise Exception("Taskcluster rejected the authentication")
    except Exception as e:
        logger.warning(f"TC auth error: {e}")
        logger.warning(f"TC auth details: {payload}")
        return NO_AUTH

    return TaskclusterUser(resp)
Example #3
0
def app_heartbeat():
    try:
        ping = taskcluster.Auth().ping()
        assert ping['alive'] is True
    except Exception as e:
        raise backend_common.dockerflow.HeartbeatException(
            'Cannot connect to the taskcluster auth service.')
    def __init__(self,
                 root_url,
                 client_id=None,
                 access_token=None,
                 unit_testing_this=False):
        # TODO: remove when backfill tool' soft launch is complete ->
        if not unit_testing_this:
            raise RuntimeError(
                f"Must not instantiate real {self.__class__.__name__} instance "
                f"before backfill tool' soft launch is complete")
        # <- up to here
        options = {'rootUrl': root_url}
        credentials = {}

        if client_id:
            credentials['clientId'] = client_id
        if access_token:
            credentials['accessToken'] = access_token

        # Taskcluster APIs
        self.hooks = taskcluster.Hooks({**options, 'credentials': credentials})

        # Following least-privilege principle, as services
        # bellow don't really need authorization credentials.
        self.queue = taskcluster.Queue(options)
        self.auth = taskcluster.Auth(options)
Example #5
0
def app_heartbeat():
    config = flask.current_app.config
    if config.get('TASKCLUSTER_AUTH') is True:
        auth = taskcluster.Auth(dict(rootUrl=flask.current_app.config['TASKCLUSTER_ROOT_URL']))
        try:
            ping = auth.ping()
            assert ping['alive'] is True
        except Exception as e:
            logger.exception(e)
            raise tooltool_api.lib.dockerflow.HeartbeatException('Cannot connect to the taskcluster auth service.')
Example #6
0
def auth():
    # tests using this fixture need *some* auth service, but it actually
    # doesn't matter which one
    if "TASKCLUSTER_ROOT_URL" not in os.environ:
        msg = "TASKCLUSTER_ROOT_URL not set"
        if "NO_TEST_SKIP" in os.environ:
            pytest.fail(msg)
        else:
            pytest.skip(msg)
    return taskcluster.Auth(taskcluster.optionsFromEnvironment())
Example #7
0
def app_heartbeat():
    config = flask.current_app.config
    if config.get("TASKCLUSTER_AUTH") is True:
        auth = taskcluster.Auth(
            dict(rootUrl=flask.current_app.config["TASKCLUSTER_ROOT_URL"]))
        try:
            ping = auth.ping()
            assert ping["alive"] is True
        except Exception as e:
            logger.exception(e)
            raise treestatus_api.lib.dockerflow.HeartbeatException(
                "Cannot connect to the taskcluster auth service.")
Example #8
0
def main(client_id):
    assert client_id.startswith("project/servo/")

    print("Client ID: `%s`" % client_id)
    print("Creating a new access token will invalidate the current one.")
    if input("Continue? [y/n] ") != "y":
        return 1

    options = taskcluster.optionsFromEnvironment()
    result = taskcluster.Auth(options).resetAccessToken(client_id)

    key = "project/servo/tc-client/" + client_id[len("project/servo/"):]
    secret = {"client_id": client_id, "access_token": result["accessToken"]}
    payload = {"secret": secret, "expires": result["expires"]}
    taskcluster.Secrets(options).set(key, payload)
Example #9
0
def parse_header(request):

    auth_header = request.headers.get('Authorization')
    if not auth_header:
        return

    if flask.current_app.config.get('CHECK_FOR_RELENGAPI_TOKEN') is True:
        header = auth_header.split()
        if len(header) != 2:
            return

        user = is_relengapi_token(header[1])
        if user != IS_NOT_RELENAPI_TOKEN_USER:
            return user

    # Get Endpoint configuration
    if ':' in request.host:
        host, port = request.host.split(':')
    else:
        host = request.host
        port = request.environ.get('HTTP_X_FORWARDED_PORT')
        if port is None:
            port = request.scheme == 'https' and 443 or 80
    method = request.method.lower()

    # Build taskcluster payload
    payload = {
        'resource': request.path,
        'method': method,
        'host': host,
        'port': int(port),
        'authorization': auth_header,
    }

    # Auth with taskcluster
    auth = taskcluster.Auth()
    try:
        resp = auth.authenticateHawk(payload)
        if not resp.get('status') == 'auth-success':
            raise Exception('Taskcluster rejected the authentication')
    except Exception as e:
        logger.error('TC auth error: {}'.format(e))
        logger.error('TC auth details: {}'.format(payload))

        # Abort with a 401 status code
        return UNAUTHORIZED_JSON, 401

    return TaskclusterUser(resp)
Example #10
0
def upload_s3(paths):
    auth = taskcluster.Auth(get_taskcluster_options())
    response = auth.awsS3Credentials("read-write", "communitytc-bugbug", "data/")
    credentials = response["credentials"]

    client = boto3.client(
        "s3",
        aws_access_key_id=credentials["accessKeyId"],
        aws_secret_access_key=credentials["secretAccessKey"],
        aws_session_token=credentials["sessionToken"],
    )
    transfer = boto3.s3.transfer.S3Transfer(client)

    for path in paths:
        assert path.startswith("data/")
        transfer.upload_file(path, "communitytc-bugbug", path)
Example #11
0
    def __init__(self, root_url, client_id=None, access_token=None):
        options = {'rootUrl': root_url}
        credentials = {}

        if client_id:
            credentials['clientId'] = client_id
        if access_token:
            credentials['accessToken'] = access_token

        # Taskcluster APIs
        self.hooks = taskcluster.Hooks({**options, 'credentials': credentials})

        # Following least-privilege principle, as services
        # bellow don't really need authorization credentials.
        self.queue = taskcluster.Queue(options)
        self.auth = taskcluster.Auth(options)
Example #12
0
    def authenticate(self, request):
        """A DRF authentication class.

        Takes a request and returns the tuple of (TaskClusterUser, None)
        for a successful authenticateHawk response or raises an
        AuthenticationFailed exception.

        When behind a proxy enable the djanog USE_X_FORWARDED_PORT settings should be enable to use the right port.
        https://docs.djangoproject.com/en/1.11/ref/settings/#std:setting-USE_X_FORWARDED_PORT
        """
        if request.method == 'OPTIONS':
            return None, None

        tc_client = taskcluster.Auth()

        # auth input schema:
        # http://schemas.taskcluster.net/auth/v1/authenticate-hawk-request.json
        payload = dict(method=request.method.lower(),
                       resource=request.get_full_path(),
                       host=request.get_host(),
                       port=int(request.get_port()),
                       authorization=request.META.get('HTTP_AUTHORIZATION',
                                                      ''))

        if request.META.get('HTTP_X_FORWARDED_PROTO') == 'https':
            payload['port'] = 443

        # auth output schema: http://schemas.taskcluster.net/auth/v1/authenticate-hawk-response.json
        auth_response = tc_client.authenticateHawk(payload)

        client_id = auth_response.get('clientId', '')
        logger.debug("client_id:{}".format(client_id))

        if 'status' not in auth_response:
            raise exceptions.AuthenticationFailed(
                '\'status\' not found invalid auth response')
        elif auth_response['status'] == 'auth-failed':
            raise exceptions.AuthenticationFailed(
                auth_response.get('message', 'Unknown auth failure.'))
        elif auth_response['status'] != 'auth-success':
            raise exceptions.AuthenticationFailed(
                'invalid auth response status: %s %s' %
                (auth_response['status'], auth_response.get('message', '')))

        return TaskclusterUser(client_id=client_id,
                               scopes=auth_response.get('scopes', []),
                               is_authenticated=True), None
Example #13
0
def parse_header_taskcluster(request):
    auth_header = request.headers.get('Authorization')
    if not auth_header:
        auth_header = request.headers.get('Authentication')
    if not auth_header:
        return NO_AUTH
    if not auth_header.startswith('Hawk'):
        return NO_AUTH

    # Get Endpoint configuration
    if ':' in request.host:
        host, port = request.host.split(':')
    else:
        host = request.host
        port = request.environ.get('HTTP_X_FORWARDED_PORT')
        if port is None:
            port = request.scheme == 'https' and 443 or 80
    method = request.method.lower()

    # Build taskcluster payload
    payload = {
        'resource': request.path,
        'method': method,
        'host': host,
        'port': int(port),
        'authorization': auth_header,
    }

    # Auth with taskcluster
    auth = taskcluster.Auth(dict(rootUrl=flask.current_app.config['TASKCLUSTER_ROOT_URL']))
    try:
        resp = auth.authenticateHawk(payload)
        if not resp.get('status') == 'auth-success':
            raise Exception('Taskcluster rejected the authentication')
    except Exception as e:
        logger.error(f'TC auth error: {e}')
        logger.error(f'TC auth details: {payload}')
        return NO_AUTH

    return TaskclusterUser(resp)
Example #14
0
    def wsgi_app(environ, start_response):
        nonlocal client_id
        nonlocal access_token
        nonlocal missing_scopes
        query = urllib.parse.parse_qs(environ['QUERY_STRING'])
        client_id, = query["clientId"]
        access_token, = query["accessToken"]

        auth = taskcluster.Auth({
            "rootUrl": root_url,
            "credentials": {
                "clientId": client_id,
                "accessToken": access_token
            },
        })
        missing_scopes = set(scopes) - set(auth.currentScopes()["scopes"])
        if missing_scopes:
            start_response("401 Unauthorized", [("Content-Type", "text/html")])
            return [
                b"""
                <!doctype html>
                <title>Missing scopes</title>
                <h1>You have successfully signed in,
                    but are missing some of the requested scopes</h1>
                <ul>
            """ + "\n".join(
                    ("<li><code>%s</code></li>" % html.escape(scope))
                    for scope in sorted(missing_scopes)).encode("utf-8")
            ]

        start_response("200 OK", [("Content-Type", "text/html")])
        return [
            b"""
            <!doctype html>
            <title>Sign-In Successful</title>
            <h1>You have successfully signed in</h1>
            <p>You may now close this tab.</p>
        """
        ]
import os
import slugid
import taskcluster
import yaml
from cib import createTask, diskImageManifestHasChanged, machineImageManifestHasChanged, machineImageExists
from azure.common.credentials import ServicePrincipalCredentials
from azure.mgmt.compute import ComputeManagementClient

taskclusterOptions = {'rootUrl': os.environ['TASKCLUSTER_PROXY_URL']}

auth = taskcluster.Auth(taskclusterOptions)
queue = taskcluster.Queue(taskclusterOptions)
index = taskcluster.Index(taskclusterOptions)
secrets = taskcluster.Secrets(taskclusterOptions)

secret = secrets.get('project/relops/image-builder/dev')['secret']

platformClient = {
    'azure':
    ComputeManagementClient(
        ServicePrincipalCredentials(client_id=secret['azure']['id'],
                                    secret=secret['azure']['key'],
                                    tenant=secret['azure']['account']),
        secret['azure']['subscription'])
}

commitSha = os.getenv('GITHUB_HEAD_SHA')
taskGroupId = os.getenv('TASK_ID')
print('debug: auth.currentScopes')
print(auth.currentScopes())
azurePurgeTaskId = slugid.nice()
secretsOverridePath = '{}/../config/{}-options.yml'.format(
    basePath, os.getenv('USER'))
secretsPath = secretsOverridePath if os.path.isfile(
    secretsOverridePath
) else '{}/../config/taskcluster-client-options.yml'.format(basePath)
with open(secretsPath, 'r') as secretsStream:
    taskclusterOptions = yaml.safe_load(secretsStream)
    with open('{}/../config/deploy-on-demand.yml'.format(basePath),
              'r') as deployStream:
        deployConfig = yaml.safe_load(deployStream)
        for environment in deployConfig:
            print('taskcluster {} rootUrl: {}'.format(
                environment, taskclusterOptions[environment]['rootUrl']))

            authClient = taskcluster.Auth(taskclusterOptions[environment])
            workerManagerClient = taskcluster.WorkerManager(
                taskclusterOptions[environment])

            # clients
            for clientId in deployConfig[environment]['client']:
                clientConfigRelativePath = '{}/../config/{}/client/{}.yml'.format(
                    basePath, environment, clientId)
                if not os.path.isfile(clientConfigRelativePath):
                    clientConfigRelativePath = '{}/../config/{}/client/{}.yaml'.format(
                        basePath, environment, clientId)
                clientConfigPath = os.path.abspath(clientConfigRelativePath)
                print('- updating client: {}/{} with {}'.format(
                    environment, clientId, clientConfigPath))
                try:
                    updateClient(authClient, clientConfigPath, clientId)
Example #17
0
def get_s3_credentials() -> dict:
    auth = taskcluster.Auth(get_taskcluster_options())
    response = auth.awsS3Credentials("read-write", "communitytc-bugbug",
                                     "data/")
    return response["credentials"]
            lambda x: x not in map(
                lambda x: x.lower().strip(),
                next(line for line in lines
                     if line.lower().startswith('exclude environments:')).
                replace('exclude environments:', '').split(',')),
            includeEnvironments))
    print(
        'info: **exclude environments** commit syntax detected. ci will process environments: {}'
        .format(', '.join(includeEnvironments)))
if currentEnvironment not in includeEnvironments:
    print(
        'info: current environment ({}) is excluded. skipping pool and role checks'
        .format(currentEnvironment))
    quit()

taskclusterAuth = taskcluster.Auth(taskcluster.optionsFromEnvironment())
taskclusterWorkerManager = taskcluster.WorkerManager(
    taskcluster.optionsFromEnvironment())

updateRole(
    auth=taskclusterAuth,
    configPath='ci/config/role/branch-main.yaml',
    roleId=
    'repo:github.com/mozilla-platform-ops/cloud-image-builder:branch:main')

for pool in ['gecko-t/win10-64-azure', 'gecko-t/win7-32-azure']:
    updateRole(auth=taskclusterAuth,
               configPath='ci/config/role/{}/{}.yaml'.format(
                   currentEnvironment, pool),
               roleId='worker-pool:{}'.format(pool))
Example #19
0
def parse_header(auth_header):

    header = auth_header.split()
    if len(header) == 2 and header[0].lower() != 'bearer':

        TOKENAUTH_ISSUER = 'ra2'
        token_str = header[1]
        tokenauth_serializer = itsdangerous.JSONWebSignatureSerializer(flask.current_app.secret_key)

        try:
            claims = tokenauth_serializer.loads(token_str)
        except itsdangerous.BadData:
            logger.warning('Got invalid signature in token %r', token_str)
            return None
        except Exception:
            logger.exception('Error processing signature in token %r', token_str)
            return None

        # convert v1 to ra2
        if claims.get('v') == 1:
            claims = {'iss': 'ra2', 'typ': 'prm', 'jti': 't%d' % claims['id']}

        if claims.get('iss') != TOKENAUTH_ISSUER:
            return

        if claims['typ'] == 'prm':
            token_id = jti2id(claims['jti'])
            token_data = RelengapiToken.query.filter_by(id=token_id).first()
            if token_data:
                assert token_data.typ == 'prm'
                return RelengapiTokenUser(claims,
                                          permissions=token_data.permissions,
                                          token_data=token_data)

        elif claims['typ'] == 'tmp':
            now = time.time()
            if now < claims['nbf'] or now > claims['exp']:
                return
            permissions = [i for i in claims['prm'] if i]
            return RelengapiTokenUser(claims, permissions=permissions)

        elif claims['typ'] == 'usr':
            token_id = jti2id(claims['jti'])
            token_data = RelengapiToken.query.filter_by(id=token_id).first()
            if token_data and not token_data.disabled:
                assert token_data.typ == 'usr'
                return RelengapiTokenUser(claims,
                                          permissions=token_data.permissions,
                                          token_data=token_data,
                                          authenticated_email=token_data.user)
        else:
            return

    # Get Endpoint configuration
    if ':' in flask.request.host:
        host, port = flask.request.host.split(':')
    else:
        host = flask.request.host
        port = flask.request.environ.get('HTTP_X_FORWARDED_PORT')
        if port is None:
            port = flask.request.scheme == 'https' and 443 or 80
    method = flask.request.method.lower()

    # Build taskcluster payload
    payload = {
        'resource': flask.request.path,
        'method': method,
        'host': host,
        'port': int(port),
        'authorization': auth_header,
    }

    # Auth with taskcluster
    auth = taskcluster.Auth()
    try:
        resp = auth.authenticateHawk(payload)
        if not resp.get('status') == 'auth-success':
            raise Exception('Taskcluster rejected the authentication')
    except Exception as e:
        logger.error('TC auth error: {}'.format(e))
        logger.error('TC auth details: {}'.format(payload))

        # Abort with a 401 status code
        return {
            'error_title': 'Unauthorized',
            'error_message': 'Invalid taskcluster auth.',
        }, 401

    return TaskclusterUser(resp)
Example #20
0
environment = 'staging' if 'stage' in os.environ.get('TASKCLUSTER_ROOT_URL',
                                                     '') else 'production'
#if environment != 'staging':
#    print('info: skipping non staging deployments')
#    quit()

basePath = os.path.abspath(os.path.dirname(__file__))
cfgPath = os.path.abspath(os.path.join(basePath, '../config', environment))
taskclusterOptions = {'rootUrl': os.environ['TASKCLUSTER_PROXY_URL']}

print('- taskcluster {} deployment'.format(environment))
print('  - rootUrl: {}'.format(os.environ.get('TASKCLUSTER_ROOT_URL', '')))
print('  - proxyUrl: {}'.format(os.environ.get('TASKCLUSTER_PROXY_URL', '')))

authClient = taskcluster.Auth(taskclusterOptions)
queueClient = taskcluster.Queue(taskclusterOptions)
workerManagerClient = taskcluster.WorkerManager(taskclusterOptions)

commitSha = os.getenv('GITHUB_HEAD_SHA')
taskGroupId = os.getenv('TASK_ID')

createTask(
    queueClient=queueClient,
    image=
    'grenade/opencloudconfig@sha256:26b6d65e4a8136c97ea1805fdbf947d80a843178f7ba1fd6501d249ac887d671',
    taskId=slugid.nice(),
    taskName='01 :: apply {} configurations from {}'.format(
        environment, commitSha[:7]),
    taskDescription=
    'apply {} taskcluster configurations from [{}/{}](https://github.com/mozilla-platform-ops/cloud-image-deploy/tree/{}/.deploy/{})'
Example #21
0
def get_s3_credentials(bucket, prefix):
    auth = taskcluster.Auth(get_taskcluster_options())
    response = auth.awsS3Credentials("read-write", bucket, prefix)
    return response["credentials"]