Compare commits

..

10 Commits
2.0.8 ... 2.0.9

32 changed files with 593 additions and 160 deletions

38
Dockerfile.arm64 Normal file
View File

@ -0,0 +1,38 @@
FROM jc21/nginx-proxy-manager-base:arm64
MAINTAINER Jamie Curnow <jc@jc21.com>
LABEL maintainer="Jamie Curnow <jc@jc21.com>"
ENV SUPPRESS_NO_CONFIG_WARNING=1
ENV S6_FIX_ATTRS_HIDDEN=1
RUN echo "fs.file-max = 65535" > /etc/sysctl.conf
# Nginx, Node and required packages should already be installed from the base image
# root filesystem
COPY rootfs /
# s6 overlay
RUN curl -L -o /tmp/s6-overlay-aarch64.tar.gz "https://github.com/just-containers/s6-overlay/releases/download/v1.21.8.0/s6-overlay-aarch64.tar.gz" \
&& tar xzf /tmp/s6-overlay-aarch64.tar.gz -C /
# App
ENV NODE_ENV=production
ADD dist /app/dist
ADD node_modules /app/node_modules
ADD src/backend /app/src/backend
ADD package.json /app/package.json
ADD knexfile.js /app/knexfile.js
# Volumes
VOLUME [ "/data", "/etc/letsencrypt" ]
CMD [ "/init" ]
# Ports
EXPOSE 80
EXPOSE 81
EXPOSE 443
EXPOSE 9876
HEALTHCHECK --interval=15s --timeout=3s CMD curl -f http://localhost:9876/health || exit 1

View File

@ -1,4 +1,4 @@
FROM jc21/nginx-proxy-manager-base:latest-armhf FROM jc21/nginx-proxy-manager-base:armhf
MAINTAINER Jamie Curnow <jc@jc21.com> MAINTAINER Jamie Curnow <jc@jc21.com>
LABEL maintainer="Jamie Curnow <jc@jc21.com>" LABEL maintainer="Jamie Curnow <jc@jc21.com>"

148
Jenkinsfile vendored
View File

@ -5,12 +5,13 @@ pipeline {
} }
agent any agent any
environment { environment {
IMAGE_NAME = "nginx-proxy-manager" IMAGE = "nginx-proxy-manager"
BASE_IMAGE_NAME = "jc21/nginx-proxy-manager-base:latest" BASE_IMAGE = "jc21/nginx-proxy-manager-base"
TEMP_IMAGE_NAME = "nginx-proxy-manager-build_${BUILD_NUMBER}" TEMP_IMAGE = "nginx-proxy-manager-build_${BUILD_NUMBER}"
TEMP_IMAGE_NAME_ARM = "nginx-proxy-manager-arm-build_${BUILD_NUMBER}" TEMP_IMAGE_ARM = "nginx-proxy-manager-arm-build_${BUILD_NUMBER}"
TAG_VERSION = getPackageVersion() TEMP_IMAGE_ARM64 = "nginx-proxy-manager-arm64-build_${BUILD_NUMBER}"
MAJOR_VERSION = "2" TAG_VERSION = getPackageVersion()
MAJOR_VERSION = "2"
} }
stages { stages {
stage('Prepare') { stage('Prepare') {
@ -25,30 +26,30 @@ pipeline {
steps { steps {
ansiColor('xterm') { ansiColor('xterm') {
// Codebase // Codebase
sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE_NAME yarn install' sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE yarn install'
sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE_NAME npm run-script build' sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE npm run-script build'
sh 'rm -rf node_modules' sh 'rm -rf node_modules'
sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE_NAME yarn install --prod' sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE yarn install --prod'
sh 'docker run --rm -v $(pwd):/data $DOCKER_CI_TOOLS node-prune' sh 'docker run --rm -v $(pwd):/data $DOCKER_CI_TOOLS node-prune'
// Docker Build // Docker Build
sh 'docker build --pull --no-cache --squash --compress -t $TEMP_IMAGE_NAME .' sh 'docker build --pull --no-cache --squash --compress -t $TEMP_IMAGE .'
// Dockerhub // Dockerhub
sh 'docker tag $TEMP_IMAGE_NAME docker.io/jc21/$IMAGE_NAME:develop' sh 'docker tag $TEMP_IMAGE docker.io/jc21/$IMAGE:develop'
withCredentials([usernamePassword(credentialsId: 'jc21-dockerhub', passwordVariable: 'dpass', usernameVariable: 'duser')]) { withCredentials([usernamePassword(credentialsId: 'jc21-dockerhub', passwordVariable: 'dpass', usernameVariable: 'duser')]) {
sh "docker login -u '${duser}' -p '$dpass'" sh "docker login -u '${duser}' -p '$dpass'"
sh 'docker push docker.io/jc21/$IMAGE_NAME:develop' sh 'docker push docker.io/jc21/$IMAGE:develop'
} }
// Private Registry // Private Registry
sh 'docker tag $TEMP_IMAGE_NAME $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:develop' sh 'docker tag $TEMP_IMAGE $DOCKER_PRIVATE_REGISTRY/$IMAGE:develop'
withCredentials([usernamePassword(credentialsId: 'jc21-private-registry', passwordVariable: 'dpass', usernameVariable: 'duser')]) { withCredentials([usernamePassword(credentialsId: 'jc21-private-registry', passwordVariable: 'dpass', usernameVariable: 'duser')]) {
sh "docker login -u '${duser}' -p '$dpass' $DOCKER_PRIVATE_REGISTRY" sh "docker login -u '${duser}' -p '$dpass' $DOCKER_PRIVATE_REGISTRY"
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:develop' sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE:develop'
} }
sh 'docker rmi $TEMP_IMAGE_NAME' sh 'docker rmi $TEMP_IMAGE'
} }
} }
} }
@ -61,40 +62,40 @@ pipeline {
steps { steps {
ansiColor('xterm') { ansiColor('xterm') {
// Codebase // Codebase
sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE_NAME yarn install' sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE yarn install'
sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE_NAME npm run-script build' sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE npm run-script build'
sh 'rm -rf node_modules' sh 'rm -rf node_modules'
sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE_NAME yarn install --prod' sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE yarn install --prod'
sh 'docker run --rm -v $(pwd):/data $DOCKER_CI_TOOLS node-prune' sh 'docker run --rm -v $(pwd):/data $DOCKER_CI_TOOLS node-prune'
// Docker Build // Docker Build
sh 'docker build --pull --no-cache --squash --compress -t $TEMP_IMAGE_NAME .' sh 'docker build --pull --no-cache --squash --compress -t $TEMP_IMAGE .'
// Dockerhub // Dockerhub
sh 'docker tag $TEMP_IMAGE_NAME docker.io/jc21/$IMAGE_NAME:$TAG_VERSION' sh 'docker tag $TEMP_IMAGE docker.io/jc21/$IMAGE:$TAG_VERSION'
sh 'docker tag $TEMP_IMAGE_NAME docker.io/jc21/$IMAGE_NAME:$MAJOR_VERSION' sh 'docker tag $TEMP_IMAGE docker.io/jc21/$IMAGE:$MAJOR_VERSION'
sh 'docker tag $TEMP_IMAGE_NAME docker.io/jc21/$IMAGE_NAME:latest' sh 'docker tag $TEMP_IMAGE docker.io/jc21/$IMAGE:latest'
withCredentials([usernamePassword(credentialsId: 'jc21-dockerhub', passwordVariable: 'dpass', usernameVariable: 'duser')]) { withCredentials([usernamePassword(credentialsId: 'jc21-dockerhub', passwordVariable: 'dpass', usernameVariable: 'duser')]) {
sh "docker login -u '${duser}' -p '$dpass'" sh "docker login -u '${duser}' -p '$dpass'"
sh 'docker push docker.io/jc21/$IMAGE_NAME:$TAG_VERSION' sh 'docker push docker.io/jc21/$IMAGE:$TAG_VERSION'
sh 'docker push docker.io/jc21/$IMAGE_NAME:$MAJOR_VERSION' sh 'docker push docker.io/jc21/$IMAGE:$MAJOR_VERSION'
sh 'docker push docker.io/jc21/$IMAGE_NAME:latest' sh 'docker push docker.io/jc21/$IMAGE:latest'
} }
// Private Registry // Private Registry
sh 'docker tag $TEMP_IMAGE_NAME $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$TAG_VERSION' sh 'docker tag $TEMP_IMAGE $DOCKER_PRIVATE_REGISTRY/$IMAGE:$TAG_VERSION'
sh 'docker tag $TEMP_IMAGE_NAME $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$MAJOR_VERSION' sh 'docker tag $TEMP_IMAGE $DOCKER_PRIVATE_REGISTRY/$IMAGE:$MAJOR_VERSION'
sh 'docker tag $TEMP_IMAGE_NAME $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:latest' sh 'docker tag $TEMP_IMAGE $DOCKER_PRIVATE_REGISTRY/$IMAGE:latest'
withCredentials([usernamePassword(credentialsId: 'jc21-private-registry', passwordVariable: 'dpass', usernameVariable: 'duser')]) { withCredentials([usernamePassword(credentialsId: 'jc21-private-registry', passwordVariable: 'dpass', usernameVariable: 'duser')]) {
sh "docker login -u '${duser}' -p '$dpass' $DOCKER_PRIVATE_REGISTRY" sh "docker login -u '${duser}' -p '$dpass' $DOCKER_PRIVATE_REGISTRY"
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$TAG_VERSION' sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE:$TAG_VERSION'
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$MAJOR_VERSION' sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE:$MAJOR_VERSION'
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:latest' sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE:latest'
} }
sh 'docker rmi $TEMP_IMAGE_NAME' sh 'docker rmi $TEMP_IMAGE'
} }
} }
} }
@ -108,39 +109,88 @@ pipeline {
steps { steps {
ansiColor('xterm') { ansiColor('xterm') {
// Codebase // Codebase
sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE_NAME-armhf yarn install' sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE:armhf yarn install'
sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE_NAME-armhf npm run-script build' sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE:armhf npm run-script build'
sh 'rm -rf node_modules' sh 'rm -rf node_modules'
sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE_NAME-armhf yarn install --prod' sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE:armhf yarn install --prod'
// Docker Build // Docker Build
sh 'docker build --pull --no-cache --squash --compress -t $TEMP_IMAGE_NAME_ARM -f Dockerfile.armhf .' sh 'docker build --pull --no-cache --squash --compress -t $TEMP_IMAGE_ARM -f Dockerfile.armhf .'
// Dockerhub // Dockerhub
sh 'docker tag $TEMP_IMAGE_NAME_ARM docker.io/jc21/$IMAGE_NAME:$TAG_VERSION-armhf' sh 'docker tag $TEMP_IMAGE_ARM docker.io/jc21/$IMAGE:$TAG_VERSION-armhf'
sh 'docker tag $TEMP_IMAGE_NAME_ARM docker.io/jc21/$IMAGE_NAME:$MAJOR_VERSION-armhf' sh 'docker tag $TEMP_IMAGE_ARM docker.io/jc21/$IMAGE:$MAJOR_VERSION-armhf'
sh 'docker tag $TEMP_IMAGE_NAME_ARM docker.io/jc21/$IMAGE_NAME:latest-armhf' sh 'docker tag $TEMP_IMAGE_ARM docker.io/jc21/$IMAGE:latest-armhf'
withCredentials([usernamePassword(credentialsId: 'jc21-dockerhub', passwordVariable: 'dpass', usernameVariable: 'duser')]) { withCredentials([usernamePassword(credentialsId: 'jc21-dockerhub', passwordVariable: 'dpass', usernameVariable: 'duser')]) {
sh "docker login -u '${duser}' -p '$dpass'" sh "docker login -u '${duser}' -p '$dpass'"
sh 'docker push docker.io/jc21/$IMAGE_NAME:$TAG_VERSION-armhf' sh 'docker push docker.io/jc21/$IMAGE:$TAG_VERSION-armhf'
sh 'docker push docker.io/jc21/$IMAGE_NAME:$MAJOR_VERSION-armhf' sh 'docker push docker.io/jc21/$IMAGE:$MAJOR_VERSION-armhf'
sh 'docker push docker.io/jc21/$IMAGE_NAME:latest-armhf' sh 'docker push docker.io/jc21/$IMAGE:latest-armhf'
} }
// Private Registry // Private Registry
sh 'docker tag $TEMP_IMAGE_NAME_ARM $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$TAG_VERSION-armhf' sh 'docker tag $TEMP_IMAGE_ARM $DOCKER_PRIVATE_REGISTRY/$IMAGE:$TAG_VERSION-armhf'
sh 'docker tag $TEMP_IMAGE_NAME_ARM $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$MAJOR_VERSION-armhf' sh 'docker tag $TEMP_IMAGE_ARM $DOCKER_PRIVATE_REGISTRY/$IMAGE:$MAJOR_VERSION-armhf'
sh 'docker tag $TEMP_IMAGE_NAME_ARM $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:latest-armhf' sh 'docker tag $TEMP_IMAGE_ARM $DOCKER_PRIVATE_REGISTRY/$IMAGE:latest-armhf'
withCredentials([usernamePassword(credentialsId: 'jc21-private-registry', passwordVariable: 'dpass', usernameVariable: 'duser')]) { withCredentials([usernamePassword(credentialsId: 'jc21-private-registry', passwordVariable: 'dpass', usernameVariable: 'duser')]) {
sh "docker login -u '${duser}' -p '$dpass' $DOCKER_PRIVATE_REGISTRY" sh "docker login -u '${duser}' -p '$dpass' $DOCKER_PRIVATE_REGISTRY"
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$TAG_VERSION-armhf' sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE:$TAG_VERSION-armhf'
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$MAJOR_VERSION-armhf' sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE:$MAJOR_VERSION-armhf'
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:latest-armhf' sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE:latest-armhf'
} }
sh 'docker rmi $TEMP_IMAGE_NAME_ARM' sh 'docker rmi $TEMP_IMAGE_ARM'
}
}
}
stage('arm64') {
when {
branch 'master'
}
agent {
label 'arm64'
}
steps {
ansiColor('xterm') {
// Codebase
sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE:arm64 yarn install'
sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE:arm64 npm run-script build'
sh 'sudo rm -rf node_modules'
sh 'docker run --rm -v $(pwd):/app -w /app $BASE_IMAGE:arm64 yarn install --prod'
// Docker Build
sh 'docker build --pull --no-cache --squash --compress -t $TEMP_IMAGE_ARM64 -f Dockerfile.arm64 .'
// Dockerhub
sh 'docker tag $TEMP_IMAGE_ARM64 docker.io/jc21/$IMAGE:$TAG_VERSION-arm64'
sh 'docker tag $TEMP_IMAGE_ARM64 docker.io/jc21/$IMAGE:$MAJOR_VERSION-arm64'
sh 'docker tag $TEMP_IMAGE_ARM64 docker.io/jc21/$IMAGE:latest-arm64'
withCredentials([usernamePassword(credentialsId: 'jc21-dockerhub', passwordVariable: 'dpass', usernameVariable: 'duser')]) {
sh "docker login -u '${duser}' -p '$dpass'"
sh 'docker push docker.io/jc21/$IMAGE:$TAG_VERSION-arm64'
sh 'docker push docker.io/jc21/$IMAGE:$MAJOR_VERSION-arm64'
sh 'docker push docker.io/jc21/$IMAGE:latest-arm64'
}
// Private Registry
sh 'docker tag $TEMP_IMAGE_ARM64 $DOCKER_PRIVATE_REGISTRY/$IMAGE:$TAG_VERSION-arm64'
sh 'docker tag $TEMP_IMAGE_ARM64 $DOCKER_PRIVATE_REGISTRY/$IMAGE:$MAJOR_VERSION-arm64'
sh 'docker tag $TEMP_IMAGE_ARM64 $DOCKER_PRIVATE_REGISTRY/$IMAGE:latest-arm64'
withCredentials([usernamePassword(credentialsId: 'jc21-private-registry', passwordVariable: 'dpass', usernameVariable: 'duser')]) {
sh "docker login -u '${duser}' -p '$dpass' $DOCKER_PRIVATE_REGISTRY"
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE:$TAG_VERSION-arm64'
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE:$MAJOR_VERSION-arm64'
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE:latest-arm64'
}
sh 'docker rmi $TEMP_IMAGE_ARM64'
// Hack to clean up ec2 instance for next build
sh 'sudo chown -R ec2-user:ec2-user *'
} }
} }
} }

View File

@ -2,7 +2,7 @@
# Nginx Proxy Manager # Nginx Proxy Manager
![Version](https://img.shields.io/badge/version-2.0.8-green.svg?style=for-the-badge) ![Version](https://img.shields.io/badge/version-2.0.9-green.svg?style=for-the-badge)
![Stars](https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge) ![Stars](https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge)
![Pulls](https://img.shields.io/docker/pulls/jc21/nginx-proxy-manager.svg?style=for-the-badge) ![Pulls](https://img.shields.io/docker/pulls/jc21/nginx-proxy-manager.svg?style=for-the-badge)

View File

@ -2,7 +2,7 @@
# Nginx Proxy Manager # Nginx Proxy Manager
![Version](https://img.shields.io/badge/version-2.0.8-green.svg?style=for-the-badge) ![Version](https://img.shields.io/badge/version-2.0.9-green.svg?style=for-the-badge)
![Stars](https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge) ![Stars](https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge)
![Pulls](https://img.shields.io/docker/pulls/jc21/nginx-proxy-manager.svg?style=for-the-badge) ![Pulls](https://img.shields.io/docker/pulls/jc21/nginx-proxy-manager.svg?style=for-the-badge)

View File

@ -44,7 +44,7 @@ It's easy to use another docker container for your database also and link it as
version: "3" version: "3"
services: services:
app: app:
image: jc21/nginx-proxy-manager:2 image: jc21/nginx-proxy-manager:latest
restart: always restart: always
ports: ports:
- 80:80 - 80:80
@ -77,7 +77,7 @@ Via `docker-compose`:
version: "3" version: "3"
services: services:
app: app:
image: jc21/nginx-proxy-manager:2 image: jc21/nginx-proxy-manager:latest
restart: always restart: always
ports: ports:
- 80:80 - 80:80
@ -100,15 +100,17 @@ docker run -d \
-v /path/to/config.json:/app/config/production.json \ -v /path/to/config.json:/app/config/production.json \
-v /path/to/data:/data \ -v /path/to/data:/data \
-v /path/to/letsencrypt:/etc/letsencrypt \ -v /path/to/letsencrypt:/etc/letsencrypt \
jc21/nginx-proxy-manager:2 jc21/nginx-proxy-manager:latest
``` ```
### Running on Raspberry PI / `armhf` ### Running on Raspberry PI / `armhf`
I have created a `armhf` docker container just for you. There may be issues with it, I have created `armhf` and `arm64` docker containers just for you. There may be issues with it,
if you have issues please report them here. if you have issues please report them here.
Note: Rpi v2 and below won't work with these images.
```bash ```bash
docker run -d \ docker run -d \
--name nginx-proxy-manager-app \ --name nginx-proxy-manager-app \
@ -118,7 +120,7 @@ docker run -d \
-v /path/to/config.json:/app/config/production.json \ -v /path/to/config.json:/app/config/production.json \
-v /path/to/data:/data \ -v /path/to/data:/data \
-v /path/to/letsencrypt:/etc/letsencrypt \ -v /path/to/letsencrypt:/etc/letsencrypt \
jc21/nginx-proxy-manager:2-armhf jc21/nginx-proxy-manager:latest-armhf
``` ```

View File

@ -1,6 +1,6 @@
{ {
"name": "nginx-proxy-manager", "name": "nginx-proxy-manager",
"version": "2.0.8", "version": "2.0.9",
"description": "A beautiful interface for creating Nginx endpoints", "description": "A beautiful interface for creating Nginx endpoints",
"main": "src/backend/index.js", "main": "src/backend/index.js",
"devDependencies": { "devDependencies": {

View File

@ -7,6 +7,3 @@ ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA
ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AE ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AE
S128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA'; S128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
ssl_prefer_server_ciphers on; ssl_prefer_server_ciphers on;
# HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
add_header Strict-Transport-Security max-age=15768000;

View File

@ -47,6 +47,7 @@ const internalDeadHost = {
.then(() => { .then(() => {
// At this point the domains should have been checked // At this point the domains should have been checked
data.owner_user_id = access.token.getUserId(1); data.owner_user_id = access.token.getUserId(1);
data = internalHost.cleanSslHstsData(data);
return deadHostModel return deadHostModel
.query() .query()
@ -89,11 +90,11 @@ const internalDeadHost = {
// Add to audit log // Add to audit log
return internalAuditLog.add(access, { return internalAuditLog.add(access, {
action: 'created', action: 'created',
object_type: 'dead-host', object_type: 'dead-host',
object_id: row.id, object_id: row.id,
meta: data meta: data
}) })
.then(() => { .then(() => {
return row; return row;
}); });
@ -144,9 +145,9 @@ const internalDeadHost = {
if (create_certificate) { if (create_certificate) {
return internalCertificate.createQuickCertificate(access, { return internalCertificate.createQuickCertificate(access, {
domain_names: data.domain_names || row.domain_names, domain_names: data.domain_names || row.domain_names,
meta: _.assign({}, row.meta, data.meta) meta: _.assign({}, row.meta, data.meta)
}) })
.then(cert => { .then(cert => {
// update host with cert id // update host with cert id
data.certificate_id = cert.id; data.certificate_id = cert.id;
@ -162,7 +163,9 @@ const internalDeadHost = {
// Add domain_names to the data in case it isn't there, so that the audit log renders correctly. The order is important here. // Add domain_names to the data in case it isn't there, so that the audit log renders correctly. The order is important here.
data = _.assign({}, { data = _.assign({}, {
domain_names: row.domain_names domain_names: row.domain_names
},data); }, data);
data = internalHost.cleanSslHstsData(data, row);
return deadHostModel return deadHostModel
.query() .query()
@ -171,11 +174,11 @@ const internalDeadHost = {
.then(saved_row => { .then(saved_row => {
// Add to audit log // Add to audit log
return internalAuditLog.add(access, { return internalAuditLog.add(access, {
action: 'updated', action: 'updated',
object_type: 'dead-host', object_type: 'dead-host',
object_id: row.id, object_id: row.id,
meta: data meta: data
}) })
.then(() => { .then(() => {
return _.omit(saved_row, omissions()); return _.omit(saved_row, omissions());
}); });
@ -183,15 +186,15 @@ const internalDeadHost = {
}) })
.then(() => { .then(() => {
return internalDeadHost.get(access, { return internalDeadHost.get(access, {
id: data.id, id: data.id,
expand: ['owner', 'certificate'] expand: ['owner', 'certificate']
}) })
.then(row => { .then(row => {
// Configure nginx // Configure nginx
return internalNginx.configure(deadHostModel, 'dead_host', row) return internalNginx.configure(deadHostModel, 'dead_host', row)
.then(new_meta => { .then(new_meta => {
row.meta = new_meta; row.meta = new_meta;
row = internalHost.cleanRowCertificateMeta(row); row = internalHost.cleanRowCertificateMeta(row);
return _.omit(row, omissions()); return _.omit(row, omissions());
}); });
}); });

View File

@ -1,11 +1,40 @@
'use strict'; const _ = require('lodash');
const proxyHostModel = require('../models/proxy_host'); const proxyHostModel = require('../models/proxy_host');
const redirectionHostModel = require('../models/redirection_host'); const redirectionHostModel = require('../models/redirection_host');
const deadHostModel = require('../models/dead_host'); const deadHostModel = require('../models/dead_host');
const internalHost = { const internalHost = {
/**
* Makes sure that the ssl_* and hsts_* fields play nicely together.
* ie: if there is no cert, then force_ssl is off.
* if force_ssl is off, then hsts_enabled is definitely off.
*
* @param {object} data
* @param {object} [existing_data]
* @returns {object}
*/
cleanSslHstsData: function (data, existing_data) {
existing_data = existing_data === undefined ? {} : existing_data;
let combined_data = _.assign({}, existing_data, data);
if (!combined_data.certificate_id) {
combined_data.ssl_forced = false;
combined_data.http2_support = false;
}
if (!combined_data.ssl_forced) {
combined_data.hsts_enabled = false;
}
if (!combined_data.hsts_enabled) {
combined_data.hsts_subdomains = false;
}
return combined_data;
},
/** /**
* used by the getAll functions of hosts, this removes the certificate meta if present * used by the getAll functions of hosts, this removes the certificate meta if present
* *

View File

@ -1,5 +1,3 @@
'use strict';
const _ = require('lodash'); const _ = require('lodash');
const error = require('../lib/error'); const error = require('../lib/error');
const proxyHostModel = require('../models/proxy_host'); const proxyHostModel = require('../models/proxy_host');
@ -47,6 +45,7 @@ const internalProxyHost = {
.then(() => { .then(() => {
// At this point the domains should have been checked // At this point the domains should have been checked
data.owner_user_id = access.token.getUserId(1); data.owner_user_id = access.token.getUserId(1);
data = internalHost.cleanSslHstsData(data);
return proxyHostModel return proxyHostModel
.query() .query()
@ -90,11 +89,11 @@ const internalProxyHost = {
// Add to audit log // Add to audit log
return internalAuditLog.add(access, { return internalAuditLog.add(access, {
action: 'created', action: 'created',
object_type: 'proxy-host', object_type: 'proxy-host',
object_id: row.id, object_id: row.id,
meta: data meta: data
}) })
.then(() => { .then(() => {
return row; return row;
}); });
@ -109,7 +108,7 @@ const internalProxyHost = {
*/ */
update: (access, data) => { update: (access, data) => {
let create_certificate = data.certificate_id === 'new'; let create_certificate = data.certificate_id === 'new';
console.log('PH UPDATE:', data);
if (create_certificate) { if (create_certificate) {
delete data.certificate_id; delete data.certificate_id;
} }
@ -145,9 +144,9 @@ const internalProxyHost = {
if (create_certificate) { if (create_certificate) {
return internalCertificate.createQuickCertificate(access, { return internalCertificate.createQuickCertificate(access, {
domain_names: data.domain_names || row.domain_names, domain_names: data.domain_names || row.domain_names,
meta: _.assign({}, row.meta, data.meta) meta: _.assign({}, row.meta, data.meta)
}) })
.then(cert => { .then(cert => {
// update host with cert id // update host with cert id
data.certificate_id = cert.id; data.certificate_id = cert.id;
@ -165,6 +164,8 @@ const internalProxyHost = {
domain_names: row.domain_names domain_names: row.domain_names
}, data); }, data);
data = internalHost.cleanSslHstsData(data, row);
return proxyHostModel return proxyHostModel
.query() .query()
.where({id: data.id}) .where({id: data.id})
@ -172,11 +173,11 @@ const internalProxyHost = {
.then(saved_row => { .then(saved_row => {
// Add to audit log // Add to audit log
return internalAuditLog.add(access, { return internalAuditLog.add(access, {
action: 'updated', action: 'updated',
object_type: 'proxy-host', object_type: 'proxy-host',
object_id: row.id, object_id: row.id,
meta: data meta: data
}) })
.then(() => { .then(() => {
return _.omit(saved_row, omissions()); return _.omit(saved_row, omissions());
}); });
@ -184,9 +185,9 @@ const internalProxyHost = {
}) })
.then(() => { .then(() => {
return internalProxyHost.get(access, { return internalProxyHost.get(access, {
id: data.id, id: data.id,
expand: ['owner', 'certificate', 'access_list'] expand: ['owner', 'certificate', 'access_list']
}) })
.then(row => { .then(row => {
// Configure nginx // Configure nginx
return internalNginx.configure(proxyHostModel, 'proxy_host', row) return internalNginx.configure(proxyHostModel, 'proxy_host', row)

View File

@ -47,6 +47,7 @@ const internalRedirectionHost = {
.then(() => { .then(() => {
// At this point the domains should have been checked // At this point the domains should have been checked
data.owner_user_id = access.token.getUserId(1); data.owner_user_id = access.token.getUserId(1);
data = internalHost.cleanSslHstsData(data);
return redirectionHostModel return redirectionHostModel
.query() .query()
@ -89,11 +90,11 @@ const internalRedirectionHost = {
// Add to audit log // Add to audit log
return internalAuditLog.add(access, { return internalAuditLog.add(access, {
action: 'created', action: 'created',
object_type: 'redirection-host', object_type: 'redirection-host',
object_id: row.id, object_id: row.id,
meta: data meta: data
}) })
.then(() => { .then(() => {
return row; return row;
}); });
@ -144,9 +145,9 @@ const internalRedirectionHost = {
if (create_certificate) { if (create_certificate) {
return internalCertificate.createQuickCertificate(access, { return internalCertificate.createQuickCertificate(access, {
domain_names: data.domain_names || row.domain_names, domain_names: data.domain_names || row.domain_names,
meta: _.assign({}, row.meta, data.meta) meta: _.assign({}, row.meta, data.meta)
}) })
.then(cert => { .then(cert => {
// update host with cert id // update host with cert id
data.certificate_id = cert.id; data.certificate_id = cert.id;
@ -162,7 +163,9 @@ const internalRedirectionHost = {
// Add domain_names to the data in case it isn't there, so that the audit log renders correctly. The order is important here. // Add domain_names to the data in case it isn't there, so that the audit log renders correctly. The order is important here.
data = _.assign({}, { data = _.assign({}, {
domain_names: row.domain_names domain_names: row.domain_names
},data); }, data);
data = internalHost.cleanSslHstsData(data, row);
return redirectionHostModel return redirectionHostModel
.query() .query()
@ -171,11 +174,11 @@ const internalRedirectionHost = {
.then(saved_row => { .then(saved_row => {
// Add to audit log // Add to audit log
return internalAuditLog.add(access, { return internalAuditLog.add(access, {
action: 'updated', action: 'updated',
object_type: 'redirection-host', object_type: 'redirection-host',
object_id: row.id, object_id: row.id,
meta: data meta: data
}) })
.then(() => { .then(() => {
return _.omit(saved_row, omissions()); return _.omit(saved_row, omissions());
}); });
@ -183,15 +186,15 @@ const internalRedirectionHost = {
}) })
.then(() => { .then(() => {
return internalRedirectionHost.get(access, { return internalRedirectionHost.get(access, {
id: data.id, id: data.id,
expand: ['owner', 'certificate'] expand: ['owner', 'certificate']
}) })
.then(row => { .then(row => {
// Configure nginx // Configure nginx
return internalNginx.configure(redirectionHostModel, 'redirection_host', row) return internalNginx.configure(redirectionHostModel, 'redirection_host', row)
.then(new_meta => { .then(new_meta => {
row.meta = new_meta; row.meta = new_meta;
row = internalHost.cleanRowCertificateMeta(row); row = internalHost.cleanRowCertificateMeta(row);
return _.omit(row, omissions()); return _.omit(row, omissions());
}); });
}); });

View File

@ -0,0 +1,53 @@
'use strict';
const migrate_name = 'hsts';
const logger = require('../logger').migrate;
/**
* Migrate
*
* @see http://knexjs.org/#Schema
*
* @param {Object} knex
* @param {Promise} Promise
* @returns {Promise}
*/
exports.up = function (knex/*, Promise*/) {
logger.info('[' + migrate_name + '] Migrating Up...');
return knex.schema.table('proxy_host', function (proxy_host) {
proxy_host.integer('hsts_enabled').notNull().unsigned().defaultTo(0);
proxy_host.integer('hsts_subdomains').notNull().unsigned().defaultTo(0);
})
.then(() => {
logger.info('[' + migrate_name + '] proxy_host Table altered');
return knex.schema.table('redirection_host', function (redirection_host) {
redirection_host.integer('hsts_enabled').notNull().unsigned().defaultTo(0);
redirection_host.integer('hsts_subdomains').notNull().unsigned().defaultTo(0);
});
})
.then(() => {
logger.info('[' + migrate_name + '] redirection_host Table altered');
return knex.schema.table('dead_host', function (dead_host) {
dead_host.integer('hsts_enabled').notNull().unsigned().defaultTo(0);
dead_host.integer('hsts_subdomains').notNull().unsigned().defaultTo(0);
});
})
.then(() => {
logger.info('[' + migrate_name + '] dead_host Table altered');
});
};
/**
* Undo Migrate
*
* @param {Object} knex
* @param {Promise} Promise
* @returns {Promise}
*/
exports.down = function (knex, Promise) {
logger.warn('[' + migrate_name + '] You can\'t migrate down this one.');
return Promise.resolve(true);
};

View File

@ -187,6 +187,16 @@
"example": false, "example": false,
"type": "boolean" "type": "boolean"
}, },
"hsts_enabled": {
"description": "Is HSTS Enabled",
"example": false,
"type": "boolean"
},
"hsts_subdomains": {
"description": "Is HSTS applicable to all subdomains",
"example": false,
"type": "boolean"
},
"ssl_provider": { "ssl_provider": {
"type": "string", "type": "string",
"pattern": "^(letsencrypt|other)$" "pattern": "^(letsencrypt|other)$"

View File

@ -24,6 +24,12 @@
"ssl_forced": { "ssl_forced": {
"$ref": "../definitions.json#/definitions/ssl_forced" "$ref": "../definitions.json#/definitions/ssl_forced"
}, },
"hsts_enabled": {
"$ref": "../definitions.json#/definitions/hsts_enabled"
},
"hsts_subdomains": {
"$ref": "../definitions.json#/definitions/hsts_subdomains"
},
"http2_support": { "http2_support": {
"$ref": "../definitions.json#/definitions/http2_support" "$ref": "../definitions.json#/definitions/http2_support"
}, },
@ -56,6 +62,12 @@
"ssl_forced": { "ssl_forced": {
"$ref": "#/definitions/ssl_forced" "$ref": "#/definitions/ssl_forced"
}, },
"hsts_enabled": {
"$ref": "#/definitions/hsts_enabled"
},
"hsts_subdomains": {
"$ref": "#/definitions/hsts_subdomains"
},
"http2_support": { "http2_support": {
"$ref": "#/definitions/http2_support" "$ref": "#/definitions/http2_support"
}, },
@ -113,6 +125,12 @@
"ssl_forced": { "ssl_forced": {
"$ref": "#/definitions/ssl_forced" "$ref": "#/definitions/ssl_forced"
}, },
"hsts_enabled": {
"$ref": "#/definitions/hsts_enabled"
},
"hsts_subdomains": {
"$ref": "#/definitions/hsts_enabled"
},
"http2_support": { "http2_support": {
"$ref": "#/definitions/http2_support" "$ref": "#/definitions/http2_support"
}, },
@ -153,6 +171,12 @@
"ssl_forced": { "ssl_forced": {
"$ref": "#/definitions/ssl_forced" "$ref": "#/definitions/ssl_forced"
}, },
"hsts_enabled": {
"$ref": "#/definitions/hsts_enabled"
},
"hsts_subdomains": {
"$ref": "#/definitions/hsts_enabled"
},
"http2_support": { "http2_support": {
"$ref": "#/definitions/http2_support" "$ref": "#/definitions/http2_support"
}, },

View File

@ -38,6 +38,12 @@
"ssl_forced": { "ssl_forced": {
"$ref": "../definitions.json#/definitions/ssl_forced" "$ref": "../definitions.json#/definitions/ssl_forced"
}, },
"hsts_enabled": {
"$ref": "../definitions.json#/definitions/hsts_enabled"
},
"hsts_subdomains": {
"$ref": "../definitions.json#/definitions/hsts_subdomains"
},
"http2_support": { "http2_support": {
"$ref": "../definitions.json#/definitions/http2_support" "$ref": "../definitions.json#/definitions/http2_support"
}, },
@ -93,6 +99,12 @@
"ssl_forced": { "ssl_forced": {
"$ref": "#/definitions/ssl_forced" "$ref": "#/definitions/ssl_forced"
}, },
"hsts_enabled": {
"$ref": "#/definitions/hsts_enabled"
},
"hsts_subdomains": {
"$ref": "#/definitions/hsts_subdomains"
},
"http2_support": { "http2_support": {
"$ref": "#/definitions/http2_support" "$ref": "#/definitions/http2_support"
}, },
@ -174,6 +186,12 @@
"ssl_forced": { "ssl_forced": {
"$ref": "#/definitions/ssl_forced" "$ref": "#/definitions/ssl_forced"
}, },
"hsts_enabled": {
"$ref": "#/definitions/hsts_enabled"
},
"hsts_subdomains": {
"$ref": "#/definitions/hsts_enabled"
},
"http2_support": { "http2_support": {
"$ref": "#/definitions/http2_support" "$ref": "#/definitions/http2_support"
}, },
@ -238,6 +256,12 @@
"ssl_forced": { "ssl_forced": {
"$ref": "#/definitions/ssl_forced" "$ref": "#/definitions/ssl_forced"
}, },
"hsts_enabled": {
"$ref": "#/definitions/hsts_enabled"
},
"hsts_subdomains": {
"$ref": "#/definitions/hsts_enabled"
},
"http2_support": { "http2_support": {
"$ref": "#/definitions/http2_support" "$ref": "#/definitions/http2_support"
}, },

View File

@ -32,6 +32,12 @@
"ssl_forced": { "ssl_forced": {
"$ref": "../definitions.json#/definitions/ssl_forced" "$ref": "../definitions.json#/definitions/ssl_forced"
}, },
"hsts_enabled": {
"$ref": "../definitions.json#/definitions/hsts_enabled"
},
"hsts_subdomains": {
"$ref": "../definitions.json#/definitions/hsts_subdomains"
},
"http2_support": { "http2_support": {
"$ref": "../definitions.json#/definitions/http2_support" "$ref": "../definitions.json#/definitions/http2_support"
}, },
@ -73,6 +79,12 @@
"ssl_forced": { "ssl_forced": {
"$ref": "#/definitions/ssl_forced" "$ref": "#/definitions/ssl_forced"
}, },
"hsts_enabled": {
"$ref": "#/definitions/hsts_enabled"
},
"hsts_subdomains": {
"$ref": "#/definitions/hsts_subdomains"
},
"http2_support": { "http2_support": {
"$ref": "#/definitions/http2_support" "$ref": "#/definitions/http2_support"
}, },
@ -140,6 +152,12 @@
"ssl_forced": { "ssl_forced": {
"$ref": "#/definitions/ssl_forced" "$ref": "#/definitions/ssl_forced"
}, },
"hsts_enabled": {
"$ref": "#/definitions/hsts_enabled"
},
"hsts_subdomains": {
"$ref": "#/definitions/hsts_enabled"
},
"http2_support": { "http2_support": {
"$ref": "#/definitions/http2_support" "$ref": "#/definitions/http2_support"
}, },
@ -189,6 +207,12 @@
"ssl_forced": { "ssl_forced": {
"$ref": "#/definitions/ssl_forced" "$ref": "#/definitions/ssl_forced"
}, },
"hsts_enabled": {
"$ref": "#/definitions/hsts_enabled"
},
"hsts_subdomains": {
"$ref": "#/definitions/hsts_enabled"
},
"http2_support": { "http2_support": {
"$ref": "#/definitions/http2_support" "$ref": "#/definitions/http2_support"
}, },

View File

@ -0,0 +1,8 @@
{% if certificate and certificate_id > 0 -%}
{% if ssl_forced == 1 or ssl_forced == true %}
{% if hsts_enabled == 1 or hsts_enabled == true %}
# HSTS (ngx_http_headers_module is required) (31536000 seconds = 1 year)
add_header Strict-Transport-Security "max-age=31536000;{% if hsts_subdomains == 1 or hsts_subdomains == true -%} includeSubDomains;{% endif %} preload" always;
{% endif %}
{% endif %}
{% endif %}

View File

@ -4,11 +4,16 @@
server { server {
{% include "_listen.conf" %} {% include "_listen.conf" %}
{% include "_certificates.conf" %} {% include "_certificates.conf" %}
{% include "_hsts.conf" %}
access_log /data/logs/dead_host-{{ id }}.log standard; access_log /data/logs/dead_host-{{ id }}.log standard;
{{ advanced_config }} {{ advanced_config }}
return 404; location / {
{% include "_forced_ssl.conf" %}
{% include "_hsts.conf" %}
return 404;
}
} }
{% endif %} {% endif %}

View File

@ -10,6 +10,7 @@ server {
{% include "_certificates.conf" %} {% include "_certificates.conf" %}
{% include "_assets.conf" %} {% include "_assets.conf" %}
{% include "_exploits.conf" %} {% include "_exploits.conf" %}
{% include "_hsts.conf" %}
access_log /data/logs/proxy_host-{{ id }}.log proxy; access_log /data/logs/proxy_host-{{ id }}.log proxy;
@ -23,6 +24,7 @@ server {
{%- endif %} {%- endif %}
{% include "_forced_ssl.conf" %} {% include "_forced_ssl.conf" %}
{% include "_hsts.conf" %}
{% if allow_websocket_upgrade == 1 or allow_websocket_upgrade == true %} {% if allow_websocket_upgrade == 1 or allow_websocket_upgrade == true %}
proxy_set_header Upgrade $http_upgrade; proxy_set_header Upgrade $http_upgrade;

View File

@ -6,15 +6,15 @@ server {
{% include "_certificates.conf" %} {% include "_certificates.conf" %}
{% include "_assets.conf" %} {% include "_assets.conf" %}
{% include "_exploits.conf" %} {% include "_exploits.conf" %}
{% include "_hsts.conf" %}
access_log /data/logs/redirection_host-{{ id }}.log standard; access_log /data/logs/redirection_host-{{ id }}.log standard;
{{ advanced_config }} {{ advanced_config }}
# TODO: Preserve Path Option
location / { location / {
{% include "_forced_ssl.conf" %} {% include "_forced_ssl.conf" %}
{% include "_hsts.conf" %}
{% if preserve_path == 1 or preserve_path == true %} {% if preserve_path == 1 or preserve_path == true %}
return 301 $scheme://{{ forward_domain_name }}$request_uri; return 301 $scheme://{{ forward_domain_name }}$request_uri;

View File

@ -12,7 +12,7 @@ require('selectize');
module.exports = Mn.View.extend({ module.exports = Mn.View.extend({
template: template, template: template,
className: 'modal-dialog', className: 'modal-dialog',
max_file_size: 5120, max_file_size: 102400,
ui: { ui: {
form: 'form', form: 'form',
@ -56,7 +56,7 @@ module.exports = Mn.View.extend({
return; return;
} else { } else {
if (this.ui.other_certificate[0].files[0].size > this.max_file_size) { if (this.ui.other_certificate[0].files[0].size > this.max_file_size) {
alert('Certificate file is too large (> 5kb)'); alert('Certificate file is too large (> 100kb)');
return; return;
} }
ssl_files.push({name: 'certificate', file: this.ui.other_certificate[0].files[0]}); ssl_files.push({name: 'certificate', file: this.ui.other_certificate[0].files[0]});
@ -67,7 +67,7 @@ module.exports = Mn.View.extend({
return; return;
} else { } else {
if (this.ui.other_certificate_key[0].files[0].size > this.max_file_size) { if (this.ui.other_certificate_key[0].files[0].size > this.max_file_size) {
alert('Certificate key file is too large (> 5kb)'); alert('Certificate key file is too large (> 100kb)');
return; return;
} }
ssl_files.push({name: 'certificate_key', file: this.ui.other_certificate_key[0].files[0]}); ssl_files.push({name: 'certificate_key', file: this.ui.other_certificate_key[0].files[0]});
@ -75,7 +75,7 @@ module.exports = Mn.View.extend({
if (this.ui.other_intermediate_certificate[0].files.length && this.ui.other_intermediate_certificate[0].files[0].size) { if (this.ui.other_intermediate_certificate[0].files.length && this.ui.other_intermediate_certificate[0].files[0].size) {
if (this.ui.other_intermediate_certificate[0].files[0].size > this.max_file_size) { if (this.ui.other_intermediate_certificate[0].files[0].size > this.max_file_size) {
alert('Intermediate Certificate file is too large (> 5kb)'); alert('Intermediate Certificate file is too large (> 100kb)');
return; return;
} }
ssl_files.push({name: 'intermediate_certificate', file: this.ui.other_intermediate_certificate[0].files[0]}); ssl_files.push({name: 'intermediate_certificate', file: this.ui.other_intermediate_certificate[0].files[0]});

View File

@ -54,6 +54,24 @@
</label> </label>
</div> </div>
</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="hsts_enabled" value="1"<%- hsts_enabled ? ' checked' : '' %><%- certificate_id && ssl_forced ? '' : ' disabled' %>>
<span class="custom-switch-indicator"></span>
<span class="custom-switch-description"><%- i18n('all-hosts', 'hsts-enabled') %> <a href="https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security" target="_blank"><i class="fe fe-help-circle"></i></a></span>
</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="hsts_subdomains" value="1"<%- hsts_subdomains ? ' checked' : '' %><%- certificate_id && ssl_forced && hsts_enabled ? '' : ' disabled' %>>
<span class="custom-switch-indicator"></span>
<span class="custom-switch-description"><%- i18n('all-hosts', 'hsts-subdomains') %></span>
</label>
</div>
</div>
<!-- Lets encrypt --> <!-- Lets encrypt -->
<div class="col-sm-12 col-md-12 letsencrypt"> <div class="col-sm-12 col-md-12 letsencrypt">

View File

@ -22,6 +22,8 @@ module.exports = Mn.View.extend({
save: 'button.save', save: 'button.save',
certificate_select: 'select[name="certificate_id"]', certificate_select: 'select[name="certificate_id"]',
ssl_forced: 'input[name="ssl_forced"]', ssl_forced: 'input[name="ssl_forced"]',
hsts_enabled: 'input[name="hsts_enabled"]',
hsts_subdomains: 'input[name="hsts_subdomains"]',
http2_support: 'input[name="http2_support"]', http2_support: 'input[name="http2_support"]',
letsencrypt: '.letsencrypt' letsencrypt: '.letsencrypt'
}, },
@ -36,11 +38,44 @@ module.exports = Mn.View.extend({
} }
let enabled = id === 'new' || parseInt(id, 10) > 0; let enabled = id === 'new' || parseInt(id, 10) > 0;
this.ui.ssl_forced.add(this.ui.http2_support)
let inputs = this.ui.ssl_forced.add(this.ui.http2_support);
inputs
.prop('disabled', !enabled) .prop('disabled', !enabled)
.parents('.form-group') .parents('.form-group')
.css('opacity', enabled ? 1 : 0.5); .css('opacity', enabled ? 1 : 0.5);
this.ui.http2_support.prop('disabled', !enabled);
if (!enabled) {
inputs.prop('checked', false);
}
inputs.trigger('change');
},
'change @ui.ssl_forced': function () {
let checked = this.ui.ssl_forced.prop('checked');
this.ui.hsts_enabled
.prop('disabled', !checked)
.parents('.form-group')
.css('opacity', checked ? 1 : 0.5);
if (!checked) {
this.ui.hsts_enabled.prop('checked', false);
}
this.ui.hsts_enabled.trigger('change');
},
'change @ui.hsts_enabled': function () {
let checked = this.ui.hsts_enabled.prop('checked');
this.ui.hsts_subdomains
.prop('disabled', !checked)
.parents('.form-group')
.css('opacity', checked ? 1 : 0.5);
if (!checked) {
this.ui.hsts_subdomains.prop('checked', false);
}
}, },
'click @ui.save': function (e) { 'click @ui.save': function (e) {
@ -55,13 +90,10 @@ module.exports = Mn.View.extend({
let data = this.ui.form.serializeJSON(); let data = this.ui.form.serializeJSON();
// Manipulate // Manipulate
if (typeof data.ssl_forced !== 'undefined' && data.ssl_forced === '1') { data.hsts_enabled = !!data.hsts_enabled;
data.ssl_forced = true; data.hsts_subdomains = !!data.hsts_subdomains;
} data.http2_support = !!data.http2_support;
data.ssl_forced = !!data.ssl_forced;
if (typeof data.http2_support !== 'undefined') {
data.http2_support = !!data.http2_support;
}
if (typeof data.domain_names === 'string' && data.domain_names) { if (typeof data.domain_names === 'string' && data.domain_names) {
data.domain_names = data.domain_names.split(','); data.domain_names = data.domain_names.split(',');

View File

@ -110,6 +110,24 @@
</label> </label>
</div> </div>
</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="hsts_enabled" value="1"<%- hsts_enabled ? ' checked' : '' %><%- certificate_id && ssl_forced ? '' : ' disabled' %>>
<span class="custom-switch-indicator"></span>
<span class="custom-switch-description"><%- i18n('all-hosts', 'hsts-enabled') %> <a href="https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security" target="_blank"><i class="fe fe-help-circle"></i></a></span>
</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="hsts_subdomains" value="1"<%- hsts_subdomains ? ' checked' : '' %><%- certificate_id && ssl_forced && hsts_enabled ? '' : ' disabled' %>>
<span class="custom-switch-indicator"></span>
<span class="custom-switch-description"><%- i18n('all-hosts', 'hsts-subdomains') %></span>
</label>
</div>
</div>
<!-- Lets encrypt --> <!-- Lets encrypt -->
<div class="col-sm-12 col-md-12 letsencrypt"> <div class="col-sm-12 col-md-12 letsencrypt">

View File

@ -25,6 +25,8 @@ module.exports = Mn.View.extend({
certificate_select: 'select[name="certificate_id"]', certificate_select: 'select[name="certificate_id"]',
access_list_select: 'select[name="access_list_id"]', access_list_select: 'select[name="access_list_id"]',
ssl_forced: 'input[name="ssl_forced"]', ssl_forced: 'input[name="ssl_forced"]',
hsts_enabled: 'input[name="hsts_enabled"]',
hsts_subdomains: 'input[name="hsts_subdomains"]',
http2_support: 'input[name="http2_support"]', http2_support: 'input[name="http2_support"]',
forward_scheme: 'select[name="forward_scheme"]', forward_scheme: 'select[name="forward_scheme"]',
letsencrypt: '.letsencrypt' letsencrypt: '.letsencrypt'
@ -40,10 +42,44 @@ module.exports = Mn.View.extend({
} }
let enabled = id === 'new' || parseInt(id, 10) > 0; let enabled = id === 'new' || parseInt(id, 10) > 0;
this.ui.ssl_forced.add(this.ui.http2_support)
let inputs = this.ui.ssl_forced.add(this.ui.http2_support);
inputs
.prop('disabled', !enabled) .prop('disabled', !enabled)
.parents('.form-group') .parents('.form-group')
.css('opacity', enabled ? 1 : 0.5); .css('opacity', enabled ? 1 : 0.5);
if (!enabled) {
inputs.prop('checked', false);
}
inputs.trigger('change');
},
'change @ui.ssl_forced': function () {
let checked = this.ui.ssl_forced.prop('checked');
this.ui.hsts_enabled
.prop('disabled', !checked)
.parents('.form-group')
.css('opacity', checked ? 1 : 0.5);
if (!checked) {
this.ui.hsts_enabled.prop('checked', false);
}
this.ui.hsts_enabled.trigger('change');
},
'change @ui.hsts_enabled': function () {
let checked = this.ui.hsts_enabled.prop('checked');
this.ui.hsts_subdomains
.prop('disabled', !checked)
.parents('.form-group')
.css('opacity', checked ? 1 : 0.5);
if (!checked) {
this.ui.hsts_subdomains.prop('checked', false);
}
}, },
'click @ui.save': function (e) { 'click @ui.save': function (e) {
@ -58,18 +94,14 @@ module.exports = Mn.View.extend({
let data = this.ui.form.serializeJSON(); let data = this.ui.form.serializeJSON();
// Manipulate // Manipulate
data.forward_port = parseInt(data.forward_port, 10); data.forward_port = parseInt(data.forward_port, 10);
data.block_exploits = !!data.block_exploits; data.block_exploits = !!data.block_exploits;
data.caching_enabled = !!data.caching_enabled; data.caching_enabled = !!data.caching_enabled;
data.allow_websocket_upgrade = !!data.allow_websocket_upgrade; data.allow_websocket_upgrade = !!data.allow_websocket_upgrade;
data.http2_support = !!data.http2_support;
if (typeof data.ssl_forced !== 'undefined' && data.ssl_forced === '1') { data.hsts_enabled = !!data.hsts_enabled;
data.ssl_forced = true; data.hsts_subdomains = !!data.hsts_subdomains;
} data.ssl_forced = !!data.ssl_forced;
if (typeof data.http2_support !== 'undefined') {
data.http2_support = !!data.http2_support;
}
if (typeof data.domain_names === 'string' && data.domain_names) { if (typeof data.domain_names === 'string' && data.domain_names) {
data.domain_names = data.domain_names.split(','); data.domain_names = data.domain_names.split(',');
@ -131,6 +163,9 @@ module.exports = Mn.View.extend({
onRender: function () { onRender: function () {
let view = this; let view = this;
this.ui.ssl_forced.trigger('change');
this.ui.hsts_enabled.trigger('change');
// Domain names // Domain names
this.ui.domain_names.selectize({ this.ui.domain_names.selectize({
delimiter: ',', delimiter: ',',

View File

@ -78,6 +78,24 @@
</label> </label>
</div> </div>
</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="hsts_enabled" value="1"<%- hsts_enabled ? ' checked' : '' %><%- certificate_id && ssl_forced ? '' : ' disabled' %>>
<span class="custom-switch-indicator"></span>
<span class="custom-switch-description"><%- i18n('all-hosts', 'hsts-enabled') %> <a href="https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security" target="_blank"><i class="fe fe-help-circle"></i></a></span>
</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="hsts_subdomains" value="1"<%- hsts_subdomains ? ' checked' : '' %><%- certificate_id && ssl_forced && hsts_enabled ? '' : ' disabled' %>>
<span class="custom-switch-indicator"></span>
<span class="custom-switch-description"><%- i18n('all-hosts', 'hsts-subdomains') %></span>
</label>
</div>
</div>
<!-- Lets encrypt --> <!-- Lets encrypt -->
<div class="col-sm-12 col-md-12 letsencrypt"> <div class="col-sm-12 col-md-12 letsencrypt">

View File

@ -22,6 +22,8 @@ module.exports = Mn.View.extend({
save: 'button.save', save: 'button.save',
certificate_select: 'select[name="certificate_id"]', certificate_select: 'select[name="certificate_id"]',
ssl_forced: 'input[name="ssl_forced"]', ssl_forced: 'input[name="ssl_forced"]',
hsts_enabled: 'input[name="hsts_enabled"]',
hsts_subdomains: 'input[name="hsts_subdomains"]',
http2_support: 'input[name="http2_support"]', http2_support: 'input[name="http2_support"]',
letsencrypt: '.letsencrypt' letsencrypt: '.letsencrypt'
}, },
@ -36,11 +38,44 @@ module.exports = Mn.View.extend({
} }
let enabled = id === 'new' || parseInt(id, 10) > 0; let enabled = id === 'new' || parseInt(id, 10) > 0;
this.ui.ssl_forced.add(this.ui.http2_support)
let inputs = this.ui.ssl_forced.add(this.ui.http2_support);
inputs
.prop('disabled', !enabled) .prop('disabled', !enabled)
.parents('.form-group') .parents('.form-group')
.css('opacity', enabled ? 1 : 0.5); .css('opacity', enabled ? 1 : 0.5);
this.ui.http2_support.prop('disabled', !enabled);
if (!enabled) {
inputs.prop('checked', false);
}
inputs.trigger('change');
},
'change @ui.ssl_forced': function () {
let checked = this.ui.ssl_forced.prop('checked');
this.ui.hsts_enabled
.prop('disabled', !checked)
.parents('.form-group')
.css('opacity', checked ? 1 : 0.5);
if (!checked) {
this.ui.hsts_enabled.prop('checked', false);
}
this.ui.hsts_enabled.trigger('change');
},
'change @ui.hsts_enabled': function () {
let checked = this.ui.hsts_enabled.prop('checked');
this.ui.hsts_subdomains
.prop('disabled', !checked)
.parents('.form-group')
.css('opacity', checked ? 1 : 0.5);
if (!checked) {
this.ui.hsts_subdomains.prop('checked', false);
}
}, },
'click @ui.save': function (e) { 'click @ui.save': function (e) {
@ -55,16 +90,12 @@ module.exports = Mn.View.extend({
let data = this.ui.form.serializeJSON(); let data = this.ui.form.serializeJSON();
// Manipulate // Manipulate
data.block_exploits = !!data.block_exploits; data.block_exploits = !!data.block_exploits;
data.preserve_path = !!data.preserve_path; data.preserve_path = !!data.preserve_path;
data.http2_support = !!data.http2_support;
if (typeof data.ssl_forced !== 'undefined' && data.ssl_forced === '1') { data.hsts_enabled = !!data.hsts_enabled;
data.ssl_forced = true; data.hsts_subdomains = !!data.hsts_subdomains;
} data.ssl_forced = !!data.ssl_forced;
if (typeof data.http2_support !== 'undefined') {
data.http2_support = !!data.http2_support;
}
if (typeof data.domain_names === 'string' && data.domain_names) { if (typeof data.domain_names === 'string' && data.domain_names) {
data.domain_names = data.domain_names.split(','); data.domain_names = data.domain_names.split(',');

View File

@ -79,7 +79,9 @@
"no-ssl": "This host will not use HTTPS", "no-ssl": "This host will not use HTTPS",
"advanced": "Advanced", "advanced": "Advanced",
"advanced-warning": "Enter your custom Nginx configuration here at your own risk!", "advanced-warning": "Enter your custom Nginx configuration here at your own risk!",
"advanced-config": "Custom Nginx Configuration" "advanced-config": "Custom Nginx Configuration",
"hsts-enabled": "HSTS Enabled",
"hsts-subdomains": "HSTS Subdomains"
}, },
"ssl": { "ssl": {
"letsencrypt": "Let's Encrypt", "letsencrypt": "Let's Encrypt",

View File

@ -14,6 +14,8 @@ const model = Backbone.Model.extend({
certificate_id: 0, certificate_id: 0,
ssl_forced: false, ssl_forced: false,
http2_support: false, http2_support: false,
hsts_enabled: false,
hsts_subdomains: false,
enabled: true, enabled: true,
meta: {}, meta: {},
advanced_config: '', advanced_config: '',

View File

@ -17,6 +17,8 @@ const model = Backbone.Model.extend({
access_list_id: 0, access_list_id: 0,
certificate_id: 0, certificate_id: 0,
ssl_forced: false, ssl_forced: false,
hsts_enabled: false,
hsts_subdomains: false,
caching_enabled: false, caching_enabled: false,
allow_websocket_upgrade: false, allow_websocket_upgrade: false,
block_exploits: false, block_exploits: false,

View File

@ -15,6 +15,8 @@ const model = Backbone.Model.extend({
preserve_path: true, preserve_path: true,
certificate_id: 0, certificate_id: 0,
ssl_forced: false, ssl_forced: false,
hsts_enabled: false,
hsts_subdomains: false,
block_exploits: false, block_exploits: false,
http2_support: false, http2_support: false,
advanced_config: '', advanced_config: '',