Skip to content

Commit 24869d5

Browse files
committed
feat: opt-in DNSLink redirect
In past, it was opt-out via redirect opt-out per hostname. This changes default behavior and disables redirect of DNSLink websites to local gateway, as noted in #701 Adds DNSLink section to Preferences screen. Closes #701, #667
1 parent 0ef6765 commit 24869d5

File tree

8 files changed

+101
-40
lines changed

8 files changed

+101
-40
lines changed

add-on/_locales/en/messages.json

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,18 @@
275275
"message": "Redirect requests for IPFS resources to the Custom gateway",
276276
"description": "An option description on the Preferences screen (option_useCustomGateway_description)"
277277
},
278+
"option_dnslinkRedirect_title": {
279+
"message": "Force load from Custom Gateway",
280+
"description": "An option title on the Preferences screen (option_dnslinkRedirect_title)"
281+
},
282+
"option_dnslinkRedirect_description": {
283+
"message": "If global redirect is enabled, this will include DNSLink websites and redirect them to respective /ipns/{fqdn} paths at Custom Gateway",
284+
"description": "An option description on the Preferences screen (option_dnslinkRedirect_description)"
285+
},
286+
"option_dnslinkRedirect_warning": {
287+
"message": "Redirecting to a path-based gateway breaks Origin-based security isolation of DNSLink website! Please leave this disabled unless you are aware of (and ok with) related risks.",
288+
"description": "A warning on the Preferences screen, displayed when URL does not belong to Secure Context (option_customGatewayUrl_warning)"
289+
},
278290
"option_noRedirectHostnames_title": {
279291
"message": "Redirect Opt-Outs",
280292
"description": "An option title on the Preferences screen (option_noRedirectHostnames_title)"
@@ -327,6 +339,10 @@
327339
"message": "Toggle use of Custom Gateway when IPFS API availability changes",
328340
"description": "An option description on the Preferences screen (option_automaticMode_description)"
329341
},
342+
"option_header_dnslink": {
343+
"message": "DNSLink",
344+
"description": "A section header on the Preferences screen (option_header_dnslink)"
345+
},
330346
"option_header_experiments": {
331347
"message": "Experiments",
332348
"description": "A section header on the Preferences screen (option_header_experiments)"
@@ -364,11 +380,11 @@
364380
"description": "An option description on the Preferences screen (option_linkify_description)"
365381
},
366382
"option_dnslinkPolicy_title": {
367-
"message": "DNSLink Support",
383+
"message": "DNSLink Lookup",
368384
"description": "An option title on the Preferences screen (option_dnslinkPolicy_title)"
369385
},
370386
"option_dnslinkPolicy_description": {
371-
"message": "Select DNS TXT lookup policy to load IPFS hosted sites over IPFS where possible",
387+
"message": "Lookup policy for displaying context actions on websites with DNSLink",
372388
"description": "An option description on the Preferences screen (option_dnslinkPolicy_description)"
373389
},
374390
"option_dnslinkPolicy_disabled": {

add-on/src/lib/ipfs-companion.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,7 @@ module.exports = async function init () {
676676
case 'preloadAtPublicGateway':
677677
case 'openViaWebUI':
678678
case 'noRedirectHostnames':
679+
case 'dnslinkRedirect':
679680
state[key] = change.newValue
680681
break
681682
}

add-on/src/lib/ipfs-request.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ function createRequestModifier (getState, dnslinkResolver, ipfsPathValidator, ru
142142
return redirectToGateway(request.url, state, ipfsPathValidator)
143143
}
144144
// Detect dnslink using heuristics enabled in Preferences
145-
if (state.dnslinkPolicy && dnslinkResolver.canLookupURL(request.url)) {
145+
if (state.dnslinkRedirect && state.dnslinkPolicy && dnslinkResolver.canLookupURL(request.url)) {
146146
const dnslinkRedirect = dnslinkResolver.dnslinkRedirect(request.url)
147147
if (dnslinkRedirect && isSafeToRedirect(request, runtime)) {
148148
// console.log('onBeforeRequest.dnslinkRedirect', dnslinkRedirect)
@@ -339,10 +339,9 @@ function createRequestModifier (getState, dnslinkResolver, ipfsPathValidator, ru
339339
if (header.name.toLowerCase() === 'x-ipfs-path' && isSafeToRedirect(request, runtime)) {
340340
// console.log('onHeadersReceived.request.responseHeaders', request.responseHeaders.length)
341341
const xIpfsPath = header.value
342-
log(`detected x-ipfs-path for ${request.url}: ${xIpfsPath}`)
343342
// First: Check if dnslink heuristic yields any results
344343
// Note: this depends on which dnslink lookup policy is selecten in Preferences
345-
if (state.dnslinkPolicy && dnslinkResolver.canLookupURL(request.url)) {
344+
if (state.dnslinkRedirect && state.dnslinkPolicy && dnslinkResolver.canLookupURL(request.url)) {
346345
// x-ipfs-path is a strong indicator of IPFS support
347346
// so we force dnslink lookup to pre-populate dnslink cache
348347
// in a way that works even when state.dnslinkPolicy !== 'enabled'
@@ -358,7 +357,7 @@ function createRequestModifier (getState, dnslinkResolver, ipfsPathValidator, ru
358357
if (IsIpfs.ipnsPath(xIpfsPath)) {
359358
// Ignore unhandled IPNS path by this point
360359
// (means DNSLink is disabled so we don't want to make a redirect that works like DNSLink)
361-
log(`onHeadersReceived: ignoring x-ipfs-path=${xIpfsPath} (dnslinkPolicy=false or missing DNS TXT record)`)
360+
// log(`onHeadersReceived: ignoring x-ipfs-path=${xIpfsPath} (dnslinkRedirect=false, dnslinkPolicy=false or missing DNS TXT record)`)
362361
} else if (IsIpfs.ipfsPath(xIpfsPath)) {
363362
// It is possible that someone exposed /ipfs/<cid>/ under /
364363
// and our path-based onBeforeRequest heuristics were unable

add-on/src/lib/options.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ exports.optionDefaults = Object.freeze({
1717
automaticMode: true,
1818
linkify: false,
1919
dnslinkPolicy: 'best-effort',
20+
dnslinkRedirect: false,
2021
recoverFailedHttpRequests: true,
2122
detectIpfsPathHeader: true,
2223
preloadAtPublicGateway: true,
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
'use strict'
2+
/* eslint-env browser, webextensions */
3+
4+
const browser = require('webextension-polyfill')
5+
const html = require('choo/html')
6+
const switchToggle = require('../../pages/components/switch-toggle')
7+
8+
function dnslinkForm ({
9+
dnslinkPolicy,
10+
dnslinkRedirect,
11+
onOptionChange
12+
}) {
13+
const onDnslinkPolicyChange = onOptionChange('dnslinkPolicy')
14+
const onDnslinkRedirectChange = onOptionChange('dnslinkRedirect')
15+
16+
return html`
17+
<form>
18+
<fieldset>
19+
<legend>${browser.i18n.getMessage('option_header_dnslink')}</legend>
20+
<div>
21+
<label for="dnslinkPolicy">
22+
<dl>
23+
<dt>${browser.i18n.getMessage('option_dnslinkPolicy_title')}</dt>
24+
<dd>
25+
${browser.i18n.getMessage('option_dnslinkPolicy_description')}
26+
<p><a href="https://github.com/ipfs-shipyard/ipfs-companion/blob/master/docs/dnslink.md#dnslink-support-in-ipfs-companion" target="_blank">
27+
${browser.i18n.getMessage('option_legend_readMore')}
28+
</a></p>
29+
</dd>
30+
</dl>
31+
</label>
32+
<select id="dnslinkPolicy" name='dnslinkPolicy' onchange=${onDnslinkPolicyChange}>
33+
<option
34+
value='false'
35+
selected=${String(dnslinkPolicy) === 'false'}>
36+
${browser.i18n.getMessage('option_dnslinkPolicy_disabled')}
37+
</option>
38+
<option
39+
value='best-effort'
40+
selected=${dnslinkPolicy === 'best-effort'}>
41+
${browser.i18n.getMessage('option_dnslinkPolicy_bestEffort')}
42+
</option>
43+
<option
44+
value='enabled'
45+
selected=${dnslinkPolicy === 'enabled'}>
46+
${browser.i18n.getMessage('option_dnslinkPolicy_enabled')}
47+
</option>
48+
</select>
49+
</div>
50+
<div>
51+
<label for="dnslinkRedirect">
52+
<dl>
53+
<dt>${browser.i18n.getMessage('option_dnslinkRedirect_title')}</dt>
54+
<dd>
55+
${browser.i18n.getMessage('option_dnslinkRedirect_description')}
56+
${dnslinkRedirect ? html`<p class="red i">${browser.i18n.getMessage('option_dnslinkRedirect_warning')}</p>` : null}
57+
<p><a href="https://github.com/ipfs-shipyard/ipfs-companion/issues/667" target="_blank">
58+
${browser.i18n.getMessage('option_legend_readMore')}
59+
</a></p>
60+
</dd>
61+
</dl>
62+
</label>
63+
<div>${switchToggle({ id: 'dnslinkRedirect', checked: dnslinkRedirect, onchange: onDnslinkRedirectChange })}</div>
64+
</div>
65+
</fieldset>
66+
</form>
67+
`
68+
}
69+
70+
module.exports = dnslinkForm

add-on/src/options/forms/experiments-form.js

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ function experimentsForm ({
99
displayNotifications,
1010
catchUnhandledProtocols,
1111
linkify,
12-
dnslinkPolicy,
1312
recoverFailedHttpRequests,
1413
detectIpfsPathHeader,
1514
ipfsProxy,
@@ -20,7 +19,6 @@ function experimentsForm ({
2019
const onDisplayNotificationsChange = onOptionChange('displayNotifications')
2120
const onCatchUnhandledProtocolsChange = onOptionChange('catchUnhandledProtocols')
2221
const onLinkifyChange = onOptionChange('linkify')
23-
const onDnslinkPolicyChange = onOptionChange('dnslinkPolicy')
2422
const onrecoverFailedHttpRequestsChange = onOptionChange('recoverFailedHttpRequests')
2523
const onDetectIpfsPathHeaderChange = onOptionChange('detectIpfsPathHeader')
2624
const onIpfsProxyChange = onOptionChange('ipfsProxy')
@@ -66,36 +64,6 @@ function experimentsForm ({
6664
</label>
6765
<div>${switchToggle({ id: 'linkify', checked: linkify, onchange: onLinkifyChange })}</div>
6866
</div>
69-
<div>
70-
<label for="dnslinkPolicy">
71-
<dl>
72-
<dt>${browser.i18n.getMessage('option_dnslinkPolicy_title')}</dt>
73-
<dd>
74-
${browser.i18n.getMessage('option_dnslinkPolicy_description')}
75-
<p><a href="https://github.com/ipfs-shipyard/ipfs-companion/blob/master/docs/dnslink.md#dnslink-support-in-ipfs-companion" target="_blank">
76-
${browser.i18n.getMessage('option_legend_readMore')}
77-
</a></p>
78-
</dd>
79-
</dl>
80-
</label>
81-
<select id="dnslinkPolicy" name='dnslinkPolicy' onchange=${onDnslinkPolicyChange}>
82-
<option
83-
value='false'
84-
selected=${String(dnslinkPolicy) === 'false'}>
85-
${browser.i18n.getMessage('option_dnslinkPolicy_disabled')}
86-
</option>
87-
<option
88-
value='best-effort'
89-
selected=${dnslinkPolicy === 'best-effort'}>
90-
${browser.i18n.getMessage('option_dnslinkPolicy_bestEffort')}
91-
</option>
92-
<option
93-
value='enabled'
94-
selected=${dnslinkPolicy === 'enabled'}>
95-
${browser.i18n.getMessage('option_dnslinkPolicy_enabled')}
96-
</option>
97-
</select>
98-
</div>
9967
<div>
10068
<label for="detectIpfsPathHeader">
10169
<dl>

add-on/src/options/page.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const html = require('choo/html')
55
const globalToggleForm = require('./forms/global-toggle-form')
66
const ipfsNodeForm = require('./forms/ipfs-node-form')
77
const fileImportForm = require('./forms/file-import-form')
8+
const dnslinkForm = require('./forms/dnslink-form')
89
const gatewaysForm = require('./forms/gateways-form')
910
const apiForm = require('./forms/api-form')
1011
const experimentsForm = require('./forms/experiments-form')
@@ -77,11 +78,15 @@ module.exports = function optionsPage (state, emit) {
7778
preloadAtPublicGateway: state.options.preloadAtPublicGateway,
7879
onOptionChange
7980
})}
81+
${dnslinkForm({
82+
dnslinkPolicy: state.options.dnslinkPolicy,
83+
dnslinkRedirect: state.options.dnslinkRedirect,
84+
onOptionChange
85+
})}
8086
${experimentsForm({
8187
displayNotifications: state.options.displayNotifications,
8288
catchUnhandledProtocols: state.options.catchUnhandledProtocols,
8389
linkify: state.options.linkify,
84-
dnslinkPolicy: state.options.dnslinkPolicy,
8590
recoverFailedHttpRequests: state.options.recoverFailedHttpRequests,
8691
detectIpfsPathHeader: state.options.detectIpfsPathHeader,
8792
ipfsProxy: state.options.ipfsProxy,

test/functional/lib/ipfs-request-dnslink.test.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const fakeRequestId = () => {
2121

2222
// const nodeTypes = ['external', 'embedded']
2323

24-
describe('modifyRequest processing', function () {
24+
describe('modifyRequest processing of DNSLinks', function () {
2525
let state, dnslinkResolver, ipfsPathValidator, modifyRequest, runtime
2626

2727
before(function () {
@@ -34,6 +34,7 @@ describe('modifyRequest processing', function () {
3434
ipfsNodeType: 'external',
3535
peerCount: 1,
3636
redirect: true,
37+
dnslinkRedirect: true, // NOTE: this is opt-in now
3738
catchUnhandledProtocols: true,
3839
gwURLString: 'http://127.0.0.1:8080',
3940
pubGwURLString: 'https://ipfs.io'

0 commit comments

Comments
 (0)