mirror of
https://github.com/paulmillr/encrypted-dns.git
synced 2026-03-31 01:40:26 +02:00
Update readme, add certs dir
This commit is contained in:
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
certs/cert.pem
|
||||
certs/chain.pem
|
||||
certs/fullchain.pem
|
||||
certs/privkey.pem
|
||||
certs/README
|
||||
71
README.md
71
README.md
@@ -2,19 +2,7 @@ English | [简体中文](https://github.com/paulmillr/encrypted-dns/blob/master/
|
||||
|
||||
# encrypted-dns-configs
|
||||
|
||||
Configuration profiles for [DNS over HTTPS](https://en.wikipedia.org/wiki/DNS_over_HTTPS) and [DNS over TLS](https://en.wikipedia.org/wiki/DNS_over_TLS). Check out the article for more info: [paulmillr.com/posts/encrypted-dns/](https://paulmillr.com/posts/encrypted-dns/). To add a new provider, or edit an existing one, edit json files in `src` directory.
|
||||
|
||||
## Known issues
|
||||
|
||||
1. Some apps and protocols will ignore encrypted-dns:
|
||||
- Firefox in specific regions, App Store in all regions. [More info](https://github.com/paulmillr/encrypted-dns/issues/22)
|
||||
- iCloud Private Relay, VPN clients
|
||||
- Little Snitch, LuLu
|
||||
- DNS-related CLI tools: `host`, `dig`, `nslookup` etc.
|
||||
2. [Wi-Fi captive portals](https://en.wikipedia.org/wiki/Captive_portal) in cafes, hotels, airports are exempted by Apple from eDNS rules; to simplify authentication - this is ok
|
||||
3. TLS DNS is easier for providers to block, because it uses non-standard port 853.
|
||||
[More info](https://security.googleblog.com/2022/07/dns-over-http3-in-android.html)
|
||||
4. e-dns over TOR could be better privacy-wise, but we don't have this for now.
|
||||
Configuration profiles for [DNS over HTTPS](https://en.wikipedia.org/wiki/DNS_over_HTTPS) and [DNS over TLS](https://en.wikipedia.org/wiki/DNS_over_TLS). Check out the article for more info: [paulmillr.com/posts/encrypted-dns/](https://paulmillr.com/posts/encrypted-dns/). To add a new provider, or edit an existing one: see [#contributing](#contributing).
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -22,20 +10,20 @@ Install / download profile (`.mobileconfig` file) from a table below. After that
|
||||
|
||||
iPhones, iPads:
|
||||
|
||||
1. Open the mobileconfig file in GitHub by using Safari (other browsers will just download the file and won't ask for installation)
|
||||
1. Open the file by using Safari (other browsers will just download the file and won't ask for installation)
|
||||
2. Tap on "Allow" button. The profile should download.
|
||||
3. Go to **System Settings => General => VPN, DNS & Device Management**, select downloaded profile and tap the "Install" button.
|
||||
|
||||
Mac:
|
||||
|
||||
1. Ensure the downloaded file has proper extension: NAME.mobileconfig, not NAME.mobileconfig.txt.
|
||||
2. Choose Apple menu > System Settings, click Privacy and Security in the sidebar, then click Profiles on the right. (You may need to scroll down.)
|
||||
3. You may be asked to supply your password or other information during installation.
|
||||
4. In the Downloaded section, double-click the profile. Review the profile contents then click Continue, Install or Enroll to install the profile. If an earlier version of a profile is already installed on your Mac, the settings in the updated version replace the previous ones.
|
||||
2. Choose Apple menu > System Settings, click Privacy and Security in the sidebar, then click Profiles on the right.
|
||||
You may need to scroll down. You may be asked to supply your password or other information during installation.
|
||||
3. In the Downloaded section, double-click the profile. Review the profile contents then click Continue, Install or Enroll to install the profile. If an earlier version of a profile is already installed on your Mac, the settings in the updated version replace the previous ones.
|
||||
|
||||
## Providers
|
||||
|
||||
`Censorship=yes` (also known as "filtering") means the profile will not send true information about `hostname=IP` relation for some hosts.
|
||||
Censorship (also known as "filtering") means the profile will not send true information about `hostname=IP` relation for some hosts.
|
||||
|
||||
| Name | Region | Censorship | Notes | Install | Install (unsigned) |
|
||||
| ------------------------------------------------------------------------------------ | ------ | ---------- | --------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
|
||||
@@ -77,17 +65,52 @@ Mac:
|
||||
| [Quad9 Unfiltered][quad9] | 🇨🇭 | No | Operated by Quad9 Foundation. | [HTTPS][quad9-profile-unfiltered-https-signed], [TLS][quad9-profile-unfiltered-tls-signed] | [HTTPS][quad9-profile-unfiltered-https], [TLS][quad9-profile-unfiltered-tls] |
|
||||
| [Tiarap][tiarap] | 🇸🇬 🇺🇸 | Yes | Operated by Tiarap Inc. Blocks ads, tracking, phising & malware | [HTTPS][tiarap-profile-https-signed], [TLS][tiarap-profile-tls-signed] | [HTTPS][tiarap-profile-https], [TLS][tiarap-profile-tls] |
|
||||
|
||||
## Signed Profiles
|
||||
## Known issues
|
||||
|
||||
To verify resolver IPs and hostnames, compare mobileconfig files to their documentation URLs. Internal workings of the profiles are described on [developer.apple.com](https://developer.apple.com/documentation/devicemanagement/dnssettings). In order to verify signed mobileconfigs, you will need to download them to your computer and open them in a text editor, because signing profiles makes GitHub think that they are binary files.
|
||||
1. Some apps and protocols will ignore encrypted-dns:
|
||||
- Firefox in specific regions, App Store in all regions. [More info](https://github.com/paulmillr/encrypted-dns/issues/22)
|
||||
- iCloud Private Relay, VPN clients
|
||||
- Little Snitch, LuLu
|
||||
- DNS-related CLI tools: `host`, `dig`, `nslookup` etc.
|
||||
2. [Wi-Fi captive portals](https://en.wikipedia.org/wiki/Captive_portal) in cafes, hotels, airports are exempted by Apple from eDNS rules; to simplify authentication - this is ok
|
||||
3. TLS DNS is easier for providers to block, because it uses non-standard port 853.
|
||||
[More info](https://security.googleblog.com/2022/07/dns-over-http3-in-android.html)
|
||||
4. e-dns over TOR could be better privacy-wise, but we don't have this for now.
|
||||
|
||||
## On demand activation
|
||||
## Contributing
|
||||
|
||||
You can optionally exclude some trusted Wi-Fi networks where you don't want to use encrypted DNS. To do so, add your SSIDs in the [OnDemandRules](https://github.com/paulmillr/encrypted-dns/blob/master/profiles/template-on-demand.mobileconfig#L22-L38) section inside the `PayloadContent` dictionary of a profile. Note: you can't edit signed profiles.
|
||||
- **To add / edit a profile:** edit json files in `src` directory.
|
||||
- **To verify resolver IPs / hostnames:** compare mobileconfig files to their original websites (open files in a text editor).
|
||||
- Check out [developer.apple.com](https://developer.apple.com/documentation/devicemanagement/dnssettings) for more docs.
|
||||
- **On demand activation:** You can optionally exclude some trusted Wi-Fi networks where you don't want to use encrypted DNS. To do so, add your SSIDs in the [OnDemandRules](https://github.com/paulmillr/encrypted-dns/blob/master/profiles/template-on-demand.mobileconfig#L22-L38) section inside the `PayloadContent` dictionary of a profile.
|
||||
|
||||
## Contributing a new profile
|
||||
### Scripts
|
||||
|
||||
To add a new provider, or edit an existing one, edit json files in `src` directory.
|
||||
- `npm run build` - re-build profiles, signed profiles, READMEs
|
||||
- `npm run sign` - re-sign all profiles (updates `signature` field) using an ECC SSL certificate.
|
||||
- Signing is done using [key-producer](https://github.com/paulmillr/micro-key-producer)
|
||||
- Let's Encrypt free certificates are OK, but [expire in 45 days](https://letsencrypt.org/2026/02/24/rate-limits-45-day-certs).
|
||||
- Expects following files to be present in `certs` subdirectory:
|
||||
|
||||
```
|
||||
`privkey.pem` : the private key for your certificate.
|
||||
`fullchain.pem`: the certificate file used in most server software.
|
||||
`chain.pem` : used for OCSP stapling in Nginx >=1.3.7.
|
||||
`cert.pem`
|
||||
```
|
||||
|
||||
- `npm run new` - interactively creates new profile from CLI options. Can also be ran with flags.
|
||||
- `scripts/new.test.ts` includes CLI snapshot tests and a PTY interactive flow test.
|
||||
- PTY test runs by default; set `NEW_TEST_PTY=0` to opt out.
|
||||
- `node scripts/sign-single.ts --ca cert.pem --priv_key key.pem [--chain chain.pem] path.mobileconfig` - sings single mobileconfig
|
||||
- `node scripts/sign-single-openssl.ts --ca cert.pem --priv_key key.pem [--chain chain.pem] path.mobileconfig` Sign one `.mobileconfig` using OpenSSL.
|
||||
- Uses `-nosmimecap` to match local CMS signing policy.
|
||||
- `node scripts/detach.ts signed.mobileconfig` - detach CMS signature from signed profile and print PEM to stdout.
|
||||
- `node test/sign-single.test.ts` - Parity check for `sign-single.ts` vs `sign-single-openssl.sh`.
|
||||
- Runs under `npm run test`.
|
||||
- Generates temporary test root/signer certificates and keys via OpenSSL.
|
||||
- Signs the same profile with `scripts/sign.ts` and `scripts/sign_openssl.sh`.
|
||||
- Verifies detached content and embedded certificate set parity.
|
||||
|
||||
[360-dns]: https://sdns.360.net/dnsPublic.html
|
||||
[360-dns-profile-https]: https://github.com/paulmillr/encrypted-dns/raw/master/profiles/360-https.mobileconfig
|
||||
|
||||
0
certs/.gitkeep
Normal file
0
certs/.gitkeep
Normal file
275
generate.js
275
generate.js
@@ -1,275 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const LANGUAGES_DIR = path.join(__dirname, 'src-languages');
|
||||
const PROVIDERS_PATH = path.join(__dirname, 'src');
|
||||
const DEFAULT_LANG = 'en';
|
||||
const OUTPUT_DIR = __dirname;
|
||||
const REPO_RAW = 'https://github.com/paulmillr/encrypted-dns/raw/master';
|
||||
|
||||
const REGIONS = {
|
||||
US: '🇺🇸',
|
||||
CN: '🇨🇳',
|
||||
RU: '🇷🇺',
|
||||
NL: '🇳🇱',
|
||||
DE: '🇩🇪',
|
||||
SG: '🇸🇬',
|
||||
CA: '🇨🇦',
|
||||
FR: '🇫🇷',
|
||||
CH: '🇨🇭',
|
||||
SE: '🇸🇪',
|
||||
CZ: '🇨🇿',
|
||||
};
|
||||
|
||||
const providerFile = (p, https, signed) => {
|
||||
const postfix = (p, https) => {
|
||||
if (https) {
|
||||
if (p.doh && p.doh !== true) return `doh${p.doh}`;
|
||||
if (p.doh) return 'doh';
|
||||
return 'https';
|
||||
} else {
|
||||
if (p.doh && p.doh !== true) return `dot${p.doh}`;
|
||||
if (p.doh) return 'dot';
|
||||
return 'tls';
|
||||
}
|
||||
};
|
||||
const name = p.name || p.id;
|
||||
return `${signed ? 'signed' : 'profiles'}/${name}-${postfix(p, https)}.mobileconfig`;
|
||||
};
|
||||
|
||||
const languages = fs
|
||||
.readdirSync(LANGUAGES_DIR)
|
||||
.filter((f) => f.endsWith('.json'))
|
||||
.sort()
|
||||
.map((f) => {
|
||||
const p = path.join(LANGUAGES_DIR, f);
|
||||
const data = JSON.parse(fs.readFileSync(p, 'utf8'));
|
||||
return {
|
||||
code: data.code,
|
||||
name: data.name,
|
||||
mdFile: p.replace('.json', '.md'),
|
||||
jsonFile: p,
|
||||
data: data,
|
||||
};
|
||||
});
|
||||
|
||||
const providers = fs
|
||||
.readdirSync(PROVIDERS_PATH)
|
||||
.sort()
|
||||
.map((i) => JSON.parse(fs.readFileSync(`${PROVIDERS_PATH}/${i}`)))
|
||||
.map((i) => {
|
||||
const unsigned = {
|
||||
https: !!i.https || fs.existsSync(providerFile(i, true)),
|
||||
tls: !!i.tls || fs.existsSync(providerFile(i, false)),
|
||||
};
|
||||
const signed = {
|
||||
https: unsigned.https && fs.existsSync(providerFile(i, true, true)),
|
||||
tls: unsigned.tls && fs.existsSync(providerFile(i, false, true)),
|
||||
};
|
||||
return { ...i, formats: { unsigned, signed } };
|
||||
});
|
||||
|
||||
const FULLWIDTH_PATTERN =
|
||||
/[\u1100-\u115F\u2329\u232A\u2E80-\u303E\u3040-\uA4CF\uAC00-\uD7A3\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE6F\uFF00-\uFF60\uFFE0-\uFFE6]/u;
|
||||
function chrWidth(str) {
|
||||
let width = 0;
|
||||
for (const char of str) width += FULLWIDTH_PATTERN.test(char) || REGIONS[char] ? 2 : 1;
|
||||
return width;
|
||||
}
|
||||
const padEnd = (s, len, chr) => `${s}${chr.repeat(Math.max(0, len - chrWidth(s)))}`;
|
||||
|
||||
const genTable = (rows) => {
|
||||
// first row is header
|
||||
const widths = rows[0].map((i) => 0);
|
||||
// Collect cell widths
|
||||
for (const r of rows) {
|
||||
for (let i = 0; i < r.length; i++) widths[i] = Math.max(widths[i], chrWidth(r[i]));
|
||||
}
|
||||
let table = '';
|
||||
rows.forEach((r, i) => {
|
||||
const cells = r.map((c, j) => padEnd(c, widths[j], ' ')).join(' | ');
|
||||
table += `| ${cells} |\n`;
|
||||
if (i === 0) table += `| ${r.map((c, j) => padEnd('', widths[j], '-')).join(' | ')} |\n`;
|
||||
});
|
||||
return table;
|
||||
};
|
||||
|
||||
const TAGS = {
|
||||
// Language selection header
|
||||
LANGUAGES: (currentLang) => {
|
||||
return languages
|
||||
.map((lang) => {
|
||||
if (lang.code === currentLang.code) return lang.name;
|
||||
return `[${lang.name}](https://github.com/paulmillr/encrypted-dns/${
|
||||
lang.code === DEFAULT_LANG ? '' : `blob/master/README.${lang.code}.md`
|
||||
})`;
|
||||
})
|
||||
.join(' | ');
|
||||
},
|
||||
PROVIDERS_TABLE: (currentLang) => {
|
||||
const rows = [
|
||||
// header
|
||||
[
|
||||
currentLang.data.table_columns.name,
|
||||
currentLang.data.table_columns.region,
|
||||
currentLang.data.table_columns.censorship,
|
||||
currentLang.data.table_columns.notes,
|
||||
currentLang.data.table_columns.install_signed,
|
||||
currentLang.data.table_columns.install_unsigned,
|
||||
],
|
||||
];
|
||||
const sorted = Array.from(providers).sort((a, b) => a.id.localeCompare(b.id));
|
||||
for (const provider of sorted) {
|
||||
const name = provider.names[currentLang.code] || provider.names[DEFAULT_LANG];
|
||||
const note = provider.notes[currentLang.code] || provider.notes[DEFAULT_LANG];
|
||||
const censorship = provider.censorship ? currentLang.data.yes : currentLang.data.no;
|
||||
const regionEmoji = (Array.isArray(provider.region) ? provider.region : [provider.region])
|
||||
.map((r) => REGIONS[r])
|
||||
.join(' ');
|
||||
const unsignedLinks = [];
|
||||
if (provider.formats.unsigned && provider.formats.unsigned.https)
|
||||
unsignedLinks.push(`[HTTPS][${provider.profile}-https]`);
|
||||
if (provider.formats.unsigned && provider.formats.unsigned.tls)
|
||||
unsignedLinks.push(`[TLS][${provider.profile}-tls]`);
|
||||
const signedLinks = [];
|
||||
if (provider.formats.signed && provider.formats.signed.https)
|
||||
signedLinks.push(`[HTTPS][${provider.profile}-https-signed]`);
|
||||
if (provider.formats.signed && provider.formats.signed.tls)
|
||||
signedLinks.push(`[TLS][${provider.profile}-tls-signed]`);
|
||||
rows.push([
|
||||
`[${name}][${provider.id}]`,
|
||||
regionEmoji,
|
||||
censorship,
|
||||
note,
|
||||
signedLinks.join(', '),
|
||||
unsignedLinks.join(', '),
|
||||
]);
|
||||
}
|
||||
return genTable(rows).trim();
|
||||
},
|
||||
PROVIDERS_LINKS: (currentLang) => {
|
||||
let res = '';
|
||||
const addLink = (p, https, signed) => {
|
||||
const file = providerFile(p, https, signed);
|
||||
if (!fs.existsSync(`./${file}`)) throw new Error('missing: ' + file);
|
||||
res += `[${p.profile}-${https ? 'https' : 'tls'}${
|
||||
signed ? '-signed' : ''
|
||||
}]: ${REPO_RAW}/${file}\n`;
|
||||
};
|
||||
for (const p of providers) {
|
||||
if (p.website) res += `[${p.id}]: ${p.website}\n`;
|
||||
if (p.formats.unsigned.https) addLink(p, true);
|
||||
if (p.formats.unsigned.tls) addLink(p, false);
|
||||
}
|
||||
// signed
|
||||
for (const p of providers) {
|
||||
if (p.formats.signed.https) addLink(p, true, true);
|
||||
if (p.formats.signed.tls) addLink(p, false, true);
|
||||
}
|
||||
return res;
|
||||
},
|
||||
};
|
||||
|
||||
function processTemplate(templateContent, lang) {
|
||||
let content = templateContent;
|
||||
for (const [tag, handler] of Object.entries(TAGS)) {
|
||||
const tagPattern = new RegExp(`<%${tag}%>`, 'g');
|
||||
if (content.match(tagPattern)) content = content.replace(tagPattern, handler(lang));
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
function generateReadmes() {
|
||||
for (const lang of languages) {
|
||||
if (!fs.existsSync(lang.mdFile)) throw new Error(`Template file not found: ${lang.mdFile}`);
|
||||
const tpl = fs.readFileSync(lang.mdFile, 'utf8');
|
||||
const processed = processTemplate(tpl, lang);
|
||||
const out = lang.code === DEFAULT_LANG ? 'README.md' : `README.${lang.code}.md`;
|
||||
fs.writeFileSync(path.join(OUTPUT_DIR, out), processed, 'utf8');
|
||||
console.log(`Generated ${out}`);
|
||||
}
|
||||
}
|
||||
|
||||
function generateSingle(x) {
|
||||
return `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>PayloadContent</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>DNSSettings</key>
|
||||
<dict>
|
||||
<key>DNSProtocol</key>
|
||||
<string>${x.DNSProtocol}</string>
|
||||
<key>ServerAddresses</key>
|
||||
<array>
|
||||
${x.ServerAddresses.map((i) => `\t\t\t\t\t<string>${i}</string>`).join('\n')}
|
||||
</array>
|
||||
<key>${!x.ServerURLOrName.startsWith('https://') ? 'ServerName' : 'ServerURL'}</key>
|
||||
<string>${x.ServerURLOrName}</string>
|
||||
</dict>
|
||||
<key>PayloadDescription</key>
|
||||
<string>Configures device to use ${x.name}</string>
|
||||
<key>PayloadDisplayName</key>
|
||||
<string>${x.PayloadDisplayName}</string>
|
||||
<key>PayloadIdentifier</key>
|
||||
<string>${x.PayloadIdentifier}</string>
|
||||
<key>PayloadType</key>
|
||||
<string>com.apple.dnsSettings.managed</string>
|
||||
<key>PayloadUUID</key>
|
||||
<string>${x.PayloadUUID}</string>
|
||||
<key>PayloadVersion</key>
|
||||
<integer>1</integer>
|
||||
<key>ProhibitDisablement</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</array>
|
||||
<key>PayloadDescription</key>
|
||||
<string>Adds the ${x.fullName} to Big Sur and iOS 14 based systems</string>
|
||||
<key>PayloadDisplayName</key>
|
||||
<string>${x.topName}</string>
|
||||
<key>PayloadIdentifier</key>
|
||||
<string>com.paulmillr.apple-dns</string>
|
||||
<key>PayloadRemovalDisallowed</key>
|
||||
<false/>
|
||||
<key>PayloadScope</key>
|
||||
<string>System</string>
|
||||
<key>PayloadType</key>
|
||||
<string>Configuration</string>
|
||||
<key>PayloadUUID</key>
|
||||
<string>${x.TopPayloadUUID}</string>
|
||||
<key>PayloadVersion</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</plist>
|
||||
`;
|
||||
}
|
||||
|
||||
function generateConfigs() {
|
||||
function generate(file, parsed) {
|
||||
if (!parsed) return;
|
||||
fs.writeFileSync(file, generateSingle(parsed));
|
||||
console.log(`Generated ${file}`);
|
||||
}
|
||||
for (const p of providers) {
|
||||
if (p.formats.unsigned.https) generate(providerFile(p, true), p.https);
|
||||
if (p.formats.unsigned.tls) generate(providerFile(p, false), p.tls);
|
||||
}
|
||||
}
|
||||
// Small utility to rewrite config structure
|
||||
function patchConfigs() {
|
||||
for (const f of fs.readdirSync(`./src/`)) {
|
||||
const path = `./src/${f}`;
|
||||
const json = JSON.parse(fs.readFileSync(path, 'utf8'));
|
||||
fs.writeFileSync(path, JSON.stringify(json, null, 4));
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
//patchConfigs();
|
||||
generateReadmes();
|
||||
generateConfigs();
|
||||
}
|
||||
main();
|
||||
@@ -4,8 +4,10 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build:clean": "rm -f signed/*.mobileconfig profiles/*.mobileconfig README*.md",
|
||||
"build": "npm run build:clean && node scripts/generate.ts",
|
||||
"test": "node --experimental-strip-types --test scripts/new.test.ts scripts/sign.test.ts",
|
||||
"build": "npm run build:clean && node scripts/build.ts",
|
||||
"sign": "node scripts/sign.ts",
|
||||
"new": "node scripts/new.ts",
|
||||
"test": "node --experimental-strip-types --test scripts/new.test.ts scripts/sign-single.test.ts",
|
||||
"format": "prettier --write \"src/*.json\" scripts/*.ts *.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -2,7 +2,30 @@
|
||||
|
||||
# encrypted-dns-configs
|
||||
|
||||
Configuration profiles for [DNS over HTTPS](https://en.wikipedia.org/wiki/DNS_over_HTTPS) and [DNS over TLS](https://en.wikipedia.org/wiki/DNS_over_TLS). Check out the article for more info: [paulmillr.com/posts/encrypted-dns/](https://paulmillr.com/posts/encrypted-dns/). To add a new provider, or edit an existing one, edit json files in `src` directory.
|
||||
Configuration profiles for [DNS over HTTPS](https://en.wikipedia.org/wiki/DNS_over_HTTPS) and [DNS over TLS](https://en.wikipedia.org/wiki/DNS_over_TLS). Check out the article for more info: [paulmillr.com/posts/encrypted-dns/](https://paulmillr.com/posts/encrypted-dns/). To add a new provider, or edit an existing one: see [#contributing](#contributing).
|
||||
|
||||
## Usage
|
||||
|
||||
Install / download profile (`.mobileconfig` file) from a table below. After that:
|
||||
|
||||
iPhones, iPads:
|
||||
|
||||
1. Open the file by using Safari (other browsers will just download the file and won't ask for installation)
|
||||
2. Tap on "Allow" button. The profile should download.
|
||||
3. Go to **System Settings => General => VPN, DNS & Device Management**, select downloaded profile and tap the "Install" button.
|
||||
|
||||
Mac:
|
||||
|
||||
1. Ensure the downloaded file has proper extension: NAME.mobileconfig, not NAME.mobileconfig.txt.
|
||||
2. Choose Apple menu > System Settings, click Privacy and Security in the sidebar, then click Profiles on the right.
|
||||
You may need to scroll down. You may be asked to supply your password or other information during installation.
|
||||
3. In the Downloaded section, double-click the profile. Review the profile contents then click Continue, Install or Enroll to install the profile. If an earlier version of a profile is already installed on your Mac, the settings in the updated version replace the previous ones.
|
||||
|
||||
## Providers
|
||||
|
||||
Censorship (also known as "filtering") means the profile will not send true information about `hostname=IP` relation for some hosts.
|
||||
|
||||
<%PROVIDERS_TABLE%>
|
||||
|
||||
## Known issues
|
||||
|
||||
@@ -16,39 +39,39 @@ Configuration profiles for [DNS over HTTPS](https://en.wikipedia.org/wiki/DNS_ov
|
||||
[More info](https://security.googleblog.com/2022/07/dns-over-http3-in-android.html)
|
||||
4. e-dns over TOR could be better privacy-wise, but we don't have this for now.
|
||||
|
||||
## Usage
|
||||
## Contributing
|
||||
|
||||
Install / download profile (`.mobileconfig` file) from a table below. After that:
|
||||
- **To add / edit a profile:** edit json files in `src` directory.
|
||||
- **To verify resolver IPs / hostnames:** compare mobileconfig files to their original websites (open files in a text editor).
|
||||
- Check out [developer.apple.com](https://developer.apple.com/documentation/devicemanagement/dnssettings) for more docs.
|
||||
- **On demand activation:** You can optionally exclude some trusted Wi-Fi networks where you don't want to use encrypted DNS. To do so, add your SSIDs in the [OnDemandRules](https://github.com/paulmillr/encrypted-dns/blob/master/profiles/template-on-demand.mobileconfig#L22-L38) section inside the `PayloadContent` dictionary of a profile.
|
||||
|
||||
iPhones, iPads:
|
||||
### Scripts
|
||||
|
||||
1. Open the mobileconfig file in GitHub by using Safari (other browsers will just download the file and won't ask for installation)
|
||||
2. Tap on "Allow" button. The profile should download.
|
||||
3. Go to **System Settings => General => VPN, DNS & Device Management**, select downloaded profile and tap the "Install" button.
|
||||
- `npm run build` - re-build profiles, signed profiles, READMEs
|
||||
- `npm run sign` - re-sign all profiles (updates `signature` field) using an ECC SSL certificate.
|
||||
- Signing is done using [key-producer](https://github.com/paulmillr/micro-key-producer)
|
||||
- Let's Encrypt free certificates are OK, but [expire in 45 days](https://letsencrypt.org/2026/02/24/rate-limits-45-day-certs).
|
||||
- Expects following files to be present in `certs` subdirectory:
|
||||
|
||||
Mac:
|
||||
```
|
||||
`privkey.pem` : the private key for your certificate.
|
||||
`fullchain.pem`: the certificate file used in most server software.
|
||||
`chain.pem` : used for OCSP stapling in Nginx >=1.3.7.
|
||||
`cert.pem`
|
||||
```
|
||||
|
||||
1. Ensure the downloaded file has proper extension: NAME.mobileconfig, not NAME.mobileconfig.txt.
|
||||
2. Choose Apple menu > System Settings, click Privacy and Security in the sidebar, then click Profiles on the right. (You may need to scroll down.)
|
||||
3. You may be asked to supply your password or other information during installation.
|
||||
4. In the Downloaded section, double-click the profile. Review the profile contents then click Continue, Install or Enroll to install the profile. If an earlier version of a profile is already installed on your Mac, the settings in the updated version replace the previous ones.
|
||||
|
||||
## Providers
|
||||
|
||||
`Censorship=yes` (also known as "filtering") means the profile will not send true information about `hostname=IP` relation for some hosts.
|
||||
|
||||
<%PROVIDERS_TABLE%>
|
||||
|
||||
## Signed Profiles
|
||||
|
||||
To verify resolver IPs and hostnames, compare mobileconfig files to their documentation URLs. Internal workings of the profiles are described on [developer.apple.com](https://developer.apple.com/documentation/devicemanagement/dnssettings). In order to verify signed mobileconfigs, you will need to download them to your computer and open them in a text editor, because signing profiles makes GitHub think that they are binary files.
|
||||
|
||||
## On demand activation
|
||||
|
||||
You can optionally exclude some trusted Wi-Fi networks where you don't want to use encrypted DNS. To do so, add your SSIDs in the [OnDemandRules](https://github.com/paulmillr/encrypted-dns/blob/master/profiles/template-on-demand.mobileconfig#L22-L38) section inside the `PayloadContent` dictionary of a profile. Note: you can't edit signed profiles.
|
||||
|
||||
## Contributing a new profile
|
||||
|
||||
To add a new provider, or edit an existing one, edit json files in `src` directory.
|
||||
- `npm run new` - interactively creates new profile from CLI options. Can also be ran with flags.
|
||||
- `scripts/new.test.ts` includes CLI snapshot tests and a PTY interactive flow test.
|
||||
- PTY test runs by default; set `NEW_TEST_PTY=0` to opt out.
|
||||
- `node scripts/sign-single.ts --ca cert.pem --priv_key key.pem [--chain chain.pem] path.mobileconfig` - sings single mobileconfig
|
||||
- `node scripts/sign-single-openssl.ts --ca cert.pem --priv_key key.pem [--chain chain.pem] path.mobileconfig` Sign one `.mobileconfig` using OpenSSL.
|
||||
- Uses `-nosmimecap` to match local CMS signing policy.
|
||||
- `node scripts/detach.ts signed.mobileconfig` - detach CMS signature from signed profile and print PEM to stdout.
|
||||
- `node test/sign-single.test.ts` - Parity check for `sign-single.ts` vs `sign-single-openssl.sh`.
|
||||
- Runs under `npm run test`.
|
||||
- Generates temporary test root/signer certificates and keys via OpenSSL.
|
||||
- Signs the same profile with `scripts/sign.ts` and `scripts/sign_openssl.sh`.
|
||||
- Verifies detached content and embedded certificate set parity.
|
||||
|
||||
<%PROVIDERS_LINKS%>
|
||||
|
||||
Reference in New Issue
Block a user