Example #1
0
    def _setup_crypto(self):
        if not os.path.exists(settings.CRYPTO_FOLDER):
            os.makedirs(settings.CRYPTO_FOLDER)

        # FIXME: tidy up Crypto, some of its methods are no longer used
        crypto = Crypto()
        # The server_cert attribute is created on read
        crypto.server_cert
Example #2
0
    def test_version(self):
        host_info = self.mock_servers['mynewhost']
        with timed('csr', 10):
            data = {
                'fqdn': host_info['fqdn'],
                'nodename': host_info['nodename'],
                'version': '1.0',
                'capabilities': ['manage_targets'],
                'address': 'mynewhost',
                'csr': generate_csr(host_info['fqdn']),
            }

        with patch(settings, VERSION='2.0'):
            # Try with a mis-matched version
            token = RegistrationToken.objects.create(
                profile=ServerProfile.objects.get())
            with timed('register fail', 10):
                response = Client().post("/agent/register/%s/" % token.secret,
                                         data=json.dumps(data),
                                         content_type="application/json")
            self.assertEqual(response.status_code, 400)

            # Try with a matching version
            token = RegistrationToken.objects.create(
                profile=ServerProfile.objects.get())
            settings.VERSION = '1.1'
            with timed('register pass', 10):
                response = Client().post("/agent/register/%s/" % token.secret,
                                         data=json.dumps(data),
                                         content_type="application/json")
            self.assertEqual(response.status_code, 201)
            content = json.loads(response.content)

            # reregistration should fail with unknown serial
            data = {'address': 'mynewhost', 'fqdn': 'mynewhost.newcompany.com'}
            headers = {
                'HTTP_X_SSL_CLIENT_NAME': host_info['fqdn'],
                'HTTP_X_SSL_CLIENT_SERIAL': ''
            }
            response = Client().post('/agent/reregister/',
                                     data=json.dumps(data),
                                     content_type='application/json',
                                     **headers)
            self.assertEqual(response.status_code, 403)

            # reregistration should update host's domain name
            headers['HTTP_X_SSL_CLIENT_SERIAL'] = Crypto().get_serial(
                content['certificate'])
            response = Client().post('/agent/reregister/',
                                     data=json.dumps(data),
                                     content_type='application/json',
                                     **headers)
            self.assertEqual(response.status_code, 200)
            host = ManagedHost.objects.get(id=content['host_id'])
            self.assertEqual(host.fqdn, data['fqdn'])
Example #3
0
    def test_version(self):
        host_info = self.mock_servers["mynewhost"]
        with timed("csr", 10):
            data = {
                "fqdn": host_info["fqdn"],
                "nodename": host_info["nodename"],
                "version": "1.0",
                "capabilities": ["manage_targets"],
                "address": "mynewhost",
                "csr": generate_csr(host_info["fqdn"]),
            }

        with patch(settings, VERSION="2.0"):
            # Try with a mis-matched version
            token = RegistrationToken.objects.create(
                profile=ServerProfile.objects.get())
            with timed("register fail", 10):
                response = Client().post("/agent/register/%s/" % token.secret,
                                         data=json.dumps(data),
                                         content_type="application/json")
            self.assertEqual(response.status_code, 400)

            # Try with a matching version
            token = RegistrationToken.objects.create(
                profile=ServerProfile.objects.get())
            settings.VERSION = "1.1"
            with timed("register pass", 10):
                response = Client().post("/agent/register/%s/" % token.secret,
                                         data=json.dumps(data),
                                         content_type="application/json")
            self.assertEqual(response.status_code, 201)
            content = json.loads(response.content)

            # reregistration should fail with unknown serial
            data = {"address": "mynewhost", "fqdn": "mynewhost.newcompany.com"}
            headers = {
                "HTTP_X_SSL_CLIENT_NAME": host_info["fqdn"],
                "HTTP_X_SSL_CLIENT_SERIAL": ""
            }
            response = Client().post("/agent/reregister/",
                                     data=json.dumps(data),
                                     content_type="application/json",
                                     **headers)
            self.assertEqual(response.status_code, 403)

            # reregistration should update host's domain name
            headers["HTTP_X_SSL_CLIENT_SERIAL"] = Crypto().get_serial(
                content["certificate"])
            response = Client().post("/agent/reregister/",
                                     data=json.dumps(data),
                                     content_type="application/json",
                                     **headers)
            self.assertEqual(response.status_code, 200)
            host = ManagedHost.objects.get(id=content["host_id"])
            self.assertEqual(host.fqdn, data["fqdn"])
Example #4
0
def setup(request, key):
    token_error, token = validate_token(key, credits=0)
    if token_error:
        return token_error

    base_url = str(settings.SERVER_HTTP_URL)
    reg_url = path.join(base_url, "agent/register/%s/" % key)
    repo_url = path.join(base_url, "repo/")
    crypto = Crypto()
    cert_str = open(crypto.AUTHORITY_CERT_FILE).read()

    repo_packages = "python2-iml-agent"
    server_profile = ServerProfile.objects.get(
        name=request.REQUEST["profile_name"])

    repos = server_profile.repo_contents

    try:
        if server_profile.managed:
            repo_packages += " python2-iml-agent-management"
    except (ServerProfile.DoesNotExist, KeyError) as e:
        if type(e) is KeyError:
            err = "Profile name not specified"
        else:
            err = "Profile %s not a valid profile" % request.REQUEST[
                "profile_name"]
        log.error(err)
        return HttpResponse(status=400, content=err)

    server_epoch_seconds = time.time()

    profile_json = json.dumps(server_profile.as_dict)

    # read in script template before populating (parent dir is chroma-manager basedir)
    with open(
            path.join(path.dirname(path.dirname(path.abspath(__file__))),
                      "agent-bootstrap-script.template"), "r") as f:
        setup_script_template = f.read()

    script_formatted = setup_script_template.format(
        reg_url=reg_url,
        cert_str=cert_str,
        repo_url=repo_url,
        base_url=base_url,
        repos=repos,
        server_epoch_seconds=server_epoch_seconds,
        repo_packages=repo_packages,
        profile_json=profile_json,
    )

    return HttpResponse(status=201, content=script_formatted)
def register(request, key):
    if request.method != "POST":
        return HttpResponseNotAllowed(["POST"])

    token_error, registration_token = validate_token(key)
    if token_error:
        return token_error

    host_attributes = json.loads(request.body)

    # Fail at the first if the version of the agent on the server is incorrect
    manager, agent = Version(settings.VERSION), Version(
        host_attributes["version"])
    if manager and agent and not (manager.major == agent.major
                                  and manager.minor >= agent.minor):
        err = "Version incompatibility between manager {0} and agent {1}".format(
            manager, agent)
        log.error(err)
        return HttpResponse(status=400, content=err)

    # Fulfil the registering server's request for a certificate authenticating
    # it as the owner of this FQDN.
    csr = host_attributes["csr"]

    # Check that the commonName in the CSR is the same as that in host_attributes
    # (prevent registering as one host and getting a certificate to impersonate another)
    csr_fqdn = Crypto().get_common_name(csr)
    if csr_fqdn != host_attributes["fqdn"]:
        # Terse response to attacker
        log.error("FQDN mismatch '%s' vs. '%s' from %s" %
                  (csr_fqdn, host_attributes["fqdn"],
                   request.META["HTTP_X_FORWARDED_FOR"]))
        return HttpResponse(status=400, content="")

    with transaction.atomic():
        # Isolate transaction to avoid locking ManagedHost table, this
        # is just a friendly pre-check and will be enforced again inside
        # job_scheduler.create_host
        try:
            existing_host = ManagedHost.objects.get(
                fqdn=host_attributes["fqdn"])
        except ManagedHost.DoesNotExist:
            pass
        else:
            if existing_host.state != "undeployed":
                return HttpResponse(status=400,
                                    content=json.dumps(
                                        {"fqdn": ["FQDN in use"]}))

    certificate_str = Crypto().sign(csr)
    certificate_serial = Crypto().get_serial(certificate_str)
    log.info("Generated certificate %s:%s" %
             (host_attributes["fqdn"], certificate_serial))
    ValidatedClientView.valid_certs[certificate_serial] = host_attributes[
        "fqdn"]

    # FIXME: handle the case where someone registers,
    # and then dies before saving their certificate:
    # when they come through here again, currently
    # we'll reject them because the FQDN is taken
    # ... maybe hand back the certificate here, but
    # then don't create the host until they first
    # connect using the certificate?
    # in that case to avoid handing out another cert
    # to someone else spamming our URL, we should have
    # some logic during the second addition to revoke
    # the first (should never be used) host cert.

    server_profile = registration_token.profile
    from chroma_core.services.job_scheduler.job_scheduler_client import JobSchedulerClient

    host, command = JobSchedulerClient.create_host(
        address=host_attributes["address"],
        fqdn=host_attributes["fqdn"],
        nodename=host_attributes["nodename"],
        server_profile_id=server_profile.pk,
    )

    with transaction.atomic():
        ClientCertificate.objects.create(host=host, serial=certificate_serial)

    # TODO: document this return format
    return HttpResponse(
        status=201,
        content=json.dumps({
            "command_id": command.id,
            "host_id": host.id,
            "certificate": certificate_str
        }),
        content_type="application/json",
    )
Example #6
0
def setup(request, key):
    token_error, token = validate_token(key, credits=0)
    if token_error:
        return token_error

    # the minimum repos needed on a storage server now
    repos = open("/usr/share/chroma-manager/storage_server.repo").read()

    repo_names = token.profile.bundles.values_list('bundle_name', flat=True)
    for bundle in Bundle.objects.all():
        if bundle.bundle_name != "external":
            repos += """[%s]
name=%s
baseurl={0}/%s/$releasever/
enabled=0
gpgcheck=0
sslverify = 1
sslcacert = {1}
sslclientkey = {2}
sslclientcert = {3}
proxy=_none_

""" % (bundle.bundle_name, bundle.description, bundle.bundle_name)

    base_url = str(settings.SERVER_HTTP_URL)
    reg_url = path.join(base_url, 'agent/register/%s/' % key)
    repo_url = path.join(base_url, 'repo/')
    crypto = Crypto()
    cert_str = open(crypto.AUTHORITY_CERT_FILE).read()

    repo_packages = 'chroma-agent'
    server_profile = ServerProfile.objects.get(name = request.REQUEST['profile_name'])

    try:
        if server_profile.managed:
            repo_packages += ' chroma-agent-management'
    except (ServerProfile.DoesNotExist, KeyError) as e:
        if type(e) is KeyError:
            err = "Profile name not specified"
        else:
            err = "Profile %s not a valid profile" % request.REQUEST['profile_name']
        log.error(err)
        return HttpResponse(status = 400, content = err)

    server_epoch_seconds = time.time()

    profile_json = json.dumps(server_profile.as_dict)

    # read in script template before populating (parent dir is chroma-manager basedir)
    with open(path.join(path.dirname(path.dirname(path.abspath(__file__))),
                        'agent-bootstrap-script.template'), 'r') as f:
        setup_script_template = f.read()

    script_formatted = setup_script_template.format(reg_url=reg_url, cert_str=cert_str,
                                                    repo_url=repo_url, base_url=base_url,
                                                    repos=repos, repo_names=",".join(repo_names),
                                                    server_epoch_seconds=server_epoch_seconds,
                                                    repo_packages=repo_packages,
                                                    profile_json=profile_json)

    return HttpResponse(status = 201, content = script_formatted)