Repository files navigation

CertStream-Server

Aggregate and broadcast SSL certs as they're issued live.

Certstream-server is a service written in elixir to aggregate, parse, and stream certificate data from multiple certificate transparency logs. It leverages the amazing power of elixir/erlang to achieve great network throughput and concurrency with very little resource usage.

This is a rewrite of the original version written in python, and is much more efficient than the original and currently ships millions of certificates a day on a single Hetzner dedicated server without issue (~250TB of data every month!).

Getting Started

Getting up and running is pretty easy (especially if you use Heroku, as we include a Dockerfile!).

First install elixir (assuming you're on OSX, otherwise follow instructions for your platform)

$ brew install elixir

Then fetch the dependencies

$ mix deps.get

From there you can run it

$ mix run --no-halt

Alternatively, you can run it in an iex session (so you can interact with it while it's running) for development

$ iex -S mix
Interactive Elixir (1.8.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> :observer.start

This will open up an http/websocket server on port 4000 (override this by setting a PORT environment variable).

Usage

Once you have the server runing on some port, you can visit the server to be greeted with the index page.

If you point a websocket client at the root URL (/), it'll give you a data structure that doesn't bundle the DER-encoded certificate.

If you want the certificate data as well, you can point a websocket client to /full-stream and it'll also bundle the DER-encoded certificate along with each certificate.

Internals

The structure of the application is pretty simple. The dataflow basically looks like this:

 CertificateBuffer
|
HTTP Watcher \ | / Websocket Connection Process
HTTP Watcher - ClientManager - Websocket Connection Process
HTTP Watcher / \ Websocket Connection Process

HTTP Watchers

First we spin up 1 process for every entry in the known CTL list from the CT project, with each of them being responsible for sleeping for 10 seconds and checking to see if the top of the merkle tree has changed. Once a difference has been found, they go out and download the certificate data, parsing it and coercing it to a hashmap structure using the EasySSL library and sending it to the ClientManager.

ClientManager

This agent is responsible for brokering communication between the CT watchers and the currently connected websocket clients. Certificates are broadcast to websocket connection processes through an erlang pobox in order to properly load-shed when a slow client isn't reading certificates fast enough. The ClientManager also sends a copy of every certificate received to the CertificateBuffer.

CertificateBuffer

This agent is responsible for keeping a ring-buffer in memory of the most recently seen 25 certificates, as well as counting the certificates processed by Certstream.

Websocket Connection Process

Under the hood we use the erlang library Cowboy to handle static content serving, the json APIs, and websocket connections. There's nothing too special about them other than they're assigned a paired pobox at the start of every connection.

HTTP Routes

/latest.json - Get the most recent 25 certificates CertStream has seen

/example.json - Get the most recent certificate CertStream has seen

Data Structure

Certificate Updates

{
"message_type": "certificate_update",
"data": {
"update_type": "X509LogEntry",
"leaf_cert": {
"subject": {
"aggregated": "/CN=e-zigarette-liquid-shop.de",
"C": null,
"ST": null,
"L": null,
"O": null,
"OU": null,
"CN": "e-zigarette-liquid-shop.de"
},
"extensions": {
"keyUsage": "Digital Signature, Key Encipherment",
"extendedKeyUsage": "TLS Web Server Authentication, TLS Web Client Authentication",
"basicConstraints": "CA:FALSE",
"subjectKeyIdentifier": "AC:4C:7B:3C:E9:C8:7F:CB:E2:7D:5D:64:F2:25:0C:89:C2:AE:F0:5E",
"authorityKeyIdentifier": "keyid:A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1\n",
"authorityInfoAccess": "OCSP - URI:http://ocsp.int-x3.letsencrypt.org\nCA Issuers - URI:http://cert.int-x3.letsencrypt.org/\n",
"subjectAltName": "DNS:e-zigarette-liquid-shop.de, DNS:www.e-zigarette-liquid-shop.de",
"certificatePolicies": "Policy: 2.23.140.1.2.1\nPolicy: 1.3.6.1.4.1.44947.1.1.1\n CPS: http://cps.letsencrypt.org\n User Notice:\n Explicit Text: This Certificate may only be relied upon by Relying Parties and only in accordance with the Certificate Policy found at https://letsencrypt.org/repository/\n"
},
"not_before": 1508123861.0,
"not_after": 1515899861.0,
"as_der": "::BASE64_DER_CERT::",
"all_domains": [
"e-zigarette-liquid-shop.de",
"www.e-zigarette-liquid-shop.de"
]
},
"chain": [
{
"subject": {
"aggregated": "/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3",
"C": "US",
"ST": null,
"L": null,
"O": "Let's Encrypt",
"OU": null,
"CN": "Let's Encrypt Authority X3"
},
"extensions": {
"basicConstraints": "CA:TRUE, pathlen:0",
"keyUsage": "Digital Signature, Certificate Sign, CRL Sign",
"authorityInfoAccess": "OCSP - URI:http://isrg.trustid.ocsp.identrust.com\nCA Issuers - URI:http://apps.identrust.com/roots/dstrootcax3.p7c\n",
"authorityKeyIdentifier": "keyid:C4:A7:B1:A4:7B:2C:71:FA:DB:E1:4B:90:75:FF:C4:15:60:85:89:10\n",
"certificatePolicies": "Policy: 2.23.140.1.2.1\nPolicy: 1.3.6.1.4.1.44947.1.1.1\n CPS: http://cps.root-x1.letsencrypt.org\n",
"crlDistributionPoints": "\nFull Name:\n URI:http://crl.identrust.com/DSTROOTCAX3CRL.crl\n",
"subjectKeyIdentifier": "A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1"
},
"not_before": 1458232846.0,
"not_after": 1615999246.0,
"as_der": "::BASE64_DER_CERT::"
},
{
"subject": {
"aggregated": "/O=Digital Signature Trust Co./CN=DST Root CA X3",
"C": null,
"ST": null,
"L": null,
"O": "Digital Signature Trust Co.",
"OU": null,
"CN": "DST Root CA X3"
},
"extensions": {
"basicConstraints": "CA:TRUE",
"keyUsage": "Certificate Sign, CRL Sign",
"subjectKeyIdentifier": "C4:A7:B1:A4:7B:2C:71:FA:DB:E1:4B:90:75:FF:C4:15:60:85:89:10"
},
"not_before": 970348339.0,
"not_after": 1633010475.0,
"as_der": "::BASE64_DER_CERT::"
}
],
"cert_index": 19587936,
"seen": 1508483726.8601687,
"source": {
"url": "mammoth.ct.comodo.com",
"name": "Comodo 'Mammoth' CT log"
}
}
}

update test

redeploy trigger

About

Certificate Transparency Log aggregation, parsing, and streaming service written in Elixir

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

CertStream-Server

Aggregate and broadcast SSL certs as they're issued live.

Certstream-server is a service written in elixir to aggregate, parse, and stream certificate data from multiple certificate transparency logs. It leverages the amazing power of elixir/erlang to achieve great network throughput and concurrency with very little resource usage.

This is a rewrite of the original version written in python, and is much more efficient than the original and currently ships millions of certificates a day on a single Hetzner dedicated server without issue (~250TB of data every month!).

Getting Started

Getting up and running is pretty easy (especially if you use Heroku, as we include a Dockerfile!).

First install elixir (assuming you're on OSX, otherwise follow instructions for your platform)

$ brew install elixir

Then fetch the dependencies

$ mix deps.get

From there you can run it

$ mix run --no-halt

Alternatively, you can run it in an iex session (so you can interact with it while it's running) for development

$ iex -S mix
Interactive Elixir (1.8.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> :observer.start

This will open up an http/websocket server on port 4000 (override this by setting a PORT environment variable).

Usage

Once you have the server runing on some port, you can visit the server to be greeted with the index page.

If you point a websocket client at the root URL (/), it'll give you a data structure that doesn't bundle the DER-encoded certificate.

If you want the certificate data as well, you can point a websocket client to /full-stream and it'll also bundle the DER-encoded certificate along with each certificate.

Internals

The structure of the application is pretty simple. The dataflow basically looks like this:

 CertificateBuffer
|
HTTP Watcher \ | / Websocket Connection Process
HTTP Watcher - ClientManager - Websocket Connection Process
HTTP Watcher / \ Websocket Connection Process

HTTP Watchers

First we spin up 1 process for every entry in the known CTL list from the CT project, with each of them being responsible for sleeping for 10 seconds and checking to see if the top of the merkle tree has changed. Once a difference has been found, they go out and download the certificate data, parsing it and coercing it to a hashmap structure using the EasySSL library and sending it to the ClientManager.

ClientManager

This agent is responsible for brokering communication between the CT watchers and the currently connected websocket clients. Certificates are broadcast to websocket connection processes through an erlang pobox in order to properly load-shed when a slow client isn't reading certificates fast enough. The ClientManager also sends a copy of every certificate received to the CertificateBuffer.

CertificateBuffer

This agent is responsible for keeping a ring-buffer in memory of the most recently seen 25 certificates, as well as counting the certificates processed by Certstream.

Websocket Connection Process

Under the hood we use the erlang library Cowboy to handle static content serving, the json APIs, and websocket connections. There's nothing too special about them other than they're assigned a paired pobox at the start of every connection.

HTTP Routes

/latest.json - Get the most recent 25 certificates CertStream has seen

/example.json - Get the most recent certificate CertStream has seen

Data Structure

Certificate Updates

{
"message_type": "certificate_update",
"data": {
"update_type": "X509LogEntry",
"leaf_cert": {
"subject": {
"aggregated": "/CN=e-zigarette-liquid-shop.de",
"C": null,
"ST": null,
"L": null,
"O": null,
"OU": null,
"CN": "e-zigarette-liquid-shop.de"
},
"extensions": {
"keyUsage": "Digital Signature, Key Encipherment",
"extendedKeyUsage": "TLS Web Server Authentication, TLS Web Client Authentication",
"basicConstraints": "CA:FALSE",
"subjectKeyIdentifier": "AC:4C:7B:3C:E9:C8:7F:CB:E2:7D:5D:64:F2:25:0C:89:C2:AE:F0:5E",
"authorityKeyIdentifier": "keyid:A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1\n",
"authorityInfoAccess": "OCSP - URI:http://ocsp.int-x3.letsencrypt.org\nCA Issuers - URI:http://cert.int-x3.letsencrypt.org/\n",
"subjectAltName": "DNS:e-zigarette-liquid-shop.de, DNS:www.e-zigarette-liquid-shop.de",
"certificatePolicies": "Policy: 2.23.140.1.2.1\nPolicy: 1.3.6.1.4.1.44947.1.1.1\n CPS: http://cps.letsencrypt.org\n User Notice:\n Explicit Text: This Certificate may only be relied upon by Relying Parties and only in accordance with the Certificate Policy found at https://letsencrypt.org/repository/\n"
},
"not_before": 1508123861.0,
"not_after": 1515899861.0,
"as_der": "::BASE64_DER_CERT::",
"all_domains": [
"e-zigarette-liquid-shop.de",
"www.e-zigarette-liquid-shop.de"
]
},
"chain": [
{
"subject": {
"aggregated": "/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3",
"C": "US",
"ST": null,
"L": null,
"O": "Let's Encrypt",
"OU": null,
"CN": "Let's Encrypt Authority X3"
},
"extensions": {
"basicConstraints": "CA:TRUE, pathlen:0",
"keyUsage": "Digital Signature, Certificate Sign, CRL Sign",
"authorityInfoAccess": "OCSP - URI:http://isrg.trustid.ocsp.identrust.com\nCA Issuers - URI:http://apps.identrust.com/roots/dstrootcax3.p7c\n",
"authorityKeyIdentifier": "keyid:C4:A7:B1:A4:7B:2C:71:FA:DB:E1:4B:90:75:FF:C4:15:60:85:89:10\n",
"certificatePolicies": "Policy: 2.23.140.1.2.1\nPolicy: 1.3.6.1.4.1.44947.1.1.1\n CPS: http://cps.root-x1.letsencrypt.org\n",
"crlDistributionPoints": "\nFull Name:\n URI:http://crl.identrust.com/DSTROOTCAX3CRL.crl\n",
"subjectKeyIdentifier": "A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1"
},
"not_before": 1458232846.0,
"not_after": 1615999246.0,
"as_der": "::BASE64_DER_CERT::"
},
{
"subject": {
"aggregated": "/O=Digital Signature Trust Co./CN=DST Root CA X3",
"C": null,
"ST": null,
"L": null,
"O": "Digital Signature Trust Co.",
"OU": null,
"CN": "DST Root CA X3"
},
"extensions": {
"basicConstraints": "CA:TRUE",
"keyUsage": "Certificate Sign, CRL Sign",
"subjectKeyIdentifier": "C4:A7:B1:A4:7B:2C:71:FA:DB:E1:4B:90:75:FF:C4:15:60:85:89:10"
},
"not_before": 970348339.0,
"not_after": 1633010475.0,
"as_der": "::BASE64_DER_CERT::"
}
],
"cert_index": 19587936,
"seen": 1508483726.8601687,
"source": {
"url": "mammoth.ct.comodo.com",
"name": "Comodo 'Mammoth' CT log"
}
}
}

update test

redeploy trigger

About

Certificate Transparency Log aggregation, parsing, and streaming service written in Elixir

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

CertStream-Server

Aggregate and broadcast SSL certs as they're issued live.

Certstream-server is a service written in elixir to aggregate, parse, and stream certificate data from multiple certificate transparency logs. It leverages the amazing power of elixir/erlang to achieve great network throughput and concurrency with very little resource usage.

This is a rewrite of the original version written in python, and is much more efficient than the original and currently ships millions of certificates a day on a single Hetzner dedicated server without issue (~250TB of data every month!).

Getting Started

Getting up and running is pretty easy (especially if you use Heroku, as we include a Dockerfile!).

First install elixir (assuming you're on OSX, otherwise follow instructions for your platform)

$ brew install elixir

Then fetch the dependencies

$ mix deps.get

From there you can run it

$ mix run --no-halt

Alternatively, you can run it in an iex session (so you can interact with it while it's running) for development

$ iex -S mix
Interactive Elixir (1.8.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> :observer.start

This will open up an http/websocket server on port 4000 (override this by setting a PORT environment variable).

Usage

Once you have the server runing on some port, you can visit the server to be greeted with the index page.

If you point a websocket client at the root URL (/), it'll give you a data structure that doesn't bundle the DER-encoded certificate.

If you want the certificate data as well, you can point a websocket client to /full-stream and it'll also bundle the DER-encoded certificate along with each certificate.

Internals

The structure of the application is pretty simple. The dataflow basically looks like this:

 CertificateBuffer
|
HTTP Watcher \ | / Websocket Connection Process
HTTP Watcher - ClientManager - Websocket Connection Process
HTTP Watcher / \ Websocket Connection Process

HTTP Watchers

First we spin up 1 process for every entry in the known CTL list from the CT project, with each of them being responsible for sleeping for 10 seconds and checking to see if the top of the merkle tree has changed. Once a difference has been found, they go out and download the certificate data, parsing it and coercing it to a hashmap structure using the EasySSL library and sending it to the ClientManager.

ClientManager

This agent is responsible for brokering communication between the CT watchers and the currently connected websocket clients. Certificates are broadcast to websocket connection processes through an erlang pobox in order to properly load-shed when a slow client isn't reading certificates fast enough. The ClientManager also sends a copy of every certificate received to the CertificateBuffer.

CertificateBuffer

This agent is responsible for keeping a ring-buffer in memory of the most recently seen 25 certificates, as well as counting the certificates processed by Certstream.

Websocket Connection Process

Under the hood we use the erlang library Cowboy to handle static content serving, the json APIs, and websocket connections. There's nothing too special about them other than they're assigned a paired pobox at the start of every connection.

HTTP Routes

/latest.json - Get the most recent 25 certificates CertStream has seen

/example.json - Get the most recent certificate CertStream has seen

Data Structure

Certificate Updates

{
"message_type": "certificate_update",
"data": {
"update_type": "X509LogEntry",
"leaf_cert": {
"subject": {
"aggregated": "/CN=e-zigarette-liquid-shop.de",
"C": null,
"ST": null,
"L": null,
"O": null,
"OU": null,
"CN": "e-zigarette-liquid-shop.de"
},
"extensions": {
"keyUsage": "Digital Signature, Key Encipherment",
"extendedKeyUsage": "TLS Web Server Authentication, TLS Web Client Authentication",
"basicConstraints": "CA:FALSE",
"subjectKeyIdentifier": "AC:4C:7B:3C:E9:C8:7F:CB:E2:7D:5D:64:F2:25:0C:89:C2:AE:F0:5E",
"authorityKeyIdentifier": "keyid:A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1\n",
"authorityInfoAccess": "OCSP - URI:http://ocsp.int-x3.letsencrypt.org\nCA Issuers - URI:http://cert.int-x3.letsencrypt.org/\n",
"subjectAltName": "DNS:e-zigarette-liquid-shop.de, DNS:www.e-zigarette-liquid-shop.de",
"certificatePolicies": "Policy: 2.23.140.1.2.1\nPolicy: 1.3.6.1.4.1.44947.1.1.1\n CPS: http://cps.letsencrypt.org\n User Notice:\n Explicit Text: This Certificate may only be relied upon by Relying Parties and only in accordance with the Certificate Policy found at https://letsencrypt.org/repository/\n"
},
"not_before": 1508123861.0,
"not_after": 1515899861.0,
"as_der": "::BASE64_DER_CERT::",
"all_domains": [
"e-zigarette-liquid-shop.de",
"www.e-zigarette-liquid-shop.de"
]
},
"chain": [
{
"subject": {
"aggregated": "/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3",
"C": "US",
"ST": null,
"L": null,
"O": "Let's Encrypt",
"OU": null,
"CN": "Let's Encrypt Authority X3"
},
"extensions": {
"basicConstraints": "CA:TRUE, pathlen:0",
"keyUsage": "Digital Signature, Certificate Sign, CRL Sign",
"authorityInfoAccess": "OCSP - URI:http://isrg.trustid.ocsp.identrust.com\nCA Issuers - URI:http://apps.identrust.com/roots/dstrootcax3.p7c\n",
"authorityKeyIdentifier": "keyid:C4:A7:B1:A4:7B:2C:71:FA:DB:E1:4B:90:75:FF:C4:15:60:85:89:10\n",
"certificatePolicies": "Policy: 2.23.140.1.2.1\nPolicy: 1.3.6.1.4.1.44947.1.1.1\n CPS: http://cps.root-x1.letsencrypt.org\n",
"crlDistributionPoints": "\nFull Name:\n URI:http://crl.identrust.com/DSTROOTCAX3CRL.crl\n",
"subjectKeyIdentifier": "A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1"
},
"not_before": 1458232846.0,
"not_after": 1615999246.0,
"as_der": "::BASE64_DER_CERT::"
},
{
"subject": {
"aggregated": "/O=Digital Signature Trust Co./CN=DST Root CA X3",
"C": null,
"ST": null,
"L": null,
"O": "Digital Signature Trust Co.",
"OU": null,
"CN": "DST Root CA X3"
},
"extensions": {
"basicConstraints": "CA:TRUE",
"keyUsage": "Certificate Sign, CRL Sign",
"subjectKeyIdentifier": "C4:A7:B1:A4:7B:2C:71:FA:DB:E1:4B:90:75:FF:C4:15:60:85:89:10"
},
"not_before": 970348339.0,
"not_after": 1633010475.0,
"as_der": "::BASE64_DER_CERT::"
}
],
"cert_index": 19587936,
"seen": 1508483726.8601687,
"source": {
"url": "mammoth.ct.comodo.com",
"name": "Comodo 'Mammoth' CT log"
}
}
}

update test

redeploy trigger

About

Certificate Transparency Log aggregation, parsing, and streaming service written in Elixir

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

CertStream-Server

Aggregate and broadcast SSL certs as they're issued live.

Certstream-server is a service written in elixir to aggregate, parse, and stream certificate data from multiple certificate transparency logs. It leverages the amazing power of elixir/erlang to achieve great network throughput and concurrency with very little resource usage.

This is a rewrite of the original version written in python, and is much more efficient than the original and currently ships millions of certificates a day on a single Hetzner dedicated server without issue (~250TB of data every month!).

Getting Started

Getting up and running is pretty easy (especially if you use Heroku, as we include a Dockerfile!).

First install elixir (assuming you're on OSX, otherwise follow instructions for your platform)

$ brew install elixir

Then fetch the dependencies

$ mix deps.get

From there you can run it

$ mix run --no-halt

Alternatively, you can run it in an iex session (so you can interact with it while it's running) for development

$ iex -S mix
Interactive Elixir (1.8.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> :observer.start

This will open up an http/websocket server on port 4000 (override this by setting a PORT environment variable).

Usage

Once you have the server runing on some port, you can visit the server to be greeted with the index page.

If you point a websocket client at the root URL (/), it'll give you a data structure that doesn't bundle the DER-encoded certificate.

If you want the certificate data as well, you can point a websocket client to /full-stream and it'll also bundle the DER-encoded certificate along with each certificate.

Internals

The structure of the application is pretty simple. The dataflow basically looks like this:

 CertificateBuffer
|
HTTP Watcher \ | / Websocket Connection Process
HTTP Watcher - ClientManager - Websocket Connection Process
HTTP Watcher / \ Websocket Connection Process

HTTP Watchers

First we spin up 1 process for every entry in the known CTL list from the CT project, with each of them being responsible for sleeping for 10 seconds and checking to see if the top of the merkle tree has changed. Once a difference has been found, they go out and download the certificate data, parsing it and coercing it to a hashmap structure using the EasySSL library and sending it to the ClientManager.

ClientManager

This agent is responsible for brokering communication between the CT watchers and the currently connected websocket clients. Certificates are broadcast to websocket connection processes through an erlang pobox in order to properly load-shed when a slow client isn't reading certificates fast enough. The ClientManager also sends a copy of every certificate received to the CertificateBuffer.

CertificateBuffer

This agent is responsible for keeping a ring-buffer in memory of the most recently seen 25 certificates, as well as counting the certificates processed by Certstream.

Websocket Connection Process

Under the hood we use the erlang library Cowboy to handle static content serving, the json APIs, and websocket connections. There's nothing too special about them other than they're assigned a paired pobox at the start of every connection.

HTTP Routes

/latest.json - Get the most recent 25 certificates CertStream has seen

/example.json - Get the most recent certificate CertStream has seen

Data Structure

Certificate Updates

{
"message_type": "certificate_update",
"data": {
"update_type": "X509LogEntry",
"leaf_cert": {
"subject": {
"aggregated": "/CN=e-zigarette-liquid-shop.de",
"C": null,
"ST": null,
"L": null,
"O": null,
"OU": null,
"CN": "e-zigarette-liquid-shop.de"
},
"extensions": {
"keyUsage": "Digital Signature, Key Encipherment",
"extendedKeyUsage": "TLS Web Server Authentication, TLS Web Client Authentication",
"basicConstraints": "CA:FALSE",
"subjectKeyIdentifier": "AC:4C:7B:3C:E9:C8:7F:CB:E2:7D:5D:64:F2:25:0C:89:C2:AE:F0:5E",
"authorityKeyIdentifier": "keyid:A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1\n",
"authorityInfoAccess": "OCSP - URI:http://ocsp.int-x3.letsencrypt.org\nCA Issuers - URI:http://cert.int-x3.letsencrypt.org/\n",
"subjectAltName": "DNS:e-zigarette-liquid-shop.de, DNS:www.e-zigarette-liquid-shop.de",
"certificatePolicies": "Policy: 2.23.140.1.2.1\nPolicy: 1.3.6.1.4.1.44947.1.1.1\n CPS: http://cps.letsencrypt.org\n User Notice:\n Explicit Text: This Certificate may only be relied upon by Relying Parties and only in accordance with the Certificate Policy found at https://letsencrypt.org/repository/\n"
},
"not_before": 1508123861.0,
"not_after": 1515899861.0,
"as_der": "::BASE64_DER_CERT::",
"all_domains": [
"e-zigarette-liquid-shop.de",
"www.e-zigarette-liquid-shop.de"
]
},
"chain": [
{
"subject": {
"aggregated": "/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3",
"C": "US",
"ST": null,
"L": null,
"O": "Let's Encrypt",
"OU": null,
"CN": "Let's Encrypt Authority X3"
},
"extensions": {
"basicConstraints": "CA:TRUE, pathlen:0",
"keyUsage": "Digital Signature, Certificate Sign, CRL Sign",
"authorityInfoAccess": "OCSP - URI:http://isrg.trustid.ocsp.identrust.com\nCA Issuers - URI:http://apps.identrust.com/roots/dstrootcax3.p7c\n",
"authorityKeyIdentifier": "keyid:C4:A7:B1:A4:7B:2C:71:FA:DB:E1:4B:90:75:FF:C4:15:60:85:89:10\n",
"certificatePolicies": "Policy: 2.23.140.1.2.1\nPolicy: 1.3.6.1.4.1.44947.1.1.1\n CPS: http://cps.root-x1.letsencrypt.org\n",
"crlDistributionPoints": "\nFull Name:\n URI:http://crl.identrust.com/DSTROOTCAX3CRL.crl\n",
"subjectKeyIdentifier": "A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1"
},
"not_before": 1458232846.0,
"not_after": 1615999246.0,
"as_der": "::BASE64_DER_CERT::"
},
{
"subject": {
"aggregated": "/O=Digital Signature Trust Co./CN=DST Root CA X3",
"C": null,
"ST": null,
"L": null,
"O": "Digital Signature Trust Co.",
"OU": null,
"CN": "DST Root CA X3"
},
"extensions": {
"basicConstraints": "CA:TRUE",
"keyUsage": "Certificate Sign, CRL Sign",
"subjectKeyIdentifier": "C4:A7:B1:A4:7B:2C:71:FA:DB:E1:4B:90:75:FF:C4:15:60:85:89:10"
},
"not_before": 970348339.0,
"not_after": 1633010475.0,
"as_der": "::BASE64_DER_CERT::"
}
],
"cert_index": 19587936,
"seen": 1508483726.8601687,
"source": {
"url": "mammoth.ct.comodo.com",
"name": "Comodo 'Mammoth' CT log"
}
}
}

update test

redeploy trigger

About

Certificate Transparency Log aggregation, parsing, and streaming service written in Elixir

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

CertStream-Server

Aggregate and broadcast SSL certs as they're issued live.

Certstream-server is a service written in elixir to aggregate, parse, and stream certificate data from multiple certificate transparency logs. It leverages the amazing power of elixir/erlang to achieve great network throughput and concurrency with very little resource usage.

This is a rewrite of the original version written in python, and is much more efficient than the original and currently ships millions of certificates a day on a single Hetzner dedicated server without issue (~250TB of data every month!).

Getting Started

Getting up and running is pretty easy (especially if you use Heroku, as we include a Dockerfile!).

First install elixir (assuming you're on OSX, otherwise follow instructions for your platform)

$ brew install elixir

Then fetch the dependencies

$ mix deps.get

From there you can run it

$ mix run --no-halt

Alternatively, you can run it in an iex session (so you can interact with it while it's running) for development

$ iex -S mix
Interactive Elixir (1.8.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> :observer.start

This will open up an http/websocket server on port 4000 (override this by setting a PORT environment variable).

Usage

Once you have the server runing on some port, you can visit the server to be greeted with the index page.

If you point a websocket client at the root URL (/), it'll give you a data structure that doesn't bundle the DER-encoded certificate.

If you want the certificate data as well, you can point a websocket client to /full-stream and it'll also bundle the DER-encoded certificate along with each certificate.

Internals

The structure of the application is pretty simple. The dataflow basically looks like this:

 CertificateBuffer
|
HTTP Watcher \ | / Websocket Connection Process
HTTP Watcher - ClientManager - Websocket Connection Process
HTTP Watcher / \ Websocket Connection Process

HTTP Watchers

First we spin up 1 process for every entry in the known CTL list from the CT project, with each of them being responsible for sleeping for 10 seconds and checking to see if the top of the merkle tree has changed. Once a difference has been found, they go out and download the certificate data, parsing it and coercing it to a hashmap structure using the EasySSL library and sending it to the ClientManager.

ClientManager

This agent is responsible for brokering communication between the CT watchers and the currently connected websocket clients. Certificates are broadcast to websocket connection processes through an erlang pobox in order to properly load-shed when a slow client isn't reading certificates fast enough. The ClientManager also sends a copy of every certificate received to the CertificateBuffer.

CertificateBuffer

This agent is responsible for keeping a ring-buffer in memory of the most recently seen 25 certificates, as well as counting the certificates processed by Certstream.

Websocket Connection Process

Under the hood we use the erlang library Cowboy to handle static content serving, the json APIs, and websocket connections. There's nothing too special about them other than they're assigned a paired pobox at the start of every connection.

HTTP Routes

/latest.json - Get the most recent 25 certificates CertStream has seen

/example.json - Get the most recent certificate CertStream has seen

Data Structure

Certificate Updates

{
"message_type": "certificate_update",
"data": {
"update_type": "X509LogEntry",
"leaf_cert": {
"subject": {
"aggregated": "/CN=e-zigarette-liquid-shop.de",
"C": null,
"ST": null,
"L": null,
"O": null,
"OU": null,
"CN": "e-zigarette-liquid-shop.de"
},
"extensions": {
"keyUsage": "Digital Signature, Key Encipherment",
"extendedKeyUsage": "TLS Web Server Authentication, TLS Web Client Authentication",
"basicConstraints": "CA:FALSE",
"subjectKeyIdentifier": "AC:4C:7B:3C:E9:C8:7F:CB:E2:7D:5D:64:F2:25:0C:89:C2:AE:F0:5E",
"authorityKeyIdentifier": "keyid:A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1\n",
"authorityInfoAccess": "OCSP - URI:http://ocsp.int-x3.letsencrypt.org\nCA Issuers - URI:http://cert.int-x3.letsencrypt.org/\n",
"subjectAltName": "DNS:e-zigarette-liquid-shop.de, DNS:www.e-zigarette-liquid-shop.de",
"certificatePolicies": "Policy: 2.23.140.1.2.1\nPolicy: 1.3.6.1.4.1.44947.1.1.1\n CPS: http://cps.letsencrypt.org\n User Notice:\n Explicit Text: This Certificate may only be relied upon by Relying Parties and only in accordance with the Certificate Policy found at https://letsencrypt.org/repository/\n"
},
"not_before": 1508123861.0,
"not_after": 1515899861.0,
"as_der": "::BASE64_DER_CERT::",
"all_domains": [
"e-zigarette-liquid-shop.de",
"www.e-zigarette-liquid-shop.de"
]
},
"chain": [
{
"subject": {
"aggregated": "/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3",
"C": "US",
"ST": null,
"L": null,
"O": "Let's Encrypt",
"OU": null,
"CN": "Let's Encrypt Authority X3"
},
"extensions": {
"basicConstraints": "CA:TRUE, pathlen:0",
"keyUsage": "Digital Signature, Certificate Sign, CRL Sign",
"authorityInfoAccess": "OCSP - URI:http://isrg.trustid.ocsp.identrust.com\nCA Issuers - URI:http://apps.identrust.com/roots/dstrootcax3.p7c\n",
"authorityKeyIdentifier": "keyid:C4:A7:B1:A4:7B:2C:71:FA:DB:E1:4B:90:75:FF:C4:15:60:85:89:10\n",
"certificatePolicies": "Policy: 2.23.140.1.2.1\nPolicy: 1.3.6.1.4.1.44947.1.1.1\n CPS: http://cps.root-x1.letsencrypt.org\n",
"crlDistributionPoints": "\nFull Name:\n URI:http://crl.identrust.com/DSTROOTCAX3CRL.crl\n",
"subjectKeyIdentifier": "A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1"
},
"not_before": 1458232846.0,
"not_after": 1615999246.0,
"as_der": "::BASE64_DER_CERT::"
},
{
"subject": {
"aggregated": "/O=Digital Signature Trust Co./CN=DST Root CA X3",
"C": null,
"ST": null,
"L": null,
"O": "Digital Signature Trust Co.",
"OU": null,
"CN": "DST Root CA X3"
},
"extensions": {
"basicConstraints": "CA:TRUE",
"keyUsage": "Certificate Sign, CRL Sign",
"subjectKeyIdentifier": "C4:A7:B1:A4:7B:2C:71:FA:DB:E1:4B:90:75:FF:C4:15:60:85:89:10"
},
"not_before": 970348339.0,
"not_after": 1633010475.0,
"as_der": "::BASE64_DER_CERT::"
}
],
"cert_index": 19587936,
"seen": 1508483726.8601687,
"source": {
"url": "mammoth.ct.comodo.com",
"name": "Comodo 'Mammoth' CT log"
}
}
}

update test

redeploy trigger

About

Certificate Transparency Log aggregation, parsing, and streaming service written in Elixir

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

CertStream-Server

Aggregate and broadcast SSL certs as they're issued live.

Certstream-server is a service written in elixir to aggregate, parse, and stream certificate data from multiple certificate transparency logs. It leverages the amazing power of elixir/erlang to achieve great network throughput and concurrency with very little resource usage.

This is a rewrite of the original version written in python, and is much more efficient than the original and currently ships millions of certificates a day on a single Hetzner dedicated server without issue (~250TB of data every month!).

Getting Started

Getting up and running is pretty easy (especially if you use Heroku, as we include a Dockerfile!).

First install elixir (assuming you're on OSX, otherwise follow instructions for your platform)

$ brew install elixir

Then fetch the dependencies

$ mix deps.get

From there you can run it

$ mix run --no-halt

Alternatively, you can run it in an iex session (so you can interact with it while it's running) for development

$ iex -S mix
Interactive Elixir (1.8.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> :observer.start

This will open up an http/websocket server on port 4000 (override this by setting a PORT environment variable).

Usage

Once you have the server runing on some port, you can visit the server to be greeted with the index page.

If you point a websocket client at the root URL (/), it'll give you a data structure that doesn't bundle the DER-encoded certificate.

If you want the certificate data as well, you can point a websocket client to /full-stream and it'll also bundle the DER-encoded certificate along with each certificate.

Internals

The structure of the application is pretty simple. The dataflow basically looks like this:

 CertificateBuffer
|
HTTP Watcher \ | / Websocket Connection Process
HTTP Watcher - ClientManager - Websocket Connection Process
HTTP Watcher / \ Websocket Connection Process

HTTP Watchers

First we spin up 1 process for every entry in the known CTL list from the CT project, with each of them being responsible for sleeping for 10 seconds and checking to see if the top of the merkle tree has changed. Once a difference has been found, they go out and download the certificate data, parsing it and coercing it to a hashmap structure using the EasySSL library and sending it to the ClientManager.

ClientManager

This agent is responsible for brokering communication between the CT watchers and the currently connected websocket clients. Certificates are broadcast to websocket connection processes through an erlang pobox in order to properly load-shed when a slow client isn't reading certificates fast enough. The ClientManager also sends a copy of every certificate received to the CertificateBuffer.

CertificateBuffer

This agent is responsible for keeping a ring-buffer in memory of the most recently seen 25 certificates, as well as counting the certificates processed by Certstream.

Websocket Connection Process

Under the hood we use the erlang library Cowboy to handle static content serving, the json APIs, and websocket connections. There's nothing too special about them other than they're assigned a paired pobox at the start of every connection.

HTTP Routes

/latest.json - Get the most recent 25 certificates CertStream has seen

/example.json - Get the most recent certificate CertStream has seen

Data Structure

Certificate Updates

{
"message_type": "certificate_update",
"data": {
"update_type": "X509LogEntry",
"leaf_cert": {
"subject": {
"aggregated": "/CN=e-zigarette-liquid-shop.de",
"C": null,
"ST": null,
"L": null,
"O": null,
"OU": null,
"CN": "e-zigarette-liquid-shop.de"
},
"extensions": {
"keyUsage": "Digital Signature, Key Encipherment",
"extendedKeyUsage": "TLS Web Server Authentication, TLS Web Client Authentication",
"basicConstraints": "CA:FALSE",
"subjectKeyIdentifier": "AC:4C:7B:3C:E9:C8:7F:CB:E2:7D:5D:64:F2:25:0C:89:C2:AE:F0:5E",
"authorityKeyIdentifier": "keyid:A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1\n",
"authorityInfoAccess": "OCSP - URI:http://ocsp.int-x3.letsencrypt.org\nCA Issuers - URI:http://cert.int-x3.letsencrypt.org/\n",
"subjectAltName": "DNS:e-zigarette-liquid-shop.de, DNS:www.e-zigarette-liquid-shop.de",
"certificatePolicies": "Policy: 2.23.140.1.2.1\nPolicy: 1.3.6.1.4.1.44947.1.1.1\n CPS: http://cps.letsencrypt.org\n User Notice:\n Explicit Text: This Certificate may only be relied upon by Relying Parties and only in accordance with the Certificate Policy found at https://letsencrypt.org/repository/\n"
},
"not_before": 1508123861.0,
"not_after": 1515899861.0,
"as_der": "::BASE64_DER_CERT::",
"all_domains": [
"e-zigarette-liquid-shop.de",
"www.e-zigarette-liquid-shop.de"
]
},
"chain": [
{
"subject": {
"aggregated": "/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3",
"C": "US",
"ST": null,
"L": null,
"O": "Let's Encrypt",
"OU": null,
"CN": "Let's Encrypt Authority X3"
},
"extensions": {
"basicConstraints": "CA:TRUE, pathlen:0",
"keyUsage": "Digital Signature, Certificate Sign, CRL Sign",
"authorityInfoAccess": "OCSP - URI:http://isrg.trustid.ocsp.identrust.com\nCA Issuers - URI:http://apps.identrust.com/roots/dstrootcax3.p7c\n",
"authorityKeyIdentifier": "keyid:C4:A7:B1:A4:7B:2C:71:FA:DB:E1:4B:90:75:FF:C4:15:60:85:89:10\n",
"certificatePolicies": "Policy: 2.23.140.1.2.1\nPolicy: 1.3.6.1.4.1.44947.1.1.1\n CPS: http://cps.root-x1.letsencrypt.org\n",
"crlDistributionPoints": "\nFull Name:\n URI:http://crl.identrust.com/DSTROOTCAX3CRL.crl\n",
"subjectKeyIdentifier": "A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1"
},
"not_before": 1458232846.0,
"not_after": 1615999246.0,
"as_der": "::BASE64_DER_CERT::"
},
{
"subject": {
"aggregated": "/O=Digital Signature Trust Co./CN=DST Root CA X3",
"C": null,
"ST": null,
"L": null,
"O": "Digital Signature Trust Co.",
"OU": null,
"CN": "DST Root CA X3"
},
"extensions": {
"basicConstraints": "CA:TRUE",
"keyUsage": "Certificate Sign, CRL Sign",
"subjectKeyIdentifier": "C4:A7:B1:A4:7B:2C:71:FA:DB:E1:4B:90:75:FF:C4:15:60:85:89:10"
},
"not_before": 970348339.0,
"not_after": 1633010475.0,
"as_der": "::BASE64_DER_CERT::"
}
],
"cert_index": 19587936,
"seen": 1508483726.8601687,
"source": {
"url": "mammoth.ct.comodo.com",
"name": "Comodo 'Mammoth' CT log"
}
}
}

update test

redeploy trigger

About

Certificate Transparency Log aggregation, parsing, and streaming service written in Elixir

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

CertStream-Server

Aggregate and broadcast SSL certs as they're issued live.

Certstream-server is a service written in elixir to aggregate, parse, and stream certificate data from multiple certificate transparency logs. It leverages the amazing power of elixir/erlang to achieve great network throughput and concurrency with very little resource usage.

This is a rewrite of the original version written in python, and is much more efficient than the original and currently ships millions of certificates a day on a single Hetzner dedicated server without issue (~250TB of data every month!).

Getting Started

Getting up and running is pretty easy (especially if you use Heroku, as we include a Dockerfile!).

First install elixir (assuming you're on OSX, otherwise follow instructions for your platform)

$ brew install elixir

Then fetch the dependencies

$ mix deps.get

From there you can run it

$ mix run --no-halt

Alternatively, you can run it in an iex session (so you can interact with it while it's running) for development

$ iex -S mix
Interactive Elixir (1.8.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> :observer.start

This will open up an http/websocket server on port 4000 (override this by setting a PORT environment variable).

Usage

Once you have the server runing on some port, you can visit the server to be greeted with the index page.

If you point a websocket client at the root URL (/), it'll give you a data structure that doesn't bundle the DER-encoded certificate.

If you want the certificate data as well, you can point a websocket client to /full-stream and it'll also bundle the DER-encoded certificate along with each certificate.

Internals

The structure of the application is pretty simple. The dataflow basically looks like this:

 CertificateBuffer
|
HTTP Watcher \ | / Websocket Connection Process
HTTP Watcher - ClientManager - Websocket Connection Process
HTTP Watcher / \ Websocket Connection Process

HTTP Watchers

First we spin up 1 process for every entry in the known CTL list from the CT project, with each of them being responsible for sleeping for 10 seconds and checking to see if the top of the merkle tree has changed. Once a difference has been found, they go out and download the certificate data, parsing it and coercing it to a hashmap structure using the EasySSL library and sending it to the ClientManager.

ClientManager

This agent is responsible for brokering communication between the CT watchers and the currently connected websocket clients. Certificates are broadcast to websocket connection processes through an erlang pobox in order to properly load-shed when a slow client isn't reading certificates fast enough. The ClientManager also sends a copy of every certificate received to the CertificateBuffer.

CertificateBuffer

This agent is responsible for keeping a ring-buffer in memory of the most recently seen 25 certificates, as well as counting the certificates processed by Certstream.

Websocket Connection Process

Under the hood we use the erlang library Cowboy to handle static content serving, the json APIs, and websocket connections. There's nothing too special about them other than they're assigned a paired pobox at the start of every connection.

HTTP Routes

/latest.json - Get the most recent 25 certificates CertStream has seen

/example.json - Get the most recent certificate CertStream has seen

Data Structure

Certificate Updates

{
"message_type": "certificate_update",
"data": {
"update_type": "X509LogEntry",
"leaf_cert": {
"subject": {
"aggregated": "/CN=e-zigarette-liquid-shop.de",
"C": null,
"ST": null,
"L": null,
"O": null,
"OU": null,
"CN": "e-zigarette-liquid-shop.de"
},
"extensions": {
"keyUsage": "Digital Signature, Key Encipherment",
"extendedKeyUsage": "TLS Web Server Authentication, TLS Web Client Authentication",
"basicConstraints": "CA:FALSE",
"subjectKeyIdentifier": "AC:4C:7B:3C:E9:C8:7F:CB:E2:7D:5D:64:F2:25:0C:89:C2:AE:F0:5E",
"authorityKeyIdentifier": "keyid:A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1\n",
"authorityInfoAccess": "OCSP - URI:http://ocsp.int-x3.letsencrypt.org\nCA Issuers - URI:http://cert.int-x3.letsencrypt.org/\n",
"subjectAltName": "DNS:e-zigarette-liquid-shop.de, DNS:www.e-zigarette-liquid-shop.de",
"certificatePolicies": "Policy: 2.23.140.1.2.1\nPolicy: 1.3.6.1.4.1.44947.1.1.1\n CPS: http://cps.letsencrypt.org\n User Notice:\n Explicit Text: This Certificate may only be relied upon by Relying Parties and only in accordance with the Certificate Policy found at https://letsencrypt.org/repository/\n"
},
"not_before": 1508123861.0,
"not_after": 1515899861.0,
"as_der": "::BASE64_DER_CERT::",
"all_domains": [
"e-zigarette-liquid-shop.de",
"www.e-zigarette-liquid-shop.de"
]
},
"chain": [
{
"subject": {
"aggregated": "/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3",
"C": "US",
"ST": null,
"L": null,
"O": "Let's Encrypt",
"OU": null,
"CN": "Let's Encrypt Authority X3"
},
"extensions": {
"basicConstraints": "CA:TRUE, pathlen:0",
"keyUsage": "Digital Signature, Certificate Sign, CRL Sign",
"authorityInfoAccess": "OCSP - URI:http://isrg.trustid.ocsp.identrust.com\nCA Issuers - URI:http://apps.identrust.com/roots/dstrootcax3.p7c\n",
"authorityKeyIdentifier": "keyid:C4:A7:B1:A4:7B:2C:71:FA:DB:E1:4B:90:75:FF:C4:15:60:85:89:10\n",
"certificatePolicies": "Policy: 2.23.140.1.2.1\nPolicy: 1.3.6.1.4.1.44947.1.1.1\n CPS: http://cps.root-x1.letsencrypt.org\n",
"crlDistributionPoints": "\nFull Name:\n URI:http://crl.identrust.com/DSTROOTCAX3CRL.crl\n",
"subjectKeyIdentifier": "A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1"
},
"not_before": 1458232846.0,
"not_after": 1615999246.0,
"as_der": "::BASE64_DER_CERT::"
},
{
"subject": {
"aggregated": "/O=Digital Signature Trust Co./CN=DST Root CA X3",
"C": null,
"ST": null,
"L": null,
"O": "Digital Signature Trust Co.",
"OU": null,
"CN": "DST Root CA X3"
},
"extensions": {
"basicConstraints": "CA:TRUE",
"keyUsage": "Certificate Sign, CRL Sign",
"subjectKeyIdentifier": "C4:A7:B1:A4:7B:2C:71:FA:DB:E1:4B:90:75:FF:C4:15:60:85:89:10"
},
"not_before": 970348339.0,
"not_after": 1633010475.0,
"as_der": "::BASE64_DER_CERT::"
}
],
"cert_index": 19587936,
"seen": 1508483726.8601687,
"source": {
"url": "mammoth.ct.comodo.com",
"name": "Comodo 'Mammoth' CT log"
}
}
}

update test

redeploy trigger

About

Certificate Transparency Log aggregation, parsing, and streaming service written in Elixir

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

CertStream-Server

Aggregate and broadcast SSL certs as they're issued live.

Certstream-server is a service written in elixir to aggregate, parse, and stream certificate data from multiple certificate transparency logs. It leverages the amazing power of elixir/erlang to achieve great network throughput and concurrency with very little resource usage.

This is a rewrite of the original version written in python, and is much more efficient than the original and currently ships millions of certificates a day on a single Hetzner dedicated server without issue (~250TB of data every month!).

Getting Started

Getting up and running is pretty easy (especially if you use Heroku, as we include a Dockerfile!).

First install elixir (assuming you're on OSX, otherwise follow instructions for your platform)

$ brew install elixir

Then fetch the dependencies

$ mix deps.get

From there you can run it

$ mix run --no-halt

Alternatively, you can run it in an iex session (so you can interact with it while it's running) for development

$ iex -S mix
Interactive Elixir (1.8.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> :observer.start

This will open up an http/websocket server on port 4000 (override this by setting a PORT environment variable).

Usage

Once you have the server runing on some port, you can visit the server to be greeted with the index page.

If you point a websocket client at the root URL (/), it'll give you a data structure that doesn't bundle the DER-encoded certificate.

If you want the certificate data as well, you can point a websocket client to /full-stream and it'll also bundle the DER-encoded certificate along with each certificate.

Internals

The structure of the application is pretty simple. The dataflow basically looks like this:

 CertificateBuffer
|
HTTP Watcher \ | / Websocket Connection Process
HTTP Watcher - ClientManager - Websocket Connection Process
HTTP Watcher / \ Websocket Connection Process

HTTP Watchers

First we spin up 1 process for every entry in the known CTL list from the CT project, with each of them being responsible for sleeping for 10 seconds and checking to see if the top of the merkle tree has changed. Once a difference has been found, they go out and download the certificate data, parsing it and coercing it to a hashmap structure using the EasySSL library and sending it to the ClientManager.

ClientManager

This agent is responsible for brokering communication between the CT watchers and the currently connected websocket clients. Certificates are broadcast to websocket connection processes through an erlang pobox in order to properly load-shed when a slow client isn't reading certificates fast enough. The ClientManager also sends a copy of every certificate received to the CertificateBuffer.

CertificateBuffer

This agent is responsible for keeping a ring-buffer in memory of the most recently seen 25 certificates, as well as counting the certificates processed by Certstream.

Websocket Connection Process

Under the hood we use the erlang library Cowboy to handle static content serving, the json APIs, and websocket connections. There's nothing too special about them other than they're assigned a paired pobox at the start of every connection.

HTTP Routes

/latest.json - Get the most recent 25 certificates CertStream has seen

/example.json - Get the most recent certificate CertStream has seen

Data Structure

Certificate Updates

{
"message_type": "certificate_update",
"data": {
"update_type": "X509LogEntry",
"leaf_cert": {
"subject": {
"aggregated": "/CN=e-zigarette-liquid-shop.de",
"C": null,
"ST": null,
"L": null,
"O": null,
"OU": null,
"CN": "e-zigarette-liquid-shop.de"
},
"extensions": {
"keyUsage": "Digital Signature, Key Encipherment",
"extendedKeyUsage": "TLS Web Server Authentication, TLS Web Client Authentication",
"basicConstraints": "CA:FALSE",
"subjectKeyIdentifier": "AC:4C:7B:3C:E9:C8:7F:CB:E2:7D:5D:64:F2:25:0C:89:C2:AE:F0:5E",
"authorityKeyIdentifier": "keyid:A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1\n",
"authorityInfoAccess": "OCSP - URI:http://ocsp.int-x3.letsencrypt.org\nCA Issuers - URI:http://cert.int-x3.letsencrypt.org/\n",
"subjectAltName": "DNS:e-zigarette-liquid-shop.de, DNS:www.e-zigarette-liquid-shop.de",
"certificatePolicies": "Policy: 2.23.140.1.2.1\nPolicy: 1.3.6.1.4.1.44947.1.1.1\n CPS: http://cps.letsencrypt.org\n User Notice:\n Explicit Text: This Certificate may only be relied upon by Relying Parties and only in accordance with the Certificate Policy found at https://letsencrypt.org/repository/\n"
},
"not_before": 1508123861.0,
"not_after": 1515899861.0,
"as_der": "::BASE64_DER_CERT::",
"all_domains": [
"e-zigarette-liquid-shop.de",
"www.e-zigarette-liquid-shop.de"
]
},
"chain": [
{
"subject": {
"aggregated": "/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3",
"C": "US",
"ST": null,
"L": null,
"O": "Let's Encrypt",
"OU": null,
"CN": "Let's Encrypt Authority X3"
},
"extensions": {
"basicConstraints": "CA:TRUE, pathlen:0",
"keyUsage": "Digital Signature, Certificate Sign, CRL Sign",
"authorityInfoAccess": "OCSP - URI:http://isrg.trustid.ocsp.identrust.com\nCA Issuers - URI:http://apps.identrust.com/roots/dstrootcax3.p7c\n",
"authorityKeyIdentifier": "keyid:C4:A7:B1:A4:7B:2C:71:FA:DB:E1:4B:90:75:FF:C4:15:60:85:89:10\n",
"certificatePolicies": "Policy: 2.23.140.1.2.1\nPolicy: 1.3.6.1.4.1.44947.1.1.1\n CPS: http://cps.root-x1.letsencrypt.org\n",
"crlDistributionPoints": "\nFull Name:\n URI:http://crl.identrust.com/DSTROOTCAX3CRL.crl\n",
"subjectKeyIdentifier": "A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1"
},
"not_before": 1458232846.0,
"not_after": 1615999246.0,
"as_der": "::BASE64_DER_CERT::"
},
{
"subject": {
"aggregated": "/O=Digital Signature Trust Co./CN=DST Root CA X3",
"C": null,
"ST": null,
"L": null,
"O": "Digital Signature Trust Co.",
"OU": null,
"CN": "DST Root CA X3"
},
"extensions": {
"basicConstraints": "CA:TRUE",
"keyUsage": "Certificate Sign, CRL Sign",
"subjectKeyIdentifier": "C4:A7:B1:A4:7B:2C:71:FA:DB:E1:4B:90:75:FF:C4:15:60:85:89:10"
},
"not_before": 970348339.0,
"not_after": 1633010475.0,
"as_der": "::BASE64_DER_CERT::"
}
],
"cert_index": 19587936,
"seen": 1508483726.8601687,
"source": {
"url": "mammoth.ct.comodo.com",
"name": "Comodo 'Mammoth' CT log"
}
}
}

update test

redeploy trigger

About

Certificate Transparency Log aggregation, parsing, and streaming service written in Elixir

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages