Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ docs-app/bin/main.wasm
docs-app/pkg/*.tar.gz

yarn-error.log
.vscode
.vscode
.idea
3 changes: 3 additions & 0 deletions documentation/docs/backend/Backend/Backend.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ new Backend(backendConfiguration)
- Throws a [`RangeError`](../../globals/RangeError/RangeError.mdx) if the value is negative or greater than or equal to 2^32
- `useSSL` _: boolean_ _**optional**_
- Whether or not to require TLS for connections to this backend.
- `checkSSL` _: boolean_ _**optional**_
- Whether or not to validate the server certificate when using TLS.
- Defaults to `true` when using TLS.
- `dontPool` _: boolean_ _**optional**_
- Determine whether or not connections to the same backend should be pooled across different sessions.
- Fastly considers two backends “the same” if they're registered with the same name and the exact same settings.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1968,6 +1968,293 @@ routes.set('/backend/timeout', async () => {
);
}

// checkSSL property
{
routes.set(
'/backend/constructor/parameter-checkSSL-property-valid-boolean',
async () => {
const types = [{}, [], Symbol(), 1, '2'];
for (const type of types) {
assertDoesNotThrow(() => {
new Backend({
name: 'checkSSL-property-valid-boolean-' + String(type),
target: 'a',
checkSSL: type,
});
});
}
assertDoesNotThrow(() => {
new Backend({
name: 'checkSSL-property-valid-boolean-true',
target: 'a',
checkSSL: true,
});
});
assertDoesNotThrow(() => {
new Backend({
name: 'checkSSL-property-valid-boolean-false',
target: 'a',
checkSSL: false,
});
});
},
);

routes.set(
'/backend/constructor/parameter-checkSSL-false-clears-ssl-fields',
async () => {
// When checkSSL: false, certificateHostname, caCertificate, and sniHostname
// are cleared from the config so the backend registers without them.
assertDoesNotThrow(() => {
new Backend({
name: 'checkSSL-false-clears-ssl-fields',
target: 'a',
certificateHostname: 'example.com',
caCertificate: 'cert-content',
sniHostname: 'example.com',
checkSSL: false,
});
});
},
);

routes.set(
'/backend/constructor/parameter-checkSSL-false-with-default-ssl-fields',
async () => {
// checkSSL: false should also clear SSL fields that were set via
// setDefaultDynamicBackendConfig.
setDefaultDynamicBackendConfig({
certificateHostname: 'default-host.com',
caCertificate: 'default-cert',
});
assertDoesNotThrow(() => {
new Backend({
name: 'checkSSL-false-with-default-ssl-fields',
target: 'a',
checkSSL: false,
});
});
},
);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a test that actually hits https://http-me.fastly.dev?


// checkSSL network behavior
{
// Expired certificate - no checkSSL param (thrown exception = success)
routes.set(
'/backend/checkSSL/expired-no-param-throws',
async () => {
if (isRunningLocally()) {
return;
}
const backend = new Backend({
name: 'checkSSL-expired-no-param',
target: 'expired.badssl.com',
useSSL: true,
sniHostname: 'expired.badssl.com',
certificateHostname: 'expired.badssl.com',
});
await assertRejects(async () =>
await fetch('https://expired.badssl.com/', {
backend,
cacheOverride: new CacheOverride('pass'),
}),
);
},
);

// Expired certificate - checkSSL: true (thrown exception = success)
routes.set(
'/backend/checkSSL/expired-true-throws',
async () => {
if (isRunningLocally()) {
return;
}
const backend = new Backend({
name: 'checkSSL-expired-true',
target: 'expired.badssl.com',
useSSL: true,
checkSSL: true,
sniHostname: 'expired.badssl.com',
certificateHostname: 'expired.badssl.com',
});
await assertRejects(async () =>
await fetch('https://expired.badssl.com/', {
backend,
cacheOverride: new CacheOverride('pass'),
}),
);
},
);

// Expired certificate - checkSSL: false (no throw = success)
routes.set(
'/backend/checkSSL/expired-false-does-not-throw',
async () => {
if (isRunningLocally()) {
return;
}
const backend = new Backend({
name: 'checkSSL-expired-false',
target: 'expired.badssl.com',
useSSL: true,
checkSSL: false,
sniHostname: 'expired.badssl.com',
certificateHostname: 'expired.badssl.com',
});
assertDoesNotThrow(async () => {
await fetch('https://expired.badssl.com/', {
backend,
cacheOverride: new CacheOverride('pass'),
});
});
},
);

// Wrong host cert - no checkSSL param (thrown exception = success)
routes.set(
'/backend/checkSSL/wrong-host-no-param-throws',
async () => {
if (isRunningLocally()) {
return;
}
const backend = new Backend({
name: 'checkSSL-wrong-host-no-param',
target: 'wrong.host.badssl.com',
useSSL: true,
sniHostname: 'wrong.host.badssl.com',
certificateHostname: 'wrong.host.badssl.com',
});
await assertRejects(async () =>
await fetch('https://wrong.host.badssl.com/', {
backend,
cacheOverride: new CacheOverride('pass'),
}),
);
},
);

// Wrong host cert - checkSSL: true (thrown exception = success)
routes.set(
'/backend/checkSSL/wrong-host-true-throws',
async () => {
if (isRunningLocally()) {
return;
}
const backend = new Backend({
name: 'checkSSL-wrong-host-true',
target: 'wrong.host.badssl.com',
useSSL: true,
checkSSL: true,
sniHostname: 'wrong.host.badssl.com',
certificateHostname: 'wrong.host.badssl.com',
});
await assertRejects(async () =>
await fetch('https://wrong.host.badssl.com/', {
backend,
cacheOverride: new CacheOverride('pass'),
}),
);
},
);

// Wrong host cert - checkSSL: false (no throw = success)
routes.set(
'/backend/checkSSL/wrong-host-false-does-not-throw',
async () => {
if (isRunningLocally()) {
return;
}
const backend = new Backend({
name: 'checkSSL-wrong-host-false',
target: 'wrong.host.badssl.com',
useSSL: true,
checkSSL: false,
sniHostname: 'wrong.host.badssl.com',
certificateHostname: 'wrong.host.badssl.com',
});
assertDoesNotThrow(async () => {
await fetch('https://wrong.host.badssl.com/', {
backend,
cacheOverride: new CacheOverride('pass'),
});
});
},
);

// Self signed cert - no checkSSL param (thrown exception = success)
routes.set(
'/backend/checkSSL/self-signed-no-param-throws',
async () => {
if (isRunningLocally()) {
return;
}
const backend = new Backend({
name: 'checkSSL-self-signed-no-param',
target: 'self-signed.badssl.com',
useSSL: true,
sniHostname: 'self-signed.badssl.com',
certificateHostname: 'self-signed.badssl.com',
});
await assertRejects(async () =>
await fetch('https://self-signed.badssl.com/', {
backend,
cacheOverride: new CacheOverride('pass'),
}),
);
},
);

// Self signed cert - checkSSL: true (thrown exception = success)
routes.set(
'/backend/checkSSL/self-signed-true-throws',
async () => {
if (isRunningLocally()) {
return;
}
const backend = new Backend({
name: 'checkSSL-self-signed-true',
target: 'self-signed.badssl.com',
useSSL: true,
checkSSL: true,
sniHostname: 'self-signed.badssl.com',
certificateHostname: 'self-signed.badssl.com',
});
await assertRejects(async () =>
await fetch('https://self-signed.badssl.com/', {
backend,
cacheOverride: new CacheOverride('pass'),
}),
);
},
);

// Self signed cert - checkSSL: false (no throw = success)
routes.set(
'/backend/checkSSL/self-signed-false-does-not-throw',
async () => {
if (isRunningLocally()) {
return;
}
const backend = new Backend({
name: 'checkSSL-self-signed-false',
target: 'self-signed.badssl.com',
useSSL: true,
checkSSL: false,
sniHostname: 'self-signed.badssl.com',
certificateHostname: 'self-signed.badssl.com',
});
assertDoesNotThrow(async () => {
await fetch('https://self-signed.badssl.com/', {
backend,
cacheOverride: new CacheOverride('pass'),
});
}
);
},
);
}

// clientCertificate property
{
routes.set(
Expand Down
12 changes: 12 additions & 0 deletions integration-tests/js-compute/fixtures/module-mode/tests.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,18 @@
"GET /backend/constructor/parameter-sniHostname-property-empty-string": {},
"GET /backend/constructor/parameter-sniHostname-property-calls-7.1.17-ToString": {},
"GET /backend/constructor/parameter-sniHostname-property-valid-string": {},
"GET /backend/constructor/parameter-checkSSL-property-valid-boolean": {},
"GET /backend/constructor/parameter-checkSSL-false-clears-ssl-fields": {},
"GET /backend/constructor/parameter-checkSSL-false-with-default-ssl-fields": {},
"GET /backend/checkSSL/expired-no-param-throws": {},
"GET /backend/checkSSL/expired-true-throws": {},
"GET /backend/checkSSL/expired-false-does-not-throw": {},
"GET /backend/checkSSL/wrong-host-no-param-throws": {},
"GET /backend/checkSSL/wrong-host-true-throws": {},
"GET /backend/checkSSL/wrong-host-false-does-not-throw": {},
"GET /backend/checkSSL/self-signed-no-param-throws": {},
"GET /backend/checkSSL/self-signed-true-throws": {},
"GET /backend/checkSSL/self-signed-false-does-not-throw": {},
"GET /backend/constructor/parameter-clientCertificate-property-invalid": {},
"GET /backend/constructor/parameter-clientCertificate-certificate-property-missing": {},
"GET /backend/constructor/parameter-clientCertificate-certificate-property-invalid": {},
Expand Down
15 changes: 15 additions & 0 deletions runtime/fastly/builtins/backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1066,6 +1066,21 @@ bool apply_backend_config(JSContext *cx, host_api::BackendConfig &backend,
}
}

JS::RootedValue check_ssl_val(cx);
if (!JS_HasProperty(cx, configuration, "checkSSL", &found)) {
return false;
}
if (found) {
if (!JS_GetProperty(cx, configuration, "checkSSL", &check_ssl_val)) {
return false;
}
if (!JS::ToBoolean(check_ssl_val)) {
backend.cert_hostname = std::nullopt;
backend.ca_cert = std::nullopt;
backend.sni_hostname = std::nullopt;
}
}

JS::RootedValue client_cert_val(cx);
if (!JS_HasProperty(cx, configuration, "clientCertificate", &found)) {
return false;
Expand Down
9 changes: 9 additions & 0 deletions types/backend.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,15 @@ declare module 'fastly:backend' {
* @throws {TypeError} Throws a TypeError if the value is an empty string.
*/
sniHostname?: string;
/**
* Whether to verify the SSL certificate of the backend.
*
* When set to `false`, the `certificateHostname`, `caCertificate`, and `sniHostname` options
* are ignored and SSL certificate verification is disabled for connections to this backend.
*
* @defaultValue true
*/
checkSSL?: boolean;
/**
* @experimental
*
Expand Down
Loading