Compare commits

..

22 Commits

Author SHA1 Message Date
3b47decbb0 Merge pull request #1528 from jc21/develop
v2.9.11
2021-10-27 22:11:55 +10:00
d0bfa082e0 Bump version 2021-10-27 21:43:07 +10:00
6b7a8b009e Merge pull request #1519 from jc21/fixes-incorrect-proxy-path
Updates proxy paths
2021-10-27 21:42:18 +10:00
ca59e585d8 Uses variable in proxy_pass for normal proxy hosts 2021-10-25 14:58:02 +02:00
bbde7a108a Use variable with full uri in proxy pass 2021-10-25 14:48:22 +02:00
87731a8b5c Revert "Utilise variable for custom locations proxy_pass"
This reverts commit 6c1ae77a2a.
2021-10-25 14:27:37 +02:00
29d4bd4ccf Merge pull request #1517 from jc21/develop
v2.9.10
2021-10-25 15:48:43 +10:00
925ad90f91 Merge branch 'master' into develop 2021-10-25 14:26:47 +10:00
650ae61c43 Version bump 2021-10-25 11:35:44 +10:00
02f3f9704f Merge pull request #1480 from jc21/prefer-isrg-cert-chain
Sets the cert chain to prefer ISRG Root X1
2021-10-25 08:42:31 +10:00
da7c3057b4 Merge pull request #1481 from jc21/certificate-no-domain-sorting
Removes sorting of domain names when creating a certificate
2021-10-25 08:40:19 +10:00
040b45cafa Merge pull request #1496 from jc21/fixes-cache-busting
Adds cache busting to js bundles as well
2021-10-25 08:30:15 +10:00
8ece310b9f Merge pull request #1514 from jc21/adds-dns-websupportsk-challenge
Adds Webbsupport.sk dns plugin
2021-10-25 08:26:20 +10:00
96959db3c2 Merge pull request #1504 from jc21/removes-certbot-plugin-prefixes
Removes certbot plugin prefixes
2021-10-25 08:26:03 +10:00
6360100611 Adds Webbsupport.sk dns plugin 2021-10-24 22:14:59 +02:00
b833044cea Fixes formatting 2021-10-20 18:38:21 +02:00
97909830f5 Removes dns plugin prefixes 2021-10-20 18:16:54 +02:00
8ae2de2f49 Updates certbot-dns-ionos to newest version 2021-10-20 18:08:11 +02:00
bf7b659e89 Adds cache busting to js bundles as well 2021-10-17 19:15:35 +02:00
4e3c7749af Removes sorting of domain names when creating a certificate 2021-10-12 16:18:11 +02:00
f63441921f Sets the cert chain to prefer ISRG Root X1 2021-10-12 16:11:47 +02:00
06c5f991e7 Merge pull request #1390 from jc21/develop
v2.9.9
2021-09-10 13:50:47 +10:00
49 changed files with 223 additions and 2026 deletions

View File

@ -1 +1 @@
2.9.9
2.9.11

View File

@ -1,7 +1,7 @@
<p align="center">
<img src="https://nginxproxymanager.com/github.png">
<br><br>
<img src="https://img.shields.io/badge/version-2.9.9-green.svg?style=for-the-badge">
<img src="https://img.shields.io/badge/version-2.9.11-green.svg?style=for-the-badge">
<a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager">
<img src="https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge">
</a>

View File

@ -74,10 +74,12 @@ app.use(function (err, req, res, next) {
}
// Not every error is worth logging - but this is good for now until it gets annoying.
if (process.env.NODE_ENV === 'development' || process.env.DEBUG) {
log.debug(err);
} else if (typeof err.stack !== 'undefined' && err.stack && (typeof err.public == 'undefined' || !err.public)) {
log.warn(err.message);
if (typeof err.stack !== 'undefined' && err.stack) {
if (process.env.NODE_ENV === 'development' || process.env.DEBUG) {
log.debug(err.stack);
} else if (typeof err.public == 'undefined' || !err.public) {
log.warn(err.message);
}
}
res

View File

@ -114,7 +114,7 @@ const internalCertificate = {
data.owner_user_id = access.token.getUserId(1);
if (data.provider === 'letsencrypt') {
data.nice_name = data.domain_names.sort().join(', ');
data.nice_name = data.domain_names.join(', ');
}
return certificateModel

View File

@ -1,8 +1,7 @@
const _ = require('lodash');
const proxyHostModel = require('../models/proxy_host');
const redirectionHostModel = require('../models/redirection_host');
const deadHostModel = require('../models/dead_host');
const sslPassthroughHostModel = require('../models/ssl_passthrough_host');
const _ = require('lodash');
const proxyHostModel = require('../models/proxy_host');
const redirectionHostModel = require('../models/redirection_host');
const deadHostModel = require('../models/dead_host');
const internalHost = {
@ -82,9 +81,6 @@ const internalHost = {
.query()
.where('is_deleted', 0),
deadHostModel
.query()
.where('is_deleted', 0),
sslPassthroughHostModel
.query()
.where('is_deleted', 0)
];
@ -116,12 +112,6 @@ const internalHost = {
response_object.total_count += response_object.dead_hosts.length;
}
if (promises_results[3]) {
// SSL Passthrough Hosts
response_object.ssl_passthrough_hosts = internalHost._getHostsWithDomains(promises_results[3], domain_names);
response_object.total_count += response_object.ssl_passthrough_hosts.length;
}
return response_object;
});
},
@ -147,11 +137,7 @@ const internalHost = {
deadHostModel
.query()
.where('is_deleted', 0)
.andWhere('domain_names', 'like', '%' + hostname + '%'),
sslPassthroughHostModel
.query()
.where('is_deleted', 0)
.andWhere('domain_name', '=', hostname),
.andWhere('domain_names', 'like', '%' + hostname + '%')
];
return Promise.all(promises)
@ -179,13 +165,6 @@ const internalHost = {
}
}
if (promises_results[3]) {
// SSL Passthrough Hosts
if (internalHost._checkHostnameRecordsTaken(hostname, promises_results[3], ignore_type === 'ssl_passthrough' && ignore_id ? ignore_id : 0)) {
is_taken = true;
}
}
return {
hostname: hostname,
is_taken: is_taken
@ -206,21 +185,14 @@ const internalHost = {
if (existing_rows && existing_rows.length) {
existing_rows.map(function (existing_row) {
function checkHostname(existing_hostname) {
existing_row.domain_names.map(function (existing_hostname) {
// Does this domain match?
if (existing_hostname.toLowerCase() === hostname.toLowerCase()) {
if (!ignore_id || ignore_id !== existing_row.id) {
is_taken = true;
}
}
}
if (existing_row.domain_names) {
existing_row.domain_names.map(checkHostname);
} else if (existing_row.domain_name) {
checkHostname(existing_row.domain_name);
}
});
});
}

View File

@ -1,11 +1,10 @@
const _ = require('lodash');
const fs = require('fs');
const logger = require('../logger').nginx;
const utils = require('../lib/utils');
const error = require('../lib/error');
const { Liquid } = require('liquidjs');
const passthroughHostModel = require('../models/ssl_passthrough_host');
const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
const _ = require('lodash');
const fs = require('fs');
const logger = require('../logger').nginx;
const utils = require('../lib/utils');
const error = require('../lib/error');
const { Liquid } = require('liquidjs');
const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
const internalNginx = {
@ -24,8 +23,7 @@ const internalNginx = {
* @returns {Promise}
*/
configure: (model, host_type, host) => {
let combined_meta = {};
const sslPassthroughEnabled = internalNginx.sslPassthroughEnabled();
let combined_meta = {};
return internalNginx.test()
.then(() => {
@ -34,25 +32,7 @@ const internalNginx = {
return internalNginx.deleteConfig(host_type, host); // Don't throw errors, as the file may not exist at all
})
.then(() => {
if (host_type === 'ssl_passthrough_host' && !sslPassthroughEnabled){
// ssl passthrough is disabled
const meta = {
nginx_online: false,
nginx_err: 'SSL passthrough is not enabled in environment'
};
return passthroughHostModel
.query()
.where('is_deleted', 0)
.andWhere('enabled', 1)
.patch({
meta
}).then(() => {
return internalNginx.deleteConfig('ssl_passthrough_host', host, false);
});
} else {
return internalNginx.generateConfig(host_type, host);
}
return internalNginx.generateConfig(host_type, host);
})
.then(() => {
// Test nginx again and update meta with result
@ -64,27 +44,12 @@ const internalNginx = {
nginx_err: null
});
if (host_type === 'ssl_passthrough_host'){
// If passthrough is disabled we have already marked the hosts as offline
if (sslPassthroughEnabled) {
return passthroughHostModel
.query()
.where('is_deleted', 0)
.andWhere('enabled', 1)
.patch({
meta: combined_meta
});
}
return Promise.resolve();
}
return model
.query()
.where('id', host.id)
.patch({
meta: combined_meta
});
})
.catch((err) => {
// Remove the error_log line because it's a docker-ism false positive that doesn't need to be reported.
@ -109,18 +74,6 @@ const internalNginx = {
nginx_err: valid_lines.join('\n')
});
if (host_type === 'ssl_passthrough_host'){
return passthroughHostModel
.query()
.where('is_deleted', 0)
.andWhere('enabled', 1)
.patch({
meta: combined_meta
}).then(() => {
return internalNginx.deleteConfig('ssl_passthrough_host', host, true);
});
}
return model
.query()
.where('id', host.id)
@ -172,8 +125,6 @@ const internalNginx = {
if (host_type === 'default') {
return '/data/nginx/default_host/site.conf';
} else if (host_type === 'ssl_passthrough_host') {
return '/data/nginx/ssl_passthrough_host/hosts.conf';
}
return '/data/nginx/' + host_type + '/' + host_id + '.conf';
@ -235,7 +186,7 @@ const internalNginx = {
* @param {Object} host
* @returns {Promise}
*/
generateConfig: async (host_type, host) => {
generateConfig: (host_type, host) => {
host_type = host_type.replace(new RegExp('-', 'g'), '_');
if (debug_mode) {
@ -248,87 +199,72 @@ const internalNginx = {
root: __dirname + '/../templates/'
});
let template = null;
let filename = internalNginx.getConfigName(host_type, host.id);
return new Promise((resolve, reject) => {
let template = null;
let filename = internalNginx.getConfigName(host_type, host.id);
try {
template = fs.readFileSync(__dirname + '/../templates/' + host_type + '.conf', {encoding: 'utf8'});
} catch (err) {
throw new error.ConfigurationError(err.message);
}
let locationsPromise;
let origLocations;
// Manipulate the data a bit before sending it to the template
if (host_type === 'ssl_passthrough_host') {
if (internalNginx.sslPassthroughEnabled()){
const allHosts = await passthroughHostModel
.query()
.where('is_deleted', 0)
.groupBy('id')
.omit(['is_deleted']);
host = {
all_passthrough_hosts: allHosts.map((host) => {
// Replace dots in domain
host.forwarding_host = internalNginx.addIpv6Brackets(host.forwarding_host);
return host;
}),
};
} else {
internalNginx.deleteConfig(host_type, host, false);
try {
template = fs.readFileSync(__dirname + '/../templates/' + host_type + '.conf', {encoding: 'utf8'});
} catch (err) {
reject(new error.ConfigurationError(err.message));
return;
}
} else if (host_type !== 'default') {
host.use_default_location = true;
if (typeof host.advanced_config !== 'undefined' && host.advanced_config) {
host.use_default_location = !internalNginx.advancedConfigHasDefaultLocation(host.advanced_config);
}
}
if (host.locations) {
//logger.info ('host.locations = ' + JSON.stringify(host.locations, null, 2));
origLocations = [].concat(host.locations);
locationsPromise = internalNginx.renderLocations(host).then((renderedLocations) => {
host.locations = renderedLocations;
});
let locationsPromise;
let origLocations;
// Allow someone who is using / custom location path to use it, and skip the default / location
_.map(host.locations, (location) => {
if (location.path === '/') {
host.use_default_location = false;
// Manipulate the data a bit before sending it to the template
if (host_type !== 'default') {
host.use_default_location = true;
if (typeof host.advanced_config !== 'undefined' && host.advanced_config) {
host.use_default_location = !internalNginx.advancedConfigHasDefaultLocation(host.advanced_config);
}
});
}
} else {
locationsPromise = Promise.resolve();
}
// Set the IPv6 setting for the host
host.ipv6 = internalNginx.ipv6Enabled();
return locationsPromise.then(() => {
renderEngine
.parseAndRender(template, host)
.then((config_text) => {
fs.writeFileSync(filename, config_text, {encoding: 'utf8'});
if (debug_mode) {
logger.success('Wrote config:', filename, config_text);
}
// Restore locations array
host.locations = origLocations;
return true;
})
.catch((err) => {
if (debug_mode) {
logger.warn('Could not write ' + filename + ':', err.message);
}
throw new error.ConfigurationError(err.message);
if (host.locations) {
//logger.info ('host.locations = ' + JSON.stringify(host.locations, null, 2));
origLocations = [].concat(host.locations);
locationsPromise = internalNginx.renderLocations(host).then((renderedLocations) => {
host.locations = renderedLocations;
});
// Allow someone who is using / custom location path to use it, and skip the default / location
_.map(host.locations, (location) => {
if (location.path === '/') {
host.use_default_location = false;
}
});
} else {
locationsPromise = Promise.resolve();
}
// Set the IPv6 setting for the host
host.ipv6 = internalNginx.ipv6Enabled();
locationsPromise.then(() => {
renderEngine
.parseAndRender(template, host)
.then((config_text) => {
fs.writeFileSync(filename, config_text, {encoding: 'utf8'});
if (debug_mode) {
logger.success('Wrote config:', filename, config_text);
}
// Restore locations array
host.locations = origLocations;
resolve(true);
})
.catch((err) => {
if (debug_mode) {
logger.warn('Could not write ' + filename + ':', err.message);
}
reject(new error.ConfigurationError(err.message));
});
});
});
},
@ -493,33 +429,6 @@ const internalNginx = {
}
return true;
},
/**
* @returns {boolean}
*/
sslPassthroughEnabled: function () {
if (typeof process.env.ENABLE_SSL_PASSTHROUGH !== 'undefined') {
const enabled = process.env.ENABLE_SSL_PASSTHROUGH.toLowerCase();
return (enabled === 'on' || enabled === 'true' || enabled === '1' || enabled === 'yes');
}
return false;
},
/**
* Helper function to add brackets to an IP if it is IPv6
* @returns {string}
*/
addIpv6Brackets: function (ip) {
// Only run check if ipv6 is enabled
if (internalNginx.ipv6Enabled()) {
const ipv6Regex = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/gi;
if (ipv6Regex.test(ip)){
return `[${ip}]`;
}
}
return ip;
}
};

View File

@ -1,365 +0,0 @@
const _ = require('lodash');
const error = require('../lib/error');
const passthroughHostModel = require('../models/ssl_passthrough_host');
const internalHost = require('./host');
const internalNginx = require('./nginx');
const internalAuditLog = require('./audit-log');
function omissions () {
return ['is_deleted'];
}
const internalPassthroughHost = {
/**
* @param {Access} access
* @param {Object} data
* @returns {Promise}
*/
create: (access, data) => {
return access.can('ssl_passthrough_hosts:create', data)
.then(() => {
// Get the domain name and check it against existing records
return internalHost.isHostnameTaken(data.domain_name)
.then((result) => {
if (result.is_taken) {
throw new error.ValidationError(result.hostname + ' is already in use');
}
});
}).then((/*access_data*/) => {
data.owner_user_id = access.token.getUserId(1);
if (typeof data.meta === 'undefined') {
data.meta = {};
}
return passthroughHostModel
.query()
.omit(omissions())
.insertAndFetch(data);
})
.then((row) => {
// Configure nginx
return internalNginx.configure(passthroughHostModel, 'ssl_passthrough_host', {})
.then(() => {
return internalPassthroughHost.get(access, {id: row.id, expand: ['owner']});
});
})
.then((row) => {
// Add to audit log
return internalAuditLog.add(access, {
action: 'created',
object_type: 'ssl-passthrough-host',
object_id: row.id,
meta: data
})
.then(() => {
return row;
});
});
},
/**
* @param {Access} access
* @param {Object} data
* @param {Number} data.id
* @return {Promise}
*/
update: (access, data) => {
return access.can('ssl_passthrough_hosts:update', data.id)
.then((/*access_data*/) => {
// Get the domain name and check it against existing records
if (typeof data.domain_name !== 'undefined') {
return internalHost.isHostnameTaken(data.domain_name, 'ssl_passthrough', data.id)
.then((result) => {
if (result.is_taken) {
throw new error.ValidationError(result.hostname + ' is already in use');
}
});
}
}).then((/*access_data*/) => {
return internalPassthroughHost.get(access, {id: data.id});
})
.then((row) => {
if (row.id !== data.id) {
// Sanity check that something crazy hasn't happened
throw new error.InternalValidationError('SSL Passthrough Host could not be updated, IDs do not match: ' + row.id + ' !== ' + data.id);
}
return passthroughHostModel
.query()
.omit(omissions())
.patchAndFetchById(row.id, data)
.then(() => {
return internalNginx.configure(passthroughHostModel, 'ssl_passthrough_host', {})
.then(() => {
return internalPassthroughHost.get(access, {id: row.id, expand: ['owner']});
});
})
.then((saved_row) => {
// Add to audit log
return internalAuditLog.add(access, {
action: 'updated',
object_type: 'ssl-passthrough-host',
object_id: row.id,
meta: data
})
.then(() => {
return _.omit(saved_row, omissions());
});
});
});
},
/**
* @param {Access} access
* @param {Object} data
* @param {Number} data.id
* @param {Array} [data.expand]
* @param {Array} [data.omit]
* @return {Promise}
*/
get: (access, data) => {
if (typeof data === 'undefined') {
data = {};
}
return access.can('ssl_passthrough_hosts:get', data.id)
.then((access_data) => {
let query = passthroughHostModel
.query()
.where('is_deleted', 0)
.andWhere('id', data.id)
.allowEager('[owner]')
.first();
if (access_data.permission_visibility !== 'all') {
query.andWhere('owner_user_id', access.token.getUserId(1));
}
// Custom omissions
if (typeof data.omit !== 'undefined' && data.omit !== null) {
query.omit(data.omit);
}
if (typeof data.expand !== 'undefined' && data.expand !== null) {
query.eager('[' + data.expand.join(', ') + ']');
}
return query;
})
.then((row) => {
if (row) {
return _.omit(row, omissions());
} else {
throw new error.ItemNotFoundError(data.id);
}
});
},
/**
* @param {Access} access
* @param {Object} data
* @param {Number} data.id
* @param {String} [data.reason]
* @returns {Promise}
*/
delete: (access, data) => {
return access.can('ssl_passthrough_hosts:delete', data.id)
.then(() => {
return internalPassthroughHost.get(access, {id: data.id});
})
.then((row) => {
if (!row) {
throw new error.ItemNotFoundError(data.id);
}
return passthroughHostModel
.query()
.where('id', row.id)
.patch({
is_deleted: 1
})
.then(() => {
// Update Nginx Config
return internalNginx.configure(passthroughHostModel, 'ssl_passthrough_host', {})
.then(() => {
return internalNginx.reload();
});
})
.then(() => {
// Add to audit log
return internalAuditLog.add(access, {
action: 'deleted',
object_type: 'ssl-passthrough-host',
object_id: row.id,
meta: _.omit(row, omissions())
});
});
})
.then(() => {
return true;
});
},
/**
* @param {Access} access
* @param {Object} data
* @param {Number} data.id
* @param {String} [data.reason]
* @returns {Promise}
*/
enable: (access, data) => {
return access.can('ssl_passthrough_hosts:update', data.id)
.then(() => {
return internalPassthroughHost.get(access, {
id: data.id,
expand: ['owner']
});
})
.then((row) => {
if (!row) {
throw new error.ItemNotFoundError(data.id);
} else if (row.enabled) {
throw new error.ValidationError('Host is already enabled');
}
row.enabled = 1;
return passthroughHostModel
.query()
.where('id', row.id)
.patch({
enabled: 1
})
.then(() => {
// Configure nginx
return internalNginx.configure(passthroughHostModel, 'ssl_passthrough_host', {});
})
.then(() => {
// Add to audit log
return internalAuditLog.add(access, {
action: 'enabled',
object_type: 'ssl-passthrough-host',
object_id: row.id,
meta: _.omit(row, omissions())
});
});
})
.then(() => {
return true;
});
},
/**
* @param {Access} access
* @param {Object} data
* @param {Number} data.id
* @param {String} [data.reason]
* @returns {Promise}
*/
disable: (access, data) => {
return access.can('ssl_passthrough_hosts:update', data.id)
.then(() => {
return internalPassthroughHost.get(access, {id: data.id});
})
.then((row) => {
if (!row) {
throw new error.ItemNotFoundError(data.id);
} else if (!row.enabled) {
throw new error.ValidationError('Host is already disabled');
}
row.enabled = 0;
return passthroughHostModel
.query()
.where('id', row.id)
.patch({
enabled: 0
})
.then(() => {
// Update Nginx Config
return internalNginx.configure(passthroughHostModel, 'ssl_passthrough_host', {})
.then(() => {
return internalNginx.reload();
});
})
.then(() => {
// Add to audit log
return internalAuditLog.add(access, {
action: 'disabled',
object_type: 'ssl-passthrough-host',
object_id: row.id,
meta: _.omit(row, omissions())
});
});
})
.then(() => {
return true;
});
},
/**
* All SSL Passthrough Hosts
*
* @param {Access} access
* @param {Array} [expand]
* @param {String} [search_query]
* @returns {Promise}
*/
getAll: (access, expand, search_query) => {
return access.can('ssl_passthrough_hosts:list')
.then((access_data) => {
let query = passthroughHostModel
.query()
.where('is_deleted', 0)
.groupBy('id')
.omit(['is_deleted'])
.allowEager('[owner]')
.orderBy('domain_name', 'ASC');
if (access_data.permission_visibility !== 'all') {
query.andWhere('owner_user_id', access.token.getUserId(1));
}
// Query is used for searching
if (typeof search_query === 'string') {
query.where(function () {
this.where('domain_name', 'like', '%' + search_query + '%');
});
}
if (typeof expand !== 'undefined' && expand !== null) {
query.eager('[' + expand.join(', ') + ']');
}
return query;
});
},
/**
* Report use
*
* @param {Number} user_id
* @param {String} visibility
* @returns {Promise}
*/
getCount: (user_id, visibility) => {
let query = passthroughHostModel
.query()
.count('id as count')
.where('is_deleted', 0);
if (visibility !== 'all') {
query.andWhere('owner_user_id', user_id);
}
return query.first()
.then((row) => {
return parseInt(row.count, 10);
});
}
};
module.exports = internalPassthroughHost;

View File

@ -62,15 +62,14 @@ const internalUser = {
return userPermissionModel
.query()
.insert({
user_id: user.id,
visibility: is_admin ? 'all' : 'user',
proxy_hosts: 'manage',
redirection_hosts: 'manage',
dead_hosts: 'manage',
ssl_passthrough_hosts: 'manage',
streams: 'manage',
access_lists: 'manage',
certificates: 'manage'
user_id: user.id,
visibility: is_admin ? 'all' : 'user',
proxy_hosts: 'manage',
redirection_hosts: 'manage',
dead_hosts: 'manage',
streams: 'manage',
access_lists: 'manage',
certificates: 'manage'
})
.then(() => {
return internalUser.get(access, {id: user.id, expand: ['permissions']});

View File

@ -1,23 +0,0 @@
{
"anyOf": [
{
"$ref": "roles#/definitions/admin"
},
{
"type": "object",
"required": ["permission_ssl_passthrough_hosts", "roles"],
"properties": {
"permission_ssl_passthrough_hosts": {
"$ref": "perms#/definitions/manage"
},
"roles": {
"type": "array",
"items": {
"type": "string",
"enum": ["user"]
}
}
}
}
]
}

View File

@ -1,23 +0,0 @@
{
"anyOf": [
{
"$ref": "roles#/definitions/admin"
},
{
"type": "object",
"required": ["permission_ssl_passthrough_hosts", "roles"],
"properties": {
"permission_ssl_passthrough_hosts": {
"$ref": "perms#/definitions/manage"
},
"roles": {
"type": "array",
"items": {
"type": "string",
"enum": ["user"]
}
}
}
}
]
}

View File

@ -1,23 +0,0 @@
{
"anyOf": [
{
"$ref": "roles#/definitions/admin"
},
{
"type": "object",
"required": ["permission_ssl_passthrough_hosts", "roles"],
"properties": {
"permission_ssl_passthrough_hosts": {
"$ref": "perms#/definitions/view"
},
"roles": {
"type": "array",
"items": {
"type": "string",
"enum": ["user"]
}
}
}
}
]
}

View File

@ -1,23 +0,0 @@
{
"anyOf": [
{
"$ref": "roles#/definitions/admin"
},
{
"type": "object",
"required": ["permission_ssl_passthrough_hosts", "roles"],
"properties": {
"permission_ssl_passthrough_hosts": {
"$ref": "perms#/definitions/view"
},
"roles": {
"type": "array",
"items": {
"type": "string",
"enum": ["user"]
}
}
}
}
]
}

View File

@ -1,23 +0,0 @@
{
"anyOf": [
{
"$ref": "roles#/definitions/admin"
},
{
"type": "object",
"required": ["permission_ssl_passthrough_hosts", "roles"],
"properties": {
"permission_ssl_passthrough_hosts": {
"$ref": "perms#/definitions/manage"
},
"roles": {
"type": "array",
"items": {
"type": "string",
"enum": ["user"]
}
}
}
}
]
}

View File

@ -1,85 +0,0 @@
const migrate_name = 'ssl_passthrough_host';
const logger = require('../logger').migrate;
/**
* Migrate
*
* @see http://knexjs.org/#Schema
*
* @param {Object} knex
* @param {Promise} Promise
* @returns {Promise}
*/
exports.up = async function (knex/*, Promise*/) {
logger.info('[' + migrate_name + '] Migrating Up...');
await knex.schema.createTable('ssl_passthrough_host', (table) => {
table.increments().primary();
table.dateTime('created_on').notNull();
table.dateTime('modified_on').notNull();
table.integer('owner_user_id').notNull().unsigned();
table.integer('is_deleted').notNull().unsigned().defaultTo(0);
table.string('domain_name').notNull();
table.string('forwarding_host').notNull();
table.integer('forwarding_port').notNull().unsigned();
table.integer('enabled').notNull().unsigned().defaultTo(1);
table.json('meta').notNull();
});
logger.info('[' + migrate_name + '] Table created');
// Remove unique constraint so name can be used for new table
await knex.schema.alterTable('user_permission', (table) => {
table.dropUnique('user_id');
});
await knex.schema.renameTable('user_permission', 'user_permission_old');
// We need to recreate the table since sqlite does not support altering columns
await knex.schema.createTable('user_permission', (table) => {
table.increments().primary();
table.dateTime('created_on').notNull();
table.dateTime('modified_on').notNull();
table.integer('user_id').notNull().unsigned();
table.string('visibility').notNull();
table.string('proxy_hosts').notNull();
table.string('redirection_hosts').notNull();
table.string('dead_hosts').notNull();
table.string('streams').notNull();
table.string('ssl_passthrough_hosts').notNull();
table.string('access_lists').notNull();
table.string('certificates').notNull();
table.unique('user_id');
});
await knex('user_permission_old').select('*', 'streams as ssl_passthrough_hosts').then((data) => {
if (data.length) {
return knex('user_permission').insert(data);
}
return Promise.resolve();
});
await knex.schema.dropTableIfExists('user_permission_old');
logger.info('[' + migrate_name + '] permissions updated');
};
/**
* Undo Migrate
*
* @param {Object} knex
* @param {Promise} Promise
* @returns {Promise}
*/
exports.down = function (knex/*, Promise*/) {
logger.info('[' + migrate_name + '] Migrating Down...');
return knex.schema.dropTable('stream').then(() => {
return knex.schema.table('user_permission', (table) => {
table.dropColumn('ssl_passthrough_hosts');
});
})
.then(function () {
logger.info('[' + migrate_name + '] Table altered and permissions updated');
});
};

View File

@ -1,56 +0,0 @@
// Objection Docs:
// http://vincit.github.io/objection.js/
const db = require('../db');
const Model = require('objection').Model;
const User = require('./user');
const now = require('./now_helper');
Model.knex(db);
class SslPassthrougHost extends Model {
$beforeInsert () {
this.created_on = now();
this.modified_on = now();
// Default for meta
if (typeof this.meta === 'undefined') {
this.meta = {};
}
}
$beforeUpdate () {
this.modified_on = now();
}
static get name () {
return 'SslPassthrougHost';
}
static get tableName () {
return 'ssl_passthrough_host';
}
static get jsonAttributes () {
return ['meta'];
}
static get relationMappings () {
return {
owner: {
relation: Model.HasOneRelation,
modelClass: User,
join: {
from: 'ssl_passthrough_host.owner_user_id',
to: 'user.id'
},
modify: function (qb) {
qb.where('user.is_deleted', 0);
qb.omit(['id', 'created_on', 'modified_on', 'is_deleted', 'email', 'roles']);
}
}
};
}
}
module.exports = SslPassthrougHost;

View File

@ -1,7 +1,6 @@
const express = require('express');
const pjson = require('../../package.json');
const error = require('../../lib/error');
const internalNginx = require('../../internal/nginx');
const express = require('express');
const pjson = require('../../package.json');
const error = require('../../lib/error');
let router = express.Router({
caseSensitive: true,
@ -35,18 +34,10 @@ router.use('/settings', require('./settings'));
router.use('/nginx/proxy-hosts', require('./nginx/proxy_hosts'));
router.use('/nginx/redirection-hosts', require('./nginx/redirection_hosts'));
router.use('/nginx/dead-hosts', require('./nginx/dead_hosts'));
router.use('/nginx/ssl-passthrough-hosts', require('./nginx/ssl_passthrough_hosts'));
router.use('/nginx/streams', require('./nginx/streams'));
router.use('/nginx/access-lists', require('./nginx/access_lists'));
router.use('/nginx/certificates', require('./nginx/certificates'));
router.get('/ssl-passthrough-enabled', (req, res/*, next*/) => {
res.status(200).send({
status: 'OK',
ssl_passthrough_enabled: internalNginx.sslPassthroughEnabled()
});
});
/**
* API 404 for all other routes
*

View File

@ -1,196 +0,0 @@
const express = require('express');
const validator = require('../../../lib/validator');
const jwtdecode = require('../../../lib/express/jwt-decode');
const internalSslPassthrough = require('../../../internal/ssl-passthrough-host');
const apiValidator = require('../../../lib/validator/api');
let router = express.Router({
caseSensitive: true,
strict: true,
mergeParams: true
});
/**
* /api/nginx/ssl-passthrough-hosts
*/
router
.route('/')
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
/**
* GET /api/nginx/ssl-passthrough-hosts
*
* Retrieve all ssl passthrough hosts
*/
.get((req, res, next) => {
validator({
additionalProperties: false,
properties: {
expand: {
$ref: 'definitions#/definitions/expand'
},
query: {
$ref: 'definitions#/definitions/query'
}
}
}, {
expand: (typeof req.query.expand === 'string' ? req.query.expand.split(',') : null),
query: (typeof req.query.query === 'string' ? req.query.query : null)
})
.then((data) => {
return internalSslPassthrough.getAll(res.locals.access, data.expand, data.query);
})
.then((rows) => {
res.status(200)
.send(rows);
})
.catch(next);
})
/**
* POST /api/nginx/ssl-passthrough-hosts
*
* Create a new ssl passthrough host
*/
.post((req, res, next) => {
apiValidator({$ref: 'endpoints/ssl-passthrough-hosts#/links/1/schema'}, req.body)
.then((payload) => {
return internalSslPassthrough.create(res.locals.access, payload);
})
.then((result) => {
res.status(201)
.send(result);
})
.catch(next);
});
/**
* Specific ssl passthrough host
*
* /api/nginx/ssl-passthrough-hosts/123
*/
router
.route('/:host_id')
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
/**
* GET /api/nginx/ssl-passthrough-hosts/123
*
* Retrieve a specific ssl passthrough host
*/
.get((req, res, next) => {
validator({
required: ['host_id'],
additionalProperties: false,
properties: {
host_id: {
$ref: 'definitions#/definitions/id'
},
expand: {
$ref: 'definitions#/definitions/expand'
}
}
}, {
host_id: req.params.host_id,
expand: (typeof req.query.expand === 'string' ? req.query.expand.split(',') : null)
})
.then((data) => {
return internalSslPassthrough.get(res.locals.access, {
id: parseInt(data.host_id, 10),
expand: data.expand
});
})
.then((row) => {
res.status(200)
.send(row);
})
.catch(next);
})
/**
* PUT /api/nginx/ssl-passthrough-hosts/123
*
* Update an existing ssl passthrough host
*/
.put((req, res, next) => {
apiValidator({$ref: 'endpoints/ssl-passthrough-hosts#/links/2/schema'}, req.body)
.then((payload) => {
payload.id = parseInt(req.params.host_id, 10);
return internalSslPassthrough.update(res.locals.access, payload);
})
.then((result) => {
res.status(200)
.send(result);
})
.catch(next);
})
/**
* DELETE /api/nginx/ssl-passthrough-hosts/123
*
* Delete an ssl passthrough host
*/
.delete((req, res, next) => {
internalSslPassthrough.delete(res.locals.access, {id: parseInt(req.params.host_id, 10)})
.then((result) => {
res.status(200)
.send(result);
})
.catch(next);
});
/**
* Enable ssl passthrough host
*
* /api/nginx/ssl-passthrough-hosts/123/enable
*/
router
.route('/:host_id/enable')
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode())
/**
* POST /api/nginx/ssl-passthrough-hosts/123/enable
*/
.post((req, res, next) => {
internalSslPassthrough.enable(res.locals.access, {id: parseInt(req.params.host_id, 10)})
.then((result) => {
res.status(200)
.send(result);
})
.catch(next);
});
/**
* Disable ssl passthrough host
*
* /api/nginx/ssl-passthrough-hosts/123/disable
*/
router
.route('/:host_id/disable')
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode())
/**
* POST /api/nginx/ssl-passthrough-hosts/123/disable
*/
.post((req, res, next) => {
internalSslPassthrough.disable(res.locals.access, {id: parseInt(req.params.host_id, 10)})
.then((result) => {
res.status(200)
.send(result);
})
.catch(next);
});
module.exports = router;

View File

@ -1,208 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "endpoints/ssl-passthrough-hosts",
"title": "SSL Passthrough Hosts",
"description": "Endpoints relating to SSL Passthrough Hosts",
"stability": "stable",
"type": "object",
"definitions": {
"id": {
"$ref": "../definitions.json#/definitions/id"
},
"created_on": {
"$ref": "../definitions.json#/definitions/created_on"
},
"modified_on": {
"$ref": "../definitions.json#/definitions/modified_on"
},
"domain_name": {
"$ref": "../definitions.json#/definitions/domain_name"
},
"forwarding_host": {
"anyOf": [
{
"$ref": "../definitions.json#/definitions/domain_name"
},
{
"type": "string",
"format": "ipv4"
},
{
"type": "string",
"format": "ipv6"
}
]
},
"forwarding_port": {
"type": "integer",
"minimum": 1,
"maximum": 65535
},
"enabled": {
"$ref": "../definitions.json#/definitions/enabled"
},
"meta": {
"type": "object"
}
},
"properties": {
"id": {
"$ref": "#/definitions/id"
},
"created_on": {
"$ref": "#/definitions/created_on"
},
"modified_on": {
"$ref": "#/definitions/modified_on"
},
"domain_name": {
"$ref": "#/definitions/domain_name"
},
"forwarding_host": {
"$ref": "#/definitions/forwarding_host"
},
"forwarding_port": {
"$ref": "#/definitions/forwarding_port"
},
"enabled": {
"$ref": "#/definitions/enabled"
},
"meta": {
"$ref": "#/definitions/meta"
}
},
"links": [
{
"title": "List",
"description": "Returns a list of SSL Passthrough Hosts",
"href": "/nginx/ssl-passthrough-hosts",
"access": "private",
"method": "GET",
"rel": "self",
"http_header": {
"$ref": "../examples.json#/definitions/auth_header"
},
"targetSchema": {
"type": "array",
"items": {
"$ref": "#/properties"
}
}
},
{
"title": "Create",
"description": "Creates a new SSL Passthrough Host",
"href": "/nginx/ssl-passthrough-hosts",
"access": "private",
"method": "POST",
"rel": "create",
"http_header": {
"$ref": "../examples.json#/definitions/auth_header"
},
"schema": {
"type": "object",
"additionalProperties": false,
"required": [
"domain_name",
"forwarding_host",
"forwarding_port"
],
"properties": {
"domain_name": {
"$ref": "#/definitions/domain_name"
},
"forwarding_host": {
"$ref": "#/definitions/forwarding_host"
},
"forwarding_port": {
"$ref": "#/definitions/forwarding_port"
},
"meta": {
"$ref": "#/definitions/meta"
}
}
},
"targetSchema": {
"properties": {
"$ref": "#/properties"
}
}
},
{
"title": "Update",
"description": "Updates a existing SSL Passthrough Host",
"href": "/nginx/ssl-passthrough-hosts/{definitions.identity.example}",
"access": "private",
"method": "PUT",
"rel": "update",
"http_header": {
"$ref": "../examples.json#/definitions/auth_header"
},
"schema": {
"type": "object",
"additionalProperties": false,
"properties": {
"domain_name": {
"$ref": "#/definitions/domain_name"
},
"forwarding_host": {
"$ref": "#/definitions/forwarding_host"
},
"forwarding_port": {
"$ref": "#/definitions/forwarding_port"
},
"meta": {
"$ref": "#/definitions/meta"
}
}
},
"targetSchema": {
"properties": {
"$ref": "#/properties"
}
}
},
{
"title": "Delete",
"description": "Deletes a existing SSL Passthrough Host",
"href": "/nginx/ssl-passthrough-hosts/{definitions.identity.example}",
"access": "private",
"method": "DELETE",
"rel": "delete",
"http_header": {
"$ref": "../examples.json#/definitions/auth_header"
},
"targetSchema": {
"type": "boolean"
}
},
{
"title": "Enable",
"description": "Enables a existing SSL Passthrough Host",
"href": "/nginx/ssl-passthrough-hosts/{definitions.identity.example}/enable",
"access": "private",
"method": "POST",
"rel": "update",
"http_header": {
"$ref": "../examples.json#/definitions/auth_header"
},
"targetSchema": {
"type": "boolean"
}
},
{
"title": "Disable",
"description": "Disables a existing SSL Passthrough Host",
"href": "/nginx/ssl-passthrough-hosts/{definitions.identity.example}/disable",
"access": "private",
"method": "POST",
"rel": "update",
"http_header": {
"$ref": "../examples.json#/definitions/auth_header"
},
"targetSchema": {
"type": "boolean"
}
}
]
}

View File

@ -26,9 +26,6 @@
"dead-hosts": {
"$ref": "endpoints/dead-hosts.json"
},
"ssl-passthrough-hosts": {
"$ref": "endpoints/ssl-passthrough-hosts.json"
},
"streams": {
"$ref": "endpoints/streams.json"
},

View File

@ -1,17 +1,15 @@
const fs = require('fs');
const NodeRSA = require('node-rsa');
const config = require('config');
const logger = require('./logger').setup;
const certificateModel = require('./models/certificate');
const userModel = require('./models/user');
const userPermissionModel = require('./models/user_permission');
const utils = require('./lib/utils');
const authModel = require('./models/auth');
const settingModel = require('./models/setting');
const passthroughHostModel = require('./models/ssl_passthrough_host');
const dns_plugins = require('./global/certbot-dns-plugins');
const internalNginx = require('./internal/nginx');
const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
const fs = require('fs');
const NodeRSA = require('node-rsa');
const config = require('config');
const logger = require('./logger').setup;
const certificateModel = require('./models/certificate');
const userModel = require('./models/user');
const userPermissionModel = require('./models/user_permission');
const utils = require('./lib/utils');
const authModel = require('./models/auth');
const settingModel = require('./models/setting');
const dns_plugins = require('./global/certbot-dns-plugins');
const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
/**
* Creates a new JWT RSA Keypair if not alread set on the config
@ -107,15 +105,14 @@ const setupDefaultUser = () => {
})
.then(() => {
return userPermissionModel.query().insert({
user_id: user.id,
visibility: 'all',
proxy_hosts: 'manage',
redirection_hosts: 'manage',
dead_hosts: 'manage',
ssl_passthrough_hosts: 'manage',
streams: 'manage',
access_lists: 'manage',
certificates: 'manage',
user_id: user.id,
visibility: 'all',
proxy_hosts: 'manage',
redirection_hosts: 'manage',
dead_hosts: 'manage',
streams: 'manage',
access_lists: 'manage',
certificates: 'manage',
});
});
})
@ -225,19 +222,10 @@ const setupLogrotation = () => {
return runLogrotate();
};
/**
* Makes sure the ssl passthrough option is reflected in the nginx config
* @returns {Promise}
*/
const setupSslPassthrough = () => {
return internalNginx.configure(passthroughHostModel, 'ssl_passthrough_host', {});
};
module.exports = function () {
return setupJwt()
.then(setupDefaultUser)
.then(setupDefaultSettings)
.then(setupCertbotPlugins)
.then(setupLogrotation)
.then(setupSslPassthrough);
.then(setupLogrotation);
};

View File

@ -1,5 +1,5 @@
location {{ path }} {
set $upstream {{ forward_scheme }}://{{ forward_host }}:{{ forward_port }}{{ forward_path }};
set $upstream {{ forward_scheme }}://{{ forward_host }}:{{ forward_port }}{{ forward_path }}$request_uri;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Scheme $scheme;
proxy_set_header X-Forwarded-Proto $scheme;

View File

@ -1,41 +0,0 @@
# ------------------------------------------------------------
# SSL Passthrough hosts
# ------------------------------------------------------------
map $ssl_preread_server_name $name {
{% for host in all_passthrough_hosts %}
{% if host.enabled %}
{{ host.domain_name }} ssl_passthrough_{{ host.domain_name }};
{% endif %}
{% endfor %}
default https_default_backend;
}
{% for host in all_passthrough_hosts %}
{% if host.enabled %}
upstream ssl_passthrough_{{ host.domain_name }} {
server {{host.forwarding_host}}:{{host.forwarding_port}};
}
{% endif %}
{% endfor %}
upstream https_default_backend {
server 127.0.0.1:443;
}
server {
listen 444;
{% if ipv6 -%}
listen [::]:444;
{% else -%}
#listen [::]:444;
{% endif %}
proxy_pass $name;
ssl_preread on;
error_log /data/logs/ssl-passthrough-hosts_error.log warn;
# Custom
include /data/nginx/custom/server_ssl_passthrough[.]conf;
}

View File

@ -10,8 +10,7 @@ services:
ports:
- 3080:80
- 3081:81
- 3443:443 # Ususally you would only have this one
- 3444:444 # This is to test ssl passthrough
- 3443:443
networks:
- nginx_proxy_manager
environment:
@ -23,7 +22,6 @@ services:
DB_MYSQL_USER: "npm"
DB_MYSQL_PASSWORD: "npm"
DB_MYSQL_NAME: "npm"
# ENABLE_SSL_PASSTHROUGH: "true"
# DB_SQLITE_FILE: "/data/database.sqlite"
# DISABLE_IPV6: "true"
volumes:
@ -41,8 +39,6 @@ services:
container_name: npm_db
networks:
- nginx_proxy_manager
ports:
- 33306:3306
environment:
MYSQL_ROOT_PASSWORD: "npm"
MYSQL_DATABASE: "npm"

View File

@ -3,3 +3,4 @@ non-interactive = True
webroot-path = /data/letsencrypt-acme-challenge
key-type = ecdsa
elliptic-curve = secp384r1
preferred-chain = ISRG Root X1

View File

@ -1,8 +1,9 @@
set $upstream $forward_scheme://$server:$port$request_uri;
add_header X-Served-By $host;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Scheme $scheme;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass $forward_scheme://$server:$port;
proxy_pass $upstream;

View File

@ -85,7 +85,6 @@ http {
stream {
# Files generated by NPM
include /data/nginx/ssl_passthrough_host/hosts[.]conf;
include /data/nginx/stream/*.conf;
# Custom

View File

@ -12,7 +12,6 @@ mkdir -p /tmp/nginx/body \
/data/nginx/default_www \
/data/nginx/proxy_host \
/data/nginx/redirection_host \
/data/nginx/ssl_passthrough_host \
/data/nginx/stream \
/data/nginx/dead_host \
/data/nginx/temp \

View File

@ -172,28 +172,3 @@ value by specifying it as a Docker environment variable. The default if not spec
X_FRAME_OPTIONS: "sameorigin"
...
```
## SSL Passthrough
SSL Passthrough will allow you to proxy a server without [SSL termination](https://en.wikipedia.org/wiki/TLS_termination_proxy). This means the SSL encryption of the server will be passed right through the proxy, retaining the original certificate.
Because of the SSL encryption the proxy does not know anything about the traffic and it just relies on an SSL feature called [Server Name Indication](https://en.wikipedia.org/wiki/Server_Name_Indication) to know where to send this network packet. This also means if the client does not provide this additional information, accessing the site through the proxy won't be possible. But most modern browsers include this information a HTTPS requests.
Due to nginx constraints using SSL Passthrough comes with **a performance penalty for other hosts**, since all hosts (including normal proxy hosts) now have to pass through this additional step and basically being proxied twice. If you want to retain the upstream SSL certificate but do not need your service to be available on port 443, it is recommended to use a stream host instead.
To enable SSL Passthrough on your npm instance you need to do two things: add the environment variable `ENABLE_SSL_PASSTHROUGH` with the value `"true"`, and expose port 444 instead of 443 to the outside as port 443.
```yml
version: '3'
services:
app:
...
ports:
- '80:80'
- '81:81'
- '443:444' # Expose internal port 444 instead of 443 as SSL port
environment:
...
ENABLE_SSL_PASSTHROUGH: "true" # Enable SSL Passthrough
...
```

View File

@ -515,76 +515,6 @@ module.exports = {
}
},
SslPassthroughHosts: {
/**
* @param {Array} [expand]
* @param {String} [query]
* @returns {Promise}
*/
getFeatureEnabled: function () {
return fetch('get', 'ssl-passthrough-enabled');
},
/**
* @param {Array} [expand]
* @param {String} [query]
* @returns {Promise}
*/
getAll: function (expand, query) {
return getAllObjects('nginx/ssl-passthrough-hosts', expand, query);
},
/**
* @param {Object} data
*/
create: function (data) {
return fetch('post', 'nginx/ssl-passthrough-hosts', data);
},
/**
* @param {Object} data
* @param {Number} data.id
* @returns {Promise}
*/
update: function (data) {
let id = data.id;
delete data.id;
return fetch('put', 'nginx/ssl-passthrough-hosts/' + id, data);
},
/**
* @param {Number} id
* @returns {Promise}
*/
delete: function (id) {
return fetch('delete', 'nginx/ssl-passthrough-hosts/' + id);
},
/**
* @param {Number} id
* @returns {Promise}
*/
get: function (id) {
return fetch('get', 'nginx/ssl-passthrough-hosts/' + id);
},
/**
* @param {Number} id
* @returns {Promise}
*/
enable: function (id) {
return fetch('post', 'nginx/ssl-passthrough-hosts/' + id + '/enable');
},
/**
* @param {Number} id
* @returns {Promise}
*/
disable: function (id) {
return fetch('post', 'nginx/ssl-passthrough-hosts/' + id + '/disable');
}
},
DeadHosts: {
/**
* @param {Array} [expand]

View File

@ -221,46 +221,6 @@ module.exports = {
}
},
/**
* Nginx SSL Passthrough Hosts
*/
showNginxSslPassthrough: function () {
if (Cache.User.isAdmin() || Cache.User.canView('ssl_passthrough_hosts')) {
let controller = this;
require(['./main', './nginx/ssl-passthrough/main'], (App, View) => {
controller.navigate('/nginx/ssl-passthrough');
App.UI.showAppContent(new View());
});
}
},
/**
* SSL Passthrough Hosts Form
*
* @param [model]
*/
showNginxSslPassthroughForm: function (model) {
if (Cache.User.isAdmin() || Cache.User.canManage('ssl_passthrough_hosts')) {
require(['./main', './nginx/ssl-passthrough/form'], function (App, View) {
App.UI.showModalDialog(new View({model: model}));
});
}
},
/**
* SSL Passthrough Hosts Delete Confirm
*
* @param model
*/
showNginxSslPassthroughDeleteConfirm: function (model) {
if (Cache.User.isAdmin() || Cache.User.canManage('ssl_passthrough_hosts')) {
require(['./main', './nginx/ssl-passthrough/delete'], function (App, View) {
App.UI.showModalDialog(new View({model: model}));
});
}
},
/**
* Nginx Dead Hosts
*/

View File

@ -1,19 +0,0 @@
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><%- i18n('ssl-passthrough-hosts', 'delete') %></h5>
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal">&nbsp;</button>
</div>
<div class="modal-body">
<form>
<div class="row">
<div class="col-sm-12 col-md-12">
<%= i18n('ssl-passthrough-hosts', 'delete-confirm') %>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary cancel" data-dismiss="modal"><%- i18n('str', 'cancel') %></button>
<button type="button" class="btn btn-danger save"><%- i18n('str', 'sure') %></button>
</div>
</div>

View File

@ -1,32 +0,0 @@
const Mn = require('backbone.marionette');
const App = require('../../main');
const template = require('./delete.ejs');
module.exports = Mn.View.extend({
template: template,
className: 'modal-dialog',
ui: {
form: 'form',
buttons: '.modal-footer button',
cancel: 'button.cancel',
save: 'button.save'
},
events: {
'click @ui.save': function (e) {
e.preventDefault();
App.Api.Nginx.SslPassthroughHosts.delete(this.model.get('id'))
.then(() => {
App.Controller.showNginxSslPassthrough();
App.UI.closeModal();
})
.catch(err => {
alert(err.message);
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
});
}
}
});

View File

@ -1,34 +0,0 @@
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><%- i18n('ssl-passthrough-hosts', 'form-title', {id: id}) %></h5>
<button type="button" class="close cancel" aria-label="Close" data-dismiss="modal">&nbsp;</button>
</div>
<div class="modal-body">
<form>
<div class="row">
<div class="col-sm-12 col-md-12">
<div class="form-group">
<label class="form-label"><%- i18n('all-hosts', 'domain-name') %> <span class="form-required">*</span></label>
<input type="text" name="domain_name" class="form-control" id="input-domain" placeholder="example.com" value="<%- domain_name %>" required>
</div>
</div>
<div class="col-sm-8 col-md-8">
<div class="form-group">
<label class="form-label"><%- i18n('ssl-passthrough-hosts', 'forwarding-host') %><span class="form-required">*</span></label>
<input type="text" name="forwarding_host" class="form-control text-monospace" placeholder="example.com or 10.0.0.1 or 2001:db8:3333:4444:5555:6666:7777:8888" value="<%- forwarding_host %>" autocomplete="off" maxlength="255" required>
</div>
</div>
<div class="col-sm-4 col-md-4">
<div class="form-group">
<label class="form-label"><%- i18n('ssl-passthrough-hosts', 'forwarding-port') %> <span class="form-required">*</span></label>
<input name="forwarding_port" type="number" class="form-control text-monospace" placeholder="eg: 443" value="<%- forwarding_port %>" required>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary cancel" data-dismiss="modal"><%- i18n('str', 'cancel') %></button>
<button type="button" class="btn btn-teal save"><%- i18n('str', 'save') %></button>
</div>
</div>

View File

@ -1,74 +0,0 @@
const Mn = require('backbone.marionette');
const App = require('../../main');
const SslPassthroughModel = require('../../../models/ssl-passthrough-host');
const template = require('./form.ejs');
require('jquery-serializejson');
require('jquery-mask-plugin');
require('selectize');
module.exports = Mn.View.extend({
template: template,
className: 'modal-dialog',
ui: {
form: 'form',
forwarding_host: 'input[name="forwarding_host"]',
buttons: '.modal-footer button',
cancel: 'button.cancel',
save: 'button.save'
},
events: {
'change @ui.switches': function () {
this.ui.type_error.hide();
},
'click @ui.save': function (e) {
e.preventDefault();
if (!this.ui.form[0].checkValidity()) {
$('<input type="submit">').hide().appendTo(this.ui.form).click().remove();
return;
}
let view = this;
let data = this.ui.form.serializeJSON();
// Manipulate
data.forwarding_port = parseInt(data.forwarding_port, 10);
let method = App.Api.Nginx.SslPassthroughHosts.create;
let is_new = true;
if (this.model.get('id')) {
// edit
is_new = false;
method = App.Api.Nginx.SslPassthroughHosts.update;
data.id = this.model.get('id');
}
this.ui.buttons.prop('disabled', true).addClass('btn-disabled');
method(data)
.then(result => {
view.model.set(result);
App.UI.closeModal(function () {
if (is_new) {
App.Controller.showNginxSslPassthrough();
}
});
})
.catch(err => {
alert(err.message);
this.ui.buttons.prop('disabled', false).removeClass('btn-disabled');
});
}
},
initialize: function (options) {
if (typeof options.model === 'undefined' || !options.model) {
this.model = new SslPassthroughModel.Model();
}
}
});

View File

@ -1,43 +0,0 @@
<td class="text-center">
<div class="avatar d-block" style="background-image: url(<%- owner.avatar || '/images/default-avatar.jpg' %>)" title="Owned by <%- owner.name %>">
<span class="avatar-status <%- owner.is_disabled ? 'bg-red' : 'bg-green' %>"></span>
</div>
</td>
<td>
<div class="text-monospace">
<%- domain_name %>
</div>
<div class="small text-muted">
<%- i18n('str', 'created-on', {date: formatDbDate(created_on, 'Do MMMM YYYY')}) %>
</div>
</td>
<td>
<div class="text-monospace"><%- forwarding_host %>:<%- forwarding_port %></div>
</td>
<td>
<%
var o = isOnline();
if (!enabled) { %>
<span class="status-icon bg-warning"></span> <%- i18n('str', 'disabled') %>
<% } else if (o === true) { %>
<span class="status-icon bg-success"></span> <%- i18n('str', 'online') %>
<% } else if (o === false) { %>
<span title="<%- getOfflineError() %>"><span class="status-icon bg-danger"></span> <%- i18n('str', 'offline') %></span>
<% } else { %>
<span class="status-icon bg-warning"></span> <%- i18n('str', 'unknown') %>
<% } %>
</td>
<% if (canManage) { %>
<td class="text-right">
<div class="item-action dropdown">
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
<div class="dropdown-menu dropdown-menu-right">
<span class="dropdown-header"><%- i18n('audit-log', 'ssl-passthrough-host') %> #<%- id %></span>
<a href="#" class="edit dropdown-item"><i class="dropdown-icon fe fe-edit"></i> <%- i18n('str', 'edit') %></a>
<a href="#" class="able dropdown-item"><i class="dropdown-icon fe fe-power"></i> <%- i18n('str', enabled ? 'disable' : 'enable') %></a>
<div class="dropdown-divider"></div>
<a href="#" class="delete dropdown-item"><i class="dropdown-icon fe fe-trash-2"></i> <%- i18n('str', 'delete') %></a>
</div>
</div>
</td>
<% } %>

View File

@ -1,54 +0,0 @@
const Mn = require('backbone.marionette');
const App = require('../../../main');
const template = require('./item.ejs');
module.exports = Mn.View.extend({
template: template,
tagName: 'tr',
ui: {
able: 'a.able',
edit: 'a.edit',
delete: 'a.delete'
},
events: {
'click @ui.able': function (e) {
e.preventDefault();
let id = this.model.get('id');
App.Api.Nginx.SslPassthroughHosts[this.model.get('enabled') ? 'disable' : 'enable'](id)
.then(() => {
return App.Api.Nginx.SslPassthroughHosts.get(id)
.then(row => {
this.model.set(row);
});
});
},
'click @ui.edit': function (e) {
e.preventDefault();
App.Controller.showNginxSslPassthroughForm(this.model);
},
'click @ui.delete': function (e) {
e.preventDefault();
App.Controller.showNginxSslPassthroughDeleteConfirm(this.model);
}
},
templateContext: {
canManage: App.Cache.User.canManage('ssl_passthrough_hosts'),
isOnline: function () {
return typeof this.meta.nginx_online === 'undefined' ? null : this.meta.nginx_online;
},
getOfflineError: function () {
return this.meta.nginx_err || '';
}
},
initialize: function () {
this.listenTo(this.model, 'change', this.render);
}
});

View File

@ -1,12 +0,0 @@
<thead>
<th width="30">&nbsp;</th>
<th><%- i18n('all-hosts', 'domain-name') %></th>
<th><%- i18n('str', 'destination') %></th>
<th><%- i18n('str', 'status') %></th>
<% if (canManage) { %>
<th>&nbsp;</th>
<% } %>
</thead>
<tbody>
<!-- items -->
</tbody>

View File

@ -1,32 +0,0 @@
const Mn = require('backbone.marionette');
const App = require('../../../main');
const ItemView = require('./item');
const template = require('./main.ejs');
const TableBody = Mn.CollectionView.extend({
tagName: 'tbody',
childView: ItemView
});
module.exports = Mn.View.extend({
tagName: 'table',
className: 'table table-hover table-outline table-vcenter card-table',
template: template,
regions: {
body: {
el: 'tbody',
replaceElement: true
}
},
templateContext: {
canManage: App.Cache.User.canManage('ssl_passthrough_hosts')
},
onRender: function () {
this.showChildView('body', new TableBody({
collection: this.collection
}));
}
});

View File

@ -1,23 +0,0 @@
<div class="card">
<div class="card-status bg-dark"></div>
<div class="card-header">
<h3 class="card-title"><%- i18n('ssl-passthrough-hosts', 'title') %></h3>
<div class="card-options">
<a href="#" class="btn btn-outline-secondary btn-sm ml-2 help"><i class="fe fe-help-circle"></i></a>
<% if (showAddButton) { %>
<a href="#" class="btn btn-outline-dark btn-sm ml-2 add-item"><%- i18n('ssl-passthrough-hosts', 'add') %></a>
<% } %>
</div>
</div>
<div class="card-body no-padding min-100">
<div id="ssl-passthrough-disabled-info" class="alert alert-danger rounded-0 mb-0">
<%= i18n('ssl-passthrough-hosts', 'is-disabled-warning', {url: 'https://nginxproxymanager.com/advanced-config/#ssl-passthrough'}) %>
</div>
<div class="dimmer active">
<div class="loader"></div>
<div class="dimmer-content list-region">
<!-- List Region -->
</div>
</div>
</div>
</div>

View File

@ -1,91 +0,0 @@
const Mn = require('backbone.marionette');
const App = require('../../main');
const SslPassthroughModel = require('../../../models/ssl-passthrough-host');
const ListView = require('./list/main');
const ErrorView = require('../../error/main');
const EmptyView = require('../../empty/main');
const template = require('./main.ejs');
module.exports = Mn.View.extend({
id: 'nginx-ssl-passthrough',
template: template,
ui: {
list_region: '.list-region',
add: '.add-item',
help: '.help',
dimmer: '.dimmer',
disabled_info: '#ssl-passthrough-disabled-info'
},
regions: {
list_region: '@ui.list_region'
},
events: {
'click @ui.add': function (e) {
e.preventDefault();
App.Controller.showNginxSslPassthroughForm();
},
'click @ui.help': function (e) {
e.preventDefault();
App.Controller.showHelp(App.i18n('ssl-passthrough-hosts', 'help-title'), App.i18n('ssl-passthrough-hosts', 'help-content'));
}
},
templateContext: {
showAddButton: App.Cache.User.canManage('ssl_passthrough_hosts')
},
onRender: function () {
let view = this;
view.ui.disabled_info.hide();
App.Api.Nginx.SslPassthroughHosts.getFeatureEnabled().then((response) => {
if (response.ssl_passthrough_enabled === false) {
view.ui.disabled_info.show();
} else {
view.ui.disabled_info.hide();
}
});
App.Api.Nginx.SslPassthroughHosts.getAll(['owner'])
.then(response => {
if (!view.isDestroyed()) {
if (response && response.length) {
view.showChildView('list_region', new ListView({
collection: new SslPassthroughModel.Collection(response)
}));
} else {
let manage = App.Cache.User.canManage('ssl_passthrough_hosts');
view.showChildView('list_region', new EmptyView({
title: App.i18n('ssl-passthrough-hosts', 'empty'),
subtitle: App.i18n('all-hosts', 'empty-subtitle', {manage: manage}),
link: manage ? App.i18n('ssl-passthrough-hosts', 'add') : null,
btn_color: 'dark',
permission: 'ssl-passthrough-hosts',
action: function () {
App.Controller.showNginxSslPassthroughForm();
}
}));
}
}
})
.catch(err => {
view.showChildView('list_region', new ErrorView({
code: err.code,
message: err.message,
retry: function () {
App.Controller.showNginxSslPassthrough();
}
}));
console.error(err);
})
.then(() => {
view.ui.dimmer.removeClass('active');
});
}
});

View File

@ -4,17 +4,16 @@ const Controller = require('./controller');
module.exports = AppRouter.default.extend({
controller: Controller,
appRoutes: {
users: 'showUsers',
logout: 'logout',
'nginx/proxy': 'showNginxProxy',
'nginx/redirection': 'showNginxRedirection',
'nginx/404': 'showNginxDead',
'nginx/ssl-passthrough': 'showNginxSslPassthrough',
'nginx/stream': 'showNginxStream',
'nginx/access': 'showNginxAccess',
'nginx/certificates': 'showNginxCertificates',
'audit-log': 'showAuditLog',
'settings': 'showSettings',
'*default': 'showDashboard'
users: 'showUsers',
logout: 'logout',
'nginx/proxy': 'showNginxProxy',
'nginx/redirection': 'showNginxRedirection',
'nginx/404': 'showNginxDead',
'nginx/stream': 'showNginxStream',
'nginx/access': 'showNginxAccess',
'nginx/certificates': 'showNginxCertificates',
'audit-log': 'showAuditLog',
'settings': 'showSettings',
'*default': 'showDashboard'
}
});

View File

@ -20,10 +20,6 @@
<a href="/nginx/stream" class="dropdown-item "><%- i18n('streams', 'title') %></a>
<% } %>
<% if (canShow('ssl_passthrough_hosts')) { %>
<a href="/nginx/ssl-passthrough" class="dropdown-item "><%- i18n('ssl-passthrough-hosts', 'title') %></a>
<% } %>
<% if (canShow('dead_hosts')) { %>
<a href="/nginx/404" class="dropdown-item "><%- i18n('dead-hosts', 'title') %></a>
<% } %>

View File

@ -31,9 +31,9 @@
</div>
<%
var list = ['proxy-hosts', 'redirection-hosts', 'dead-hosts', 'streams', 'ssl-passthrough-hosts', 'access-lists', 'certificates'];
var list = ['proxy-hosts', 'redirection-hosts', 'dead-hosts', 'streams', 'access-lists', 'certificates'];
list.map(function(item) {
var perm = item.replace(/-/g, '_');
var perm = item.replace('-', '_');
%>
<div class="col-sm-12 col-md-12">
<div class="form-group">

View File

@ -29,13 +29,12 @@ module.exports = Mn.View.extend({
if (view.model.isAdmin()) {
// Force some attributes for admin
data = _.assign({}, data, {
access_lists: 'manage',
dead_hosts: 'manage',
proxy_hosts: 'manage',
redirection_hosts: 'manage',
ssl_passthrough_hosts: 'manage',
streams: 'manage',
certificates: 'manage'
access_lists: 'manage',
dead_hosts: 'manage',
proxy_hosts: 'manage',
redirection_hosts: 'manage',
streams: 'manage',
certificates: 'manage'
});
}

View File

@ -72,7 +72,6 @@
"enable-ssl": "Enable SSL",
"force-ssl": "Force SSL",
"http2-support": "HTTP/2 Support",
"domain-name": "Domain Name",
"domain-names": "Domain Names",
"cert-provider": "Certificate Provider",
"block-exploits": "Block Common Exploits",
@ -116,19 +115,6 @@
"processing-info": "Processing... This might take a few minutes.",
"passphrase-protection-support-info": "Key files protected with a passphrase are not supported."
},
"ssl-passthrough-hosts": {
"title": "SSL Passthrough Hosts",
"empty": "There are no SSL Passthrough Hosts",
"add": "Add SSL Passthrough Hosts",
"form-title": "{id, select, undefined{New} other{Edit}} SSL Passthrough Host",
"forwarding-host": "Forward Host",
"forwarding-port": "Forward Port",
"delete": "Delete SSL Passthrough Host",
"delete-confirm": "Are you sure you want to delete this SSL Passthrough Host?",
"is-disabled-warning": "SSL Passthrough Hosts are not enabled in the environment. Please see <a href=\"{url}\" target=\"_blank\">the docs</a> for more information.",
"help-title": "What is an SSL Passthrough Host?",
"help-content": "An SSL Passthrough Host will allow you to proxy a server without SSL termination. This means the SSL encryption of the server will be passed right through the proxy, retaining the upstream certificate.\n Because of the SSL encryption the proxy does not know anything about the traffic, and it just relies on an SSL feature called Server Name Indication to know where to send this packet. This also means if the client does not provide this additional information, accessing the site through the proxy won't be possible. But most modern browsers include this information in HTTP requests.\n\nDue to nginx constraints using SSL Passthrough comes with a performance penalty for other hosts, since all hosts (including normal proxy hosts) now have to pass through this additional step and basically being proxied twice. If you want to retain the upstream SSL certificate but do not need your service to be available on port 443, it is recommended to use a stream host instead."
},
"proxy-hosts": {
"title": "Proxy Hosts",
"empty": "There are no Proxy Hosts",
@ -262,7 +248,6 @@
"proxy-host": "Proxy Host",
"redirection-host": "Redirection Host",
"dead-host": "404 Host",
"ssl-passthrough-host": "SSL Passthrough Host",
"stream": "Stream",
"user": "User",
"certificate": "Certificate",

View File

@ -1,27 +0,0 @@
const Backbone = require('backbone');
const model = Backbone.Model.extend({
idAttribute: 'id',
defaults: function () {
return {
id: undefined,
created_on: null,
modified_on: null,
domain_name: null,
forwarding_host: null,
forwarding_port: null,
enabled: true,
meta: {},
// The following are expansions:
owner: null
};
}
});
module.exports = {
Model: model,
Collection: Backbone.Collection.extend({
model: model
})
};

View File

@ -12,15 +12,6 @@ a:hover {
color: darken($primary-color, 10%);
}
.alert-danger a {
color: #6b1110;
text-decoration: underline;
}
a:hover {
color: darken(#6b1110, 10%);
}
.dropdown-header {
padding-left: 1rem;
}

View File

@ -13,8 +13,8 @@ module.exports = {
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/[name].bundle.js',
chunkFilename: 'js/[name].bundle.[id].js',
filename: `js/[name].bundle.js?v=${PACKAGE.version}`,
chunkFilename: `js/[name].bundle.[id].js?v=${PACKAGE.version}`,
publicPath: '/'
},
resolve: {

View File

@ -12,7 +12,7 @@
* version_requirement: "Optional package version requirements (e.g. ==1.3 or >=1.2,<2.0, see https://www.python.org/dev/peps/pep-0440/#version-specifiers)",
* dependencies: "Additional dependencies, space separated (as you would pass it to pip install)",
* credentials: `Template of the credentials file`,
* full_plugin_name: "The full plugin name as used in the commandline with certbot, including prefixes, e.g. 'certbot-dns-njalla:dns-njalla'",
* full_plugin_name: "The full plugin name as used in the commandline with certbot, e.g. 'dns-njalla'",
* },
* ...
* }
@ -26,18 +26,18 @@ module.exports = {
package_name: 'certbot-dns-acmedns',
version_requirement: '~=0.1.0',
dependencies: '',
credentials: `certbot_dns_acmedns:dns_acmedns_api_url = http://acmedns-server/
certbot_dns_acmedns:dns_acmedns_registration_file = /data/acme-registration.json`,
full_plugin_name: 'certbot-dns-acmedns:dns-acmedns',
credentials: `dns_acmedns_api_url = http://acmedns-server/
dns_acmedns_registration_file = /data/acme-registration.json`,
full_plugin_name: 'dns-acmedns',
},
aliyun: {
display_name: 'Aliyun',
package_name: 'certbot-dns-aliyun',
version_requirement: '~=0.38.1',
dependencies: '',
credentials: `certbot_dns_aliyun:dns_aliyun_access_key = 12345678
certbot_dns_aliyun:dns_aliyun_access_key_secret = 1234567890abcdef1234567890abcdef`,
full_plugin_name: 'certbot-dns-aliyun:dns-aliyun',
credentials: `dns_aliyun_access_key = 12345678
dns_aliyun_access_key_secret = 1234567890abcdef1234567890abcdef`,
full_plugin_name: 'dns-aliyun',
},
//####################################################//
azure: {
@ -107,9 +107,9 @@ dns_cloudxns_secret_key = 1122334455667788`,
package_name: 'certbot-dns-corenetworks',
version_requirement: '~=0.1.4',
dependencies: '',
credentials: `certbot_dns_corenetworks:dns_corenetworks_username = asaHB12r
certbot_dns_corenetworks:dns_corenetworks_password = secure_password`,
full_plugin_name: 'certbot-dns-corenetworks:dns-corenetworks',
credentials: `dns_corenetworks_username = asaHB12r
dns_corenetworks_password = secure_password`,
full_plugin_name: 'dns-corenetworks',
},
//####################################################//
cpanel: {
@ -117,10 +117,10 @@ certbot_dns_corenetworks:dns_corenetworks_password = secure_password`,
package_name: 'certbot-dns-cpanel',
version_requirement: '~=0.2.2',
dependencies: '',
credentials: `certbot_dns_cpanel:cpanel_url = https://cpanel.example.com:2083
certbot_dns_cpanel:cpanel_username = user
certbot_dns_cpanel:cpanel_password = hunter2`,
full_plugin_name: 'certbot-dns-cpanel:cpanel',
credentials: `cpanel_url = https://cpanel.example.com:2083
cpanel_username = user
cpanel_password = hunter2`,
full_plugin_name: 'cpanel',
},
//####################################################//
desec: {
@ -128,9 +128,9 @@ certbot_dns_cpanel:cpanel_password = hunter2`,
package_name: 'certbot-dns-desec',
version_requirement: '~=0.3.0',
dependencies: '',
credentials: `certbot_dns_desec:dns_desec_token = YOUR_DESEC_API_TOKEN
certbot_dns_desec:dns_desec_endpoint = https://desec.io/api/v1/`,
full_plugin_name: 'certbot-dns-desec:dns-desec',
credentials: `dns_desec_token = YOUR_DESEC_API_TOKEN
dns_desec_endpoint = https://desec.io/api/v1/`,
full_plugin_name: 'dns-desec',
},
//####################################################//
duckdns: {
@ -186,9 +186,9 @@ dns_dnsmadeeasy_secret_key = c9b5625f-9834-4ff8-baba-4ed5f32cae55`,
package_name: 'certbot-dns-dnspod',
version_requirement: '~=0.1.0',
dependencies: '',
credentials: `certbot_dns_dnspod:dns_dnspod_email = "DNSPOD-API-REQUIRES-A-VALID-EMAIL"
certbot_dns_dnspod:dns_dnspod_api_token = "DNSPOD-API-TOKEN"`,
full_plugin_name: 'certbot-dns-dnspod:dns-dnspod',
credentials: `dns_dnspod_email = "DNSPOD-API-REQUIRES-A-VALID-EMAIL"
dns_dnspod_api_token = "DNSPOD-API-TOKEN"`,
full_plugin_name: 'dns-dnspod',
},
//####################################################//
dynu: {
@ -196,8 +196,8 @@ certbot_dns_dnspod:dns_dnspod_api_token = "DNSPOD-API-TOKEN"`,
package_name: 'certbot-dns-dynu',
version_requirement: '~=0.0.1',
dependencies: '',
credentials: 'certbot_dns_dynu:dns_dynu_auth_token = YOUR_DYNU_AUTH_TOKEN',
full_plugin_name: 'certbot-dns-dynu:dns-dynu',
credentials: 'dns_dynu_auth_token = YOUR_DYNU_AUTH_TOKEN',
full_plugin_name: 'dns-dynu',
},
//####################################################//
eurodns: {
@ -208,16 +208,20 @@ certbot_dns_dnspod:dns_dnspod_api_token = "DNSPOD-API-TOKEN"`,
credentials: `dns_eurodns_applicationId = myuser
dns_eurodns_apiKey = mysecretpassword
dns_eurodns_endpoint = https://rest-api.eurodns.com/user-api-gateway/proxy`,
full_plugin_name: 'certbot-dns-eurodns:dns-eurodns',
full_plugin_name: 'dns-eurodns',
},
//####################################################//
gandi: {
display_name: 'Gandi Live DNS',
package_name: 'certbot_plugin_gandi',
version_requirement: '~=1.2.5',
version_requirement: '~=1.3.2',
dependencies: '',
credentials: 'certbot_plugin_gandi:dns_api_key = APIKEY',
full_plugin_name: 'certbot-plugin-gandi:dns',
credentials: `# live dns v5 api key
dns_gandi_api_key=APIKEY
# optional organization id, remove it if not used
dns_gandi_sharing_id=SHARINGID`,
full_plugin_name: 'dns-gandi',
},
//####################################################//
godaddy: {
@ -247,8 +251,8 @@ dns_godaddy_key = abcdef0123456789abcdef01234567abcdef0123`,
package_name: 'certbot-dns-hetzner',
version_requirement: '~=1.0.4',
dependencies: '',
credentials: 'certbot_dns_hetzner:dns_hetzner_api_token = 0123456789abcdef0123456789abcdef',
full_plugin_name: 'certbot-dns-hetzner:dns-hetzner',
credentials: 'dns_hetzner_api_token = 0123456789abcdef0123456789abcdef',
full_plugin_name: 'dns-hetzner',
},
//####################################################//
infomaniak: {
@ -256,8 +260,8 @@ dns_godaddy_key = abcdef0123456789abcdef01234567abcdef0123`,
package_name: 'certbot-dns-infomaniak',
version_requirement: '~=0.1.12',
dependencies: '',
credentials: 'certbot_dns_infomaniak:dns_infomaniak_token = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
full_plugin_name: 'certbot-dns-infomaniak:dns-infomaniak',
credentials: 'dns_infomaniak_token = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
full_plugin_name: 'dns-infomaniak',
},
//####################################################//
inwx: {
@ -265,22 +269,22 @@ dns_godaddy_key = abcdef0123456789abcdef01234567abcdef0123`,
package_name: 'certbot-dns-inwx',
version_requirement: '~=2.1.2',
dependencies: '',
credentials: `certbot_dns_inwx:dns_inwx_url = https://api.domrobot.com/xmlrpc/
certbot_dns_inwx:dns_inwx_username = your_username
certbot_dns_inwx:dns_inwx_password = your_password
certbot_dns_inwx:dns_inwx_shared_secret = your_shared_secret optional`,
full_plugin_name: 'certbot-dns-inwx:dns-inwx',
credentials: `dns_inwx_url = https://api.domrobot.com/xmlrpc/
dns_inwx_username = your_username
dns_inwx_password = your_password
dns_inwx_shared_secret = your_shared_secret optional`,
full_plugin_name: 'dns-inwx',
},
//####################################################//
ionos: {
display_name: 'IONOS',
package_name: 'certbot-dns-ionos',
version_requirement: '~=0.0.7',
version_requirement: '==2021.9.20.post1',
dependencies: '',
credentials: `certbot_dns_ionos:dns_ionos_prefix = myapikeyprefix
certbot_dns_ionos:dns_ionos_secret = verysecureapikeysecret
certbot_dns_ionos:dns_ionos_endpoint = https://api.hosting.ionos.com`,
full_plugin_name: 'certbot-dns-ionos:dns-ionos',
credentials: `dns_ionos_prefix = myapikeyprefix
dns_ionos_secret = verysecureapikeysecret
dns_ionos_endpoint = https://api.hosting.ionos.com`,
full_plugin_name: 'dns-ionos',
},
//####################################################//
ispconfig: {
@ -288,10 +292,10 @@ certbot_dns_ionos:dns_ionos_endpoint = https://api.hosting.ionos.com`,
package_name: 'certbot-dns-ispconfig',
version_requirement: '~=0.2.0',
dependencies: '',
credentials: `certbot_dns_ispconfig:dns_ispconfig_username = myremoteuser
certbot_dns_ispconfig:dns_ispconfig_password = verysecureremoteuserpassword
certbot_dns_ispconfig:dns_ispconfig_endpoint = https://localhost:8080`,
full_plugin_name: 'certbot-dns-ispconfig:dns-ispconfig',
credentials: `dns_ispconfig_username = myremoteuser
dns_ispconfig_password = verysecureremoteuserpassword
dns_ispconfig_endpoint = https://localhost:8080`,
full_plugin_name: 'dns-ispconfig',
},
//####################################################//
isset: {
@ -299,19 +303,19 @@ certbot_dns_ispconfig:dns_ispconfig_endpoint = https://localhost:8080`,
package_name: 'certbot-dns-isset',
version_requirement: '~=0.0.3',
dependencies: '',
credentials: `certbot_dns_isset:dns_isset_endpoint="https://customer.isset.net/api"
certbot_dns_isset:dns_isset_token="<token>"`,
full_plugin_name: 'certbot-dns-isset:dns-isset',
credentials: `dns_isset_endpoint="https://customer.isset.net/api"
dns_isset_token="<token>"`,
full_plugin_name: 'dns-isset',
},
joker: {
display_name: 'Joker',
package_name: 'certbot-dns-joker',
version_requirement: '~=1.1.0',
dependencies: '',
credentials: `certbot_dns_joker:dns_joker_username = <Dynamic DNS Authentication Username>
certbot_dns_joker:dns_joker_password = <Dynamic DNS Authentication Password>
certbot_dns_joker:dns_joker_domain = <Dynamic DNS Domain>`,
full_plugin_name: 'certbot-dns-joker:dns-joker',
credentials: `dns_joker_username = <Dynamic DNS Authentication Username>
dns_joker_password = <Dynamic DNS Authentication Password>
dns_joker_domain = <Dynamic DNS Domain>`,
full_plugin_name: 'dns-joker',
},
//####################################################//
linode: {
@ -349,10 +353,10 @@ dns_luadns_token = 0123456789abcdef0123456789abcdef`,
package_name: 'certbot-dns-netcup',
version_requirement: '~=1.0.0',
dependencies: '',
credentials: `certbot_dns_netcup:dns_netcup_customer_id = 123456
certbot_dns_netcup:dns_netcup_api_key = 0123456789abcdef0123456789abcdef01234567
certbot_dns_netcup:dns_netcup_api_password = abcdef0123456789abcdef01234567abcdef0123`,
full_plugin_name: 'certbot-dns-netcup:dns-netcup',
credentials: `dns_netcup_customer_id = 123456
dns_netcup_api_key = 0123456789abcdef0123456789abcdef01234567
dns_netcup_api_password = abcdef0123456789abcdef01234567abcdef0123`,
full_plugin_name: 'dns-netcup',
},
//####################################################//
njalla: {
@ -360,8 +364,8 @@ certbot_dns_netcup:dns_netcup_api_password = abcdef0123456789abcdef01234567abcde
package_name: 'certbot-dns-njalla',
version_requirement: '~=1.0.0',
dependencies: '',
credentials: 'certbot_dns_njalla:dns_njalla_token = 0123456789abcdef0123456789abcdef01234567',
full_plugin_name: 'certbot-dns-njalla:dns-njalla',
credentials: 'dns_njalla_token = 0123456789abcdef0123456789abcdef01234567',
full_plugin_name: 'dns-njalla',
},
//####################################################//
nsone: {
@ -414,9 +418,9 @@ dns_porkbun_secret=your-porkbun-api-secret`,
package_name: 'certbot-dns-powerdns',
version_requirement: '~=0.2.0',
dependencies: '',
credentials: `certbot_dns_powerdns:dns_powerdns_api_url = https://api.mypowerdns.example.org
certbot_dns_powerdns:dns_powerdns_api_key = AbCbASsd!@34`,
full_plugin_name: 'certbot-dns-powerdns:dns-powerdns',
credentials: `dns_powerdns_api_url = https://api.mypowerdns.example.org
dns_powerdns_api_key = AbCbASsd!@34`,
full_plugin_name: 'dns-powerdns',
},
//####################################################//
regru: {
@ -463,9 +467,9 @@ aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY`,
package_name: 'certbot-dns-transip',
version_requirement: '~=0.3.3',
dependencies: '',
credentials: `certbot_dns_transip:dns_transip_username = my_username
certbot_dns_transip:dns_transip_key_file = /etc/letsencrypt/transip-rsa.key`,
full_plugin_name: 'certbot-dns-transip:dns-transip',
credentials: `dns_transip_username = my_username
dns_transip_key_file = /etc/letsencrypt/transip-rsa.key`,
full_plugin_name: 'dns-transip',
},
//####################################################//
vultr: {
@ -473,7 +477,18 @@ certbot_dns_transip:dns_transip_key_file = /etc/letsencrypt/transip-rsa.key`,
package_name: 'certbot-dns-vultr',
version_requirement: '~=1.0.3',
dependencies: '',
credentials: 'certbot_dns_vultr:dns_vultr_key = YOUR_VULTR_API_KEY',
full_plugin_name: 'certbot-dns-vultr:dns-vultr',
credentials: 'dns_vultr_key = YOUR_VULTR_API_KEY',
full_plugin_name: 'dns-vultr',
},
//####################################################//
websupportsk: {
display_name: 'Websupport.sk',
package_name: 'certbot-dns-websupportsk',
version_requirement: '~=0.1.6',
dependencies: '',
credentials: `dns_websupportsk_api_key = <api_key>
dns_websupportsk_secret = <secret>
dns_websupportsk_domain = example.com`,
full_plugin_name: 'dns-websupportsk',
},
};