Exemplo n.º 1
0
def _prompt_unencrypt_context(request,
                              ctx,
                              callback_url,
                              decode_data=True,
                              decode_render=False):
    """
    Takes care of prompting user for a password and returning an unencrypted
    version of a given context "data" section and "rendered" representation.
    Decoded data is returned as strings. No "unpickling" is performed
    """
    resp = {}
    title = "Context encrypted"
    body = "The context information you are trying to use are encrypted with " \
        "a private key. Please enter such key below to decrypt:"

    if "password" in request.POST:
        # POST already contains "unicode" data!
        pwd = request.POST["password"].encode("ascii", "ignore")
        if salt_context_key(ctx.id, pwd) == ctx.key:
            # Password is OK: decrypt
            if decode_data:
                resp["data"] = crypt.decrypt(base64.b64decode(str(ctx.data)),
                                             pwd)
            if decode_render:
                render = ContextStorage.objects.get(id=ctx.id)
                m = re.search(r"^ENCRYPTED:(.*)$", render.data)
                if m:
                    resp["render"] = crypt.decrypt(
                        base64.b64decode(str(m.group(1))), pwd)
                # Response empty in case of problems
        else:
            # Password is wrong
            resp["httpresp"] = render_password_prompt(
                request, title, body, callback_url,
                {"msg_error": "Wrong password"})
    else:
        # Prompt for password
        resp["httpresp"] = render_password_prompt(request, title, body,
                                                  callback_url)

    return resp
Exemplo n.º 2
0
def _prompt_unencrypt_context(request, ctx, callback_url, decode_data=True,
                              decode_render=False):
    """
    Takes care of prompting user for a password and returning an unencrypted
    version of a given context "data" section and "rendered" representation.
    Decoded data is returned as strings. No "unpickling" is performed
    """
    resp = {}
    title = "Context encrypted"
    body = "The context information you are trying to use are encrypted with " \
        "a private key. Please enter such key below to decrypt:"

    if "password" in request.POST:
        # POST already contains "unicode" data!
        pwd = request.POST["password"].encode("ascii", "ignore")
        if salt_context_key(ctx.id, pwd) == ctx.key:
            # Password is OK: decrypt
            if decode_data:
                resp["data"] = crypt.decrypt(
                    base64.b64decode(str(ctx.data)), pwd)
            if decode_render:
                render = ContextStorage.objects.get(id=ctx.id)
                m = re.search(r"^ENCRYPTED:(.*)$", render.data)
                if m:
                    resp["render"] = crypt.decrypt(
                        base64.b64decode(str(m.group(1))), pwd)
                # Response empty in case of problems
        else:
            # Password is wrong
            resp["httpresp"] = render_password_prompt(
                request, title, body, callback_url,
                {"msg_error": "Wrong password"}
            )
    else:
        # Prompt for password
        resp["httpresp"] = render_password_prompt(request, title, body,
                                                  callback_url)

    return resp
Exemplo n.º 3
0
def _render_elastiq_plugin(resp):
    """
    Given the clean data from the cluster form it returns a string, the
    elastiq-setup amiconfig plugin settings.
    """
    plg = ""

    # Elastiq: Jobs per VM
    v = resp["elastiq"].get("n_jobs_per_vm", None)
    if v:
        plg += "elastiq_n_jobs_per_vm=%d\n" % v

    # Elastiq: VM deployment time
    v = resp["elastiq"].get("estimated_vm_deploy_time_s", None)
    if v:
        plg += "elastiq_estimated_vm_deploy_time_s=%d\n" % v

    # Elastiq: Queue / VM checking times
    v = resp["elastiq"].get("check_queue_every_s", None)
    if v:
        plg += "elastiq_check_queue_every_s=%d\n" % v
    v = resp["elastiq"].get("check_vms_every_s", None)
    if v:
        plg += "elastiq_check_vms_every_s=%d\n" % v

    # Elastiq: Idle time before killing
    v = resp["elastiq"].get("idle_for_time_s", None)
    if v:
        plg += "elastiq_idle_for_time_s=%d\n" % v

    # Elastiq: Minimum time a job is waiting
    v = resp["elastiq"].get("waiting_jobs_time_s", None)
    if v:
        plg += "elastiq_waiting_jobs_time_s=%d\n" % v

    # Elastiq: Batch sysrem
    v = resp["elastiq"].get("batch_plugin", None)
    if v:
        plg += "elastiq_batch_plugin=%s\n" % v

    # Quota
    plg += "quota_min_vms=%d\n" \
        % resp["quota"].get("min_vms", 2)
    v = resp["quota"].get("max_vms", None)
    if v:
        plg += "quota_max_vms=%d\n" % v

    # EC2: API
    plg += "ec2_api_url=%s\n" \
        % resp["ec2"]["api_url"]
    v = resp["ec2"].get("api_version", None)
    if v:
        plg += "ec2_api_version=%s\n" % v

    # EC2: Access key
    plg += "ec2_aws_access_key_id=%s\n" \
        % resp["ec2"]["aws_access_key_id"]
    plg += "ec2_aws_secret_access_key=%s\n" \
        % resp["ec2"]["aws_secret_access_key"]

    # EC2: Image
    plg += "ec2_image_id=%s\n" \
        % resp["ec2"]["image_id"]
    # EC2: Flavor
    plg += "ec2_flavour=%s\n" % resp["ec2"]["flavour"]

    # EC2: Keypair
    v = resp["ec2"].get("key_name", None)
    if v:
        plg += "ec2_key_name=%s\n" % v

    # user-data of the Worker Context
    wc = ContextStorage.objects.get(id=resp["cluster"]["worker_context_id"])
    if wc.is_encrypted:

        # You *must* handle exceptions in the caller
        wpwd = resp['cluster']['worker_context_pwd']
        wcdef = ContextDefinition.objects.get(id=resp["cluster"]["worker_context_id"])

        # Verify password before decrypting
        if salt_context_key(wcdef.id, wpwd) == wcdef.key:
            wc.decrypt(wpwd)
        else:
            raise cvmo_crypt.DecryptionError('Wrong password supplied')

    plg += "ec2_user_data_b64=%s\n" % base64.b64encode(wc.ec2_user_data)

    return ("elastiq-setup", plg)
Exemplo n.º 4
0
def save(request):

    # For debug
    # post_dict = parser.parse( unicode(request.POST.urlencode()).encode("utf-8") )
    # return uncache_response(HttpResponse(json.dumps(post_dict, indent=2), content_type="text/plain"))

    l = logging.getLogger("cvmo")

    # Validate request
    resp = _validate_for_save(request)
    if isinstance(resp, HttpResponse):
        return resp

    # For debug: output validated form
    # return uncache_response(HttpResponse(json.dumps(resp, indent=2), content_type="text/plain"))

    #
    # Preparing the full Master Context.
    #
    # This is a full new context available in ContextStorage, but with no corresponding
    # ContextDefinition. This context is the original Master Context plus some sections
    # appended.
    #
    # Note that the full Master Context is generated unencrypted, unless an appropriate
    # Cluster Password is provided, even if the original Master Context was secure.
    #

    try:
        (plg_name, plg_cont) = _render_elastiq_plugin(resp)
    except Exception:
        messages.error(request, 'Wrong Worker Context password supplied!')
        return _show_cluster_def(request, resp)

    # For debug: output generated extra section (with worker user-data embedded)
    # return uncache_response(HttpResponse(json.dumps({'plg_name':plg_name, 'plg_cont':plg_cont}, indent=2), content_type="text/plain"))

    master_ctx = ContextStorage.objects.get( id=resp["cluster"]["master_context_id"] )

    if master_ctx.is_encrypted:

        try:

            mpwd = resp['cluster']['master_context_pwd']
            mdef = ContextDefinition.objects.get( id=resp['cluster']['master_context_id'] )

            # Verify password before decrypting
            if salt_context_key(mdef.id, mpwd) == mdef.key:
                master_ctx.decrypt(mpwd)
            else:
                raise Exception()

        except Exception:

            messages.error(request, 'Wrong Head Context password supplied!')
            return _show_cluster_def(request, resp)

    new_ud = _append_plugin_in_ud(master_ctx.ec2_user_data, plg_name, plg_cont)
    if not new_ud:
        messages.error(
            request,
            "Failed to append `elastiq-setup` plugin in master context!"
        )
        l.log(
            logging.ERROR,
            "Failed to append `elastiq-setup` plugin in master context %s!"
            % resp["cluster"]["master_context_id"]
        )
        return _show_cluster_def(request, resp)

    # Do we have a passphrase for the cluster?
    if 'passphrase' in resp['cluster']:
        passphrase = resp['cluster']['passphrase']
    else:
        # Never None
        passphrase = ''

    #
    # Store the deployable context (rendered)
    #

    context_id = ContextDefinition.generate_new_id()
    cs = ContextStorage.create(
        context_id, "Cluster %s head node" % resp["cluster"]["name"],
        new_ud, master_ctx.root_ssh_key
    )
    if passphrase != '':
        cs.encrypt( passphrase )
    cs.save()

    #
    # Create the Cluster Definition
    #

    # First create a string representing the input data
    data = {}
    for k in [ 'ec2', 'quota', 'elastiq' ]:
        if k in resp:
            data[k] = resp[k]
        else:
            data[k] = {}

    # Let's store some cluster variables here as well

    data['passphrase'] = passphrase

    data_json_str = json.dumps(data, indent=2)
    #return uncache_response( HttpResponse( data_json_str, content_type='text/plain' ) )

    # Encrypt the Cluster Definition with the given passphrase
    if passphrase != '':

        # To verify, we calculate the SHA1SUM of the unencrypted JSON blob and store it
        checksum = hashlib.sha1( data_json_str ).hexdigest()

        # Now, we encrypt
        data_json_str = base64.b64encode( cvmo_crypt.encrypt(data_json_str, passphrase) )

    else:
        # Non-encrypted, plain text context
        checksum = ''

    cd = ClusterDefinition(
        name=resp["cluster"]["name"],
        description=resp["cluster"].get("description", None),
        owner=request.user,
        master_context=ContextDefinition.objects.get(
            id=resp["cluster"]["master_context_id"]
        ),
        worker_context=ContextDefinition.objects.get(
            id=resp["cluster"]["worker_context_id"]
        ),
        deployable_context=cs,
        data=data_json_str,
        encryption_checksum=checksum,
    )
    cd.save()

    messages.success(
        request, "Cluster '%s' was successfully stored!" % cd.name
    )
    return redirect("dashboard")
Exemplo n.º 5
0
def webstart_req(request):
    """
	Request a webstart of a context/config pair
	"""

    return HttpResponse(reverse("webapi_webstart_run"))

    # Get a logger
    log = logging.getLogger("cvmo.webapi")

    # Validate request
    if not "context" in request.GET:
        log.log(logging.ERROR, "`context` is required")
        raise SuspiciousOperation("`context` is required")
    if not "config" in request.GET:
        log.log(logging.ERROR, "`config` is required")
        raise SuspiciousOperation("`config` is required")

    ############################
    # Render user data
    ############################

    # Fetch context
    try:
        ctx = ContextDefinition.objects.get(id=request.GET['context'])
    except ContextDefinition.DoesNotExist:
        raise SuspiciousOperation("`context` is required")

    # Load the rendered context
    try:
        ctx_storage = ContextStorage.objects.get(id=request.GET['context'])
        ctx_storage_data = ctx_storage.data
    except ContextStorage.DoesNotExist:
        return HttpResponse("not-found-rendered", content_type="text/plain")

    # If the context is encrypted, prompt the user
    # for the password
    if ctx.key:

        # If we have password, continue
        if "password" in request.POST:

            # POST already contains "unicode" data!
            pwd = request.POST["password"].encode("ascii", "ignore")
            if salt_context_key(ctx.id, pwd) == ctx.key:

                # Descript and un-base64
                m = re.search(r"^ENCRYPTED:(.*)$", ctx_storage_data)
                if m is None:
                    return HttpResponse("render-format-error",
                                        content_type="text/plain")
                try:
                    user_data = crypt.decrypt(
                        base64.b64decode(str(m.group(1))), pwd)
                except:
                    return HttpResponse("render-encoding-error",
                                        content_type="text/plain")

            else:

                # Render password prompt with error
                return render(
                    request, "webapi/password_prompt.html", {
                        "context": request.GET['context'],
                        "config": request.GET['config'],
                        "error": "Wrong password. Please try again"
                    })

        else:

            # Render password prompt
            return render(
                request, "webapi/password_prompt.html", {
                    "context": request.GET['context'],
                    "config": request.GET['config'],
                    "error": ""
                })

    else:

        # Un-base64
        m = re.search(r"^\s*EC2_USER_DATA\s*=\s*([^\s]*)$", ctx_storage_data,
                      re.M)
        if m is None:
            return HttpResponse("render-format-error",
                                content_type="text/plain")
        try:
            user_data = base64.b64decode(str(m.group(1)))
        except:
            return HttpResponse("render-encoding-error",
                                content_type="text/plain")

    ############################
    # Fetch WebAPI configuration
    ############################

    vm_config_id = int(request.GET['config'])
    try:
        vm_config = settings.WEBAPI_CONFIGURATIONS[vm_config_id]
    except IndexError:
        return HttpResponse("not-found-config", content_type="text/plain")

    ############################
    # Prepare VMCP One-Time Tag
    ############################

    # Create VMCP settings
    vmcp_settings = {
        'name':
        '%s-%s' % (UNSAFE_CHARS.sub("_", ctx.name), "".join([
            random.choice(string.digits + string.letters)
            for x in range(0, 10)
        ])),
        'userData':
        user_data,
        'memory':
        vm_config['memory'],
        'cpus':
        vm_config['cpus'],
        'disk':
        vm_config['disk_size'],
        'cernvmVersion':
        settings.WEBAPI_UCERNVM_VERSION,
        'flags':
        0x31
    }

    # Store json config on tag
    tag = WebAPIOneTimeTag(payload=json.dumps(vmcp_settings),
                           uuid=uuid.uuid4().hex)
    tag.save()
    # Redirect to the HTTP version of webstart_run
    return redirect("%s?tag=%s" % (reverse("webapi_webstart_run"), tag.uuid))
Exemplo n.º 6
0
def create(request):
    post_dict = parser.parse(unicode(request.POST.urlencode()).encode("utf-8"))

    # Just for debug
    # return uncache_response( HttpResponse( json.dumps(post_dict, indent=2), content_type='text/plain' ) )

    # The values of all the plugins and the enabled plugins
    values = post_dict.get("values")
    enabled = post_dict.get("enabled")
    abstract = post_dict.get("abstract")

    # Generate a UUID for this context
    c_uuid = gen_context_key()

    # We need to generate hashes for passwords here: only on uCernVM for
    # compatibility reasons
    if "cvm_version" in values["general"] \
            and values["general"]["cvm_version"] == "uCernVM":
        if "users" in values["general"]:
            for k, v in values["general"]["users"].iteritems():
                # Don"t re-hash (useful when cloning)
                if not str(v["password"]).startswith("$6$"):
                    # rounds=5000 is used to avoid the $round=xxx$ placed into
                    # out string, see:
                    # http://pythonhosted.org/passlib/lib/passlib.hash.sha256_crypt.html
                    h = sha512_crypt.encrypt(str(v["password"]),
                                             salt_size=8,
                                             rounds=5000)
                    values["general"]["users"][k]["password"] = h

    # Collect data to save. Non-indexed data is pickled
    raw_values = {"values": values, "enabled": enabled}
    if abstract is not None:
        raw_values["abstract"] = abstract
        from_abstract = True
    else:
        from_abstract = False

    # Prepare pickled data for easy reconstruction
    # (in case somebody wants to clone a template)
    c_values = pickle.dumps(raw_values)
    c_config = ContextPlugins().renderContext(c_uuid, values, enabled)

    # Generate checksum of the configuration
    c_checksum = hashlib.sha1(c_config).hexdigest()

    # Get the possible secret key
    c_key = ""
    if ("protect" in values) and (values["protect"]):
        c_key = str(values["secret"])

    # If the content is encrypted process the data now
    if (c_key != ""):
        c_values = base64.b64encode(crypt.encrypt(c_values, c_key))
        c_config = "ENCRYPTED:" + \
            base64.b64encode(crypt.encrypt(c_config, c_key))
        c_key = salt_context_key(c_uuid, c_key)

    # Check if this is public
    c_public = False
    if ("public" in values) and (values["public"]):
        c_public = True

    # Save context definition
    ContextDefinition.objects.create(
        id=c_uuid,
        name=tou(values["name"]),
        description=tou(values["description"]),
        owner=request.user,
        key=c_key,
        public=c_public,
        data=c_values,
        checksum=c_checksum,
        inherited=False,
        abstract=False,  # only True for pure abstract contexts
        from_abstract=from_abstract)

    # Save context data (Should go to key/value store for speed-up)
    ContextStorage.objects.create(id=c_uuid, data=c_config)

    # Go to dashboard
    return redirect("dashboard")
Exemplo n.º 7
0
def webstart_req(request):
	"""
	Request a webstart of a context/config pair
	"""

	return HttpResponse(reverse("webapi_webstart_run"))

	# Get a logger
	log = logging.getLogger("cvmo.webapi")

	# Validate request
	if not "context" in request.GET:
		log.log(logging.ERROR, "`context` is required")
		raise SuspiciousOperation("`context` is required")
	if not "config" in request.GET:
		log.log(logging.ERROR, "`config` is required")
		raise SuspiciousOperation("`config` is required")

	############################
	# Render user data
	############################

	# Fetch context
	try:
		ctx = ContextDefinition.objects.get(id=request.GET['context'])
	except ContextDefinition.DoesNotExist:
		raise SuspiciousOperation("`context` is required")

	# Load the rendered context
	try:
		ctx_storage = ContextStorage.objects.get(id=request.GET['context'])
		ctx_storage_data = ctx_storage.data
	except ContextStorage.DoesNotExist:
		return HttpResponse("not-found-rendered",
							content_type="text/plain")

	# If the context is encrypted, prompt the user
	# for the password
	if ctx.key:

		# If we have password, continue
		if "password" in request.POST:

			# POST already contains "unicode" data!
			pwd = request.POST["password"].encode("ascii", "ignore")
			if salt_context_key(ctx.id, pwd) == ctx.key:

				# Descript and un-base64
				m = re.search(r"^ENCRYPTED:(.*)$", ctx_storage_data)
				if m is None:
					return HttpResponse("render-format-error",
										content_type="text/plain")
				try:
					user_data = crypt.decrypt(
						base64.b64decode(str(m.group(1))), pwd)
				except:
					return HttpResponse("render-encoding-error",
										content_type="text/plain")

			else:
	
				# Render password prompt with error
				return render(
					request,
					"webapi/password_prompt.html",
					{
						"context": request.GET['context'],
						"config": request.GET['config'],
						"error": "Wrong password. Please try again"
					}
				)

		else:

			# Render password prompt
			return render(
				request,
				"webapi/password_prompt.html",
				{
					"context": request.GET['context'],
					"config": request.GET['config'],
					"error": ""
				}
			)

	else:
			
		# Un-base64
		m = re.search(r"^\s*EC2_USER_DATA\s*=\s*([^\s]*)$", ctx_storage_data, re.M)
		if m is None:
			return HttpResponse("render-format-error",
								content_type="text/plain")
		try:
			user_data = base64.b64decode(str(m.group(1)))
		except:
			return HttpResponse("render-encoding-error",
								content_type="text/plain")

	############################
	# Fetch WebAPI configuration
	############################

	vm_config_id = int(request.GET['config'])
	try:
		vm_config = settings.WEBAPI_CONFIGURATIONS[vm_config_id]
	except IndexError:
		return HttpResponse("not-found-config",
							content_type="text/plain")

	############################
	# Prepare VMCP One-Time Tag
	############################

	# Create VMCP settings
	vmcp_settings = {
		'name'  		: '%s-%s' % ( UNSAFE_CHARS.sub("_", ctx.name), "".join([random.choice(string.digits + string.letters) for x in range(0,10)]) ),
		'userData' 		: user_data,
		'memory'  		: vm_config['memory'],
		'cpus' 			: vm_config['cpus'],
		'disk' 			: vm_config['disk_size'],
		'cernvmVersion'	: settings.WEBAPI_UCERNVM_VERSION,
		'flags'			: 0x31
	}

	# Store json config on tag
	tag = WebAPIOneTimeTag(
			payload=json.dumps( vmcp_settings ),
			uuid=uuid.uuid4().hex
			)
	tag.save()
	# Redirect to the HTTP version of webstart_run
	return redirect(
		"%s?tag=%s" % (reverse("webapi_webstart_run"), tag.uuid )
		)
Exemplo n.º 8
0
def create(request):
    post_dict = parser.parse(
        unicode(request.POST.urlencode()).encode("utf-8"))

    # Just for debug
    # return uncache_response( HttpResponse( json.dumps(post_dict, indent=2), content_type='text/plain' ) )

    # The values of all the plugins and the enabled plugins
    values = post_dict.get("values")
    enabled = post_dict.get("enabled")
    abstract = post_dict.get("abstract")

    # Generate a UUID for this context
    c_uuid = gen_context_key()

    # We need to generate hashes for passwords here: only on uCernVM for
    # compatibility reasons
    if "cvm_version" in values["general"] \
            and values["general"]["cvm_version"] == "uCernVM":
        if "users" in values["general"]:
            for k, v in values["general"]["users"].iteritems():
                # Don"t re-hash (useful when cloning)
                if not str(v["password"]).startswith("$6$"):
                    # rounds=5000 is used to avoid the $round=xxx$ placed into
                    # out string, see:
                    # http://pythonhosted.org/passlib/lib/passlib.hash.sha256_crypt.html
                    h = sha512_crypt.encrypt(
                        str(v["password"]), salt_size=8, rounds=5000
                    )
                    values["general"]["users"][k]["password"] = h

    # Collect data to save. Non-indexed data is pickled
    raw_values = {"values": values, "enabled": enabled}
    if abstract is not None:
        raw_values["abstract"] = abstract
        from_abstract = True
    else:
        from_abstract = False

    # Prepare pickled data for easy reconstruction
    # (in case somebody wants to clone a template)
    c_values = pickle.dumps(raw_values)
    c_config = ContextPlugins().renderContext(c_uuid, values, enabled)

    # Generate checksum of the configuration
    c_checksum = hashlib.sha1(c_config).hexdigest()

    # Get the possible secret key
    c_key = ""
    if ("protect" in values) and (values["protect"]):
        c_key = str(values["secret"])

    # If the content is encrypted process the data now
    if (c_key != ""):
        c_values = base64.b64encode(crypt.encrypt(c_values, c_key))
        c_config = "ENCRYPTED:" + \
            base64.b64encode(crypt.encrypt(c_config, c_key))
        c_key = salt_context_key(c_uuid, c_key)

    # Check if this is public
    c_public = False
    if ("public" in values) and (values["public"]):
        c_public = True

    # Save context definition
    ContextDefinition.objects.create(
        id=c_uuid,
        name=tou(values["name"]),
        description=tou(values["description"]),
        owner=request.user,
        key=c_key,
        public=c_public,
        data=c_values,
        checksum=c_checksum,
        inherited=False,
        abstract=False,  # only True for pure abstract contexts
        from_abstract=from_abstract
    )

    # Save context data (Should go to key/value store for speed-up)
    ContextStorage.objects.create(
        id=c_uuid,
        data=c_config
    )

    # Go to dashboard
    return redirect("dashboard")