Skip to content

Commit 2233baa

Browse files
Cornel-CravetaJenkins
authored andcommitted
Let relays verify the site certificate during registration
When a relay joins a Checkmk site it can now confirm it is really talking to that site, instead of trusting whatever certificate is presented during setup. The site provides the expected certificate fingerprint as part of the install command, and the relay checks it before registering. Administrators using the Setup GUI get this automatically; the previous "trust on first use" behavior remains available where a fingerprint cannot be used. Change-Id: Ied703e3f5433c6cbc3de4f7c96c161f586eaac5f JIRA-Ref: CMK-35793
1 parent a694566 commit 2233baa

7 files changed

Lines changed: 66 additions & 2 deletions

File tree

.werks/19542.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
[//]: # (werk v3)
2+
# Establish relay trust explicitly via the site certificate fingerprint
3+
4+
key | value
5+
---------- | ---
6+
date | 2026-07-14T09:33:09.144244+00:00
7+
version | 3.0.0b1
8+
class | feature
9+
edition | ultimate
10+
component | relay
11+
level | 2
12+
compatible | yes
13+
14+
When a relay registers with a Checkmk site, it connects to the site's agent
15+
receiver over TLS. Until now the relay accepted whatever certificate the site
16+
presented during registration (trust on first use), with no way to confirm out
17+
of band that it was really talking to the intended site, leaving registration
18+
open to a man-in-the-middle.
19+
20+
Relay registration now supports establishing that trust explicitly.
21+
`cmk-relay register` accepts a new `--cert-fingerprint` option that checks the
22+
presented certificate against a known SHA-256 fingerprint and stops
23+
registration if it does not match. The fingerprint is delivered through a
24+
trusted channel: the relay setup in the Setup GUI computes it from the site
25+
certificate and includes it in the generated Linux installation command, so
26+
these installs pin the certificate automatically with nothing extra to
27+
configure.
28+
29+
`--cert-fingerprint` is mutually exclusive with the existing `--trust-cert`
30+
option, which keeps the previous "accept the presented certificate" behavior
31+
for cases where a fingerprint cannot be used (for example, when a
32+
TLS-terminating proxy in front of the site presents a different certificate).
33+
The interactive confirmation prompt, which shows the certificate fingerprint
34+
before accepting it, is unchanged.

module_layers.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,7 @@ excludes = ["cmk.gui.nonfree.pro.plugins", "cmk.gui.plugins"]
929929
[components."cmk.gui.nonfree.ultimate"]
930930
allows = [
931931
"@ccc",
932+
"@crypto",
932933
"@livestatus_client",
933934
"@metric_backend",
934935
"@plugin_apis",

packages/cmk-frontend-vue/src/mode-relay/ModeCreateRelayApp.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ const openRelayOverviewPage = () => {
8585
:domain="props.domain"
8686
:agent-receiver-port="props.agent_receiver_port"
8787
:site-version="props.site_version"
88+
:cert-fingerprint="props.cert_fingerprint"
8889
:index="4"
8990
:is-completed="() => currentStep > 4"
9091
/>

packages/cmk-frontend-vue/src/mode-relay/add-relay-configuration-steps/ExecuteInstallationScript.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ const props = defineProps<
3535
domain: string
3636
agentReceiverPort: number
3737
siteVersion: string
38+
certFingerprint: string
3839
}
3940
>()
4041
@@ -49,6 +50,7 @@ const installCommand = computed(() => {
4950
` --initial-tag-version ${props.siteVersion} \\`,
5051
` --target-server ${props.domain}:${props.agentReceiverPort} \\`,
5152
` --target-site-name ${props.siteName} \\`,
53+
` --cert-fingerprint ${escapeShellArg(props.certFingerprint)} \\`,
5254
` --token ${token}`
5355
].join('\n')
5456
})

packages/cmk-frontend-vue/tests/mode-relay/ModeCreateRelayApp.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const mockProps: CreateRelay = {
1616
documentation: 'https://docs.checkmk.com/relay'
1717
},
1818
site_name: 'test_site',
19+
cert_fingerprint: 'AB:CD:EF:00:11',
1920
domain: 'localhost',
2021
agent_receiver_port: 8000,
2122
site_version: '2.5.0',

packages/cmk-frontend-vue/tests/mode-relay/add-relay-configuration-steps/ExecuteInstallationScript.test.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ const baseProps = {
1818
siteName: 'my_site',
1919
domain: 'checkmk.example.com',
2020
agentReceiverPort: 8000,
21-
siteVersion: '2.5.0'
21+
siteVersion: '2.5.0',
22+
certFingerprint: 'AB:CD:EF:00:11'
2223
}
2324

2425
const mockTokenResponse = {
@@ -83,6 +84,19 @@ describe('ExecuteInstallationScript', () => {
8384
expect(cmd).toContain('mock-token-abc')
8485
})
8586

87+
test('install command pins the certificate fingerprint', async () => {
88+
vi.spyOn(Api.prototype, 'post').mockResolvedValue(mockTokenResponse)
89+
mountWithWizardContext(ExecuteInstallationScript, baseProps)
90+
91+
await fireEvent.click(screen.getByRole('button', { name: /generate one-time token/i }))
92+
await screen.findByText(/This token remains valid for/)
93+
94+
const cmd = screen.getByTestId('run-relay-install-script').textContent ?? ''
95+
expect(cmd).toContain('--cert-fingerprint')
96+
expect(cmd).toContain('AB:CD:EF:00:11')
97+
expect(cmd).not.toContain('--trust-cert')
98+
})
99+
86100
test('Next button is blocked until a valid token is generated', async () => {
87101
const { navigation } = mountWithWizardContext(ExecuteInstallationScript, baseProps)
88102

packages/cmk-shared-typing/source/create_relay.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
"site_name": {
2222
"type": "string"
2323
},
24+
"cert_fingerprint": {
25+
"type": "string"
26+
},
2427
"domain": {
2528
"type": "string"
2629
},
@@ -40,5 +43,13 @@
4043
"type": ["integer", "null"]
4144
}
4245
},
43-
"required": ["urls", "site_name", "domain", "agent_receiver_port", "site_version", "supported_os"]
46+
"required": [
47+
"urls",
48+
"site_name",
49+
"cert_fingerprint",
50+
"domain",
51+
"agent_receiver_port",
52+
"site_version",
53+
"supported_os"
54+
]
4455
}

0 commit comments

Comments
 (0)