Skip to content

Commit 8a71921

Browse files
marksg07MongoDB Bot
authored and
MongoDB Bot
committed
SERVER-101468 Update certificate generation to be cross-platform and deterministic (#33814)
GitOrigin-RevId: 3ef1af57ac423e448ee1a7d7d9a21a6fad0ae79f
1 parent 8febf85 commit 8a71921

38 files changed

+2738
-1
lines changed

.github/CODEOWNERS

+3
Original file line numberDiff line numberDiff line change
@@ -2636,3 +2636,6 @@ WORKSPACE.bazel @10gen/devprod-build @svc-auto-approve-bot
26362636

26372637
# The following patterns are parsed from ./src/third_party/libmongocrypt/OWNERS.yml
26382638
/src/third_party/libmongocrypt/**/* @10gen/server-security @svc-auto-approve-bot
2639+
2640+
# The following patterns are parsed from ./x509/OWNERS.yml
2641+
/x509/**/* @10gen/server-security @svc-auto-approve-bot

etc/evergreen_yml_components/tasks/compile_tasks_shared.yml

+1
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ tasks:
222222
- "src/src/third_party/schemastore.org/**"
223223
- "src/poetry.lock"
224224
- "src/pyproject.toml"
225+
- "src/x509/**"
225226
exclude_files:
226227
- "src/*_test.pdb"
227228

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
load("@aspect_rules_js//js:defs.bzl", "js_library")
2+
3+
js_library(
4+
name = "all_javascript_files",
5+
srcs = glob([
6+
"*.js",
7+
]),
8+
target_compatible_with = select({
9+
"//bazel/config:ppc_or_s390x": ["@platforms//:incompatible"],
10+
"//conditions:default": [],
11+
}),
12+
visibility = ["//visibility:public"],
13+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Test that mkcert.py generates certificates deterministically, and that pkcs12 certificates, while
2+
// not deterministic, can be generated.
3+
import {getPython3Binary} from "jstests/libs/python.js";
4+
5+
const now = new Date();
6+
// Note - month is 0-indexed, so 11 is December.
7+
if (now.getMonth() == 11 && now.getDate() == 31 && now.getHours() == 23) {
8+
jsTestLog(
9+
"Deterministic certificate generation relies on the current year being constant; skipping test as there is less than an hour until the year changes.");
10+
quit();
11+
}
12+
13+
// Print the openssl version to help with debugging.
14+
clearRawMongoProgramOutput();
15+
assert.eq(runNonMongoProgram("openssl", "version"), 0);
16+
jsTest.log(rawMongoProgramOutput(".*"));
17+
18+
const basedir = MongoRunner.dataPath + "certs/";
19+
const genpath = basedir + "generated/";
20+
mkdir(genpath);
21+
22+
// Run mkcert, and ensure it succeeds and that expected results are generated successfully.
23+
jsTest.log("Running mkcert");
24+
clearRawMongoProgramOutput();
25+
let res = runNonMongoProgram(getPython3Binary(), "-m", "x509.mkcert", "--mkcrl", "-o", genpath);
26+
jsTest.log(rawMongoProgramOutput(".*"));
27+
assert.eq(res, 0);
28+
assert(fileExists(genpath + "ca.pem"));
29+
assert(fileExists(genpath + "crl.pem"));
30+
31+
// Run mkcert again, to a different path.
32+
const genpath2 = basedir + "generated2/";
33+
mkdir(genpath2);
34+
jsTest.log("Running mkcert again");
35+
res = runNonMongoProgram(getPython3Binary(), "-m", "x509.mkcert", "--mkcrl", "-o", genpath2);
36+
assert.eq(res, 0);
37+
38+
// Diff the two generation paths to make sure the contents of the paths are identical.
39+
jsTest.log("Running diff");
40+
clearRawMongoProgramOutput();
41+
res = runNonMongoProgram("diff", "-r", genpath, genpath2);
42+
assert.eq(res, 0);
43+
const diffout = rawMongoProgramOutput(".*").trim();
44+
assert.eq("", diffout, diffout);
45+
46+
// Run mkcert on the apple-certs.yml definitions file, which contains pkcs12 certificates, and
47+
// ensure a .pfx file was generated.
48+
jsTest.log("Running apple certs");
49+
res = runNonMongoProgram(
50+
getPython3Binary(), "-m", "x509.mkcert", "-o", genpath, "--config", "x509/apple-certs.yml");
51+
assert.eq(res, 0);
52+
assert(fileExists(genpath + "macos-trusted-server.pfx"));

poetry.lock

+21-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

+2
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ shrub-py = "^3.1.4"
145145
ocspresponder = "^0.5.0"
146146
flask = "^2.3.3"
147147
ocspbuilder = "^0.10.2"
148+
ecdsa = "^0.19.0"
149+
asn1crypto = "^1.5.1"
148150
toml = ">=0.10.2,<0.11.0"
149151

150152
# Werkzeug is needed for ocsp tests in ocsp_mock.py

x509/OWNERS.yml

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
version: 1.0.0
2+
filters:
3+
- "*":
4+
approvers:
5+
- 10gen/server-security

x509/README

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
This directory contains:
2+
3+
- mkcert.py
4+
Python script; uses the cryptography package to deterministically generate X509 certificates,
5+
CRLs, and digests based on the contents of certs.yml.
6+
- certs.yml
7+
Main certificate definition file.
8+
- apple-certs.yml
9+
Certificate definitions for certs to be installed on provision of OSX machines.
10+
11+
To run:
12+
13+
python -m x509.mkcert [--config CONFIG] [--mkcrl | --no-mkcrl] [-o OUTPUT] [--static-dir STATIC_DIR] [certs ...]
14+
- CONFIG is the path to the certs.yml file specifying a list of certificates, default x509/certs.yml
15+
- OUTPUT is the path to a directory where the generated items will be stored, default .
16+
- STATIC_DIR is the path where signing keys needed by certificates are stored, default x509/static
17+
- If --mkcrl is specified, CRLs will be generated after certificate generation ends. Default false.
18+
These are hardcoded and require certain certificates to be generated to work.
19+
- certs is an optional list of certificate names to generate. If it is not specified, all
20+
certificates specified in the config are generated.
21+
22+
If a certificate specified in certs references other certificates, its dependencies and
23+
subdependencies will be generated.
24+
25+
Deterministic generation is based on the current year. This means that if mkcert is run on the same
26+
definitions file twice, it will produce the same certificates if both runs were in the same year.
27+
One exception to this is the pkcs12 format; the cryptography library does not provide
28+
functionality for generating pkcs12 bundles deterministically.
29+
30+
Future work:
31+
32+
- Define CRLs in the definition file instead of hardcoding them.
33+
- Define keys in the definition file, and make a script to generate all necessary keys which would
34+
be run whenever a new key was defined.
35+
36+
certs.yml format:
37+
38+
global: # Optional, default value to use for Key1 for all certs, overridden by values in cert entries.
39+
Key1: Value1
40+
...
41+
42+
certs:
43+
# Required, this will be used as the name of the file, and for referencing issuers.
44+
- name: 'name-of-cert.pem'
45+
# Required, this will be included in the header of the generated certificate.
46+
description: Tell us about yourself.
47+
# Required, The X509 subject name.
48+
Subject: { C: US, ST: New York, etc... }
49+
# Required, Who is the (intermediate) CA for this certificate. May be 'self'.
50+
Issuer: 'ca.pem'
51+
# Required, relative (within static directory) path to the keyfile to sign this certificate with.
52+
keyfile: 'key.pem'
53+
# Optional, set to true to ignore global.Subject values.
54+
explicit_subject: false
55+
# Optional, serial number to assign this certificate (default: sequential numbers starting from 1000)
56+
serial: 42
57+
# Optional, validity start date, expressed in seconds relative to midnight on the first day of the current year.
58+
not_before: -86400 # 1 day before
59+
# Optional, validity end date, currently expressed in seconds relative to midnight on the first day of the current year.
60+
# Note that not_after - not_before, the validity period, should be less than or equal to 825 days, see:
61+
# https://support.apple.com/en-us/HT210176
62+
not_after: 71107200 # 823 days after
63+
# Optional, IDs of other public keys to append to the file
64+
append_certs: ['ca.pem', 'intermediate-ca.pem', ...]
65+
# Optional, passphrase to encript private key with
66+
passphrase: 'secret'
67+
# Optional, make a pkcs12 copy of the certificate
68+
pkcs12: true | map with keys below
69+
# Optional, all PKCS#12 keys must be encrypted. Will use cert.passphase if not provided.
70+
passphrase: 'secret'
71+
# Optional, name of PKCS#12 version of certificate. If not provided, the original cert will be overwritten with the PKCS#12 version
72+
name: 'name-of-cert.pfx'
73+
# Optional, in addition to the .pem file, write just the certificate to a .crt file and just the signing key to a .key file
74+
split_cert_and_key: true
75+
# Optional, don't write a header comment to this cert
76+
include_header: false
77+
# Optional, X.509 extensions to include in the certificate
78+
extensions: # All extensions are optional.
79+
- basicConstraints: {}
80+
- keyUsage: {}
81+
- extendedKeyUsage: {}
82+
- subjectAltName: {DNS: [...], IP: [...]}
83+
- subjectKeyIdentifier: hash
84+
- authorityKeyIdentifier: keyid | issuer
85+
- authorityInfoAccess:
86+
- method: OCSP
87+
- location: uri-to-OCSP-server
88+
- mustStaple: true
89+
- nsComment: "Comment"
90+
- mongoRoles:
91+
- {role: readWrite, db: test1}
92+
- {role: read, db: test2}
93+
- mongoClusterMembership: clusterName

x509/__init__.py

Whitespace-only changes.

x509/apple-certs.yml

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Definition for testing certificates to be created and installed into the MacOS trusted keychain upon provision.
2+
# On MacOS provision, these certificates will be written to /opt/x509.
3+
4+
global:
5+
Subject:
6+
C: "US"
7+
ST: "New York"
8+
L: "New York City"
9+
O: "MongoDB"
10+
OU: "Kernel"
11+
keyfile: "macos_key.pem"
12+
13+
certs:
14+
- name: "macos-trusted-ca.pem"
15+
description: CA for trusted MacOS client/server certificate chain.
16+
Subject: {CN: "Trusted MacOS Kernel Test CA"}
17+
Issuer: self
18+
keyfile: "macos_ca_key.pem"
19+
extensions:
20+
basicConstraints: {CA: true}
21+
subjectAltName:
22+
DNS: localhost
23+
IP: 127.0.0.1
24+
25+
- name: "macos-trusted-client.pem"
26+
description: Client certificate for trusted MacOS chain.
27+
Subject: {CN: "Trusted MacOS Kernel Test Client"}
28+
Issuer: "macos-trusted-ca.pem"
29+
pkcs12:
30+
passphrase: "qwerty"
31+
name: "macos-trusted-client.pfx"
32+
extensions:
33+
extendedKeyUsage: [clientAuth]
34+
subjectAltName:
35+
DNS: localhost
36+
IP: 127.0.0.1
37+
38+
- name: "macos-trusted-server.pem"
39+
description: Server certificate for trusted MacOS chain.
40+
Subject: {CN: "Trusted MacOS Kernel Test Server"}
41+
Issuer: "macos-trusted-ca.pem"
42+
pkcs12:
43+
passphrase: "qwerty"
44+
name: "macos-trusted-server.pfx"
45+
extensions:
46+
extendedKeyUsage: [serverAuth]
47+
subjectAltName:
48+
DNS: localhost
49+
IP: 127.0.0.1

0 commit comments

Comments
 (0)