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
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'])
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"])
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", )
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)