aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Fincham <michael@hotplate.co.nz>2018-06-03 21:51:08 +1200
committerMichael Fincham <michael@hotplate.co.nz>2018-06-03 21:51:08 +1200
commit248ab7b3b38f91d001f37d05b0c6b4548d867e0f (patch)
tree0c801c73373356e94e106bb989c3a42f9538befc
parent0c86329f1a8f08045fecb08ed65851bba2e761b4 (diff)
downloadmango-master.tar.gz
mango-master.tar.bz2
mango-master.zip
Adds tag support, example configs, some minor cleanupsHEADmaster
-rw-r--r--api/__init__.py1
-rw-r--r--api/admin.py36
-rw-r--r--api/apps.py1
-rw-r--r--api/migrations/0001_initial.py57
-rw-r--r--api/migrations/0002_auto_20180603_2107.py43
-rw-r--r--api/migrations/0003_auto_20180603_2121.py25
-rw-r--r--api/migrations/__init__.py0
-rw-r--r--api/models.py38
-rw-r--r--api/views.py44
-rw-r--r--mango/urls.py2
-rw-r--r--osquery.flags9
-rw-r--r--stunnel.conf8
-rw-r--r--test_server.key27
-rw-r--r--test_server.pem17
-rw-r--r--test_server_ca.pem18
15 files changed, 256 insertions, 70 deletions
diff --git a/api/__init__.py b/api/__init__.py
index e69de29..23ca7a6 100644
--- a/api/__init__.py
+++ b/api/__init__.py
@@ -0,0 +1 @@
+default_app_config = 'api.apps.ApiConfig'
diff --git a/api/admin.py b/api/admin.py
index 22a32ba..17a3d8c 100644
--- a/api/admin.py
+++ b/api/admin.py
@@ -2,6 +2,15 @@ from django.contrib import admin
from .models import *
+class TagAdmin(admin.ModelAdmin):
+ list_display = ('name', 'host_count', 'query_count')
+
+ def host_count(self, tag):
+ return tag.host_set.count()
+
+ def query_count(self, tag):
+ return tag.logquery_set.count()
+
class LogQueryAdmin(admin.ModelAdmin):
list_display = ('name', 'query', 'interval')
@@ -9,30 +18,23 @@ class LogEntryAdmin(admin.ModelAdmin):
list_display = ('host', 'name', 'action', 'shortened_output', 'created')
list_filter = ['created','action']
- def shortened_output(self, obj):
- if len(obj.output) > 90:
- return "%s..." % obj.output[:90]
- return obj.output
-
-# class PackageAdmin(admin.ModelAdmin):
-# list_display = ('name', 'version', 'host', 'architecture', 'created')
-# search_fields = ('name', )
-
-# class PackageInline(admin.TabularInline):
-# model = Package
+ def shortened_output(self, entry):
+ if len(entry.output) > 90:
+ return "%s..." % entry.output[:90]
+ return entry.output
class LogEntryInline(admin.TabularInline):
model = LogEntry
extra = 0
class HostAdmin(admin.ModelAdmin):
- list_display = ('identifier', 'release', 'architecture', 'cpu', 'ram_gib', 'last_seen', 'alive')
- inlines = [
- LogEntryInline,
- ]
- list_filter = ['release', 'architecture']
+ list_display = ('identifier', 'tag_list', 'release', 'architecture', 'cpu', 'ram_gib', 'last_seen', 'alive')
+ list_filter = ['release', 'architecture', 'tags']
+
+ def tag_list(self, host):
+ return ", ".join([tag.name for tag in host.tags.all()])
admin.site.register(Host, HostAdmin)
admin.site.register(LogEntry, LogEntryAdmin)
admin.site.register(LogQuery, LogQueryAdmin)
-# admin.site.register(Package, PackageAdmin)
+admin.site.register(Tag, TagAdmin)
diff --git a/api/apps.py b/api/apps.py
index d87006d..614fd32 100644
--- a/api/apps.py
+++ b/api/apps.py
@@ -3,3 +3,4 @@ from django.apps import AppConfig
class ApiConfig(AppConfig):
name = 'api'
+ verbose_name = 'API'
diff --git a/api/migrations/0001_initial.py b/api/migrations/0001_initial.py
new file mode 100644
index 0000000..a5f1faa
--- /dev/null
+++ b/api/migrations/0001_initial.py
@@ -0,0 +1,57 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.13 on 2018-06-03 08:44
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Host',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('node_key', models.CharField(db_index=True, help_text='Secret key this host uses to identify itself.', max_length=32, unique=True)),
+ ('identifier', models.CharField(db_index=True, help_text='Unique identifier for this system (usually hostname).', max_length=255)),
+ ('last_seen', models.DateTimeField(auto_now_add=True, help_text='Last time this host checked in.')),
+ ('invalidate', models.BooleanField(default=False, help_text='Whether this node should be re-enrolled from scratch next time it checks in.')),
+ ('architecture', models.CharField(blank=True, db_index=True, help_text='Machine architecture.', max_length=200)),
+ ('release', models.CharField(blank=True, db_index=True, help_text='Operating system release.', max_length=200)),
+ ('cpu', models.CharField(blank=True, help_text='Model of CPU installed.', max_length=200)),
+ ('ram', models.BigIntegerField(blank=True, help_text='Amount of RAM installed (KiB).')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='LogEntry',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(db_index=True, max_length=255)),
+ ('action', models.CharField(max_length=255)),
+ ('output', models.TextField()),
+ ('created', models.DateTimeField(auto_now_add=True)),
+ ('host', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Host')),
+ ],
+ options={
+ 'verbose_name_plural': 'log entries',
+ },
+ ),
+ migrations.CreateModel(
+ name='LogQuery',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(help_text='A descriptive name for the query', max_length=255)),
+ ('query', models.CharField(help_text='The query to be executed', max_length=255)),
+ ('interval', models.IntegerField(default=10, help_text='How often should the query be run (in seconds)?')),
+ ],
+ options={
+ 'verbose_name_plural': 'log queries',
+ },
+ ),
+ ]
diff --git a/api/migrations/0002_auto_20180603_2107.py b/api/migrations/0002_auto_20180603_2107.py
new file mode 100644
index 0000000..8c16d53
--- /dev/null
+++ b/api/migrations/0002_auto_20180603_2107.py
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.13 on 2018-06-03 09:07
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('api', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Tag',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.SlugField(allow_unicode=True, help_text='Descriptive slug name for the tag', max_length=22)),
+ ],
+ ),
+ migrations.AddField(
+ model_name='logquery',
+ name='snapshot',
+ field=models.BooleanField(default=False, help_text='Whether this query should send a report even when the data may not have changed. Useful for time series metrics and keepalives.'),
+ preserve_default=False,
+ ),
+ migrations.AlterField(
+ model_name='logquery',
+ name='name',
+ field=models.CharField(help_text='Descriptive name for the query', max_length=255),
+ ),
+ migrations.AddField(
+ model_name='host',
+ name='tags',
+ field=models.ManyToManyField(to='api.Tag'),
+ ),
+ migrations.AddField(
+ model_name='logquery',
+ name='tags',
+ field=models.ManyToManyField(to='api.Tag'),
+ ),
+ ]
diff --git a/api/migrations/0003_auto_20180603_2121.py b/api/migrations/0003_auto_20180603_2121.py
new file mode 100644
index 0000000..35890e5
--- /dev/null
+++ b/api/migrations/0003_auto_20180603_2121.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.13 on 2018-06-03 09:21
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('api', '0002_auto_20180603_2107'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='host',
+ name='tags',
+ field=models.ManyToManyField(help_text='Only queries tagged with these tags will be run on this host.', to='api.Tag'),
+ ),
+ migrations.AlterField(
+ model_name='logquery',
+ name='tags',
+ field=models.ManyToManyField(help_text='Only hosts tagged with these tags will run these queries. Specifying no tags makes a query run on all hosts.', to='api.Tag'),
+ ),
+ ]
diff --git a/api/migrations/__init__.py b/api/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/api/migrations/__init__.py
diff --git a/api/models.py b/api/models.py
index b3d9551..e7de8c2 100644
--- a/api/models.py
+++ b/api/models.py
@@ -6,9 +6,19 @@ from django.utils import timezone
import math
import datetime
+class Tag(models.Model):
+ """
+ A tag used to associate queries with hosts, and hosts with one another.
+ """
+
+ name = models.SlugField(max_length=22, allow_unicode=True, help_text="Descriptive slug name for the tag")
+
+ def __str__(self):
+ return self.name
+
class Host(models.Model):
"""
- One computer
+ One computer.
"""
node_key = models.CharField(max_length=32, db_index=True, unique=True, help_text="Secret key this host uses to identify itself.")
@@ -19,6 +29,7 @@ class Host(models.Model):
release = models.CharField(max_length=200, db_index=True, help_text="Operating system release.", blank=True)
cpu = models.CharField(max_length=200, help_text="Model of CPU installed.", blank=True)
ram = models.BigIntegerField(help_text="Amount of RAM installed (KiB).", blank=True)
+ tags = models.ManyToManyField(Tag, help_text="Only queries tagged with these tags will be run on this host.")
def __str__(self):
if self.identifier:
@@ -32,33 +43,16 @@ class Host(models.Model):
return self.last_seen > timezone.now() - datetime.timedelta(minutes=30)
alive.boolean = True
-# Not enabled in this demo:
-#
-# class Package(models.Model):
-# """
-# Operating system package
-# """
-#
-# name = models.CharField(db_index=True, max_length=200, help_text="Name of package from the operating system's package manager.")
-# host = models.ForeignKey(Host, db_index=True)
-# version = models.CharField(db_index=True, max_length=200, help_text="The package manager's version for this package.")
-# architecture = models.CharField(max_length=200, help_text="Package architecture, which may differ from the host architecture.")
-# created = models.DateTimeField(auto_now_add=True)
-#
-# class Meta:
-# unique_together = (("name", "host", "architecture"),)
-#
-# def __unicode__(self):
-# return "%s" % self.name
-
class LogQuery(models.Model):
"""
- Query to be run on all hosts.
+ Query to be run on all hosts with an intersecting set of tags.
"""
- name = models.CharField(max_length=255, help_text="A descriptive name for the query")
+ name = models.CharField(max_length=255, help_text="Descriptive name for the query")
query = models.CharField(max_length=255, help_text="The query to be executed")
interval = models.IntegerField(help_text="How often should the query be run (in seconds)?", default=10)
+ snapshot = models.BooleanField(help_text="Whether this query should send a report even when the data may not have changed. Useful for time series metrics and keepalives.")
+ tags = models.ManyToManyField(Tag, help_text="Only hosts tagged with these tags will run these queries. Specifying no tags makes a query run on all hosts.")
class Meta:
verbose_name_plural = "log queries"
diff --git a/api/views.py b/api/views.py
index 36feb23..91730aa 100644
--- a/api/views.py
+++ b/api/views.py
@@ -15,6 +15,7 @@ import json
import random
import string
import functools
+import hmac
def decode_json_body(fn):
"""
@@ -61,7 +62,7 @@ def enroll(request, form):
Called by osqueryd during initial configuration of a new node. Returns the node's unique access key, which will be used from then on.
"""
- if form['enroll_secret'] == settings.OSQUERY_ENROLL_SECRET:
+ if hmac.compare_digest(form['enroll_secret'], settings.OSQUERY_ENROLL_SECRET):
rand = random.SystemRandom();
node_key = "".join(rand.choice(string.hexdigits) for _ in range(32)).lower() # generate a 128 bit key
response = {
@@ -87,20 +88,16 @@ def config(request, form, host):
response = {
"schedule": {
- "hotplatehosts_os-version": {
+ "mango_os-version": {
"query": "SELECT * FROM os_version;",
"interval": 10
},
- "hotplatehosts_system-info": {
+ "mango_system-info": {
"query": "SELECT * FROM system_info;",
"interval": 60,
"snapshot": True # this query is used as a "keepalive"
},
- # "hotplatehosts_deb-packages": { <-- not enabled in this demo
- # "query": "SELECT * FROM deb_packages;",
- # "interval": 10
- # },
- "hotplatehosts_osrelease": {
+ "mango_osrelease": {
"query": "select current_value from system_controls where name = 'kernel.osrelease';",
"interval": 10
},
@@ -108,10 +105,11 @@ def config(request, form, host):
"node_invalid": False
}
- for query in LogQuery.objects.all():
- response['schedule']['hotplatehosts_db_%s' % slugify(query.name)] = {
+ for query in LogQuery.objects.filter(tags__in=host.tags.all()):
+ response['schedule']['mango_db_%s' % slugify(query.name)] = {
"query": "%s;" % query.query,
- "interval": query.interval
+ "interval": query.interval,
+ "snapshot": query.snapshot,
}
return JsonResponse(response)
@@ -135,35 +133,21 @@ def logger(request, form, host):
recognised_action = False
if entry_action == 'added':
entry_output = submitted_log_entry['columns']
- if entry_name == "hotplatehosts_os-version":
+ if entry_name == "mango_os-version":
host.release = entry_output['version'].split()[-1].strip('()')
recognised_action = True
- elif entry_name == "hotplatehosts_osrelease":
+ elif entry_name == "mango_osrelease":
host.architecture = entry_output['current_value'].split('-')[-1]
recognised_action = True
- elif entry_name == "hotplatehosts_deb-packages":
- with transaction.atomic():
- try:
- Package.objects.create(name=entry_output['name'], host=host, version=entry_output['version'], architecture=entry_output['arch'])
- except IntegrityError: # ignore duplicates
- continue
- recognised_action = True
-
- elif entry_action == 'removed':
- entry_output = submitted_log_entry['columns']
- if entry_name == "hotplatehosts_deb-packages":
- recognised_action = True
- Package.objects.filter(name=entry_output['name'], host=host, version=entry_output['version'], architecture=entry_output['arch']).delete()
-
elif entry_action == 'snapshot': # full snapshot query
entry_outputs = submitted_log_entry['snapshot']
for entry_output in entry_outputs:
- if entry_name == "hotplatehosts_system-info":
+ if entry_name == "mango_system-info":
host.cpu = entry_output['cpu_brand']
host.ram = int(entry_output['physical_memory'])
- if recognised_action == False and entry_action in ('added', 'removed') and entry_name.startswith('hotplatehosts_db_'): # just log in to the db
- log_entry = LogEntry(name=entry_name[17:], action=entry_action, output=repr(entry_output), host=host)
+ if recognised_action == False and entry_action in ('added', 'removed') and entry_name.startswith('mango_db_'): # just log to the db
+ log_entry = LogEntry(name=entry_name[len('mango_db_'):], action=entry_action, output=repr(entry_output), host=host)
log_entry.save()
host.save()
diff --git a/mango/urls.py b/mango/urls.py
index f20a969..2e4c326 100644
--- a/mango/urls.py
+++ b/mango/urls.py
@@ -3,5 +3,5 @@ from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
- url(r'', include('api.urls')),
+ url(r'^osquery/', include('api.urls')),
]
diff --git a/osquery.flags b/osquery.flags
new file mode 100644
index 0000000..7471827
--- /dev/null
+++ b/osquery.flags
@@ -0,0 +1,9 @@
+--tls_hostname=localhost:4433
+--tls_server_certs=test_server_ca.pem
+--config_plugin=tls
+--config_tls_endpoint=/osquery/config
+--config_refresh=30
+--logger_tls_endpoint=/osquery/logger
+--logger_plugin=tls
+--enroll_tls_endpoint=/osquery/enroll
+--enroll_secret_path=/etc/osquery/enroll_secret
diff --git a/stunnel.conf b/stunnel.conf
new file mode 100644
index 0000000..11bc2c0
--- /dev/null
+++ b/stunnel.conf
@@ -0,0 +1,8 @@
+cert = test_server.pem
+key = test_server.key
+foreground = yes
+pid = /tmp/mango-stunnel.pid
+
+[api]
+accept = localhost:4433
+connect = 8000
diff --git a/test_server.key b/test_server.key
new file mode 100644
index 0000000..5fea5a1
--- /dev/null
+++ b/test_server.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA7y/3c/qkoMMfXY/BHQ3nmFeBTjka3s+gkocYhFspQ3m3iRFx
+1FxPcuwfSvwqX5RqWq3n5rZO8oCmgTKBC00WlVfgT48fIj0DaLqpR8/071Q7aaPf
+G/CiqTd/Uyu7m4pIGuoYuc8lMYfwmbwRxxmESbb0soFhmxTG7ek5Kr9fa89QK2Nh
+Oi2Xa+hmrpaTZp0elFRf0rOYNI8rS3FomIHRsy/rZwupSLAwdudrQCV9borEz/0u
+s0/hd1IjUArnsd1UFrFB13WDHps1qZ838ELuH5Iq5eIuUAXgpcoCLIH7BO7Rxtsv
+3Ecqp8qbMoelEqLGu7zBBBWF0Ta4orEPP2ph4QIDAQABAoIBADJIAhwGd6839ZME
+klMaRjJXSt530LdALIBBGB1S0KTXpIaS/TvoP+dnzdhElF/NYmI3psVwU75U3yvP
+wyLuDK5Ob+AptSDMdLgCbW2kQNhC+85kXZWRC5DJEuIYEnNLKYdG6PW+nxH/gsu+
+pnoVWiLo7B3OZwdj4cHHwnXDDzspTKN9UJHtN75feW7qj8Wx+IObcEJUrDwK/v/P
+MjewHJ8OWmgr/nUVqFxmVYn+3VQOcoF1jFXShihqg4op3LeHbGcCenY78+MBFOrE
+xzBQC9IQiRV/ayrHhEo1RGMg7vFOAqPFgcXMA7y2blrjgngMJ+SofBthuZH28mPB
+JjDR1wUCgYEA+xFl/OHmcW0oVOIfPMQ7NNv6JAE3+i+5nYuJcIZoEH85Xdr5YtF5
+xGyn5pZ3HLR6OiNMRBRhwvHSIMEkI/357EjAjbnM2OG8kU6CzQ+6tG/jsVeYDNKN
+CQejL9atkOwC1b2UmP8ajfRu2tsXQmOCi/iddk/7qWmUYOJsrvbxN58CgYEA8+LS
+VkrnLhq2MArdFDfziKg+NwdL9ppk2t4F/x2FsKTBI/mMGo5Acqdvs6GXM1CLGJpx
+XvQ0Q/b3SWXKj0bGI/3m3XcovxGTLudfDV9Ph7VvbKQTmYrzG8fR7ev2wIjzpCYc
+nyoRu6msmVnpst9QsRNG0ca92GWeIwDPVL6T9n8CgYAz9IPAcxb2/fnMpwaD0q/V
+3nfDH6Vv1pR4r7l/WbELSOicLYZSFrs2FK4iH50Cia6JfWh45ibc6qHrOUy7TgF8
+DgoaygpED2KwRyj2On0OfeEGf/PtI10gMz5n1esRBGYJyTOI/bGHEsAl9hS4HlOT
+l50uMJsJkdmsuu52vo9oTQKBgQDWsh2SI5xB5PfvcRDQBLVZ3yntzXmc3KveVMeY
+nweayl7QaZVhp0qq5CUcUCtH3CanAQa+nWIJVra4oWhhGt/AvXpoCccP9MvJ5Zqy
+re3YPOubCxHKAB0lnpF6zlfJhIZfQcG+iA1WU/cChLmLYrWpPJwCfd+QSVyd2c+q
+/Z5JxwKBgQDuitXKKlkMfXzlubcPYt2f3sRpv6op3u9BpNDWH6HJg+APO0a3F4vE
+C7BexnegFYhGJemkTnPvcrBUGvtVcailgZbWLUZfiQC3xPvWGnqqKN+KALHL77Gz
+/ZmV6BPQ+becihdTEGkyC8KHDgCy4TGQefds4IUAVDETWm5Hti8ruQ==
+-----END RSA PRIVATE KEY-----
diff --git a/test_server.pem b/test_server.pem
new file mode 100644
index 0000000..373d685
--- /dev/null
+++ b/test_server.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICnjCCAgcCAQgwDQYJKoZIhvcNAQEFBQAwWzELMAkGA1UEBhMCVVMxEzARBgNV
+BAgTCkNhbGlmb3JuaWExGDAWBgNVBAoTD29zcXVlcnktdGVzdGluZzEdMBsGA1UE
+AxMUb3NxdWVyeS11bml0dGVzdHMtY2EwHhcNMTUwNjEzMDkxMjE2WhcNMjUwNjEw
+MDkxMjE2WjBQMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEYMBYG
+A1UEChMPb3NxdWVyeS10ZXN0aW5nMRIwEAYDVQQDEwlsb2NhbGhvc3QwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDvL/dz+qSgwx9dj8EdDeeYV4FOORre
+z6CShxiEWylDebeJEXHUXE9y7B9K/CpflGparefmtk7ygKaBMoELTRaVV+BPjx8i
+PQNouqlHz/TvVDtpo98b8KKpN39TK7ubikga6hi5zyUxh/CZvBHHGYRJtvSygWGb
+FMbt6Tkqv19rz1ArY2E6LZdr6GaulpNmnR6UVF/Ss5g0jytLcWiYgdGzL+tnC6lI
+sDB252tAJX1uisTP/S6zT+F3UiNQCuex3VQWsUHXdYMemzWpnzfwQu4fkirl4i5Q
+BeClygIsgfsE7tHG2y/cRyqnypsyh6USosa7vMEEFYXRNriisQ8/amHhAgMBAAEw
+DQYJKoZIhvcNAQEFBQADgYEAUfVwwbK05VfVdRuZ/vwy2mX2PCwLVCIQoK4eYQxB
+xscEKbOpgpk8twWWMqnfvXbzR8glKu7gExtasae07s2NMUYf2x/mSZG+SbpYdYdu
+6VgZ8DXNmRxo1GfetMiMnqAuS94+G6eIqZmAQGI/j/Feld/Gi5dGaZ/qW1PSDex8
+BQU=
+-----END CERTIFICATE-----
diff --git a/test_server_ca.pem b/test_server_ca.pem
new file mode 100644
index 0000000..ef99897
--- /dev/null
+++ b/test_server_ca.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC9TCCAl6gAwIBAgIJAPQteZms04jzMA0GCSqGSIb3DQEBBQUAMFsxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRgwFgYDVQQKEw9vc3F1ZXJ5LXRl
+c3RpbmcxHTAbBgNVBAMTFG9zcXVlcnktdW5pdHRlc3RzLWNhMB4XDTE1MDYxMzA5
+MTA0MVoXDTI1MDYxMDA5MTA0MVowWzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNh
+bGlmb3JuaWExGDAWBgNVBAoTD29zcXVlcnktdGVzdGluZzEdMBsGA1UEAxMUb3Nx
+dWVyeS11bml0dGVzdHMtY2EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMF0
+ATxoOhtKaGfudIupedTjUfn6ruUkr8f6VTLUJ2TSigGMvGg5HJpguFegO+e2Gawp
+Dp7Y4lHROLfzpjofWTHTU6b+tHW7OqqGTQ06tn/Mtx8mq+qePuWjVlktFjgnUqsw
+fMJmsVAC9bH7WUQXYO7jI/VzHlTKWX1L7H/h8MRNAgMBAAGjgcAwgb0wHQYDVR0O
+BBYEFL3igtnOtftEOPtUPklj2Dm4Z6nWMIGNBgNVHSMEgYUwgYKAFL3igtnOtftE
+OPtUPklj2Dm4Z6nWoV+kXTBbMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZv
+cm5pYTEYMBYGA1UEChMPb3NxdWVyeS10ZXN0aW5nMR0wGwYDVQQDExRvc3F1ZXJ5
+LXVuaXR0ZXN0cy1jYYIJAPQteZms04jzMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcN
+AQEFBQADgYEAB2cBWFMbR5FhQ1fGQRIoXYsp96DbMa7nhBFsdHvTbiqEMQuTUQbO
+yB9UFylMlzPU0OjMgH0R6ILhPXaSIS7hyK1cmp2ZqgUBrR3G1VV/6TWpP4Y+lnXG
+4vdEtyEEo7p8XLmcm88Ig7LgqUzZrtgoknd2fIPsKjEqdq4P7cCiqFc=
+-----END CERTIFICATE-----