aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRasmus Dahlberg <rasmus@rgdd.se>2024-06-01 15:35:45 +0200
committerRasmus Dahlberg <rasmus@rgdd.se>2024-06-02 13:04:04 +0200
commit62f94ac6a1404834ac6f0723ef57e25fcd5e67f9 (patch)
tree5a70ce3dec39229d37816dafed0d944016c1dd54
parent279de6a1195adb739a8d1f2afb445b68793b28b8 (diff)
Improve terminology and documentationmain
-rw-r--r--README.md119
-rw-r--r--docs/design.md (renamed from docs/introduction.md)83
-rw-r--r--docs/feedback.md23
-rw-r--r--docs/storage.md3
-rw-r--r--docs/submission.md22
-rw-r--r--pkg/policy/node.go8
-rw-r--r--pkg/policy/policy.go2
-rw-r--r--pkg/policy/wildcard.go6
8 files changed, 133 insertions, 133 deletions
diff --git a/README.md b/README.md
index ef27a10..bda9f48 100644
--- a/README.md
+++ b/README.md
@@ -2,83 +2,83 @@
An implementation of a silent Certificate Transparency monitor.
-**Status:** drafty prototype, please do not use for anything serious.
+## Status
+
+Drafty prototype, please do not use for anything too serious yet.
## How it works
A monitor downloads all certificates from Certificate Transparency logs. This
-gives a concise view of which certificates have been issued for what domains.
+provides a concise view of which certificates have been issued for what domains.
-The monitor is configured to pull legitimately issued certificates from trusted
-nodes. Each node packages its legitimate certificates as a single submission.
-This submission is then made available for download on a URL via HTTP GET.
+The monitor is configured to pull certificates from trusted systems that
+legitimately request certificates. The legitimately issued certificates are
+typically made available on HTTP(S) URLs that are polled periodically.
-To convince the monitor that a submission was generated by a trusted node, a
-shared secret is established between the node and the monitor. The node creates
-a message authentication code using the shared secret. The monitor verifies it.
+To convince the monitor that the file of legitimately issued certificates is
+authentic, it is integrity protected using a message authentication code. So,
+the monitor and its trusted systems need to be configured with shared secrets.
-What makes this setup _silent_ is the fact that the monitor can compute the
-difference between any discovered and legitimately issued certificates. If a
-certificate is found that no node submitted, only then is an alert printed.
+What makes this setup "silent" is that the monitor can compute the difference
+between any downloaded and legitimately issued certificates. If a certificate
+is found that no trusted system made available, only then an alert is emitted.
-## Quick start
+See the [silentct design](./docs/design.md) for a lengthier introduction.
-### Setup a node
+## Quickstart
-You will need the `silentct-mac` tool to create submissions that the monitor can
-pull. Install:
+### Setup a trusted system
- $ go install rgdd.se/silentct/cmd/silentct-mac@latest
+Install the `silentct-mac` tool.
-Locate the node's certificates that are still valid (i.e., not expired) and
-prepare a submission for them:
+ $ go install rgdd.se/silentct/cmd/silentct-mac@latest
- $ silentct-mac -n NAME -s SECRET /path/to/chain-1.pem /path/to/chain-2.pem ...
+Mark all certificates that have yet to expire as legitimately issued. The below
+specifies one certificate chain, but it is possible to list multiple ones.
-`NAME` is an arbitrary name of the node.
+ $ silentct-mac -n example.org -s sikritpassword /etc/letsencrypt/live/example.org/fullchain.pem
-`SECRET` is a secret that will only be shared with the monitor.
+`-n` sets an arbitrary name of the trusted system.
-The output includes the node name, the computed message authentication code, and
-the list of certificate chains that was specified. Use the `-o` option if you
-prefer to save the output directly to a file rather than getting it on stdout.
+`-s` sets a secret that will be shared with the monitor.
-Make the generated submission file available on a URL. Typically each node
-already serves web content, in which case you can copy it into some directory.
+The output includes the name, a message authentication code, and the list of
+certificate chains that was specified. Record the output as a file the monitor
+can pull, e.g., by saving it in a web root or transferring it to one. Ensure
+that this file gets updated each time a new certificate is legitimately issued.
+Below is an example that keeps the file up-to-date with `crontab` and `certbot`.
-Finally, ensure that anytime the node requests a new certificate to be issued,
-then a new submission is generated that replaces or extends the previous one.
-For example, if `certbot` is run by `cron`, then that is a good place to hook.
+ # crontab -l
+ 35 3 * * * certbot renew --post-hook "silentct-mac -n example.org -s sikritpassword -o /var/www/example.org/silentct/allowlist /etc/letsencrypt/live/example.org/fullchain.pem"
-Repeat this setup if there are multiple nodes.
+**Note:** the `--post-hook` option can be specified per site in the
+`[renewalparams]` section of `/etc/letsencrypt/renewal/example.org.conf`.
### Setup the monitor
-Install on the system that will run the monitor:
+Install the `silentct-mon` tool:
$ go install rgdd.se/silentct/cmd/silentct-mon@latest
-Create a monitor policy file in JSON format. Below is an example that looks for
-all certificates related to `example.org`, expect for certificates that are
-associated with `test.example.org`. You need at least one wildcard to match on.
+Create a configuration file.
- $ cat policy.json
+ $ cat config.json
{
"monitor": [
{
"bootstrap_at": "2024-01-01T00:00:00Z",
- "wildcard": "example.org",
+ "suffix": "example.org",
"excludes": [
"test"
]
}
],
- "nodes": [
+ "certificate_requesters": [
{
- "name": "NODE_NAME",
- "secret": "NODE_SECRET",
- "url": "SUBMISSION_URL",
- "issues": [
+ "name": "example.org",
+ "secret": "sikritpassword",
+ "location": "https://www.example.org/silentct/allowlist",
+ "requests": [
"example.org",
"www.example.org"
]
@@ -86,30 +86,35 @@ associated with `test.example.org`. You need at least one wildcard to match on.
]
}
-The `excludes` keyword is optional. The `bootstrap_at` keyword is required, and
-should be set to the current time of adding the wildcard for monitoring. Any
-certificate that expired before the specified bootstrap time will be ignored.
+`bootstrap_at` is the time the monitor started looking for certificates that
+match `suffix`. The monitor considers a certificate to match iff (i) it expired
+after the bootstrap time, and (ii) at least one subject alternative name ends
+with the specified suffix without a longer match being available when taking the
+optional `excludes` list of subdomains into account. For example, the above
+configuration matches `www.example.org` but not `foo.test.example.org`.
-Populate the list of nodes based on the names, secrets, and URLs selected during
-your setup. Also add the domains each node is allowed to put into certificates.
+Each entry in the `"certificate-requesters"` list corresponds to a trusted
+system and the domains it requests certificates for. Set `name`, `secret`,
+`location` (filename or URL to pull from), and `requests` to match the
+configuration of each trusted system. The monitor will refuse to mark a
+certificate as legitimate unless the trusted system that requested it had
+permission to do so. This adds a layer of separation between trusted systems.
-Bootstrap the monitor in a non-existent directory:
+### Start the monitor
- $ silentct-mon --bootstrap -f policy.json -d /path/to/directory -v INFO
- ...
+Start the monitor:
-Leave the monitor running:
+ $ silentct-mon -c config.json -d ~/.local/lib/silentct
- $ silentct-mon -f policy.json -d /path/to/directory
+Use the `--bootstrap` flag when running the monitor for the first time.
-Any noteworthy events (like a potentially mis-issued certificate that no node
-submitted) will be printed on stdout. If you prefer to get the monitor's output
-in a file, use the `-o` option. Please note that it is your job to watch the
-output, and/or to hook it up to a notification system like email or similar.
+Noteworthy events will be printed on stdout using the NOTICE level. If you
+prefer to get the monitor's output in a file, use the `-o` option.
-### Further documentation
+### Stop the monitor
- - A lengthier [introduction](./docs/introduction.md) to the overall design
+Stop the monitor gracefully by sending the SIGINT or SIGTERM signals. Nothing
+bad will happen on an ungraceful exit (just redundant work on next startup).
## Contact
diff --git a/docs/introduction.md b/docs/design.md
index 0aab2cc..83f2b59 100644
--- a/docs/introduction.md
+++ b/docs/design.md
@@ -1,23 +1,25 @@
-# Silent Certificate Transparency
+# silentct
This document introduces a silent Certificate Transparency monitor design.
## Setting
-We consider a setting where one or more trusted _nodes_ request certificates for
-a specified list of domain names. The domain names that a node requests
-certificates for may overlap with the domain names of other nodes. For example,
-there may be two distinct nodes that request certificates for a given domain.
+We consider a setting where one or more trusted systems request certificates for
+a list of domains. The domains that a system request certificates for may
+overlap with the domains of other systems. For example, there may be two
+distinct systems that host and request certificates for `www.example.org`.
+Other examples of "systems" that request certificates could include
+`jitsi.example.org`, `etherpad.example.org` and `gitlab.example.org`.
The threat we are worried about is certificate mis-issuance. Due to considering
-a multi-node setting with overlapping domain names, no single node can be aware
-of all legitimately issued certificates for the domain names that it manages.
+a multi-system setting with overlapping domains, no single system can be aware
+of all legitimately issued certificates for the domains that are being managed.
A certificate is considered mis-issued if it contains:
- 1. at least one domain name that any of the trusted nodes manage _but without
- any of the trusted nodes requesting that certificate to be issued_, or
- 2. at least one subdomain of the domain names that any of the trusted nodes
+ 1. at least one domain that any of the trusted systems manage _but without any
+ of the trusted systems requesting that certificate to be issued_, or
+ 2. at least one subdomain of the domains that any of the trusted systems
manage _unless that subdomain is explicitly specified as out of scope_.
The cause of certificate mis-issuance can vary, ranging from BGP and DNS hijacks
@@ -25,32 +27,39 @@ to certificate authorities that are coerced, compromised, or actively malicious.
## Goals and non-scope
-The goal is to detect certificate mis-issuance, not to prevent it. It is out of
-scope to detect certificate mis-issuance that happened in the past. In other
-words, if the architecture described herein is put into operation at time `T`,
-then any certificate mis-issuance that happened before time `T` is out of scope.
+The goal is to detect certificate mis-issuance. It is however out of scope to
+detect certificate mis-issuance that happened in the past. In other words, if
+the design described herein is put into operation at time `T`, then any
+certificate mis-issuance that happened before time `T` is out of scope. This is
+an important constraint that makes it _a lot less costly_ to bootstrap the
+monitor. For example, old certificate backlogs can simply be ignored.
It is also out of scope to detect certificate mis-issuance that targets web
browsers without Certificate Transparency enforcement. This is because we
cannot get a concise view of all certificates without Certificate Transparency.
-To achieve the goal of certificate mis-issuance, we want a _monitor_ that:
+To detect certificate mis-issuance, we want to construct a monitor that:
- 1. _is easy to self-host_, because you trust yourself or can then find someone
- else that is appropriate and willing to host your infrastructure, and
+ 1. _is easy to self-host_, because you trust yourself or can then (more
+ easily) find someone you trust to do the monitoring on your behalf, and
2. _is silent_, so that there is little or no noise unless certificate
- mis-issuance is suspected or other noteworthy log events are happening.
+ mis-issuance is actually suspected. In other words, there should not be a
+ notification every time a legitimate certificate is issued or renewed.
+
+The "silent" property helps a lot for system administrators that manage more
+than a few certificates. It also helps in the third-party monitoring setting,
+as it would not be more noisy to subscribe to notifications from >1 monitor.
## Assumptions
- The attacker is unable to control two independent logs that count towards
the SCT checks in web browsers. So, we need not worry about split-views and
can just download the logs while verifying that they are locally consistent.
- - The nodes that request certificates start in good states but may be
+ - The systems that request certificates start in good states but may be
compromised sometime in the future. Detection of certificate mis-issuance
- is then out of scope for all domains that the compromised nodes managed.
+ is then out of scope for all domains that the compromised systems managed.
- A mis-issued certificate will only be used to target connections from a
- fixed set of IP addresses. Any party that can distinguish between
+ fixed set of IP addresses. A party that can distinguish between
certificates that are legitimate and mis-issued will never be targeted.
- A domain owner notices alerts about suspected certificate mis-issuance. The
monitor that generates these alerts is trusted and never compromised.
@@ -69,35 +78,23 @@ To filter out certificates that are not relevant, the monitor is configured with
a list of domains to match on. Only matching certificates will be stored, which
means there are nearly no storage requirements to run this type of monitor.
-To get the property of _silence_, the monitor pulls the trusted nodes via HTTP
-GET for legitimately issued certificates (periodic job). The monitor will use
-this feedback to filter the downloaded certificates that matched. If any
-certificates are found that no node pushed to the monitor, an alert is printed.
+To get the "silent" property, the monitor pulls the trusted systems for
+legitimately issued certificates via HTTP GET. Alternatively, the monitor can
+read a local file in case it is co-located with a single trusted system. The
+monitor uses this as [feedback](./feedback.md) to filter the downloaded
+certificates that matched. If a certificate is found that none of the trusted
+systems made available, only then is an alert emitted (NOTICE level output).
-The communication channel between the trusted nodes and the monitor can be
+The communication channel between the trusted systems and the monitor can be
tampered with. For example, it may be plain HTTP or an HTTPS connection that
the attacker trivially hijacks by obtaining yet another mis-issued certificate.
Owning that the communication channel is insecure helps avoid misconfiguration.
-A shared secret is used for each node to authenticate with the monitor. This
+A shared secret is used for each system to authenticate with the monitor. This
secret is never shown on the wire: an HMAC key is derived from it, which is used
to produce message authentication codes. All a machine-in-the-middle attacker
-can do is replay or block integrity-protected submissions that a node generated.
+can do is replay or block integrity-protected files that a system generated.
"Replays" can happen either way because the monitor polls periodically, i.e.,
-the monitor needs to account for the fact that it may poll the same thing twice.
+the monitor needs to account for the fact that it may poll the same file twice.
Blocking can not be solved by cryptography and would simply result in alerts.
-
-## Further reading
-
-docdoc
-
-## Future ideas
-
- - Reduce the amount of bandwidth that the monitor spends downloading
- certificates that are either way discarded (non-matches). This can be
- achieved by introducing a _verifiable proxy_ supporting wildcard
- (non-)membership proofs, see [verifiable light-weight monitoring][]. Ignore
- the parts about changing the logs; that is easily solved by the proxy alone.
-
-[verifiable light-weight monitoring]: https://arxiv.org/pdf/1711.03952.pdf
diff --git a/docs/feedback.md b/docs/feedback.md
new file mode 100644
index 0000000..d79d57f
--- /dev/null
+++ b/docs/feedback.md
@@ -0,0 +1,23 @@
+# Feedback
+
+This document describes the integrity-protected file format that a trusted
+system uses when making legitimately issued certificates available to a monitor.
+
+## Format
+
+ NAME MAC
+ <CERTIFICATE CHAIN>
+ ...
+ <CERTIFICATE CHAIN>
+
+`NAME`: identifier that the monitor uses to locate the shared secret.
+
+`MAC`: HMAC with SHA256 as the hash function, computed for line two and forward.
+The shared HMAC key is derived as follows by the trusted system and the monitor:
+
+ hkdf := hkdf.New(sha256.New, SECRET, []byte("silentct"), NAME)
+ key := make([]byte, 16)
+ io.ReadFull(hkdf, key)
+
+`<CERTIFICATE CHAIN>`: certificate chain in PEM format that the trusted system
+considers legitimate. Can be repeated, then delimited by "silentct:separator".
diff --git a/docs/storage.md b/docs/storage.md
deleted file mode 100644
index a0616ed..0000000
--- a/docs/storage.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Storage
-
-docdoc
diff --git a/docs/submission.md b/docs/submission.md
deleted file mode 100644
index 1d9c189..0000000
--- a/docs/submission.md
+++ /dev/null
@@ -1,22 +0,0 @@
-# Submission
-
-docdoc
-
-## Format
-
- NAME MAC
- <PEM CHAIN>
- silentct:separator
- ...
- <PEM CHAIN>
-
-`NAME`: identifier that the monitor uses to locate the right secret.
-
-`MAC`: HMAC with SHA256 as the hash function, computed for line two and forward.
-The HMAC key is derived by the node and the monitor from their shared secret:
-
- hkdf := hkdf.New(sha256.New, SECRET, []byte("silentct"), NAME)
- key := make([]byte, 16)
- io.ReadFull(hkdf, key)
-
-`<PEM CHAIN>`: certificate chain in PEM format the node considers legitimate.
diff --git a/pkg/policy/node.go b/pkg/policy/node.go
index 607dbc3..d933e7f 100644
--- a/pkg/policy/node.go
+++ b/pkg/policy/node.go
@@ -11,10 +11,10 @@ import (
)
type Node struct {
- Name string `json:"name"` // Artbirary node name to authenticate
- Secret string `json:"secret"` // Arbitrary node secret for authentication
- URL string `json:"url"` // Where the node's submissions can be downloaded
- Domains []string `json:"issues"` // Exact-match domain names allowed to be issued
+ Name string `json:"name"` // Artbirary node name to authenticate
+ Secret string `json:"secret"` // Arbitrary node secret for authentication
+ URL string `json:"location"` // Where the node's submissions can be downloaded
+ Domains []string `json:"requests"` // Exact-match domain names allowed to be issued
key [16]byte
}
diff --git a/pkg/policy/policy.go b/pkg/policy/policy.go
index 8ee4867..3c2fec2 100644
--- a/pkg/policy/policy.go
+++ b/pkg/policy/policy.go
@@ -10,7 +10,7 @@ import (
type Policy struct {
Monitor Wildcards `json:"monitor"`
- Nodes []Node `json:"nodes"`
+ Nodes []Node `json:"certificate_requesters"`
// Optional
StaticLogs []metadata.Log `json:"static_logs"`
diff --git a/pkg/policy/wildcard.go b/pkg/policy/wildcard.go
index c67e1d9..b0bdf61 100644
--- a/pkg/policy/wildcard.go
+++ b/pkg/policy/wildcard.go
@@ -38,7 +38,7 @@ func (w *Wildcards) match(sans []string, notAfter time.Time) bool {
// 2. the certificate expired before the BootstrapAt timestamp.
type Wildcard struct {
BootstrapAt time.Time `json:"bootstrap_at"`
- Wildcard string `json:"wildcard"`
+ Wildcard string `json:"suffix"`
Excludes []string `json:"excludes,omitempty"`
}
@@ -52,10 +52,10 @@ func (w *Wildcard) UnmarshalJSON(data []byte) error {
func (w *Wildcard) Validate() error {
if w.BootstrapAt.IsZero() {
- return fmt.Errorf("bootstrap time is required")
+ return fmt.Errorf("bootstrap_at is required")
}
if len(w.Wildcard) == 0 {
- return fmt.Errorf("wildcard is required")
+ return fmt.Errorf("suffix is required")
}
return nil
}