From c64555008148f68d16e2bb41913a759b208fb1d0 Mon Sep 17 00:00:00 2001 From: Pim van Pelt Date: Sat, 21 Feb 2026 16:24:09 +0000 Subject: [PATCH] Typo fixes and grammar improvements, h/t Claude --- content/articles/2025-07-26-ctlog-1.md | 30 +++++++++++++------------- content/articles/2025-08-10-ctlog-2.md | 20 ++++++++--------- content/articles/2025-08-24-ctlog-3.md | 26 +++++++++++----------- 3 files changed, 38 insertions(+), 38 deletions(-) diff --git a/content/articles/2025-07-26-ctlog-1.md b/content/articles/2025-07-26-ctlog-1.md index 55860b8..0e8d3e8 100644 --- a/content/articles/2025-07-26-ctlog-1.md +++ b/content/articles/2025-07-26-ctlog-1.md @@ -60,7 +60,7 @@ with no merge delay. {{< image width="18em" float="right" src="/assets/ctlog/MPLS Backbone - CTLog.svg" alt="ctlog at ipng" >}} In the diagram, I've drawn an overview of IPng's network. In {{< boldcolor color="red" >}}red{{< -/boldcolor >}} a european backbone network is provided by a [[BGP Free Core +/boldcolor >}} a European backbone network is provided by a [[BGP Free Core network]({{< ref 2022-12-09-oem-switch-2 >}})]. It operates a private IPv4, IPv6, and MPLS network, called _IPng Site Local_, which is not connected to the internet. On top of that, IPng offers L2 and L3 services, for example using [[VPP]({{< ref 2021-02-27-network >}})]. @@ -81,7 +81,7 @@ of them will be running one of the _Log_ implementations. IPng provides two larg for offsite backup, in case a hypervisor decides to check out, and daily backups to an S3 bucket using Restic. -Having explained all of this, I am well aware that end to end reliability will be coming from the +Having explained all of this, I am well aware that end-to-end reliability will be coming from the fact that there are many independent _Log_ operators, and folks wanting to validate certificates can simply monitor many. If there is a gap in coverage, say due to any given _Log_'s downtime, this will not necessarily be problematic. It does mean that I may have to suppress the SRE in me... @@ -93,8 +93,8 @@ this article, maybe a simpler, more elegant design could be superior, precisely log reliability is not _as important_ as having many available log _instances_ to choose from. From operators in the field I understand that the world-wide generation of certificates is roughly -17M/day, which amounts of some 200-250qps of writes. Antonis explains that certs with a validity -if 180 days or less will need two CT log entries, while certs with a validity more than 180d will +17M/day, which amounts to some 200-250qps of writes. Antonis explains that certs with a validity +of 180 days or less will need two CT log entries, while certs with a validity of more than 180d will need three CT log entries. So the write rate is roughly 2.2x that, as an upper bound. My first thought is to see how fast my open source S3 machines can go, really. I'm curious also as @@ -128,7 +128,7 @@ dedicated to their task of running MinIO. {{< image width="100%" src="/assets/ctlog/minio_8kb_performance.png" alt="MinIO 8kb disk vs SSD" >}} The left-hand side graph feels pretty natural to me. With one thread, uploading 8kB objects will -quickly hit the IOPS rate of the disks, each of which have to participate in the write due to EC:3 +quickly hit the IOPS rate of the disks, each of which has to participate in the write due to EC:3 encoding when using six disks, and it tops out at ~56 PUT/s. The single thread hitting SSDs will not hit that limit, and has ~371 PUT/s which I found a bit underwhelming. But, when performing the loadtest with either 8 or 32 write threads, the hard disks become only marginally faster (topping @@ -170,7 +170,7 @@ large objects: This makes me draw an interesting conclusion: seeing as CT Logs are read/write heavy (every couple of seconds, the Merkle tree is recomputed which is reasonably disk-intensive), SeaweedFS might be a -slight better choice. IPng Networks has three MinIO deployments, but no SeaweedFS deployments. Yet. +slightly better choice. IPng Networks has three MinIO deployments, but no SeaweedFS deployments. Yet. # Tessera @@ -181,11 +181,11 @@ predecessor called [[Trillian](https://github.com/google/trillian)]. The impleme bake-in current best-practices based on the lessons learned over the past decade of building and operating transparency logs in production environments and at scale. -Tessera was introduced at the Transparency.Dev summit in October 2024. I first watch Al and Martin +Tessera was introduced at the Transparency.Dev summit in October 2024. I first watched Al and Martin [[introduce](https://www.youtube.com/watch?v=9j_8FbQ9qSc)] it at last year's summit. At a high -level, it wraps what used to be a whole kubernetes cluster full of components, into a single library +level, it wraps what used to be a whole Kubernetes cluster full of components, into a single library that can be used with Cloud based services, either like AWS S3 and RDS database, or like GCP's GCS -storage and Spanner database. However, Google also made is easy to use a regular POSIX filesystem +storage and Spanner database. However, Google also made it easy to use a regular POSIX filesystem implementation. ## TesseraCT @@ -206,12 +206,12 @@ It's time for me to figure out what this TesseraCT thing can do .. are you ready ### TesseraCT: S3 and SQL -TesseraCT comes with a few so-called _personalities_. Those are an implementation of the underlying +TesseraCT comes with a few so-called _personalities_. These are implementations of the underlying storage infrastructure in an opinionated way. The first personality I look at is the `aws` one in `cmd/tesseract/aws`. I notice that this personality does make hard assumptions about the use of AWS which is unfortunate as the documentation says '.. or self-hosted S3 and MySQL database'. However, the `aws` personality assumes the AWS SecretManager in order to fetch its signing key. Before I -can be successful, I need to detangle that. +can be successful, I need to untangle that. #### TesseraCT: AWS and Local Signer @@ -339,7 +339,7 @@ infrastructure, each POST is expected to come from one of the certificate author Then, the `--origin` flag designates how my log calls itself. In the resulting `checkpoint` file it will enumerate a hash of the latest merged and published Merkle tree. In case a server serves -multiple logs, it uses the `--origin` flag to make the destinction which checksum belongs to which. +multiple logs, it uses the `--origin` flag to make the distinction which checksum belongs to which. ``` pim@ctlog-test:~/src/tesseract$ curl http://tesseract-test.minio-ssd.lab.ipng.ch:9000/checkpoint @@ -363,7 +363,7 @@ sets up read and write traffic to a Static CT API log to test correctness and pe load. The traffic is sent according to the [[Static CT API](https://c2sp.org/static-ct-api)] spec. Slick! -The tool start a text-based UI (my favorite! also when using Cisco T-Rex loadtester) in the terminal +The tool starts a text-based UI (my favorite! also when using Cisco T-Rex loadtester) in the terminal that shows the current status, logs, and supports increasing/decreasing read and write traffic. This TUI allows for a level of interactivity when probing a new configuration of a log in order to find any cliffs where performance degrades. For real load-testing applications, especially headless runs @@ -408,7 +408,7 @@ TesseraCT accepts them. I raise the write load by using the '>' key a few times. I notice things are great at 500qps, which is nice because that's double what we are to expect. But I start seeing a bit more noise at 600qps. When I raise the write-rate to 1000qps, all hell breaks loose on the logs of the server (and similar -logs in the `hammer` loadtester: +logs in the `hammer` loadtester): ``` W0727 15:54:33.419881 348475 handlers.go:168] ctlog-test.lab.ipng.ch/test-ecdsa: AddChain handler error: couldn't store the leaf: failed to fetch entry bundle at index 0: failed to fetch resource: getObject: failed to create reader for object "tile/data/000" in bucket "tesseract-test": operation error S3: GetObject, context deadline exceeded @@ -689,7 +689,7 @@ But after a little bit of fiddling, I can assert my conclusion: ## What's Next -I am going to offer such a machine in production together with Antonis Chariton, and Jeroen Massar. +I am going to offer such a machine in production together with Antonis Chariton and Jeroen Massar. I plan to do a few additional things: * Test Sunlight as well on the same hardware. It would be nice to see a comparison between write diff --git a/content/articles/2025-08-10-ctlog-2.md b/content/articles/2025-08-10-ctlog-2.md index 4d4eb6a..69dd402 100644 --- a/content/articles/2025-08-10-ctlog-2.md +++ b/content/articles/2025-08-10-ctlog-2.md @@ -29,7 +29,7 @@ audit the certificate logs themselves. The intent is that eventually clients wo certificates that do not appear in a log, effectively forcing CAs to add all issued certificates to the logs. -In a [[previous article]({{< ref 2025-07-26-ctlog-1 >}})], I took a deep dive into an upcoming +In a [[previous article]({{< ref 2025-07-26-ctlog-1 >}})], I took a deep dive into a new open source implementation of Static CT Logs made by Google. There is however a very competent alternative called [[Sunlight](https://sunlight.dev/)], which deserves some attention to get to know its look and feel, as well as its performance characteristics. @@ -230,7 +230,7 @@ pim@ctlog-test:~$ curl -k https://ctlog-test.lab.ipng.ch:1443/log.v3.json I'm starting to think that using a non-standard listen port won't work, or more precisely, adding a port in the `monitoringprefix` won't work. I notice that the logname is called -`ctlog-test.lab.ipng.ch:1443` which I don't think is supposed to have a portname in it. So instead, +`ctlog-test.lab.ipng.ch:1443` which I don't think is supposed to have a port number in it. So instead, I make Sunlight `listen` on port 443 and omit the port in the `submissionprefix`, and give it and its companion Skylight the needed privileges to bind the privileged port like so: @@ -293,7 +293,7 @@ pim@ctlog-test:/etc/sunlight$ T=0; O=0; while :; do \ ``` On the first commandline I'll start the loadtest at 100 writes/sec with the standard duplication -probability of 10%, which allows me to test Sunlights ability to avoid writing duplicates. This +probability of 10%, which allows me to test Sunlight's ability to avoid writing duplicates. This means I should see on average a growth of the tree at about 90/s. Check. I raise the write-load to 500/s: @@ -387,7 +387,7 @@ A few interesting observations: likely explained because the sqlite3 database lives on ZFS here, while TesseraCT uses MariaDB running on a different filesystem. * The MinIO usage is a lot lighter. As I reduce the load to 1'000/s, as was the case in the TesseraCT - test, I can see the ratio of Get:Put was 93:4 in TesseraCT, while it's 70:30 here. TesseraCT as + test, I can see the ratio of Get:Put was 93:4 in TesseraCT, while it's 70:30 here. TesseraCT was also consuming more IOPS, running at about 10.5k requests/minute, while Sunlight is significantly calmer at 2.8k requests/minute (almost 4x less!) * The burst capacity of Sunlight is a fair bit higher than TesseraCT, likely due to its more @@ -510,7 +510,7 @@ pim@ctlog-test:/etc/sunlight$ while :; do curl -ksS https://ctlog-test.lab.ipng. ``` This rate boils down to `(6576712-6008831)/120` or 4'700/s of written certs, which at a duplication -ratio of 10% means approximately 5'200/s of total accepted certs. This rate, Sunlight is consuming +ratio of 10% means approximately 5'200/s of total accepted certs. At this rate, Sunlight is consuming about 10.3 CPUs/s, while Skylight is at 0.1 CPUs/s and the CT Hammer is at 11.1 CPUs/s; Given the 40 threads on this machine, I am not saturating the CPU, but I'm curious as this rate is significantly lower than TesseraCT. I briefly turn off the hammer on `ctlog-test` to allow Sunlight to monopolize @@ -616,16 +616,16 @@ this setup. ## Wrapup - Observations -From an operators point of view, TesseraCT and Sunlight handle quite differently. Both are easily up +From an operator's point of view, TesseraCT and Sunlight handle quite differently. Both are easily up to the task of serving the current write-load (which is about 250/s). * ***S3***: When using the S3 backend, TesseraCT became quite unhappy above 800/s while Sunlight - went all the way up to 4'200/s and sent significantly less requests to MinIO (about 4x less), + went all the way up to 4'200/s and sent significantly fewer requests to MinIO (about 4x less), while showing good telemetry on the use of S3 backends. In this mode, TesseraCT uses MySQL (in my case, MariaDB) which was not on the ZFS pool, but on the boot-disk. -* ***POSIX***: When using normal filesystem, Sunlight seems to peak at 4'800/s while TesseraCT - went all the way to 12'000/s. When doing so, Disk IO was quite similar between the two +* ***POSIX***: When using a normal filesystem, Sunlight seems to peak at 4'800/s while TesseraCT + went all the way to 12'000/s. When doing so, disk I/O was quite similar between the two solutions, taking into account that TesseraCT runs BadgerDB, while Sunlight uses sqlite3, both are using their respective ZFS pool. @@ -647,7 +647,7 @@ to the task of serving the current write-load (which is about 250/s). instance. It uses flags to construct the environment, and is much more forgiving for creative `origin` (log name), and submission- and monitoring URLs. It's happy to use regular 'http://' for both, which comes in handy in those architectures where the system is serving behind a - reversed proxy. + reverse proxy. * The TesseraCT Hammer tool then again does not like using self-signed certificates, and needs to be told to skip certificate validation in the case of Sunlight loadtests while it is diff --git a/content/articles/2025-08-24-ctlog-3.md b/content/articles/2025-08-24-ctlog-3.md index 12bba46..9c0b53f 100644 --- a/content/articles/2025-08-24-ctlog-3.md +++ b/content/articles/2025-08-24-ctlog-3.md @@ -29,8 +29,8 @@ audit the certificate logs themselves. The intent is that eventually clients wo certificates that do not appear in a log, effectively forcing CAs to add all issued certificates to the logs. -In the first two articles of this series, I explored [[Sunlight]({{< ref 2025-07-26-ctlog-1 >}})] -and [[TesseraCT]({{< ref 2025-08-10-ctlog-2 >}})], two open source implementations of the Static CT +In the first two articles of this series, I explored [[TesseraCT]({{< ref 2025-07-26-ctlog-1 >}})] +and [[Sunlight]({{< ref 2025-08-10-ctlog-2 >}})], two open source implementations of the Static CT protocol. In this final article, I'll share the details on how I created the environment and production instances for four logs that IPng will be providing: Rennet and Lipase are two ingredients to make cheese and will serve as our staging/testing logs. Gouda and Halloumi are two @@ -74,7 +74,7 @@ ctlog1:~$ for log in lipase; do \ ``` The hypervisor will use PCI passthrough for the NVMe drives, and we'll handle ZFS directly on the -VM. The first command creates a ZFS raidz2 pool using 4kB blocks, turns of _atime_ (which avoids one +VM. The first command creates a ZFS raidz2 pool using 4kB blocks, turns off _atime_ (which avoids one metadata write for each read!), and turns on SSD trimming in ZFS, a very useful feature. Then I'll create an encrypted volume for the configuration and key material. This way, if the @@ -87,12 +87,12 @@ _Rennet_ for the testing log and _Gouda_ for the production log. {{< image width="10em" float="right" src="/assets/ctlog/sunlight-logo.png" alt="Sunlight logo" >}} -I set up Sunlight first. as its authors have extensive operational notes both in terms of the +I set up Sunlight first, as its authors have extensive operational notes both in terms of the [[config](https://config.sunlight.geomys.org/)] of Geomys' _Tuscolo_ log, as well as on the [[Sunlight](https://sunlight.dev)] homepage. I really appreciate that Filippo added some [[Gists](https://gist.github.com/FiloSottile/989338e6ba8e03f2c699590ce83f537b)] and [[Doc](https://docs.google.com/document/d/1ID8dX5VuvvrgJrM0Re-jt6Wjhx1eZp-trbpSIYtOhRE/edit?tab=t.0#heading=h.y3yghdo4mdij)] -with pretty much all I need to know to run one too. Our Rennet and Gouda logs use very similar +with pretty much all I need to know to run one too. Our Rennet and Gouda logs use a very similar approach for their configuration, with one notable exception: the VMs do not have a public IP address, and are tucked away in a private network called IPng Site Local. I'll get back to that later. @@ -136,8 +136,8 @@ the automatic certificate renewals, and will handle SSL upstream. A few notes on 1. Most importantly, I will be using a common frontend pool with a wildcard certificate for `*.ct.ipng.ch`. I wrote about [[DNS-01]({{< ref 2023-03-24-lego-dns01 >}})] before, it's a very -convenient way for IPng to do certificate pool management. I will be sharing certificate for all log -types under this certificate. +convenient way for IPng to do certificate pool management. I will be sharing a certificate for all log +types. 1. ACME/HTTP-01 could be made to work with a bit of effort; plumbing through the `/.well-known/` URIs on the frontend and pointing them to these instances. But then the cert would have to be copied from Sunlight back to the frontends. @@ -165,7 +165,7 @@ us going in a public `tesseract-posix` instance. One thing I liked about Sunlight is its compact YAML file that described the pertinent bits of the system, and that I can serve any number of logs with the same process. On the other hand, TesseraCT -can serve only one log per process. Both have pro's and con's, notably if any poisonous submission +can serve only one log per process. Both have pros and cons, notably if any poisonous submission would be offered, Sunlight might take down all logs, while TesseraCT would only take down the log receiving the offensive submission. On the other hand, maintaining separate processes is cumbersome, and all log instances need to be meticulously configured. @@ -198,7 +198,7 @@ logs: EOF ``` -With this snippet, I have all the information I need. Here's the steps I take to construct the log +With this snippet, I have all the information I need. Here are the steps I take to construct the log itself: ***1. Generate keys*** @@ -245,7 +245,7 @@ Creating /ssd-vol0/logs/lipase2027h2/data/log.v3.json {{< image width="60%" src="/assets/ctlog/lipase.png" alt="TesseraCT Lipase Log" >}} It's nice to see a familiar look-and-feel for these logs appear in those `index.html` (which all -cross-link to each other within the logs specificied in `tesseract-staging.yaml`, which is dope. +cross-link to each other within the logs specified in `tesseract-staging.yaml`, which is dope. ***3. Generate Roots*** @@ -347,7 +347,7 @@ read path. Filippo explained that the HTTP headers matter in the Static CT speci Issuers, and Checkpoint must all have specific caching and content type headers set. This is what makes Skylight such a gem - I get to read it (and the spec!) to see what I'm supposed to be serving. -And thus, `gen-nginx` command is born, and listens on port `:8080` for requests: +And thus, the `gen-nginx` command is born, and listens on port `:8080` for requests: ``` ctlog@ctlog1:/ssd-vol0/enc/tesseract$ tesseract-genconf -c tesseract-staging.yaml gen-nginx @@ -487,14 +487,14 @@ server { } ``` -Taking _Lipase_ shard 2025h2 as an example, The submission path (on `*.log.ct.ipng.ch`) will show +Taking _Lipase_ shard 2025h2 as an example, the submission path (on `*.log.ct.ipng.ch`) will show the same `index.html` as the monitoring path (on `*.mon.ct.ipng.ch`), to provide some consistency with Sunlight logs. Otherwise, the `/metrics` endpoint is forwarded to the `otelcol` running on port `:9464`, and the rest (the `/ct/v1/` and so on) are sent to the first port `:16900` of the TesseraCT. Then the read-path makes a special-case of the `/checkpoint` endpoint, which it does not cache. That -request (as all others) are forwarded to port `:8080` which is where NGINX is running. Other +request (as all others) is forwarded to port `:8080` which is where NGINX is running. Other requests (notably `/tile` and `/issuer`) are cacheable, so I'll cache these on the upstream NGINX servers, both for resilience as well as for performance. Having four of these NGINX upstream will allow the Static CT logs (regardless of being Sunlight or TesseraCT) to serve very high read-rates.