Merge 08f95a9be649f5b31dfc1d66dd9a693274917f72 into 79d28f03d035114b80dcd04845306ecb98175074

This commit is contained in:
milad nazari 2025-02-23 16:21:31 +00:00 committed by GitHub
commit eb2d759fd8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 239 additions and 14 deletions

View File

@ -576,6 +576,7 @@ const internalCertificate = {
return internalCertificate.create(access, {
provider: 'letsencrypt',
domain_names: data.domain_names,
ssl_key_type: data.ssl_key_type,
meta: data.meta
});
},
@ -838,6 +839,7 @@ const internalCertificate = {
const cmd = `${certbotCommand} certonly ` +
`--config '${letsencryptConfig}' ` +
`--key-type '${certificate.ssl_key_type}' ` +
'--work-dir "/tmp/letsencrypt-lib" ' +
'--logs-dir "/tmp/letsencrypt-log" ' +
`--cert-name "npm-${certificate.id}" ` +
@ -879,6 +881,7 @@ const internalCertificate = {
let mainCmd = certbotCommand + ' certonly ' +
`--config '${letsencryptConfig}' ` +
`--key-type '${certificate.ssl_key_type}' ` +
'--work-dir "/tmp/letsencrypt-lib" ' +
'--logs-dir "/tmp/letsencrypt-log" ' +
`--cert-name 'npm-${certificate.id}' ` +
@ -975,6 +978,7 @@ const internalCertificate = {
const cmd = certbotCommand + ' renew --force-renewal ' +
`--config '${letsencryptConfig}' ` +
`--key-type '${certificate.ssl_key_type}' ` +
'--work-dir "/tmp/letsencrypt-lib" ' +
'--logs-dir "/tmp/letsencrypt-log" ' +
`--cert-name 'npm-${certificate.id}' ` +
@ -1008,6 +1012,7 @@ const internalCertificate = {
let mainCmd = certbotCommand + ' renew --force-renewal ' +
`--config "${letsencryptConfig}" ` +
`--key-type '${certificate.ssl_key_type}' ` +
'--work-dir "/tmp/letsencrypt-lib" ' +
'--logs-dir "/tmp/letsencrypt-log" ' +
`--cert-name 'npm-${certificate.id}' ` +
@ -1038,9 +1043,10 @@ const internalCertificate = {
*/
revokeLetsEncryptSsl: (certificate, throw_errors) => {
logger.info('Revoking Let\'sEncrypt certificates for Cert #' + certificate.id + ': ' + certificate.domain_names.join(', '));
const mainCmd = certbotCommand + ' revoke ' +
`--config '${letsencryptConfig}' ` +
`--key-type '${certificate.ssl_key_type}' ` +
'--work-dir "/tmp/letsencrypt-lib" ' +
'--logs-dir "/tmp/letsencrypt-log" ' +
`--cert-path '/etc/letsencrypt/live/npm-${certificate.id}/fullchain.pem' ` +

View File

@ -229,8 +229,32 @@ const internalHost = {
}
return response;
}
},
/**
* Internal use only, checks to see if the there is another default server record
*
* @param {String} hostname
* @param {String} [ignore_type] 'proxy', 'redirection', 'dead'
* @param {Integer} [ignore_id] Must be supplied if type was also supplied
* @returns {Promise}
*/
checkDefaultServerNotExist: function (hostname) {
let promises = proxyHostModel
.query()
.where('default_server', true)
.andWhere('domain_names', 'not like', '%' + hostname + '%');
return Promise.resolve(promises)
.then((promises_results) => {
if (promises_results.length > 0){
return false;
}
return true;
});
}
};
module.exports = internalHost;

View File

@ -44,6 +44,22 @@ const internalProxyHost = {
});
});
})
.then(() => {
// Get a list of the domain names and check each of them against default records
if (data.default_server){
if (data.domain_names.length > 1) {
throw new error.ValidationError('Default server cant be set for multiple domain!');
}
return internalHost
.checkDefaultServerNotExist(data.domain_names[0])
.then((result) => {
if (!result){
throw new error.ValidationError('One default server already exists');
}
});
}
})
.then(() => {
// At this point the domains should have been checked
data.owner_user_id = access.token.getUserId(1);
@ -141,6 +157,22 @@ const internalProxyHost = {
});
}
})
.then(() => {
// Get a list of the domain names and check each of them against default records
if (data.default_server){
if (data.domain_names.length > 1) {
throw new error.ValidationError('Default server cant be set for multiple domain!');
}
return internalHost
.checkDefaultServerNotExist(data.domain_names[0])
.then((result) => {
if (!result){
throw new error.ValidationError('One default server already exists');
}
});
}
})
.then(() => {
return internalProxyHost.get(access, {id: data.id});
})
@ -153,6 +185,7 @@ const internalProxyHost = {
if (create_certificate) {
return internalCertificate.createQuickCertificate(access, {
domain_names: data.domain_names || row.domain_names,
ssl_key_type: data.ssl_key_type || row.ssl_key_type,
meta: _.assign({}, row.meta, data.meta)
})
.then((cert) => {

View File

@ -0,0 +1,51 @@
const migrate_name = 'identifier_for_migrate';
const logger = require('../logger').migrate;
/**
* Migrate
*
* @see http://knexjs.org/#Schema
*
* @param {Object} knex
* @param {Promise} Promise
* @returns {Promise}
*/
exports.up = function (knex) {
logger.info(`[${migrate_name}] Migrating Up...`);
return knex.schema.alterTable('proxy_host', (table) => {
table.enum('ssl_key_type', ['ecdsa', 'rsa']).defaultTo('ecdsa').notNullable();
}).then(() => {
logger.info(`[${migrate_name}] Column 'ssl_key_type' added to table 'proxy_host'`);
return knex.schema.alterTable('certificate', (table) => {
table.enum('ssl_key_type', ['ecdsa', 'rsa']).defaultTo('ecdsa').notNullable();
});
}).then(() => {
logger.info(`[${migrate_name}] Column 'ssl_key_type' added to table 'proxy_host'`);
});
};
/**
* Undo Migrate
*
* @param {Object} knex
* @param {Promise} Promise
* @returns {Promise}
*/
exports.down = function (knex) {
logger.info(`[${migrate_name}] Migrating Down...`);
return knex.schema.alterTable('proxy_host', (table) => {
table.dropColumn('ssl_key_type');
}).then(() => {
logger.info(`[${migrate_name}] Column 'ssl_key_type' removed from table 'proxy_host'`);
return knex.schema.alterTable('certificate', (table) => {
table.dropColumn('ssl_key_type');
});
}).then(() => {
logger.info(`[${migrate_name}] Column 'ssl_key_type' removed from table 'proxy_host'`);
});
};

View File

@ -0,0 +1,40 @@
const migrate_name = 'default_server';
const logger = require('../logger').migrate;
/**
* Migrate Up
*
* @param {Object} knex
* @param {Promise} Promise
* @returns {Promise}
*/
exports.up = function (knex) {
logger.info(`[${migrate_name}] Migrating Up...`);
// Add default_server column to proxy_host table
return knex.schema.table('proxy_host', (table) => {
table.boolean('default_server').notNullable().defaultTo(false);
})
.then(() => {
logger.info(`[${migrate_name}] Column 'default_server' added to 'proxy_host' table`);
});
};
/**
* Migrate Down
*
* @param {Object} knex
* @param {Promise} Promise
* @returns {Promise}
*/
exports.down = function (knex) {
logger.info(`[${migrate_name}] Migrating Down...`);
// Remove default_server column from proxy_host table
return knex.schema.table('proxy_host', (table) => {
table.dropColumn('default_server');
})
.then(() => {
logger.info(`[${migrate_name}] Column 'default_server' removed from 'proxy_host' table`);
});
};

View File

@ -21,6 +21,7 @@ const boolFields = [
'enabled',
'hsts_enabled',
'hsts_subdomains',
'default_server',
];
class ProxyHost extends Model {

View File

@ -41,6 +41,11 @@
"owner": {
"$ref": "./user-object.json"
},
"ssl_key_type": {
"type": "string",
"enum": ["ecdsa", "rsa"],
"description": "Type of SSL key (either ecdsa or rsa)"
},
"meta": {
"type": "object",
"additionalProperties": false,

View File

@ -22,6 +22,7 @@
"enabled",
"locations",
"hsts_enabled",
"default_server",
"hsts_subdomains"
],
"additionalProperties": false,
@ -148,6 +149,15 @@
"$ref": "./access-list-object.json"
}
]
},
"ssl_key_type": {
"type": "string",
"enum": ["ecdsa", "rsa"],
"description": "Type of SSL key (either ecdsa or rsa)"
},
"default_server": {
"type": "boolean",
"description": "Defines if the server is the default for unmatched requests"
}
}
}

View File

@ -79,6 +79,12 @@
},
"locations": {
"$ref": "../../../../components/proxy-host-object.json#/properties/locations"
},
"ssl_key_type": {
"$ref": "../../../../components/proxy-host-object.json#/properties/ssl_key_type"
},
"default_server": {
"$ref": "../../../../components/proxy-host-object.json#/properties/default_server"
}
}
}

View File

@ -67,6 +67,12 @@
},
"locations": {
"$ref": "../../../components/proxy-host-object.json#/properties/locations"
},
"ssl_key_type": {
"$ref": "../../../components/proxy-host-object.json#/properties/ssl_key_type"
},
"default_server": {
"$ref": "../../../components/proxy-host-object.json#/properties/default_server"
}
}
}

View File

@ -1,13 +1,13 @@
listen 80;
listen 80{% if default_server == true %} default_server{% endif %};
{% if ipv6 -%}
listen [::]:80;
listen [::]:80{% if default_server == true %} default_server{% endif %};
{% else -%}
#listen [::]:80;
{% endif %}
{% if certificate -%}
listen 443 ssl;
listen 443 ssl{% if default_server == true %} default_server{% endif %};
{% if ipv6 -%}
listen [::]:443 ssl;
listen [::]:443 ssl{% if default_server == true %} default_server{% endif %};
{% else -%}
#listen [::]:443;
{% endif %}

View File

@ -53,9 +53,11 @@ COPY --from=testca /home/step/certs/root_ca.crt /etc/ssl/certs/NginxProxyManager
# Remove frontend service not required for prod, dev nginx config as well
RUN rm -rf /etc/s6-overlay/s6-rc.d/user/contents.d/frontend /etc/nginx/conf.d/dev.conf \
&& chmod 644 /etc/logrotate.d/nginx-proxy-manager
COPY docker/start-container /usr/local/bin/start-container
RUN chmod +x /usr/local/bin/start-container
VOLUME [ "/data" ]
ENTRYPOINT [ "/init" ]
ENTRYPOINT [ "start-container" ]
LABEL org.label-schema.schema-version="1.0" \
org.label-schema.license="MIT" \

View File

@ -36,5 +36,8 @@ RUN rm -f /etc/nginx/conf.d/production.conf \
COPY --from=pebbleca /test/certs/pebble.minica.pem /etc/ssl/certs/pebble.minica.pem
COPY --from=testca /home/step/certs/root_ca.crt /etc/ssl/certs/NginxProxyManager.crt
COPY start-container /usr/local/bin/start-container
RUN chmod +x /usr/local/bin/start-container
EXPOSE 80 81 443
ENTRYPOINT [ "/init" ]
ENTRYPOINT [ "start-container" ]

View File

@ -1,7 +1,5 @@
text = True
non-interactive = True
webroot-path = /data/letsencrypt-acme-challenge
key-type = ecdsa
elliptic-curve = secp384r1
preferred-chain = ISRG Root X1
server =

View File

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

View File

@ -1,4 +1,6 @@
# intermediate configuration. tweak to your needs.
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
ssl_ciphers "ALL:RC4-SHA:AES128-SHA:AES256-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:AES256-GCM-SHA384:AES128-GCM-SHA256:RSA-AES256-CBC-SHA:RC4-MD5:DES-CBC3-SHA:AES256-SHA:RC4-SHA:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
ssl_prefer_server_ciphers off;
ssl_ecdh_curve X25519:prime256v1:secp384r1;
ssl_dhparam /etc/ssl/certs/dhparam.pem;

13
docker/start-container Normal file
View File

@ -0,0 +1,13 @@
#!/usr/bin/env bash
FILE="/etc/ssl/certs/dhparam.pem"
if [ ! -f "$FILE" ]; then
echo "the $FILE does not exist, creating..."
openssl dhparam -out "$FILE" 2048
else
echo "the $FILE already exists, skipping..."
fi
echo "run default script"
exec /init

View File

@ -72,7 +72,7 @@
</label>
</div>
</div>
<div class="col-sm-12 col-md-12">
<div class="col-sm-6 col-md-6">
<div class="form-group">
<label class="custom-switch">
<input type="checkbox" class="custom-switch-input" name="allow_websocket_upgrade" value="1"<%- allow_websocket_upgrade ? ' checked' : '' %>>
@ -81,6 +81,15 @@
</label>
</div>
</div>
<div class="col-sm-6 col-md-6">
<div class="form-group">
<label class="custom-switch">
<input type="checkbox" class="custom-switch-input" name="default_server" value="1"<%- default_server ? ' checked' : '' %>>
<span class="custom-switch-indicator"></span>
<span class="custom-switch-description"><%- i18n('proxy-hosts', 'default-server') %></span>
</label>
</div>
</div>
<div class="col-sm-12 col-md-12">
<div class="form-group">
@ -105,6 +114,15 @@
</select>
</div>
</div>
<div class="col-sm-12 col-md-12 letsencrypt">
<div class="form-group">
<label class="form-label"><%- i18n('all-hosts', 'ssl-key-type') %></label>
<select name="ssl_key_type" class="form-control custom-select">
<option value="ecdsa" data-data="{&quot;id&quot;:&quot;ecdsa&quot;}" <%- ssl_key_type == 'ecdsa' ? 'selected' : '' %>>ECDSA</option>
<option value="rsa" data-data="{&quot;id&quot;:&quot;rsa&quot;}" <%- ssl_key_type == 'rsa' ? 'selected' : '' %>>RSA</option>
</select>
</div>
</div>
<div class="col-sm-6 col-md-6">
<div class="form-group">
<label class="custom-switch">

View File

@ -167,6 +167,7 @@ module.exports = Mn.View.extend({
data.hsts_enabled = !!data.hsts_enabled;
data.hsts_subdomains = !!data.hsts_subdomains;
data.ssl_forced = !!data.ssl_forced;
data.default_server = !!data.default_server;
if (typeof data.meta === 'undefined') data.meta = {};
data.meta.letsencrypt_agree = data.meta.letsencrypt_agree == 1;

View File

@ -77,6 +77,7 @@
"block-exploits": "Block Common Exploits",
"caching-enabled": "Cache Assets",
"ssl-certificate": "SSL Certificate",
"ssl-key-type": "SSL Key Type",
"none": "None",
"new-cert": "Request a new SSL Certificate",
"with-le": "with Let's Encrypt",
@ -131,6 +132,7 @@
"help-content": "A Proxy Host is the incoming endpoint for a web service that you want to forward.\nIt provides optional SSL termination for your service that might not have SSL support built in.\nProxy Hosts are the most common use for the Nginx Proxy Manager.",
"access-list": "Access List",
"allow-websocket-upgrade": "Websockets Support",
"default-server": "Default Server",
"ignore-invalid-upstream-ssl": "Ignore Invalid SSL",
"custom-forward-host-help": "Add a path for sub-folder forwarding.\nExample: 203.0.113.25/path/",
"search": "Search Host…"

View File

@ -10,6 +10,8 @@ const model = Backbone.Model.extend({
modified_on: null,
domain_names: [],
certificate_id: 0,
ssl_key_type: 'ecdsa',
default_server: false,
ssl_forced: false,
http2_support: false,
hsts_enabled: false,

View File

@ -14,6 +14,8 @@ const model = Backbone.Model.extend({
forward_port: null,
access_list_id: 0,
certificate_id: 0,
ssl_key_type: 'ecdsa',
default_server: false,
ssl_forced: false,
hsts_enabled: false,
hsts_subdomains: false,

View File

@ -14,6 +14,8 @@ const model = Backbone.Model.extend({
forward_domain_name: '',
preserve_path: true,
certificate_id: 0,
ssl_key_type: 'ecdsa',
default_server: false,
ssl_forced: false,
hsts_enabled: false,
hsts_subdomains: false,