Add SSL, certbot & go live setup steps
parent
7584d018f6
commit
2809d5fa7a
|
@ -81,10 +81,9 @@ $highlight: #f2c94c;
|
|||
.panel {
|
||||
margin-top: 0;
|
||||
padding: 1.5rem 0 2rem;
|
||||
text-align: left;
|
||||
|
||||
&.presets {
|
||||
text-align: left;
|
||||
|
||||
.header-group,
|
||||
.buttons-group {
|
||||
display: flex;
|
||||
|
@ -115,21 +114,20 @@ $highlight: #f2c94c;
|
|||
}
|
||||
|
||||
&.setup {
|
||||
ol {
|
||||
p {
|
||||
color: $dark-blue;
|
||||
overflow-wrap: break-word;
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
ol {
|
||||
margin: 0 1rem;
|
||||
text-align: left;
|
||||
|
||||
li {
|
||||
margin: 0 0 1.5rem;
|
||||
|
||||
p {
|
||||
overflow-wrap: break-word;
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,77 +1,94 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="field is-horizontal is-aligned-top">
|
||||
<div v-if="!sslProfileEnabled" class="field is-horizontal is-aligned-top">
|
||||
<div class="field-label">
|
||||
<label class="label">SSL profile</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<label class="text">
|
||||
HTTPS must be enabled on at least one site to configure global HTTPS settings.
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template v-else>
|
||||
<div class="field is-horizontal is-aligned-top">
|
||||
<div class="field-label">
|
||||
<label class="label">SSL profile</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<div v-for="(name, value) in $props.data.sslProfile.options"
|
||||
:class="`control${sslProfileChanged && value === sslProfile ? ' is-changed' : ''}`"
|
||||
>
|
||||
<div class="radio">
|
||||
<PrettyRadio v-model="sslProfile" :value="value" class="p-default p-round p-fill p-icon">
|
||||
<i slot="extra" class="icon fas fa-check"></i>
|
||||
{{ name }}
|
||||
</PrettyRadio>
|
||||
<div class="field">
|
||||
<div v-for="(name, value) in $props.data.sslProfile.options"
|
||||
:class="`control${sslProfileChanged && value === sslProfile ? ' is-changed' : ''}`"
|
||||
>
|
||||
<div class="radio">
|
||||
<PrettyRadio v-model="sslProfile" :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 class="field is-horizontal is-aligned-top">
|
||||
<div class="field-label">
|
||||
<label class="label">OCSP DNS Resolvers</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<div :class="`control${ocspCloudflareChanged ? ' is-changed' : ''}`">
|
||||
<div class="checkbox">
|
||||
<PrettyCheck v-model="ocspCloudflare" class="p-default p-curve p-fill p-icon">
|
||||
<i slot="extra" class="icon fas fa-check"></i>
|
||||
Cloudflare Resolver
|
||||
</PrettyCheck>
|
||||
<div class="field is-horizontal is-aligned-top">
|
||||
<div class="field-label">
|
||||
<label class="label">OCSP DNS Resolvers</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<div :class="`control${ocspCloudflareChanged ? ' is-changed' : ''}`">
|
||||
<div class="checkbox">
|
||||
<PrettyCheck v-model="ocspCloudflare" class="p-default p-curve p-fill p-icon">
|
||||
<i slot="extra" class="icon fas fa-check"></i>
|
||||
Cloudflare Resolver
|
||||
</PrettyCheck>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="`control${ocspGoogleChanged ? ' is-changed' : ''}`">
|
||||
<div class="checkbox">
|
||||
<PrettyCheck v-model="ocspGoogle" class="p-default p-curve p-fill p-icon">
|
||||
<i slot="extra" class="icon fas fa-check"></i>
|
||||
Google Public DNS
|
||||
</PrettyCheck>
|
||||
<div :class="`control${ocspGoogleChanged ? ' is-changed' : ''}`">
|
||||
<div class="checkbox">
|
||||
<PrettyCheck v-model="ocspGoogle" class="p-default p-curve p-fill p-icon">
|
||||
<i slot="extra" class="icon fas fa-check"></i>
|
||||
Google Public DNS
|
||||
</PrettyCheck>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="`control${ocspOpenDnsChanged ? ' is-changed' : ''}`">
|
||||
<div class="checkbox">
|
||||
<PrettyCheck v-model="ocspOpenDns" class="p-default p-curve p-fill p-icon">
|
||||
<i slot="extra" class="icon fas fa-check"></i>
|
||||
OpenDNS
|
||||
</PrettyCheck>
|
||||
<div :class="`control${ocspOpenDnsChanged ? ' is-changed' : ''}`">
|
||||
<div class="checkbox">
|
||||
<PrettyCheck v-model="ocspOpenDns" class="p-default p-curve p-fill p-icon">
|
||||
<i slot="extra" class="icon fas fa-check"></i>
|
||||
OpenDNS
|
||||
</PrettyCheck>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="letsEncryptRootEnabled" class="field is-horizontal">
|
||||
<div class="field-label">
|
||||
<label class="label">Let's Encrypt webroot</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<div :class="`control${letsEncryptRootChanged ? ' is-changed' : ''}`">
|
||||
<input v-model="letsEncryptRoot"
|
||||
class="input"
|
||||
type="text"
|
||||
:placeholder="$props.data.letsEncryptRoot.default"
|
||||
/>
|
||||
<div v-if="letsEncryptRootEnabled" class="field is-horizontal">
|
||||
<div class="field-label">
|
||||
<label class="label">Let's Encrypt webroot</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<div :class="`control${letsEncryptRootChanged ? ' is-changed' : ''}`">
|
||||
<input v-model="letsEncryptRoot"
|
||||
class="input"
|
||||
type="text"
|
||||
:placeholder="$props.data.letsEncryptRoot.default"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -139,19 +156,52 @@
|
|||
},
|
||||
deep: true,
|
||||
},
|
||||
// Enable LE webroot if any site uses LE
|
||||
'$parent.$parent.$data.domains': {
|
||||
handler(data) {
|
||||
let httpsEnabled = false, leEnabled = false;
|
||||
|
||||
for (const domain of data) {
|
||||
// Enable HTTPS server settings if any site uses HTTPS
|
||||
if (domain && domain.https && domain.https.https && domain.https.https.computed) {
|
||||
this.$props.data.sslProfile.enabled = true;
|
||||
this.$props.data.sslProfile.computed = this.$props.data.sslProfile.value;
|
||||
this.$props.data.ocspCloudflare.enabled = true;
|
||||
this.$props.data.ocspCloudflare.computed = this.$props.data.ocspCloudflare.value;
|
||||
this.$props.data.ocspGoogle.enabled = true;
|
||||
this.$props.data.ocspGoogle.computed = this.$props.data.ocspGoogle.value;
|
||||
this.$props.data.ocspOpenDns.enabled = true;
|
||||
this.$props.data.ocspOpenDns.computed = this.$props.data.ocspOpenDns.value;
|
||||
this.$props.data.letsEncryptRoot.enabled = true;
|
||||
this.$props.data.letsEncryptRoot.computed = this.$props.data.letsEncryptRoot.value;
|
||||
httpsEnabled = true;
|
||||
}
|
||||
|
||||
// Enable LE webroot if any site uses LE
|
||||
if (domain && domain.https && domain.https.certType
|
||||
&& domain.https.certType.computed === 'letsEncrypt') {
|
||||
this.$props.data.letsEncryptRoot.enabled = true;
|
||||
this.$props.data.letsEncryptRoot.computed = this.$props.data.letsEncryptRoot.value;
|
||||
return;
|
||||
leEnabled = true;
|
||||
}
|
||||
}
|
||||
this.$props.data.letsEncryptRoot.enabled = false;
|
||||
this.$props.data.letsEncryptRoot.computed = '';
|
||||
|
||||
if (!httpsEnabled) {
|
||||
this.$props.data.sslProfile.enabled = false;
|
||||
this.$props.data.sslProfile.computed = '';
|
||||
this.$props.data.ocspCloudflare.enabled = false;
|
||||
this.$props.data.ocspCloudflare.computed = false;
|
||||
this.$props.data.ocspGoogle.enabled = false;
|
||||
this.$props.data.ocspGoogle.computed = false;
|
||||
this.$props.data.ocspOpenDns.enabled = false;
|
||||
this.$props.data.ocspOpenDns.computed = false;
|
||||
this.$props.data.letsEncryptRoot.enabled = false;
|
||||
this.$props.data.letsEncryptRoot.computed = '';
|
||||
}
|
||||
|
||||
if (!leEnabled) {
|
||||
this.$props.data.letsEncryptRoot.enabled = false;
|
||||
this.$props.data.letsEncryptRoot.computed = '';
|
||||
}
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
<template>
|
||||
<div>
|
||||
<ol v-if="letsEncryptActive">
|
||||
<li>
|
||||
<p>
|
||||
Comment out SSL related directives in the configuration:
|
||||
<br />
|
||||
<code class="slim">sed -i -r 's/(listen .*443)/\1;#/g;
|
||||
s/(ssl_(certificate|certificate_key|trusted_certificate) )/#;#\1/g' {{ sitesAvailable }}</code>
|
||||
</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<p>
|
||||
Reload your NGINX server:
|
||||
<br />
|
||||
<code class="slim">sudo nginx -t && sudo systemctl reload nginx</code>
|
||||
</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<p>
|
||||
Obtain SSL certificates from Let's Encrypt using Certbot:
|
||||
<template v-for="cmd in certbotCmds">
|
||||
<br />
|
||||
<code class="slim">{{ cmd }}</code>
|
||||
</template>
|
||||
</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<p>
|
||||
Uncomment SSL related directives in the configuration:
|
||||
<br />
|
||||
<code class="slim">sed -i -r 's/#?;#//g' {{ sitesAvailable }}</code>
|
||||
</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<p>
|
||||
Reload your NGINX server:
|
||||
<br />
|
||||
<code class="slim">sudo nginx -t && sudo systemctl reload nginx</code>
|
||||
</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<p>
|
||||
Configure Certbot to reload NGINX when it successfully renews certificates:
|
||||
<br />
|
||||
<code class="slim">echo -e '#!/bin/bash\nnginx -t && systemctl reload nginx' | sudo tee /etc/letsencrypt/renewal-hooks/post/nginx-reload.sh</code>
|
||||
<br />
|
||||
<code class="slim">sudo chmod a+x /etc/letsencrypt/renewal-hooks/post/nginx-reload.sh</code>
|
||||
</p>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<div v-else class="field is-horizontal">
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<label class="text">
|
||||
Certbot does not need to be set up for your NGINX configuration.
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import i18n from '../../i18n';
|
||||
|
||||
export default {
|
||||
name: 'SetupCertbot',
|
||||
display: 'Certbot',
|
||||
key: 'certbot',
|
||||
props: {
|
||||
data: Object,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
i18n,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
nginxDir() {
|
||||
return this.$props.data.global.nginx.nginxConfigDirectory.computed.replace(/\/+$/, '');
|
||||
},
|
||||
letsEncryptDir() {
|
||||
return this.$props.data.global.https.letsEncryptRoot.computed.replace(/\/+$/, '');
|
||||
},
|
||||
letsEncryptActive() {
|
||||
for (const domain of this.$props.data.domains) {
|
||||
if (domain && domain.https.certType.computed === 'letsEncrypt') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
sitesAvailable() {
|
||||
const enabledAvailable = this.$props.data.global.tools.symlinkVhost.computed ? 'available' : 'enabled';
|
||||
return this.$props.data.domains
|
||||
.filter(domain => domain.https.certType.computed === 'letsEncrypt')
|
||||
.map(domain => `${this.nginxDir}/sites-${enabledAvailable}/${domain.server.domain.computed}.conf`)
|
||||
.join(' ');
|
||||
},
|
||||
certbotCmds() {
|
||||
return this.$props.data.domains
|
||||
.filter(domain => domain.https.certType.computed === 'letsEncrypt')
|
||||
.map(domain => (
|
||||
[
|
||||
'certbot certonly --webroot',
|
||||
`-d ${domain.server.domain.computed}`,
|
||||
domain.server.wwwSubdomain.computed ? `-d www.${domain.server.domain.computed}` : null,
|
||||
domain.server.cdnSubdomain.computed ? `-d cdn.${domain.server.domain.computed}` : null,
|
||||
`--email ${domain.https.letsEncryptEmail.computed}`,
|
||||
`-w ${this.letsEncryptDir}`,
|
||||
'-n --agree-tos --force-renewal',
|
||||
].filter(x => x !== null).join(' ')
|
||||
));
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,30 @@
|
|||
<template>
|
||||
<div>
|
||||
<p>
|
||||
<b>Let's go live!</b> 🎉
|
||||
</p>
|
||||
<p>
|
||||
Reload NGINX to load in your new configuration:
|
||||
<br />
|
||||
<code class="slim">sudo nginx -t && sudo systemctl reload nginx</code>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import i18n from '../../i18n';
|
||||
|
||||
export default {
|
||||
name: 'SetupGoLive',
|
||||
display: 'Go live!',
|
||||
key: 'goLive',
|
||||
props: {
|
||||
data: Object,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
i18n,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -1 +1,4 @@
|
|||
export { default as Download } from './download';
|
||||
export { default as SSL } from './ssl';
|
||||
export { default as Certbot } from './certbot';
|
||||
export { default as GoLive } from './go_live';
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
<template>
|
||||
<div>
|
||||
<ol v-if="diffieHellmanValue || letsEncryptActive">
|
||||
<li v-if="diffieHellmanValue">
|
||||
<p>
|
||||
Generate <b>Diffie-Hellman keys</b> by running this command on your server:
|
||||
<br />
|
||||
<code class="slim">openssl dhparam -out {{ nginxDir }}/dhparam.pem {{ diffieHellmanValue }}</code>
|
||||
</p>
|
||||
</li>
|
||||
|
||||
<li v-if="letsEncryptActive">
|
||||
<p>
|
||||
Create a common <b>ACME-challenge</b> directory (for <b>Let's Encrypt</b>):
|
||||
<br />
|
||||
<code class="slim">mkdir -p {{ letsEncryptDir }}</code>
|
||||
<br />
|
||||
<code class="slim">chown {{ nginxUser }} {{ letsEncryptDir }}</code>
|
||||
</p>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<div v-else class="field is-horizontal">
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<label class="text">
|
||||
No additional steps are needed to set up SSL for your NGINX configuration.
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import i18n from '../../i18n';
|
||||
|
||||
export default {
|
||||
name: 'SetupSSL',
|
||||
display: 'SSL init',
|
||||
key: 'ssl',
|
||||
props: {
|
||||
data: Object,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
i18n,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
nginxDir() {
|
||||
return this.$props.data.global.nginx.nginxConfigDirectory.computed.replace(/\/+$/, '');
|
||||
},
|
||||
letsEncryptDir() {
|
||||
return this.$props.data.global.https.letsEncryptRoot.computed.replace(/\/+$/, '');
|
||||
},
|
||||
nginxUser() {
|
||||
return this.$props.data.global.nginx.user.computed;
|
||||
},
|
||||
diffieHellmanValue() {
|
||||
switch (this.$props.data.global.https.sslProfile.computed) {
|
||||
case 'intermediate':
|
||||
return 2048;
|
||||
case 'old':
|
||||
return 1024;
|
||||
case 'modern':
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
letsEncryptActive() {
|
||||
for (const domain of this.$props.data.domains) {
|
||||
if (domain && domain.https.certType.computed === 'letsEncrypt') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
Loading…
Reference in New Issue