Merge remote-tracking branch 'upstream/master' into hotfix/drupal.conf
commit
89db117a9e
|
@ -107,29 +107,39 @@ export default (domains, global) => {
|
||||||
config.http.push(['ssl_stapling', 'on']);
|
config.http.push(['ssl_stapling', 'on']);
|
||||||
config.http.push(['ssl_stapling_verify', 'on']);
|
config.http.push(['ssl_stapling_verify', 'on']);
|
||||||
|
|
||||||
if (global.https.ocspCloudflare.computed
|
const ips = [];
|
||||||
|| global.https.ocspGoogle.computed
|
if (global.https.ocspCloudflare.computed) {
|
||||||
|| global.https.ocspOpenDns.computed) {
|
if (['ipv4', 'both'].includes(global.https.ocspCloudflareType.computed))
|
||||||
const ips = [];
|
ips.push('1.1.1.1', '1.0.0.1');
|
||||||
if (global.https.ocspCloudflare.computed) {
|
if (['ipv6', 'both'].includes(global.https.ocspCloudflareType.computed))
|
||||||
if (['ipv4', 'both'].includes(global.https.ocspCloudflareType.computed))
|
ips.push('[2606:4700:4700::1111]', '[2606:4700:4700::1001]');
|
||||||
ips.push('1.1.1.1', '1.0.0.1');
|
}
|
||||||
if (['ipv6', 'both'].includes(global.https.ocspCloudflareType.computed))
|
if (global.https.ocspGoogle.computed) {
|
||||||
ips.push('[2606:4700:4700::1111]', '[2606:4700:4700::1001]');
|
if (['ipv4', 'both'].includes(global.https.ocspGoogleType.computed))
|
||||||
}
|
ips.push('8.8.8.8', '8.8.4.4');
|
||||||
if (global.https.ocspGoogle.computed) {
|
if (['ipv6', 'both'].includes(global.https.ocspGoogleType.computed))
|
||||||
if (['ipv4', 'both'].includes(global.https.ocspGoogleType.computed))
|
ips.push('[2001:4860:4860::8888]', '[2001:4860:4860::8844]');
|
||||||
ips.push('8.8.8.8', '8.8.4.4');
|
}
|
||||||
if (['ipv6', 'both'].includes(global.https.ocspGoogleType.computed))
|
if (global.https.ocspOpenDns.computed) {
|
||||||
ips.push('[2001:4860:4860::8888]', '[2001:4860:4860::8844]');
|
if (['ipv4', 'both'].includes(global.https.ocspOpenDnsType.computed))
|
||||||
}
|
ips.push('208.67.222.222', '208.67.220.220');
|
||||||
if (global.https.ocspOpenDns.computed) {
|
if (['ipv6', 'both'].includes(global.https.ocspOpenDnsType.computed))
|
||||||
if (['ipv4', 'both'].includes(global.https.ocspOpenDnsType.computed))
|
ips.push('[2620:119:35::35]', '[2620:119:53::53]');
|
||||||
ips.push('208.67.222.222', '208.67.220.220');
|
}
|
||||||
if (['ipv6', 'both'].includes(global.https.ocspOpenDnsType.computed))
|
if (global.https.ocspQuad9.computed) {
|
||||||
ips.push('[2620:119:35::35]', '[2620:119:53::53]');
|
if (['ipv4', 'both'].includes(global.https.ocspQuad9Type.computed))
|
||||||
}
|
ips.push('9.9.9.9', '149.112.112.112');
|
||||||
|
if (['ipv6', 'both'].includes(global.https.ocspQuad9Type.computed))
|
||||||
|
ips.push('[2620:fe::fe]', '[2620:fe::9]');
|
||||||
|
}
|
||||||
|
if (global.https.ocspVerisign.computed) {
|
||||||
|
if (['ipv4', 'both'].includes(global.https.ocspVerisignType.computed))
|
||||||
|
ips.push('64.6.64.6', '64.6.65.6');
|
||||||
|
if (['ipv6', 'both'].includes(global.https.ocspVerisignType.computed))
|
||||||
|
ips.push('[2620:74:1b::1:1]', '[2620:74:1c::2:2]');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ips.length) {
|
||||||
config.http.push(['resolver', `${ips.join(' ')} valid=60s`]);
|
config.http.push(['resolver', `${ips.join(' ')} valid=60s`]);
|
||||||
config.http.push(['resolver_timeout', '2s']);
|
config.http.push(['resolver_timeout', '2s']);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,8 @@ export default {
|
||||||
cloudflareResolver: 'Cloudflare Resolver',
|
cloudflareResolver: 'Cloudflare Resolver',
|
||||||
googlePublicDns: 'Google Public DNS',
|
googlePublicDns: 'Google Public DNS',
|
||||||
openDns: 'OpenDNS',
|
openDns: 'OpenDNS',
|
||||||
|
quad9: 'Quad9',
|
||||||
|
verisign: 'Verisign',
|
||||||
letsEncryptWebroot: `${common.letsEncrypt} webroot`,
|
letsEncryptWebroot: `${common.letsEncrypt} webroot`,
|
||||||
mozillaModern: `${mozilla} Modern`,
|
mozillaModern: `${mozilla} Modern`,
|
||||||
mozillaIntermediate: `${mozilla} Intermediate`,
|
mozillaIntermediate: `${mozilla} Intermediate`,
|
||||||
|
|
|
@ -70,8 +70,8 @@ limitations under the License.
|
||||||
<div :class="`column ${splitColumn ? 'is-half' : 'is-full'} is-full-mobile is-full-tablet`">
|
<div :class="`column ${splitColumn ? 'is-half' : 'is-full'} is-full-mobile is-full-tablet`">
|
||||||
<h2>{{ i18n.templates.app.configFiles }}</h2>
|
<h2>{{ i18n.templates.app.configFiles }}</h2>
|
||||||
<div ref="files" class="columns is-multiline">
|
<div ref="files" class="columns is-multiline">
|
||||||
<NginxPrism v-for="conf in confFilesOutput"
|
<NginxPrism v-for="(conf, i) in confFilesOutput"
|
||||||
:key="`${conf[0]}-${hash(conf[1])}`"
|
:key="`${conf[0]}-${i}-${hash(conf[1])}`"
|
||||||
:name="`${nginxDir}/${conf[0]}`"
|
:name="`${nginxDir}/${conf[0]}`"
|
||||||
:conf="conf[1]"
|
:conf="conf[1]"
|
||||||
:half="confFilesOutput.length > 1 && !splitColumn"
|
:half="confFilesOutput.length > 1 && !splitColumn"
|
||||||
|
@ -169,7 +169,18 @@ limitations under the License.
|
||||||
return '';
|
return '';
|
||||||
},
|
},
|
||||||
add() {
|
add() {
|
||||||
this.$data.domains.push(clone(Domain.delegated));
|
const data = clone(Domain.delegated);
|
||||||
|
|
||||||
|
// Avoid dupe domains
|
||||||
|
let count = 1;
|
||||||
|
while (this.$data.domains.some(d => d && d.server.domain.computed === data.server.domain.computed)) {
|
||||||
|
count++;
|
||||||
|
data.server.domain.computed = data.server.domain.default.replace('.com', `${count}.com`);
|
||||||
|
}
|
||||||
|
data.server.domain.value = data.server.domain.computed;
|
||||||
|
|
||||||
|
// Store
|
||||||
|
this.$data.domains.push(data);
|
||||||
this.$data.active = this.$data.domains.length - 1;
|
this.$data.active = this.$data.domains.length - 1;
|
||||||
},
|
},
|
||||||
remove(index) {
|
remove(index) {
|
||||||
|
|
|
@ -138,7 +138,7 @@ limitations under the License.
|
||||||
<input v-model="letsEncryptEmail"
|
<input v-model="letsEncryptEmail"
|
||||||
class="input"
|
class="input"
|
||||||
type="text"
|
type="text"
|
||||||
:placeholder="`info@${$parent.$props.data.server.domain.computed}`"
|
:placeholder="$props.data.letsEncryptEmail.computed"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -223,6 +223,7 @@ limitations under the License.
|
||||||
},
|
},
|
||||||
letsEncryptEmail: {
|
letsEncryptEmail: {
|
||||||
default: '',
|
default: '',
|
||||||
|
computed: 'info@example.com', // No default value, but a default computed
|
||||||
enabled: true,
|
enabled: true,
|
||||||
},
|
},
|
||||||
sslCertificate: {
|
sslCertificate: {
|
||||||
|
@ -346,6 +347,23 @@ limitations under the License.
|
||||||
},
|
},
|
||||||
deep: true,
|
deep: true,
|
||||||
},
|
},
|
||||||
|
// Ensure there is a default email for Let's Encrypt
|
||||||
|
'$props.data.letsEncryptEmail': {
|
||||||
|
handler(data) {
|
||||||
|
if (!data.computed.trim()) {
|
||||||
|
data.computed = `info@${this.$parent.$props.data.server.domain.computed}`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
'$parent.$props.data.server.domain': {
|
||||||
|
handler(data) {
|
||||||
|
if (!this.$props.data.letsEncryptEmail.value.trim()) {
|
||||||
|
this.$props.data.letsEncryptEmail.computed = `info@${data.computed}`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -182,15 +182,14 @@ limitations under the License.
|
||||||
i18n,
|
i18n,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: computedFromDefaults(defaults, 'server'), // Getters & setters for the delegated data
|
computed: computedFromDefaults(defaults, 'server'), // Getters & setters for the delegated data
|
||||||
watch: {
|
watch: {
|
||||||
'$props.data.domain': {
|
'$props.data.domain': {
|
||||||
handler(data) {
|
handler(data) {
|
||||||
// This might cause recursion, but seems not to
|
// Ignore www. if given, enable WWW subdomain
|
||||||
|
|
||||||
// Ignore www. if given
|
|
||||||
if (data.computed.startsWith('www.')) {
|
if (data.computed.startsWith('www.')) {
|
||||||
data.computed = data.computed.slice(4);
|
data.computed = data.computed.slice(4);
|
||||||
|
this.wwwSubdomain = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use default if empty
|
// Use default if empty
|
||||||
|
|
|
@ -122,6 +122,48 @@ limitations under the License.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div :class="`control${ocspQuad9Changed ? ' is-changed' : ''}`">
|
||||||
|
<div class="checkbox">
|
||||||
|
<PrettyCheck v-model="ocspQuad9" class="p-default p-curve p-fill p-icon">
|
||||||
|
<i slot="extra" class="icon fas fa-check"></i>
|
||||||
|
{{ i18n.templates.globalSections.https.quad9 }}
|
||||||
|
</PrettyCheck>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="$props.data.ocspQuad9.computed" class="control field is-horizontal is-expanded">
|
||||||
|
<div v-for="(name, value) in $props.data.ocspQuad9Type.options"
|
||||||
|
:class="`control${ocspQuad9TypeChanged && value === ocspQuad9Type ? ' is-changed' : ''}`"
|
||||||
|
>
|
||||||
|
<div class="radio">
|
||||||
|
<PrettyRadio v-model="ocspQuad9Type" :value="value" class="p-default p-round p-fill p-icon">
|
||||||
|
<i slot="extra" class="icon fas fa-check"></i>
|
||||||
|
{{ name }}
|
||||||
|
</PrettyRadio>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div :class="`control${ocspVerisignChanged ? ' is-changed' : ''}`">
|
||||||
|
<div class="checkbox">
|
||||||
|
<PrettyCheck v-model="ocspVerisign" class="p-default p-curve p-fill p-icon">
|
||||||
|
<i slot="extra" class="icon fas fa-check"></i>
|
||||||
|
{{ i18n.templates.globalSections.https.verisign }}
|
||||||
|
</PrettyCheck>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="$props.data.ocspVerisign.computed" class="control field is-horizontal is-expanded">
|
||||||
|
<div v-for="(name, value) in $props.data.ocspVerisignType.options"
|
||||||
|
:class="`control${ocspVerisignTypeChanged && value === ocspVerisignType ? ' is-changed' : ''}`"
|
||||||
|
>
|
||||||
|
<div class="radio">
|
||||||
|
<PrettyRadio v-model="ocspVerisignType" :value="value" class="p-default p-round p-fill p-icon">
|
||||||
|
<i slot="extra" class="icon fas fa-check"></i>
|
||||||
|
{{ name }}
|
||||||
|
</PrettyRadio>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -195,6 +237,16 @@ limitations under the License.
|
||||||
enabled: true,
|
enabled: true,
|
||||||
},
|
},
|
||||||
ocspOpenDnsType: clone(ipType),
|
ocspOpenDnsType: clone(ipType),
|
||||||
|
ocspQuad9: {
|
||||||
|
default: false,
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
ocspQuad9Type: clone(ipType),
|
||||||
|
ocspVerisign: {
|
||||||
|
default: false,
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
ocspVerisignType: clone(ipType),
|
||||||
letsEncryptRoot: {
|
letsEncryptRoot: {
|
||||||
default: '/var/www/_letsencrypt/',
|
default: '/var/www/_letsencrypt/',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
@ -238,6 +290,14 @@ limitations under the License.
|
||||||
handler: validOptionCheck,
|
handler: validOptionCheck,
|
||||||
deep: true,
|
deep: true,
|
||||||
},
|
},
|
||||||
|
'$props.data.ocspQuad9Type': {
|
||||||
|
handler: validOptionCheck,
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
'$props.data.ocspVerisignType': {
|
||||||
|
handler: validOptionCheck,
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
'$parent.$parent.$data.domains': {
|
'$parent.$parent.$data.domains': {
|
||||||
handler(data) {
|
handler(data) {
|
||||||
let httpsEnabled = false, leEnabled = false;
|
let httpsEnabled = false, leEnabled = false;
|
||||||
|
|
|
@ -151,7 +151,7 @@ limitations under the License.
|
||||||
computed: {
|
computed: {
|
||||||
...computedFromDefaults(defaults, 'security'), // Getters & setters for the delegated data
|
...computedFromDefaults(defaults, 'security'), // Getters & setters for the delegated data
|
||||||
hasWordPress() {
|
hasWordPress() {
|
||||||
return this.$parent.$parent.$data.domains.some(d => d.php.wordPressRules.computed);
|
return this.$parent.$parent.$data.domains.some(d => d && d.php.wordPressRules.computed);
|
||||||
},
|
},
|
||||||
hasUnsafeEval() {
|
hasUnsafeEval() {
|
||||||
return this.$props.data.contentSecurityPolicy.computed.includes('\'unsafe-eval\'');
|
return this.$props.data.contentSecurityPolicy.computed.includes('\'unsafe-eval\'');
|
||||||
|
|
|
@ -121,8 +121,8 @@ export default data => {
|
||||||
// If not a global setting and if this is an integer
|
// If not a global setting and if this is an integer
|
||||||
// Then, this is probably an old domain, so we'll try to convert it as such
|
// Then, this is probably an old domain, so we'll try to convert it as such
|
||||||
if (!isNaN(parseInt(key))) {
|
if (!isNaN(parseInt(key))) {
|
||||||
data.domains = data.domains || [];
|
data.domains = isObject(data.domains) ? data.domains : {};
|
||||||
data.domains.push(data[key]);
|
data.domains[key] = data[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,35 +130,35 @@ export default data => {
|
||||||
data.global = {...(data.global || {}), ...mappedGlobal};
|
data.global = {...(data.global || {}), ...mappedGlobal};
|
||||||
|
|
||||||
// Handle converting domain settings
|
// Handle converting domain settings
|
||||||
if ('domains' in data && (Array.isArray(data.domains) || isObject(data.domains))) {
|
if ('domains' in data && isObject(data.domains)) {
|
||||||
// Ensure we're working with an array
|
for (const key in data.domains) {
|
||||||
const values = isObject(data.domains) ? Object.values(data.domains) : data.domains;
|
// Don't include inherited props
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(data.domains, key)) continue;
|
||||||
|
|
||||||
for (let i = 0; i < values.length; i++) {
|
|
||||||
// Check this is an object
|
// Check this is an object
|
||||||
if (!isObject(values[i])) continue;
|
if (!isObject(data.domains[key])) continue;
|
||||||
|
|
||||||
// Hold any mapped data
|
// Hold any mapped data
|
||||||
const mappedData = {};
|
const mappedData = {};
|
||||||
|
|
||||||
// Handle converting old domain settings to new ones
|
// Handle converting old domain settings to new ones
|
||||||
for (const key in values[i]) {
|
for (const key2 in data.domains[key]) {
|
||||||
if (!Object.prototype.hasOwnProperty.call(values[i], key)) continue;
|
// Don't include inherited props
|
||||||
if (isObject(values[i][key])) continue;
|
if (!Object.prototype.hasOwnProperty.call(data.domains[key], key2)) continue;
|
||||||
|
|
||||||
|
// Don't convert objects
|
||||||
|
if (isObject(data.domains[key][key2])) continue;
|
||||||
|
|
||||||
// Map old settings to their new ones
|
// Map old settings to their new ones
|
||||||
if (key in domainMap) {
|
if (key2 in domainMap) {
|
||||||
const map = domainMap[key];
|
const map = domainMap[key2];
|
||||||
mappedData[map[0]] = mappedData[map[0]] || {};
|
mappedData[map[0]] = mappedData[map[0]] || {};
|
||||||
mappedData[map[0]][map[1]] = map.length < 3 ? values[i][key] : map[2](values[i][key]);
|
mappedData[map[0]][map[1]] = map.length < 3 ? data.domains[key][key2] : map[2](data.domains[key][key2]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overwrite mapped properties
|
// Overwrite mapped properties
|
||||||
values[i] = {...values[i], ...mappedData};
|
data.domains[key] = {...data.domains[key], ...mappedData};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the updated domain data
|
|
||||||
data.domains = values;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -54,6 +54,7 @@ export default (query, domains, global, nextTick) => {
|
||||||
const data = qs.parse(query, {
|
const data = qs.parse(query, {
|
||||||
ignoreQueryPrefix: true,
|
ignoreQueryPrefix: true,
|
||||||
allowDots: true,
|
allowDots: true,
|
||||||
|
parseArrays: false,
|
||||||
decoder(value) {
|
decoder(value) {
|
||||||
value = decodeURIComponent(value);
|
value = decodeURIComponent(value);
|
||||||
|
|
||||||
|
@ -78,25 +79,23 @@ export default (query, domains, global, nextTick) => {
|
||||||
backwardsCompatibility(data);
|
backwardsCompatibility(data);
|
||||||
|
|
||||||
// Handle domains
|
// Handle domains
|
||||||
if ('domains' in data) {
|
if ('domains' in data && isObject(data.domains)) {
|
||||||
// Check its an array or object
|
// Work through all valid integer keys in the object
|
||||||
if (Array.isArray(data.domains) || isObject(data.domains)) {
|
const keys = Object.keys(data.domains).map(x => parseInt(x)).filter(x => !isNaN(x));
|
||||||
// Ensure we're working with an array
|
for (let i = 0; i < Math.max(...keys) + 1; i++) {
|
||||||
const values = isObject(data.domains) ? Object.values(data.domains) : data.domains;
|
// If the key doesn't exist or this isn't a valid object, assume it was an untouched example domain
|
||||||
|
if (!keys.includes(i) || !isObject(data.domains[i])) {
|
||||||
// Work through each potential domain
|
domains.push(clone(Domain.delegated));
|
||||||
for (const domainData of values) {
|
continue;
|
||||||
// Check this is an object
|
|
||||||
if (!isObject(domainData)) continue;
|
|
||||||
|
|
||||||
// Create a new domain (assume it has had custom user settings)
|
|
||||||
const domainImported = clone(Domain.delegated);
|
|
||||||
domainImported.hasUserInteraction = true;
|
|
||||||
domains.push(domainImported);
|
|
||||||
|
|
||||||
// Apply the initial values on the next Vue tick, once the watchers are ready
|
|
||||||
nextTick(() => applyCategories(domainData, domainImported));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a new domain (assume it has had custom user settings)
|
||||||
|
const domainImported = clone(Domain.delegated);
|
||||||
|
domainImported.hasUserInteraction = true;
|
||||||
|
domains.push(domainImported);
|
||||||
|
|
||||||
|
// Apply the initial values on the next Vue tick, once the watchers are ready
|
||||||
|
nextTick(() => applyCategories(data.domains[i], domainImported));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// If no configured domains, add a single default
|
// If no configured domains, add a single default
|
||||||
|
|
Loading…
Reference in New Issue