Compare commits

...

42 Commits

Author SHA1 Message Date
418899d425 Version bump 2019-02-27 17:52:30 +10:00
e7379e3683 Ignore default location when defined in advanced config (#79) 2019-02-25 10:42:16 +10:00
29bebcc73e Ignore default location when defined in advanced config (#79) 2019-02-25 10:34:55 +10:00
26064b20b8 Fix PR docker image pushing to wrong repo 2019-02-20 08:25:12 +10:00
3dc9b20543 CI Improvements (#77) 2019-02-20 14:35:10 +10:00
444dbd5160 Added PR build steps to CI 2019-02-20 08:25:12 +10:00
c2f99e253c Merge branch 'master' of github.com:jc21/nginx-proxy-manager 2019-02-20 10:04:16 +10:00
5c7fb7b698 Added armv6 Dockerfile 2019-02-20 08:25:12 +10:00
733d7d9583 Update DOCKERHUB.md 2019-02-19 17:05:26 +10:00
6d2f532806 Updated arm instructions 2019-02-18 21:14:26 +10:00
f76c9226c8 Fix workdir perms for subsequent builds 2019-02-18 21:14:26 +10:00
ecbc41b622 Arm64 build process doesn't run as root 2019-02-18 21:14:26 +10:00
4f60d3e7df Fix CI now that tags are changes 2019-02-18 21:14:26 +10:00
7d86fd223e Fix base docker images for arm packages 2019-02-18 21:12:46 +10:00
e3ed216a70 Added arm64 build 2019-02-18 21:12:41 +10:00
2a3d792591 Fixes #68 - HSTS is now part of the UI 2019-02-18 18:21:45 +10:00
4d754275ab Fixes #61 - Http/2 support can now be disabled 2019-02-18 15:33:32 +10:00
44e5f0957c Whoops, missing comma 2019-01-16 10:12:10 +10:00
83ef426b93 Increased custom ssl file size limits 2019-01-16 10:11:51 +10:00
8b8f5fac69 Updated readme and version bump 2019-01-03 21:05:00 +10:00
424ccce43c Added disable/enable to remaining objects 2019-01-03 21:04:11 +10:00
ad41cc985d Fix reconfigure not respecting expansions 2019-01-03 20:32:57 +10:00
981d5a199f Initial work for disabling hosts 2019-01-03 20:27:43 +10:00
48f2bb4cd8 Fix some ip range stuff 2019-01-03 20:25:01 +10:00
aa270925e9 Additional nginx config for real ip determination 2019-01-03 08:18:48 +10:00
3836f7c40a Fetch ip ranges for CDN servers, Cloudfront and Cloudfare 2019-01-03 17:04:53 +10:00
9fcd32c2ca Fix examples to use Aria db variant 2019-01-02 12:51:01 +10:00
2657bcf30c Revert ignoring ssl proxy errors, doesn't make a difference 2018-12-13 14:16:21 +10:00
86ad7d6238 Fix docker build order 2018-12-13 12:20:41 +10:00
c97e6ada5b Support for upstream ssl proxy hosts 2018-12-12 09:47:12 +10:00
cd40ca7f0a Use recent builds of base image with latest nginx versions, version bump 2018-12-04 07:32:05 +10:00
e2ac3b4880 Merge pull request #30 from jlesage/default-https-site-ciphers
Restored ssl_ciphers to aNULL for default HTTPs site.
2018-11-27 09:21:04 +10:00
7f8b185e48 Revert "Use default ciphers for default ssl host to prevent confusing browser errors"
This reverts commit f9876326c9.

This is to make sure the browser doesn't show a certificate warning (for a connection that will be dropped anyway) by breaking the SSL handshake early.
2018-11-26 06:38:24 -05:00
e923db7e94 Merge pull request #29 from jc21/develop
Http2 support
2018-11-26 16:48:30 +10:00
e53d9fa3eb Fix forms for http2_support 2018-11-21 08:04:31 +10:00
411734f392 Fix unsetting http2_support 2018-11-13 20:51:55 +10:00
a457a40359 Fix incorrect template var 2018-11-13 20:42:10 +10:00
caa183c8de Fix bad schema link 2018-11-12 21:48:12 +10:00
0ea5014edb Added develop build to CI 2018-11-12 21:48:12 +10:00
046cb0b76e Added HTTP/2 Support for SSL enabled hosts 2018-11-12 21:45:23 +10:00
9fd480cf77 Updated readme 2018-11-08 13:02:00 +10:00
0f94e68dca Fix redirect loop introduced in 2.0.4 2018-11-08 12:50:10 +10:00
69 changed files with 2176 additions and 267 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

38
Dockerfile.armv6 Normal file
View File

@ -0,0 +1,38 @@
FROM jc21/nginx-proxy-manager-base:armv6
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-arm.tar.gz "https://github.com/just-containers/s6-overlay/releases/download/v1.21.8.0/s6-overlay-arm.tar.gz" \
&& tar xzf /tmp/s6-overlay-arm.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

221
Jenkinsfile vendored
View File

@ -5,20 +5,82 @@ pipeline {
}
agent any
environment {
IMAGE_NAME = "nginx-proxy-manager"
BASE_IMAGE_NAME = "jc21/nginx-proxy-manager-base:v2"
TEMP_IMAGE_NAME = "nginx-proxy-manager-build_${BUILD_NUMBER}"
TEMP_IMAGE_NAME_ARM = "nginx-proxy-manager-arm-build_${BUILD_NUMBER}"
TAG_VERSION = getPackageVersion()
MAJOR_VERSION = "2"
IMAGE = "nginx-proxy-manager"
BASE_IMAGE = "jc21/nginx-proxy-manager-base"
TEMP_IMAGE = "nginx-proxy-manager-build_${BUILD_NUMBER}"
TEMP_IMAGE_ARM = "nginx-proxy-manager-arm-build_${BUILD_NUMBER}"
TEMP_IMAGE_ARM64 = "nginx-proxy-manager-arm64-build_${BUILD_NUMBER}"
TAG_VERSION = getPackageVersion()
MAJOR_VERSION = "2"
BRANCH_LOWER = "${BRANCH_NAME.toLowerCase()}"
}
stages {
stage('Prepare') {
stage('Build PR') {
when {
changeRequest()
}
steps {
sh 'docker pull $DOCKER_CI_TOOLS'
ansiColor('xterm') {
// Codebase
sh 'docker run --rm -v $(pwd):/app -w /app ${BASE_IMAGE} yarn install'
sh 'docker run --rm -v $(pwd):/app -w /app ${BASE_IMAGE} npm run-script build'
sh 'rm -rf node_modules'
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'
// Docker Build
sh 'docker build --pull --no-cache --squash --compress -t ${TEMP_IMAGE} .'
// Dockerhub
sh 'docker tag ${TEMP_IMAGE} docker.io/jc21/${IMAGE}:github-${BRANCH_LOWER}'
withCredentials([usernamePassword(credentialsId: 'jc21-dockerhub', passwordVariable: 'dpass', usernameVariable: 'duser')]) {
sh "docker login -u '${duser}' -p '${dpass}'"
sh 'docker push docker.io/jc21/${IMAGE}:github-${BRANCH_LOWER}'
}
sh 'docker rmi ${TEMP_IMAGE}'
script {
def comment = pullRequest.comment("Docker Image for build ${BUILD_NUMBER} is available on [DockerHub](https://cloud.docker.com/repository/docker/jc21/${IMAGE}) as `jc21/${IMAGE}:github-${BRANCH_LOWER}`")
}
}
}
}
stage('Build') {
stage('Build Develop') {
when {
branch 'develop'
}
steps {
ansiColor('xterm') {
// Codebase
sh 'docker run --rm -v $(pwd):/app -w /app ${BASE_IMAGE} yarn install'
sh 'docker run --rm -v $(pwd):/app -w /app ${BASE_IMAGE} npm run-script build'
sh 'rm -rf node_modules'
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'
// Docker Build
sh 'docker build --pull --no-cache --squash --compress -t ${TEMP_IMAGE} .'
// Dockerhub
sh 'docker tag ${TEMP_IMAGE} docker.io/jc21/${IMAGE}:develop'
withCredentials([usernamePassword(credentialsId: 'jc21-dockerhub', passwordVariable: 'dpass', usernameVariable: 'duser')]) {
sh "docker login -u '${duser}' -p '${dpass}'"
sh 'docker push docker.io/jc21/${IMAGE}:develop'
}
// Private Registry
sh 'docker tag ${TEMP_IMAGE} ${DOCKER_PRIVATE_REGISTRY}/${IMAGE}:develop'
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}:develop'
}
sh 'docker rmi ${TEMP_IMAGE}'
}
}
}
stage('Build Master') {
parallel {
stage('x86_64') {
when {
@ -27,36 +89,40 @@ pipeline {
steps {
ansiColor('xterm') {
// 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_NAME npm run-script build'
sh 'docker run --rm -v $(pwd):/app -w /app ${BASE_IMAGE} yarn install'
sh 'docker run --rm -v $(pwd):/app -w /app ${BASE_IMAGE} npm run-script build'
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):/data $DOCKER_CI_TOOLS node-prune'
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'
// Docker Build
sh 'docker build --pull --no-cache --squash --compress -t $TEMP_IMAGE_NAME .'
// Private Registry
sh 'docker tag $TEMP_IMAGE_NAME $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$TAG_VERSION'
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$TAG_VERSION'
sh 'docker tag $TEMP_IMAGE_NAME $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$MAJOR_VERSION'
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$MAJOR_VERSION'
sh 'docker tag $TEMP_IMAGE_NAME $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:latest'
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:latest'
sh 'docker build --pull --no-cache --squash --compress -t ${TEMP_IMAGE} .'
// Dockerhub
sh 'docker tag $TEMP_IMAGE_NAME docker.io/jc21/$IMAGE_NAME:$TAG_VERSION'
sh 'docker tag $TEMP_IMAGE_NAME docker.io/jc21/$IMAGE_NAME:$MAJOR_VERSION'
sh 'docker tag $TEMP_IMAGE_NAME docker.io/jc21/$IMAGE_NAME:latest'
sh 'docker tag ${TEMP_IMAGE} docker.io/jc21/${IMAGE}:${TAG_VERSION}'
sh 'docker tag ${TEMP_IMAGE} docker.io/jc21/${IMAGE}:${MAJOR_VERSION}'
sh 'docker tag ${TEMP_IMAGE} docker.io/jc21/${IMAGE}:latest'
withCredentials([usernamePassword(credentialsId: 'jc21-dockerhub', passwordVariable: 'dpass', usernameVariable: 'duser')]) {
sh "docker login -u '${duser}' -p '$dpass'"
sh 'docker push docker.io/jc21/$IMAGE_NAME:$TAG_VERSION'
sh 'docker push docker.io/jc21/$IMAGE_NAME:$MAJOR_VERSION'
sh 'docker push docker.io/jc21/$IMAGE_NAME:latest'
sh "docker login -u '${duser}' -p '${dpass}'"
sh 'docker push docker.io/jc21/${IMAGE}:${TAG_VERSION}'
sh 'docker push docker.io/jc21/${IMAGE}:${MAJOR_VERSION}'
sh 'docker push docker.io/jc21/${IMAGE}:latest'
}
sh 'docker rmi $TEMP_IMAGE_NAME'
// Private Registry
sh 'docker tag ${TEMP_IMAGE} ${DOCKER_PRIVATE_REGISTRY}/${IMAGE}:${TAG_VERSION}'
sh 'docker tag ${TEMP_IMAGE} ${DOCKER_PRIVATE_REGISTRY}/${IMAGE}:${MAJOR_VERSION}'
sh 'docker tag ${TEMP_IMAGE} ${DOCKER_PRIVATE_REGISTRY}/${IMAGE}:latest'
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}'
sh 'docker push ${DOCKER_PRIVATE_REGISTRY}/${IMAGE}:${MAJOR_VERSION}'
sh 'docker push ${DOCKER_PRIVATE_REGISTRY}/${IMAGE}:latest'
}
sh 'docker rmi ${TEMP_IMAGE}'
}
}
}
@ -70,35 +136,88 @@ pipeline {
steps {
ansiColor('xterm') {
// 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_NAME-armhf npm run-script build'
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}:armhf npm run-script build'
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
sh 'docker build --pull --no-cache --squash --compress -t $TEMP_IMAGE_NAME_ARM -f Dockerfile.armhf .'
// Private Registry
sh 'docker tag $TEMP_IMAGE_NAME_ARM $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$TAG_VERSION-armhf'
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$TAG_VERSION-armhf'
sh 'docker tag $TEMP_IMAGE_NAME_ARM $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$MAJOR_VERSION-armhf'
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:$MAJOR_VERSION-armhf'
sh 'docker tag $TEMP_IMAGE_NAME_ARM $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:latest-armhf'
sh 'docker push $DOCKER_PRIVATE_REGISTRY/$IMAGE_NAME:latest-armhf'
sh 'docker build --pull --no-cache --squash --compress -t ${TEMP_IMAGE_ARM} -f Dockerfile.armhf .'
// Dockerhub
sh 'docker tag $TEMP_IMAGE_NAME_ARM docker.io/jc21/$IMAGE_NAME:$TAG_VERSION-armhf'
sh 'docker tag $TEMP_IMAGE_NAME_ARM docker.io/jc21/$IMAGE_NAME:$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}:${TAG_VERSION}-armhf'
sh 'docker tag ${TEMP_IMAGE_ARM} docker.io/jc21/${IMAGE}:${MAJOR_VERSION}-armhf'
sh 'docker tag ${TEMP_IMAGE_ARM} docker.io/jc21/${IMAGE}:latest-armhf'
withCredentials([usernamePassword(credentialsId: 'jc21-dockerhub', passwordVariable: 'dpass', usernameVariable: 'duser')]) {
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_NAME:$MAJOR_VERSION-armhf'
sh 'docker push docker.io/jc21/$IMAGE_NAME:latest-armhf'
sh "docker login -u '${duser}' -p '${dpass}'"
sh 'docker push docker.io/jc21/${IMAGE}:${TAG_VERSION}-armhf'
sh 'docker push docker.io/jc21/${IMAGE}:${MAJOR_VERSION}-armhf'
sh 'docker push docker.io/jc21/${IMAGE}:latest-armhf'
}
sh 'docker rmi $TEMP_IMAGE_NAME_ARM'
// Private Registry
sh 'docker tag ${TEMP_IMAGE_ARM} ${DOCKER_PRIVATE_REGISTRY}/${IMAGE}:${TAG_VERSION}-armhf'
sh 'docker tag ${TEMP_IMAGE_ARM} ${DOCKER_PRIVATE_REGISTRY}/${IMAGE}:${MAJOR_VERSION}-armhf'
sh 'docker tag ${TEMP_IMAGE_ARM} ${DOCKER_PRIVATE_REGISTRY}/${IMAGE}:latest-armhf'
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}-armhf'
sh 'docker push ${DOCKER_PRIVATE_REGISTRY}/${IMAGE}:${MAJOR_VERSION}-armhf'
sh 'docker push ${DOCKER_PRIVATE_REGISTRY}/${IMAGE}:latest-armhf'
}
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 *'
}
}
}
@ -118,7 +237,7 @@ pipeline {
}
def getPackageVersion() {
ver = sh(script: 'docker run --rm -v $(pwd):/data $DOCKER_CI_TOOLS bash -c "cat /data/package.json|jq -r \'.version\'"', returnStdout: true)
ver = sh(script: 'docker run --rm -v $(pwd):/data ${DOCKER_CI_TOOLS} bash -c "cat /data/package.json|jq -r \'.version\'"', returnStdout: true)
return ver.trim()
}

View File

@ -2,20 +2,22 @@
# Nginx Proxy Manager
![Version](https://img.shields.io/badge/version-2.0.3-green.svg?style=for-the-badge)
![Version](https://img.shields.io/badge/version-2.0.10-green.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)
This project comes as a pre-built docker image that enables you to easily forward to your websites
running at home or otherwise, including free SSL, without having to know too much about Nginx or Letsencrypt.
----------
**WARNING: Version 2 a complete rewrite!** If you are using the `latest` docker tag and update to version 2
without preparation, horrible things might happen. Refer to the [Importing Documentation](doc/IMPORTING.md).
----------
## Project Goal
I created this project to fill a personal need to provide users with a easy way to accomplish reverse
proxying hosts with SSL termination and it had to be so easy that a monkey could do it. This goal hasn't changed.
While there might be advanced options they are optional and the project should be as simple as possible
so that the barrier for entry here is low.
## Features
- Beautiful and Secure Admin Interface based on [Tabler](https://tabler.github.io/)

17
TODO.md
View File

@ -1,17 +0,0 @@
# TODO
- Dashboard stats are caching instead of querying
Next version:
- UI Log tail
- Enable/Disable a config
Testing:
- Access Levels
- Adding a proxy host without access to read certs or access lists
- Visibility
- Forwarding
- Cert renewals
- Custom certs

View File

@ -2,7 +2,7 @@
# Nginx Proxy Manager
![Version](https://img.shields.io/badge/version-2.0.3-green.svg?style=for-the-badge)
![Version](https://img.shields.io/badge/version-2.0.10-green.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)
@ -16,6 +16,7 @@ running at home or otherwise, including free SSL, without having to know too muc
* latest 2, 2.x.x ([Dockerfile](https://github.com/jc21/nginx-proxy-manager/blob/master/Dockerfile))
* latest-armhf, 2-armhf, 2.x.x-armhf ([Dockerfile](https://github.com/jc21/nginx-proxy-manager/blob/master/Dockerfile.armhf))
* latest-arm64, 2-arm64, 2.x.x-arm64 ([Dockerfile](https://github.com/jc21/nginx-proxy-manager/blob/master/Dockerfile.arm64))
* 1, 1.x.x ([Dockerfile](https://github.com/jc21/nginx-proxy-manager/blob/1.1.2/Dockerfile))
* 1-armhf, 1.x.x-armhf ([Dockerfile](https://github.com/jc21/nginx-proxy-manager/blob/1.1.2/Dockerfile.armhf))

View File

@ -44,7 +44,7 @@ It's easy to use another docker container for your database also and link it as
version: "3"
services:
app:
image: jc21/nginx-proxy-manager:2
image: jc21/nginx-proxy-manager:latest
restart: always
ports:
- 80:80
@ -57,7 +57,7 @@ services:
depends_on:
- db
db:
image: mariadb
image: jc21/mariadb-aria
restart: always
environment:
MYSQL_ROOT_PASSWORD: "password123"
@ -77,7 +77,7 @@ Via `docker-compose`:
version: "3"
services:
app:
image: jc21/nginx-proxy-manager:2
image: jc21/nginx-proxy-manager:latest
restart: always
ports:
- 80:80
@ -100,15 +100,17 @@ docker run -d \
-v /path/to/config.json:/app/config/production.json \
-v /path/to/data:/data \
-v /path/to/letsencrypt:/etc/letsencrypt \
jc21/nginx-proxy-manager:2
jc21/nginx-proxy-manager:latest
```
### 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.
Note: Rpi v2 and below won't work with these images.
```bash
docker run -d \
--name nginx-proxy-manager-app \
@ -118,7 +120,7 @@ docker run -d \
-v /path/to/config.json:/app/config/production.json \
-v /path/to/data:/data \
-v /path/to/letsencrypt:/etc/letsencrypt \
jc21/nginx-proxy-manager:2-armhf
jc21/nginx-proxy-manager:latest-armhf
```

View File

@ -17,7 +17,7 @@ services:
# if you want pretty colors in your docker logs:
- FORCE_COLOR=1
db:
image: mariadb
image: jc21/mariadb-aria
restart: always
environment:
MYSQL_ROOT_PASSWORD: "password123"

View File

@ -11,6 +11,7 @@ services:
- NODE_ENV=development
- FORCE_COLOR=1
volumes:
- ./data:/data
- ./data/letsencrypt:/etc/letsencrypt
- .:/app
- ./rootfs/etc/nginx:/etc/nginx

View File

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

View File

@ -8,8 +8,9 @@ server {
include conf.d/include/block-exploits.conf;
set $server 127.0.0.1;
set $port 81;
set $forward_scheme http;
set $server 127.0.0.1;
set $port 81;
location /health {
access_log off;
@ -46,7 +47,7 @@ server {
ssl_certificate /data/nginx/dummycert.pem;
ssl_certificate_key /data/nginx/dummykey.pem;
include conf.d/include/ssl-ciphers.conf;
ssl_ciphers aNULL;
return 444;
}

View File

@ -0,0 +1,2 @@
# Intentionally left blank

View File

@ -3,4 +3,4 @@ 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_pass http://$server:$port;
proxy_pass $forward_scheme://$server:$port;

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
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;
# HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
add_header Strict-Transport-Security max-age=15768000;

View File

@ -54,6 +54,20 @@ http {
# Dynamically generated resolvers file
include /etc/nginx/conf.d/include/resolvers.conf;
# Default upstream scheme
map $host $forward_scheme {
default http;
}
# Real IP Determination
# Docker subnet:
set_real_ip_from 172.0.0.0/8;
# NPM generated CDN ip ranges:
include conf.d/include/ip_ranges.conf;
# always put the following 2 lines after ip subnets:
real_ip_header X-Forwarded-For;
real_ip_recursive on;
# Files generated by NPM
include /etc/nginx/conf.d/*.conf;
include /data/nginx/proxy_host/*.conf;

View File

@ -11,6 +11,7 @@ function appStart () {
const app = require('./app');
const apiValidator = require('./lib/validator/api');
const internalCertificate = require('./internal/certificate');
const internalIpRanges = require('./internal/ip_ranges');
return migrate.latest()
.then(setup)
@ -18,9 +19,11 @@ function appStart () {
.then(() => {
return apiValidator.loadSchemas;
})
.then(internalIpRanges.fetch)
.then(() => {
internalCertificate.initTimer();
internalIpRanges.initTimer();
const server = app.listen(81, () => {
logger.info('PID ' + process.pid + ' listening on port 81 ...');

View File

@ -47,6 +47,7 @@ const internalDeadHost = {
.then(() => {
// At this point the domains should have been checked
data.owner_user_id = access.token.getUserId(1);
data = internalHost.cleanSslHstsData(data);
return deadHostModel
.query()
@ -89,11 +90,11 @@ const internalDeadHost = {
// Add to audit log
return internalAuditLog.add(access, {
action: 'created',
object_type: 'dead-host',
object_id: row.id,
meta: data
})
action: 'created',
object_type: 'dead-host',
object_id: row.id,
meta: data
})
.then(() => {
return row;
});
@ -103,7 +104,7 @@ const internalDeadHost = {
/**
* @param {Access} access
* @param {Object} data
* @param {Integer} data.id
* @param {Number} data.id
* @return {Promise}
*/
update: (access, data) => {
@ -144,9 +145,9 @@ const internalDeadHost = {
if (create_certificate) {
return internalCertificate.createQuickCertificate(access, {
domain_names: data.domain_names || row.domain_names,
meta: _.assign({}, row.meta, data.meta)
})
domain_names: data.domain_names || row.domain_names,
meta: _.assign({}, row.meta, data.meta)
})
.then(cert => {
// update host with 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.
data = _.assign({}, {
domain_names: row.domain_names
},data);
}, data);
data = internalHost.cleanSslHstsData(data, row);
return deadHostModel
.query()
@ -171,11 +174,11 @@ const internalDeadHost = {
.then(saved_row => {
// Add to audit log
return internalAuditLog.add(access, {
action: 'updated',
object_type: 'dead-host',
object_id: row.id,
meta: data
})
action: 'updated',
object_type: 'dead-host',
object_id: row.id,
meta: data
})
.then(() => {
return _.omit(saved_row, omissions());
});
@ -183,15 +186,15 @@ const internalDeadHost = {
})
.then(() => {
return internalDeadHost.get(access, {
id: data.id,
expand: ['owner', 'certificate']
})
id: data.id,
expand: ['owner', 'certificate']
})
.then(row => {
// Configure nginx
return internalNginx.configure(deadHostModel, 'dead_host', row)
.then(new_meta => {
row.meta = new_meta;
row = internalHost.cleanRowCertificateMeta(row);
row = internalHost.cleanRowCertificateMeta(row);
return _.omit(row, omissions());
});
});
@ -201,7 +204,7 @@ const internalDeadHost = {
/**
* @param {Access} access
* @param {Object} data
* @param {Integer} data.id
* @param {Number} data.id
* @param {Array} [data.expand]
* @param {Array} [data.omit]
* @return {Promise}
@ -248,7 +251,7 @@ const internalDeadHost = {
/**
* @param {Access} access
* @param {Object} data
* @param {Integer} data.id
* @param {Number} data.id
* @param {String} [data.reason]
* @returns {Promise}
*/
@ -290,6 +293,104 @@ const internalDeadHost = {
});
},
/**
* @param {Access} access
* @param {Object} data
* @param {Number} data.id
* @param {String} [data.reason]
* @returns {Promise}
*/
enable: (access, data) => {
return access.can('dead_hosts:update', data.id)
.then(() => {
return internalDeadHost.get(access, {
id: data.id,
expand: ['certificate', '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 deadHostModel
.query()
.where('id', row.id)
.patch({
enabled: 1
})
.then(() => {
// Configure nginx
return internalNginx.configure(deadHostModel, 'dead_host', row);
})
.then(() => {
// Add to audit log
return internalAuditLog.add(access, {
action: 'enabled',
object_type: 'dead-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('dead_hosts:update', data.id)
.then(() => {
return internalDeadHost.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 deadHostModel
.query()
.where('id', row.id)
.patch({
enabled: 0
})
.then(() => {
// Delete Nginx Config
return internalNginx.deleteConfig('dead_host', row)
.then(() => {
return internalNginx.reload();
});
})
.then(() => {
// Add to audit log
return internalAuditLog.add(access, {
action: 'disabled',
object_type: 'dead-host',
object_id: row.id,
meta: _.omit(row, omissions())
});
});
})
.then(() => {
return true;
});
},
/**
* All Hosts
*
@ -338,7 +439,7 @@ const internalDeadHost = {
/**
* Report use
*
* @param {Integer} user_id
* @param {Number} user_id
* @param {String} visibility
* @returns {Promise}
*/

View File

@ -1,11 +1,40 @@
'use strict';
const _ = require('lodash');
const proxyHostModel = require('../models/proxy_host');
const redirectionHostModel = require('../models/redirection_host');
const deadHostModel = require('../models/dead_host');
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
*

View File

@ -0,0 +1,150 @@
'use strict';
const https = require('https');
const fs = require('fs');
const _ = require('lodash');
const logger = require('../logger').ip_ranges;
const error = require('../lib/error');
const internalNginx = require('./nginx');
const Liquid = require('liquidjs');
const CLOUDFRONT_URL = 'https://ip-ranges.amazonaws.com/ip-ranges.json';
const CLOUDFARE_V4_URL = 'https://www.cloudflare.com/ips-v4';
const CLOUDFARE_V6_URL = 'https://www.cloudflare.com/ips-v6';
const internalIpRanges = {
interval_timeout: 1000 * 60 * 60 * 6, // 6 hours
interval: null,
interval_processing: false,
iteration_count: 0,
initTimer: () => {
logger.info('IP Ranges Renewal Timer initialized');
internalIpRanges.interval = setInterval(internalIpRanges.fetch, internalIpRanges.interval_timeout);
},
fetchUrl: url => {
return new Promise((resolve, reject) => {
logger.info('Fetching ' + url);
return https.get(url, res => {
res.setEncoding('utf8');
let raw_data = '';
res.on('data', chunk => {
raw_data += chunk;
});
res.on('end', () => {
resolve(raw_data);
});
}).on('error', err => {
reject(err);
});
});
},
/**
* Triggered at startup and then later by a timer, this will fetch the ip ranges from services and apply them to nginx.
*/
fetch: () => {
if (!internalIpRanges.interval_processing) {
internalIpRanges.interval_processing = true;
logger.info('Fetching IP Ranges from online services...');
let ip_ranges = [];
return internalIpRanges.fetchUrl(CLOUDFRONT_URL)
.then(cloudfront_data => {
let data = JSON.parse(cloudfront_data);
if (data && typeof data.prefixes !== 'undefined') {
data.prefixes.map(item => {
if (item.service === 'CLOUDFRONT') {
ip_ranges.push(item.ip_prefix);
}
});
}
if (data && typeof data.ipv6_prefixes !== 'undefined') {
data.ipv6_prefixes.map(item => {
if (item.service === 'CLOUDFRONT') {
ip_ranges.push(item.ipv6_prefix);
}
});
}
})
.then(() => {
return internalIpRanges.fetchUrl(CLOUDFARE_V4_URL);
})
.then(cloudfare_data => {
let items = cloudfare_data.split('\n');
ip_ranges = [... ip_ranges, ... items];
})
.then(() => {
return internalIpRanges.fetchUrl(CLOUDFARE_V6_URL);
})
.then(cloudfare_data => {
let items = cloudfare_data.split('\n');
ip_ranges = [... ip_ranges, ... items];
})
.then(() => {
let clean_ip_ranges = [];
ip_ranges.map(range => {
if (range) {
clean_ip_ranges.push(range);
}
});
return internalIpRanges.generateConfig(clean_ip_ranges)
.then(() => {
if (internalIpRanges.iteration_count) {
// Reload nginx
return internalNginx.reload();
}
});
})
.then(() => {
internalIpRanges.interval_processing = false;
internalIpRanges.iteration_count++;
})
.catch(err => {
logger.error(err.message);
internalIpRanges.interval_processing = false;
});
}
},
/**
* @param {Array} ip_ranges
* @returns {Promise}
*/
generateConfig: (ip_ranges) => {
let renderEngine = Liquid({
root: __dirname + '/../templates/'
});
return new Promise((resolve, reject) => {
let template = null;
let filename = '/etc/nginx/conf.d/include/ip_ranges.conf';
try {
template = fs.readFileSync(__dirname + '/../templates/ip_ranges.conf', {encoding: 'utf8'});
} catch (err) {
reject(new error.ConfigurationError(err.message));
return;
}
renderEngine
.parseAndRender(template, {ip_ranges: ip_ranges})
.then(config_text => {
fs.writeFileSync(filename, config_text, {encoding: 'utf8'});
resolve(true);
})
.catch(err => {
logger.warn('Could not write ' + filename + ':', err.message);
reject(new error.ConfigurationError(err.message));
});
});
}
};
module.exports = internalIpRanges;

View File

@ -1,5 +1,3 @@
'use strict';
const _ = require('lodash');
const fs = require('fs');
const Liquid = require('liquidjs');
@ -92,7 +90,7 @@ const internalNginx = {
})
.then(() => {
return combined_meta;
})
});
},
/**
@ -146,6 +144,7 @@ const internalNginx = {
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) {
@ -153,6 +152,12 @@ const internalNginx = {
return;
}
// Manipulate the data a bit before sending it to the template
host.use_default_location = true;
if (typeof host.advanced_config !== 'undefined' && host.advanced_config) {
host.use_default_location = !internalNginx.advancedConfigHasDefaultLocation(host.advanced_config);
}
renderEngine
.parseAndRender(template, host)
.then(config_text => {
@ -312,6 +317,14 @@ const internalNginx = {
});
return Promise.all(promises);
},
/**
* @param {string} config
* @returns {boolean}
*/
advancedConfigHasDefaultLocation: function (config) {
return !!config.match(/^(?:.*;)?\s*?location\s*?\/\s*?{/im);
}
};

View File

@ -1,5 +1,3 @@
'use strict';
const _ = require('lodash');
const error = require('../lib/error');
const proxyHostModel = require('../models/proxy_host');
@ -47,6 +45,7 @@ const internalProxyHost = {
.then(() => {
// At this point the domains should have been checked
data.owner_user_id = access.token.getUserId(1);
data = internalHost.cleanSslHstsData(data);
return proxyHostModel
.query()
@ -90,11 +89,11 @@ const internalProxyHost = {
// Add to audit log
return internalAuditLog.add(access, {
action: 'created',
object_type: 'proxy-host',
object_id: row.id,
meta: data
})
action: 'created',
object_type: 'proxy-host',
object_id: row.id,
meta: data
})
.then(() => {
return row;
});
@ -104,12 +103,12 @@ const internalProxyHost = {
/**
* @param {Access} access
* @param {Object} data
* @param {Integer} data.id
* @param {Number} data.id
* @return {Promise}
*/
update: (access, data) => {
let create_certificate = data.certificate_id === 'new';
console.log('PH UPDATE:', data);
if (create_certificate) {
delete data.certificate_id;
}
@ -145,9 +144,9 @@ const internalProxyHost = {
if (create_certificate) {
return internalCertificate.createQuickCertificate(access, {
domain_names: data.domain_names || row.domain_names,
meta: _.assign({}, row.meta, data.meta)
})
domain_names: data.domain_names || row.domain_names,
meta: _.assign({}, row.meta, data.meta)
})
.then(cert => {
// update host with cert id
data.certificate_id = cert.id;
@ -163,7 +162,9 @@ const internalProxyHost = {
// 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({}, {
domain_names: row.domain_names
},data);
}, data);
data = internalHost.cleanSslHstsData(data, row);
return proxyHostModel
.query()
@ -172,11 +173,11 @@ const internalProxyHost = {
.then(saved_row => {
// Add to audit log
return internalAuditLog.add(access, {
action: 'updated',
object_type: 'proxy-host',
object_id: row.id,
meta: data
})
action: 'updated',
object_type: 'proxy-host',
object_id: row.id,
meta: data
})
.then(() => {
return _.omit(saved_row, omissions());
});
@ -184,15 +185,15 @@ const internalProxyHost = {
})
.then(() => {
return internalProxyHost.get(access, {
id: data.id,
expand: ['owner', 'certificate', 'access_list']
})
id: data.id,
expand: ['owner', 'certificate', 'access_list']
})
.then(row => {
// Configure nginx
return internalNginx.configure(proxyHostModel, 'proxy_host', row)
.then(new_meta => {
row.meta = new_meta;
row = internalHost.cleanRowCertificateMeta(row);
row = internalHost.cleanRowCertificateMeta(row);
return _.omit(row, omissions());
});
});
@ -202,7 +203,7 @@ const internalProxyHost = {
/**
* @param {Access} access
* @param {Object} data
* @param {Integer} data.id
* @param {Number} data.id
* @param {Array} [data.expand]
* @param {Array} [data.omit]
* @return {Promise}
@ -249,7 +250,7 @@ const internalProxyHost = {
/**
* @param {Access} access
* @param {Object} data
* @param {Integer} data.id
* @param {Number} data.id
* @param {String} [data.reason]
* @returns {Promise}
*/
@ -291,6 +292,104 @@ const internalProxyHost = {
});
},
/**
* @param {Access} access
* @param {Object} data
* @param {Number} data.id
* @param {String} [data.reason]
* @returns {Promise}
*/
enable: (access, data) => {
return access.can('proxy_hosts:update', data.id)
.then(() => {
return internalProxyHost.get(access, {
id: data.id,
expand: ['certificate', 'owner', 'access_list']
});
})
.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 proxyHostModel
.query()
.where('id', row.id)
.patch({
enabled: 1
})
.then(() => {
// Configure nginx
return internalNginx.configure(proxyHostModel, 'proxy_host', row);
})
.then(() => {
// Add to audit log
return internalAuditLog.add(access, {
action: 'enabled',
object_type: 'proxy-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('proxy_hosts:update', data.id)
.then(() => {
return internalProxyHost.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 proxyHostModel
.query()
.where('id', row.id)
.patch({
enabled: 0
})
.then(() => {
// Delete Nginx Config
return internalNginx.deleteConfig('proxy_host', row)
.then(() => {
return internalNginx.reload();
});
})
.then(() => {
// Add to audit log
return internalAuditLog.add(access, {
action: 'disabled',
object_type: 'proxy-host',
object_id: row.id,
meta: _.omit(row, omissions())
});
});
})
.then(() => {
return true;
});
},
/**
* All Hosts
*
@ -339,7 +438,7 @@ const internalProxyHost = {
/**
* Report use
*
* @param {Integer} user_id
* @param {Number} user_id
* @param {String} visibility
* @returns {Promise}
*/

View File

@ -47,6 +47,7 @@ const internalRedirectionHost = {
.then(() => {
// At this point the domains should have been checked
data.owner_user_id = access.token.getUserId(1);
data = internalHost.cleanSslHstsData(data);
return redirectionHostModel
.query()
@ -89,11 +90,11 @@ const internalRedirectionHost = {
// Add to audit log
return internalAuditLog.add(access, {
action: 'created',
object_type: 'redirection-host',
object_id: row.id,
meta: data
})
action: 'created',
object_type: 'redirection-host',
object_id: row.id,
meta: data
})
.then(() => {
return row;
});
@ -103,7 +104,7 @@ const internalRedirectionHost = {
/**
* @param {Access} access
* @param {Object} data
* @param {Integer} data.id
* @param {Number} data.id
* @return {Promise}
*/
update: (access, data) => {
@ -144,9 +145,9 @@ const internalRedirectionHost = {
if (create_certificate) {
return internalCertificate.createQuickCertificate(access, {
domain_names: data.domain_names || row.domain_names,
meta: _.assign({}, row.meta, data.meta)
})
domain_names: data.domain_names || row.domain_names,
meta: _.assign({}, row.meta, data.meta)
})
.then(cert => {
// update host with 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.
data = _.assign({}, {
domain_names: row.domain_names
},data);
}, data);
data = internalHost.cleanSslHstsData(data, row);
return redirectionHostModel
.query()
@ -171,11 +174,11 @@ const internalRedirectionHost = {
.then(saved_row => {
// Add to audit log
return internalAuditLog.add(access, {
action: 'updated',
object_type: 'redirection-host',
object_id: row.id,
meta: data
})
action: 'updated',
object_type: 'redirection-host',
object_id: row.id,
meta: data
})
.then(() => {
return _.omit(saved_row, omissions());
});
@ -183,15 +186,15 @@ const internalRedirectionHost = {
})
.then(() => {
return internalRedirectionHost.get(access, {
id: data.id,
expand: ['owner', 'certificate']
})
id: data.id,
expand: ['owner', 'certificate']
})
.then(row => {
// Configure nginx
return internalNginx.configure(redirectionHostModel, 'redirection_host', row)
.then(new_meta => {
row.meta = new_meta;
row = internalHost.cleanRowCertificateMeta(row);
row = internalHost.cleanRowCertificateMeta(row);
return _.omit(row, omissions());
});
});
@ -201,7 +204,7 @@ const internalRedirectionHost = {
/**
* @param {Access} access
* @param {Object} data
* @param {Integer} data.id
* @param {Number} data.id
* @param {Array} [data.expand]
* @param {Array} [data.omit]
* @return {Promise}
@ -248,7 +251,7 @@ const internalRedirectionHost = {
/**
* @param {Access} access
* @param {Object} data
* @param {Integer} data.id
* @param {Number} data.id
* @param {String} [data.reason]
* @returns {Promise}
*/
@ -290,6 +293,104 @@ const internalRedirectionHost = {
});
},
/**
* @param {Access} access
* @param {Object} data
* @param {Number} data.id
* @param {String} [data.reason]
* @returns {Promise}
*/
enable: (access, data) => {
return access.can('redirection_hosts:update', data.id)
.then(() => {
return internalRedirectionHost.get(access, {
id: data.id,
expand: ['certificate', '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 redirectionHostModel
.query()
.where('id', row.id)
.patch({
enabled: 1
})
.then(() => {
// Configure nginx
return internalNginx.configure(redirectionHostModel, 'redirection_host', row);
})
.then(() => {
// Add to audit log
return internalAuditLog.add(access, {
action: 'enabled',
object_type: 'redirection-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('redirection_hosts:update', data.id)
.then(() => {
return internalRedirectionHost.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 redirectionHostModel
.query()
.where('id', row.id)
.patch({
enabled: 0
})
.then(() => {
// Delete Nginx Config
return internalNginx.deleteConfig('redirection_host', row)
.then(() => {
return internalNginx.reload();
});
})
.then(() => {
// Add to audit log
return internalAuditLog.add(access, {
action: 'disabled',
object_type: 'redirection-host',
object_id: row.id,
meta: _.omit(row, omissions())
});
});
})
.then(() => {
return true;
});
},
/**
* All Hosts
*
@ -338,7 +439,7 @@ const internalRedirectionHost = {
/**
* Report use
*
* @param {Integer} user_id
* @param {Number} user_id
* @param {String} visibility
* @returns {Promise}
*/

View File

@ -56,7 +56,7 @@ const internalStream = {
/**
* @param {Access} access
* @param {Object} data
* @param {Integer} data.id
* @param {Number} data.id
* @return {Promise}
*/
update: (access, data) => {
@ -75,6 +75,12 @@ const internalStream = {
.query()
.omit(omissions())
.patchAndFetchById(row.id, data)
.then(saved_row => {
return internalNginx.configure(streamModel, 'stream', saved_row)
.then(() => {
return internalStream.get(access, {id: row.id, expand: ['owner']});
});
})
.then(saved_row => {
// Add to audit log
return internalAuditLog.add(access, {
@ -93,7 +99,7 @@ const internalStream = {
/**
* @param {Access} access
* @param {Object} data
* @param {Integer} data.id
* @param {Number} data.id
* @param {Array} [data.expand]
* @param {Array} [data.omit]
* @return {Promise}
@ -139,7 +145,7 @@ const internalStream = {
/**
* @param {Access} access
* @param {Object} data
* @param {Integer} data.id
* @param {Number} data.id
* @param {String} [data.reason]
* @returns {Promise}
*/
@ -181,6 +187,104 @@ const internalStream = {
});
},
/**
* @param {Access} access
* @param {Object} data
* @param {Number} data.id
* @param {String} [data.reason]
* @returns {Promise}
*/
enable: (access, data) => {
return access.can('streams:update', data.id)
.then(() => {
return internalStream.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 streamModel
.query()
.where('id', row.id)
.patch({
enabled: 1
})
.then(() => {
// Configure nginx
return internalNginx.configure(streamModel, 'stream', row);
})
.then(() => {
// Add to audit log
return internalAuditLog.add(access, {
action: 'enabled',
object_type: 'stream',
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('streams:update', data.id)
.then(() => {
return internalStream.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 streamModel
.query()
.where('id', row.id)
.patch({
enabled: 0
})
.then(() => {
// Delete Nginx Config
return internalNginx.deleteConfig('stream', row)
.then(() => {
return internalNginx.reload();
});
})
.then(() => {
// Add to audit log
return internalAuditLog.add(access, {
action: 'disabled',
object_type: 'stream-host',
object_id: row.id,
meta: _.omit(row, omissions())
});
});
})
.then(() => {
return true;
});
},
/**
* All Streams
*
@ -222,7 +326,7 @@ const internalStream = {
/**
* Report use
*
* @param {Integer} user_id
* @param {Number} user_id
* @param {String} visibility
* @returns {Promise}
*/

View File

@ -1,12 +1,13 @@
const {Signale} = require('signale');
module.exports = {
global: new Signale({scope: 'Global '}),
migrate: new Signale({scope: 'Migrate '}),
express: new Signale({scope: 'Express '}),
access: new Signale({scope: 'Access '}),
nginx: new Signale({scope: 'Nginx '}),
ssl: new Signale({scope: 'SSL '}),
import: new Signale({scope: 'Importer'}),
setup: new Signale({scope: 'Setup '})
global: new Signale({scope: 'Global '}),
migrate: new Signale({scope: 'Migrate '}),
express: new Signale({scope: 'Express '}),
access: new Signale({scope: 'Access '}),
nginx: new Signale({scope: 'Nginx '}),
ssl: new Signale({scope: 'SSL '}),
import: new Signale({scope: 'Importer '}),
setup: new Signale({scope: 'Setup '}),
ip_ranges: new Signale({scope: 'IP Ranges'})
};

View File

@ -0,0 +1,51 @@
'use strict';
const migrate_name = 'http2_support';
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('http2_support').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('http2_support').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('http2_support').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

@ -0,0 +1,36 @@
'use strict';
const migrate_name = 'forward_scheme';
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.string('forward_scheme').notNull().defaultTo('http');
})
.then(() => {
logger.info('[' + migrate_name + '] proxy_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

@ -0,0 +1,57 @@
'use strict';
const migrate_name = 'disabled';
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('enabled').notNull().unsigned().defaultTo(1);
})
.then(() => {
logger.info('[' + migrate_name + '] proxy_host Table altered');
return knex.schema.table('redirection_host', function (redirection_host) {
redirection_host.integer('enabled').notNull().unsigned().defaultTo(1);
});
})
.then(() => {
logger.info('[' + migrate_name + '] redirection_host Table altered');
return knex.schema.table('dead_host', function (dead_host) {
dead_host.integer('enabled').notNull().unsigned().defaultTo(1);
});
})
.then(() => {
logger.info('[' + migrate_name + '] dead_host Table altered');
return knex.schema.table('stream', function (stream) {
stream.integer('enabled').notNull().unsigned().defaultTo(1);
});
})
.then(() => {
logger.info('[' + migrate_name + '] stream 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

@ -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

@ -20,7 +20,7 @@ router
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
.all(jwtdecode())
/**
* GET /api/nginx/access-lists
@ -79,7 +79,7 @@ router
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
.all(jwtdecode())
/**
* GET /api/nginx/access-lists/123

View File

@ -20,7 +20,7 @@ router
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
.all(jwtdecode())
/**
* GET /api/nginx/certificates
@ -79,7 +79,7 @@ router
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
.all(jwtdecode())
/**
* GET /api/nginx/certificates/123
@ -157,7 +157,7 @@ router
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
.all(jwtdecode())
/**
* POST /api/nginx/certificates/123/upload
@ -191,7 +191,7 @@ router
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
.all(jwtdecode())
/**
* POST /api/nginx/certificates/validate

View File

@ -20,7 +20,7 @@ router
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
.all(jwtdecode())
/**
* GET /api/nginx/dead-hosts
@ -79,7 +79,7 @@ router
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
.all(jwtdecode())
/**
* GET /api/nginx/dead-hosts/123
@ -147,4 +147,52 @@ router
.catch(next);
});
/**
* Enable dead-host
*
* /api/nginx/dead-hosts/123/enable
*/
router
.route('/:host_id/enable')
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode())
/**
* POST /api/nginx/dead-hosts/123/enable
*/
.post((req, res, next) => {
internalDeadHost.enable(res.locals.access, {id: parseInt(req.params.host_id, 10)})
.then(result => {
res.status(200)
.send(result);
})
.catch(next);
});
/**
* Disable dead-host
*
* /api/nginx/dead-hosts/123/disable
*/
router
.route('/:host_id/disable')
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode())
/**
* POST /api/nginx/dead-hosts/123/disable
*/
.post((req, res, next) => {
internalDeadHost.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

@ -20,7 +20,7 @@ router
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
.all(jwtdecode())
/**
* GET /api/nginx/proxy-hosts
@ -79,7 +79,7 @@ router
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
.all(jwtdecode())
/**
* GET /api/nginx/proxy-hosts/123
@ -147,4 +147,52 @@ router
.catch(next);
});
/**
* Enable proxy-host
*
* /api/nginx/proxy-hosts/123/enable
*/
router
.route('/:host_id/enable')
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode())
/**
* POST /api/nginx/proxy-hosts/123/enable
*/
.post((req, res, next) => {
internalProxyHost.enable(res.locals.access, {id: parseInt(req.params.host_id, 10)})
.then(result => {
res.status(200)
.send(result);
})
.catch(next);
});
/**
* Disable proxy-host
*
* /api/nginx/proxy-hosts/123/disable
*/
router
.route('/:host_id/disable')
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode())
/**
* POST /api/nginx/proxy-hosts/123/disable
*/
.post((req, res, next) => {
internalProxyHost.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

@ -20,7 +20,7 @@ router
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
.all(jwtdecode())
/**
* GET /api/nginx/redirection-hosts
@ -79,7 +79,7 @@ router
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
.all(jwtdecode())
/**
* GET /api/nginx/redirection-hosts/123
@ -147,4 +147,52 @@ router
.catch(next);
});
/**
* Enable redirection-host
*
* /api/nginx/redirection-hosts/123/enable
*/
router
.route('/:host_id/enable')
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode())
/**
* POST /api/nginx/redirection-hosts/123/enable
*/
.post((req, res, next) => {
internalRedirectionHost.enable(res.locals.access, {id: parseInt(req.params.host_id, 10)})
.then(result => {
res.status(200)
.send(result);
})
.catch(next);
});
/**
* Disable redirection-host
*
* /api/nginx/redirection-hosts/123/disable
*/
router
.route('/:host_id/disable')
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode())
/**
* POST /api/nginx/redirection-hosts/123/disable
*/
.post((req, res, next) => {
internalRedirectionHost.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

@ -147,4 +147,52 @@ router
.catch(next);
});
/**
* Enable stream
*
* /api/nginx/streams/123/enable
*/
router
.route('/:host_id/enable')
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode())
/**
* POST /api/nginx/streams/123/enable
*/
.post((req, res, next) => {
internalStream.enable(res.locals.access, {id: parseInt(req.params.host_id, 10)})
.then(result => {
res.status(200)
.send(result);
})
.catch(next);
});
/**
* Disable stream
*
* /api/nginx/streams/123/disable
*/
router
.route('/:host_id/disable')
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode())
/**
* POST /api/nginx/streams/123/disable
*/
.post((req, res, next) => {
internalStream.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

@ -21,7 +21,7 @@ router
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
.all(jwtdecode())
/**
* GET /api/users
@ -80,7 +80,7 @@ router
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
.all(jwtdecode())
.all(userIdFromMe)
/**
@ -160,7 +160,7 @@ router
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
.all(jwtdecode())
.all(userIdFromMe)
/**
@ -191,7 +191,7 @@ router
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
.all(jwtdecode())
.all(userIdFromMe)
/**
@ -222,7 +222,7 @@ router
.options((req, res) => {
res.sendStatus(204);
})
.all(jwtdecode()) // preferred so it doesn't apply to nonexistent routes
.all(jwtdecode())
/**
* POST /api/users/123/login

View File

@ -172,6 +172,11 @@
"pattern": "^(?:\\*\\.)?(?:[^.*]+\\.?)+[^.]$"
}
},
"enabled": {
"description": "Is Enabled",
"example": true,
"type": "boolean"
},
"ssl_enabled": {
"description": "Is SSL Enabled",
"example": true,
@ -182,10 +187,25 @@
"example": false,
"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": {
"type": "string",
"pattern": "^(letsencrypt|other)$"
},
"http2_support": {
"description": "HTTP2 Protocol Support",
"example": false,
"type": "boolean"
},
"block_exploits": {
"description": "Should we block common exploits",
"example": true,

View File

@ -24,9 +24,21 @@
"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": {
"$ref": "../definitions.json#/definitions/http2_support"
},
"advanced_config": {
"type": "string"
},
"enabled": {
"$ref": "../definitions.json#/definitions/enabled"
},
"meta": {
"type": "object"
}
@ -50,9 +62,21 @@
"ssl_forced": {
"$ref": "#/definitions/ssl_forced"
},
"hsts_enabled": {
"$ref": "#/definitions/hsts_enabled"
},
"hsts_subdomains": {
"$ref": "#/definitions/hsts_subdomains"
},
"http2_support": {
"$ref": "#/definitions/http2_support"
},
"advanced_config": {
"$ref": "#/definitions/advanced_config"
},
"enabled": {
"$ref": "#/definitions/enabled"
},
"meta": {
"$ref": "#/definitions/meta"
}
@ -101,6 +125,15 @@
"ssl_forced": {
"$ref": "#/definitions/ssl_forced"
},
"hsts_enabled": {
"$ref": "#/definitions/hsts_enabled"
},
"hsts_subdomains": {
"$ref": "#/definitions/hsts_enabled"
},
"http2_support": {
"$ref": "#/definitions/http2_support"
},
"advanced_config": {
"$ref": "#/definitions/advanced_config"
},
@ -138,6 +171,15 @@
"ssl_forced": {
"$ref": "#/definitions/ssl_forced"
},
"hsts_enabled": {
"$ref": "#/definitions/hsts_enabled"
},
"hsts_subdomains": {
"$ref": "#/definitions/hsts_enabled"
},
"http2_support": {
"$ref": "#/definitions/http2_support"
},
"advanced_config": {
"$ref": "#/definitions/advanced_config"
},
@ -165,6 +207,34 @@
"targetSchema": {
"type": "boolean"
}
},
{
"title": "Enable",
"description": "Enables a existing 404 Host",
"href": "/nginx/dead-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 404 Host",
"href": "/nginx/dead-hosts/{definitions.identity.example}/disable",
"access": "private",
"method": "POST",
"rel": "update",
"http_header": {
"$ref": "../examples.json#/definitions/auth_header"
},
"targetSchema": {
"type": "boolean"
}
}
]
}

View File

@ -18,6 +18,10 @@
"domain_names": {
"$ref": "../definitions.json#/definitions/domain_names"
},
"forward_scheme": {
"type": "string",
"enum": ["http", "https"]
},
"forward_host": {
"type": "string",
"minLength": 1,
@ -34,6 +38,15 @@
"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": {
"$ref": "../definitions.json#/definitions/http2_support"
},
"block_exploits": {
"$ref": "../definitions.json#/definitions/block_exploits"
},
@ -51,6 +64,9 @@
"advanced_config": {
"type": "string"
},
"enabled": {
"$ref": "../definitions.json#/definitions/enabled"
},
"meta": {
"type": "object"
}
@ -68,6 +84,9 @@
"domain_names": {
"$ref": "#/definitions/domain_names"
},
"forward_scheme": {
"$ref": "#/definitions/forward_scheme"
},
"forward_host": {
"$ref": "#/definitions/forward_host"
},
@ -80,6 +99,15 @@
"ssl_forced": {
"$ref": "#/definitions/ssl_forced"
},
"hsts_enabled": {
"$ref": "#/definitions/hsts_enabled"
},
"hsts_subdomains": {
"$ref": "#/definitions/hsts_subdomains"
},
"http2_support": {
"$ref": "#/definitions/http2_support"
},
"block_exploits": {
"$ref": "#/definitions/block_exploits"
},
@ -95,6 +123,9 @@
"advanced_config": {
"$ref": "#/definitions/advanced_config"
},
"enabled": {
"$ref": "#/definitions/enabled"
},
"meta": {
"$ref": "#/definitions/meta"
}
@ -132,6 +163,7 @@
"additionalProperties": false,
"required": [
"domain_names",
"forward_scheme",
"forward_host",
"forward_port"
],
@ -139,6 +171,9 @@
"domain_names": {
"$ref": "#/definitions/domain_names"
},
"forward_scheme": {
"$ref": "#/definitions/forward_scheme"
},
"forward_host": {
"$ref": "#/definitions/forward_host"
},
@ -151,6 +186,15 @@
"ssl_forced": {
"$ref": "#/definitions/ssl_forced"
},
"hsts_enabled": {
"$ref": "#/definitions/hsts_enabled"
},
"hsts_subdomains": {
"$ref": "#/definitions/hsts_enabled"
},
"http2_support": {
"$ref": "#/definitions/http2_support"
},
"block_exploits": {
"$ref": "#/definitions/block_exploits"
},
@ -166,6 +210,9 @@
"advanced_config": {
"$ref": "#/definitions/advanced_config"
},
"enabled": {
"$ref": "#/definitions/enabled"
},
"meta": {
"$ref": "#/definitions/meta"
}
@ -194,6 +241,9 @@
"domain_names": {
"$ref": "#/definitions/domain_names"
},
"forward_scheme": {
"$ref": "#/definitions/forward_scheme"
},
"forward_host": {
"$ref": "#/definitions/forward_host"
},
@ -206,6 +256,15 @@
"ssl_forced": {
"$ref": "#/definitions/ssl_forced"
},
"hsts_enabled": {
"$ref": "#/definitions/hsts_enabled"
},
"hsts_subdomains": {
"$ref": "#/definitions/hsts_enabled"
},
"http2_support": {
"$ref": "#/definitions/http2_support"
},
"block_exploits": {
"$ref": "#/definitions/block_exploits"
},
@ -221,6 +280,9 @@
"advanced_config": {
"$ref": "#/definitions/advanced_config"
},
"enabled": {
"$ref": "#/definitions/enabled"
},
"meta": {
"$ref": "#/definitions/meta"
}
@ -245,6 +307,34 @@
"targetSchema": {
"type": "boolean"
}
},
{
"title": "Enable",
"description": "Enables a existing Proxy Host",
"href": "/nginx/proxy-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 Proxy Host",
"href": "/nginx/proxy-hosts/{definitions.identity.example}/disable",
"access": "private",
"method": "POST",
"rel": "update",
"http_header": {
"$ref": "../examples.json#/definitions/auth_header"
},
"targetSchema": {
"type": "boolean"
}
}
]
}

View File

@ -32,12 +32,24 @@
"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": {
"$ref": "../definitions.json#/definitions/http2_support"
},
"block_exploits": {
"$ref": "../definitions.json#/definitions/block_exploits"
},
"advanced_config": {
"type": "string"
},
"enabled": {
"$ref": "../definitions.json#/definitions/enabled"
},
"meta": {
"type": "object"
}
@ -67,12 +79,24 @@
"ssl_forced": {
"$ref": "#/definitions/ssl_forced"
},
"hsts_enabled": {
"$ref": "#/definitions/hsts_enabled"
},
"hsts_subdomains": {
"$ref": "#/definitions/hsts_subdomains"
},
"http2_support": {
"$ref": "#/definitions/http2_support"
},
"block_exploits": {
"$ref": "#/definitions/block_exploits"
},
"advanced_config": {
"$ref": "#/definitions/advanced_config"
},
"enabled": {
"$ref": "#/definitions/enabled"
},
"meta": {
"$ref": "#/definitions/meta"
}
@ -128,6 +152,15 @@
"ssl_forced": {
"$ref": "#/definitions/ssl_forced"
},
"hsts_enabled": {
"$ref": "#/definitions/hsts_enabled"
},
"hsts_subdomains": {
"$ref": "#/definitions/hsts_enabled"
},
"http2_support": {
"$ref": "#/definitions/http2_support"
},
"block_exploits": {
"$ref": "#/definitions/block_exploits"
},
@ -174,6 +207,15 @@
"ssl_forced": {
"$ref": "#/definitions/ssl_forced"
},
"hsts_enabled": {
"$ref": "#/definitions/hsts_enabled"
},
"hsts_subdomains": {
"$ref": "#/definitions/hsts_enabled"
},
"http2_support": {
"$ref": "#/definitions/http2_support"
},
"block_exploits": {
"$ref": "#/definitions/block_exploits"
},
@ -204,6 +246,34 @@
"targetSchema": {
"type": "boolean"
}
},
{
"title": "Enable",
"description": "Enables a existing Redirection Host",
"href": "/nginx/redirection-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 Redirection Host",
"href": "/nginx/redirection-hosts/{definitions.identity.example}/disable",
"access": "private",
"method": "POST",
"rel": "update",
"http_header": {
"$ref": "../examples.json#/definitions/auth_header"
},
"targetSchema": {
"type": "boolean"
}
}
]
}

View File

@ -35,6 +35,9 @@
"udp_forwarding": {
"type": "boolean"
},
"enabled": {
"$ref": "../definitions.json#/definitions/enabled"
},
"meta": {
"type": "object"
}
@ -64,6 +67,9 @@
"udp_forwarding": {
"$ref": "#/definitions/udp_forwarding"
},
"enabled": {
"$ref": "#/definitions/enabled"
},
"meta": {
"$ref": "#/definitions/meta"
}
@ -184,6 +190,34 @@
"targetSchema": {
"type": "boolean"
}
},
{
"title": "Enable",
"description": "Enables a existing Stream",
"href": "/nginx/streams/{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 Stream",
"href": "/nginx/streams/{definitions.identity.example}/disable",
"access": "private",
"method": "POST",
"rel": "update",
"http_header": {
"$ref": "../examples.json#/definitions/auth_header"
},
"targetSchema": {
"type": "boolean"
}
}
]
}

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

@ -1,5 +1,5 @@
listen 80;
{% if certificate -%}
listen 443 ssl;
listen 443 ssl{% if http2_support %} http2{% endif %};
{% endif %}
server_name {{ domain_names | join: " " }};
server_name {{ domain_names | join: " " }};

View File

@ -1,12 +1,22 @@
{% include "_header_comment.conf" %}
{% if enabled %}
server {
{% include "_listen.conf" %}
{% include "_certificates.conf" %}
{% include "_hsts.conf" %}
access_log /data/logs/dead_host-{{ id }}.log standard;
{{ advanced_config }}
return 404;
{% if use_default_location %}
location / {
{% include "_forced_ssl.conf" %}
{% include "_hsts.conf" %}
return 404;
}
{% endif %}
}
{% endif %}

View File

@ -0,0 +1,3 @@
{% for range in ip_ranges %}
set_real_ip_from {{ range }};
{% endfor %}

View File

@ -1,18 +1,22 @@
{% include "_header_comment.conf" %}
{% if enabled %}
server {
set $server "{{ forward_host }}";
set $port {{ forward_port }};
set $forward_scheme {{ forward_scheme }};
set $server "{{ forward_host }}";
set $port {{ forward_port }};
{% include "_listen.conf" %}
{% include "_certificates.conf" %}
{% include "_assets.conf" %}
{% include "_exploits.conf" %}
{% include "_hsts.conf" %}
access_log /data/logs/proxy_host-{{ id }}.log proxy;
{{ advanced_config }}
{% if use_default_location %}
location / {
{%- if access_list_id > 0 -%}
# Access List
@ -21,14 +25,18 @@ server {
{%- endif %}
{% include "_forced_ssl.conf" %}
{% include "_hsts.conf" %}
{% if allow_websocket_upgrade == 1 or allow_websocket_upgrade == true %}
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
{% endif %}
{% if allow_websocket_upgrade == 1 or allow_websocket_upgrade == true %}
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
{% endif %}
# Proxy!
include conf.d/include/proxy.conf;
}
{% endif %}
}
{% endif %}

View File

@ -1,19 +1,21 @@
{% include "_header_comment.conf" %}
{% if enabled %}
server {
{% include "_listen.conf" %}
{% include "_certificates.conf" %}
{% include "_assets.conf" %}
{% include "_exploits.conf" %}
{% include "_hsts.conf" %}
access_log /data/logs/redirection_host-{{ id }}.log standard;
{{ advanced_config }}
# TODO: Preserve Path Option
{% if use_default_location %}
location / {
{% include "_forced_ssl.conf" %}
{% include "_hsts.conf" %}
{% if preserve_path == 1 or preserve_path == true %}
return 301 $scheme://{{ forward_domain_name }}$request_uri;
@ -21,4 +23,7 @@ server {
return 301 $scheme://{{ forward_domain_name }};
{% endif %}
}
{% endif %}
}
{% endif %}

View File

@ -2,6 +2,7 @@
# {{ incoming_port }} TCP: {{ tcp_forwarding }} UDP: {{ udp_forwarding }}
# ------------------------------------------------------------
{% if enabled %}
{% if tcp_forwarding == 1 or tcp_forwarding == true -%}
server {
listen {{ incoming_port }};
@ -14,3 +15,4 @@ server {
proxy_pass {{ forward_ip }}:{{ forwarding_port }};
}
{% endif %}
{% endif %}

View File

@ -5,5 +5,5 @@
<span class="loader"></span>
</div>
<script type="text/javascript" src="/js/main.bundle.js?v=<%= version %>"></script>
<script type="text/javascript" src="/js/login.bundle.js?v=<%= version %>"></script>
<%- include partials/footer.ejs %>

View File

@ -7,7 +7,7 @@ const Tokens = require('./tokens');
/**
* @param {String} message
* @param {*} debug
* @param {Integer} code
* @param {Number} code
* @constructor
*/
const ApiError = function (message, debug, code) {
@ -129,7 +129,7 @@ function getAllObjects (path, expand, query) {
}
/**
* @param {String} path
* @param {String} path
* @param {FormData} form_data
* @returns {Promise}
*/
@ -241,7 +241,7 @@ module.exports = {
/**
* @param {Object} data
* @param {Integer} data.id
* @param {Number} data.id
* @returns {Promise}
*/
update: function (data) {
@ -251,7 +251,7 @@ module.exports = {
},
/**
* @param {Integer} id
* @param {Number} id
* @returns {Promise}
*/
delete: function (id) {
@ -260,7 +260,7 @@ module.exports = {
/**
*
* @param {Integer} id
* @param {Number} id
* @param {Object} auth
* @returns {Promise}
*/
@ -269,7 +269,7 @@ module.exports = {
},
/**
* @param {Integer} id
* @param {Number} id
* @returns {Promise}
*/
loginAs: function (id) {
@ -278,7 +278,7 @@ module.exports = {
/**
*
* @param {Integer} id
* @param {Number} id
* @param {Object} perms
* @returns {Promise}
*/
@ -308,7 +308,7 @@ module.exports = {
/**
* @param {Object} data
* @param {Integer} data.id
* @param {Number} data.id
* @returns {Promise}
*/
update: function (data) {
@ -318,11 +318,35 @@ module.exports = {
},
/**
* @param {Integer} id
* @param {Number} id
* @returns {Promise}
*/
delete: function (id) {
return fetch('delete', 'nginx/proxy-hosts/' + id);
},
/**
* @param {Number} id
* @returns {Promise}
*/
get: function (id) {
return fetch('get', 'nginx/proxy-hosts/' + id);
},
/**
* @param {Number} id
* @returns {Promise}
*/
enable: function (id) {
return fetch('post', 'nginx/proxy-hosts/' + id + '/enable');
},
/**
* @param {Number} id
* @returns {Promise}
*/
disable: function (id) {
return fetch('post', 'nginx/proxy-hosts/' + id + '/disable');
}
},
@ -345,7 +369,7 @@ module.exports = {
/**
* @param {Object} data
* @param {Integer} data.id
* @param {Number} data.id
* @returns {Promise}
*/
update: function (data) {
@ -355,7 +379,7 @@ module.exports = {
},
/**
* @param {Integer} id
* @param {Number} id
* @returns {Promise}
*/
delete: function (id) {
@ -363,12 +387,36 @@ module.exports = {
},
/**
* @param {Integer} id
* @param {Number} id
* @returns {Promise}
*/
get: function (id) {
return fetch('get', 'nginx/redirection-hosts/' + id);
},
/**
* @param {Number} id
* @param {FormData} form_data
* @params {Promise}
*/
setCerts: function (id, form_data) {
return FileUpload('nginx/redirection-hosts/' + id + '/certificates', form_data);
},
/**
* @param {Number} id
* @returns {Promise}
*/
enable: function (id) {
return fetch('post', 'nginx/redirection-hosts/' + id + '/enable');
},
/**
* @param {Number} id
* @returns {Promise}
*/
disable: function (id) {
return fetch('post', 'nginx/redirection-hosts/' + id + '/disable');
}
},
@ -391,7 +439,7 @@ module.exports = {
/**
* @param {Object} data
* @param {Integer} data.id
* @param {Number} data.id
* @returns {Promise}
*/
update: function (data) {
@ -401,11 +449,35 @@ module.exports = {
},
/**
* @param {Integer} id
* @param {Number} id
* @returns {Promise}
*/
delete: function (id) {
return fetch('delete', 'nginx/streams/' + id);
},
/**
* @param {Number} id
* @returns {Promise}
*/
get: function (id) {
return fetch('get', 'nginx/streams/' + id);
},
/**
* @param {Number} id
* @returns {Promise}
*/
enable: function (id) {
return fetch('post', 'nginx/streams/' + id + '/enable');
},
/**
* @param {Number} id
* @returns {Promise}
*/
disable: function (id) {
return fetch('post', 'nginx/streams/' + id + '/disable');
}
},
@ -428,7 +500,7 @@ module.exports = {
/**
* @param {Object} data
* @param {Integer} data.id
* @param {Number} data.id
* @returns {Promise}
*/
update: function (data) {
@ -438,7 +510,7 @@ module.exports = {
},
/**
* @param {Integer} id
* @param {Number} id
* @returns {Promise}
*/
delete: function (id) {
@ -446,12 +518,36 @@ module.exports = {
},
/**
* @param {Integer} id
* @param {Number} id
* @returns {Promise}
*/
get: function (id) {
return fetch('get', 'nginx/dead-hosts/' + id);
},
/**
* @param {Number} id
* @param {FormData} form_data
* @params {Promise}
*/
setCerts: function (id, form_data) {
return FileUpload('nginx/dead-hosts/' + id + '/certificates', form_data);
},
/**
* @param {Number} id
* @returns {Promise}
*/
enable: function (id) {
return fetch('post', 'nginx/dead-hosts/' + id + '/enable');
},
/**
* @param {Number} id
* @returns {Promise}
*/
disable: function (id) {
return fetch('post', 'nginx/dead-hosts/' + id + '/disable');
}
},
@ -474,7 +570,7 @@ module.exports = {
/**
* @param {Object} data
* @param {Integer} data.id
* @param {Number} data.id
* @returns {Promise}
*/
update: function (data) {
@ -484,7 +580,7 @@ module.exports = {
},
/**
* @param {Integer} id
* @param {Number} id
* @returns {Promise}
*/
delete: function (id) {
@ -511,7 +607,7 @@ module.exports = {
/**
* @param {Object} data
* @param {Integer} data.id
* @param {Number} data.id
* @returns {Promise}
*/
update: function (data) {
@ -521,7 +617,7 @@ module.exports = {
},
/**
* @param {Integer} id
* @param {Number} id
* @returns {Promise}
*/
delete: function (id) {
@ -529,7 +625,7 @@ module.exports = {
},
/**
* @param {Integer} id
* @param {Number} id
* @param {FormData} form_data
* @params {Promise}
*/

View File

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

View File

@ -36,7 +36,7 @@
</select>
</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="ssl_forced" value="1"<%- ssl_forced ? ' checked' : '' %><%- certificate_id ? '' : ' disabled' %>>
@ -45,6 +45,33 @@
</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="http2_support" value="1"<%- http2_support ? ' checked' : '' %><%- certificate_id ? '' : ' disabled' %>>
<span class="custom-switch-indicator"></span>
<span class="custom-switch-description"><%- i18n('all-hosts', 'http2-support') %></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_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 -->
<div class="col-sm-12 col-md-12 letsencrypt">

View File

@ -22,6 +22,9 @@ module.exports = Mn.View.extend({
save: 'button.save',
certificate_select: 'select[name="certificate_id"]',
ssl_forced: 'input[name="ssl_forced"]',
hsts_enabled: 'input[name="hsts_enabled"]',
hsts_subdomains: 'input[name="hsts_subdomains"]',
http2_support: 'input[name="http2_support"]',
letsencrypt: '.letsencrypt'
},
@ -35,7 +38,44 @@ module.exports = Mn.View.extend({
}
let enabled = id === 'new' || parseInt(id, 10) > 0;
this.ui.ssl_forced.prop('disabled', !enabled).parents('.form-group').css('opacity', enabled ? 1 : 0.5);
let inputs = this.ui.ssl_forced.add(this.ui.http2_support);
inputs
.prop('disabled', !enabled)
.parents('.form-group')
.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) {
@ -50,9 +90,10 @@ module.exports = Mn.View.extend({
let data = this.ui.form.serializeJSON();
// Manipulate
if (typeof data.ssl_forced !== 'undefined' && data.ssl_forced === '1') {
data.ssl_forced = true;
}
data.hsts_enabled = !!data.hsts_enabled;
data.hsts_subdomains = !!data.hsts_subdomains;
data.http2_support = !!data.http2_support;
data.ssl_forced = !!data.ssl_forced;
if (typeof data.domain_names === 'string' && data.domain_names) {
data.domain_names = data.domain_names.split(',');
@ -74,7 +115,7 @@ module.exports = Mn.View.extend({
data.meta.letsencrypt_agree = data.meta.letsencrypt_agree === '1';
} else {
data.certificate_id = parseInt(data.certificate_id, 0);
data.certificate_id = parseInt(data.certificate_id, 10);
}
let method = App.Api.Nginx.DeadHosts.create;

View File

@ -28,7 +28,9 @@
<td>
<%
var o = isOnline();
if (o === true) { %>
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>
@ -42,7 +44,7 @@
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
<div class="dropdown-menu dropdown-menu-right">
<a href="#" class="edit dropdown-item"><i class="dropdown-icon fe fe-edit"></i> <%- i18n('str', 'edit') %></a>
<!--<a href="#" class="logs dropdown-item"><i class="dropdown-icon fe fe-book"></i> <%- i18n('str', 'logs') %></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>

View File

@ -9,12 +9,25 @@ module.exports = Mn.View.extend({
tagName: 'tr',
ui: {
able: 'a.able',
edit: 'a.edit',
delete: 'a.delete',
host_link: '.host-link'
},
events: {
'click @ui.able': function (e) {
e.preventDefault();
let id = this.model.get('id');
App.Api.Nginx.DeadHosts[this.model.get('enabled') ? 'disable' : 'enable'](id)
.then(() => {
return App.Api.Nginx.DeadHosts.get(id)
.then(row => {
this.model.set(row);
});
});
},
'click @ui.edit': function (e) {
e.preventDefault();
App.Controller.showNginxDeadForm(this.model);

View File

@ -20,7 +20,16 @@
<input type="text" name="domain_names" class="form-control" id="input-domains" value="<%- domain_names.join(',') %>" required>
</div>
</div>
<div class="col-sm-8 col-md-8">
<div class="col-sm-3 col-md-3">
<div class="form-group">
<label class="form-label"><%- i18n('proxy-hosts', 'forward-scheme') %><span class="form-required">*</span></label>
<select name="forward_scheme" class="form-control custom-select" placeholder="http">
<option value="http" <%- forward_scheme === 'http' ? 'selected' : '' %>>http</option>
<option value="https" <%- forward_scheme === 'https' ? 'selected' : '' %>>https</option>
</select>
</div>
</div>
<div class="col-sm-5 col-md-5">
<div class="form-group">
<label class="form-label"><%- i18n('proxy-hosts', 'forward-host') %><span class="form-required">*</span></label>
<input type="text" name="forward_host" class="form-control text-monospace" placeholder="" value="<%- forward_host %>" autocomplete="off" maxlength="50" required>
@ -59,6 +68,7 @@
</label>
</div>
</div>
<div class="col-sm-12 col-md-12">
<div class="form-group">
<label class="form-label"><%- i18n('proxy-hosts', 'access-list') %></label>
@ -82,7 +92,7 @@
</select>
</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="ssl_forced" value="1"<%- ssl_forced ? ' checked' : '' %><%- certificate_id ? '' : ' disabled' %>>
@ -91,6 +101,33 @@
</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="http2_support" value="1"<%- http2_support ? ' checked' : '' %><%- certificate_id ? '' : ' disabled' %>>
<span class="custom-switch-indicator"></span>
<span class="custom-switch-description"><%- i18n('all-hosts', 'http2-support') %></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_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 -->
<div class="col-sm-12 col-md-12 letsencrypt">
@ -115,6 +152,12 @@
<div role="tabpanel" class="tab-pane" id="advanced">
<div class="row">
<div class="col-md-12">
<p>Nginx variables available to you are:</p>
<ul class="text-monospace">
<li>$server # Host/IP</li>
<li>$port # Port Number</li>
<li>$forward_scheme # http or https</li>
</ul>
<div class="form-group mb-0">
<label class="form-label"><%- i18n('all-hosts', 'advanced-config') %></label>
<textarea name="advanced_config" rows="8" class="form-control text-monospace" placeholder="# <%- i18n('all-hosts', 'advanced-warning') %>"><%- advanced_config %></textarea>

View File

@ -25,6 +25,10 @@ module.exports = Mn.View.extend({
certificate_select: 'select[name="certificate_id"]',
access_list_select: 'select[name="access_list_id"]',
ssl_forced: 'input[name="ssl_forced"]',
hsts_enabled: 'input[name="hsts_enabled"]',
hsts_subdomains: 'input[name="hsts_subdomains"]',
http2_support: 'input[name="http2_support"]',
forward_scheme: 'select[name="forward_scheme"]',
letsencrypt: '.letsencrypt'
},
@ -38,7 +42,44 @@ module.exports = Mn.View.extend({
}
let enabled = id === 'new' || parseInt(id, 10) > 0;
this.ui.ssl_forced.prop('disabled', !enabled).parents('.form-group').css('opacity', enabled ? 1 : 0.5);
let inputs = this.ui.ssl_forced.add(this.ui.http2_support);
inputs
.prop('disabled', !enabled)
.parents('.form-group')
.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) {
@ -57,10 +98,10 @@ module.exports = Mn.View.extend({
data.block_exploits = !!data.block_exploits;
data.caching_enabled = !!data.caching_enabled;
data.allow_websocket_upgrade = !!data.allow_websocket_upgrade;
if (typeof data.ssl_forced !== 'undefined' && data.ssl_forced === '1') {
data.ssl_forced = true;
}
data.http2_support = !!data.http2_support;
data.hsts_enabled = !!data.hsts_enabled;
data.hsts_subdomains = !!data.hsts_subdomains;
data.ssl_forced = !!data.ssl_forced;
if (typeof data.domain_names === 'string' && data.domain_names) {
data.domain_names = data.domain_names.split(',');
@ -82,7 +123,7 @@ module.exports = Mn.View.extend({
data.meta.letsencrypt_agree = data.meta.letsencrypt_agree === '1';
} else {
data.certificate_id = parseInt(data.certificate_id, 0);
data.certificate_id = parseInt(data.certificate_id, 10);
}
let method = App.Api.Nginx.ProxyHosts.create;
@ -122,6 +163,9 @@ module.exports = Mn.View.extend({
onRender: function () {
let view = this;
this.ui.ssl_forced.trigger('change');
this.ui.hsts_enabled.trigger('change');
// Domain names
this.ui.domain_names.selectize({
delimiter: ',',
@ -137,7 +181,6 @@ module.exports = Mn.View.extend({
});
// Access Lists
this.ui.letsencrypt.hide();
this.ui.access_list_select.selectize({
valueField: 'id',
labelField: 'name',

View File

@ -23,7 +23,7 @@
</div>
</td>
<td>
<div class="text-monospace"><%- forward_host %>:<%- forward_port %></div>
<div class="text-monospace"><%- forward_scheme %>://<%- forward_host %>:<%- forward_port %></div>
</td>
<td>
<div><%- certificate && certificate_id ? i18n('ssl', certificate.provider) : i18n('ssl', 'none') %></div>
@ -34,7 +34,9 @@
<td>
<%
var o = isOnline();
if (o === true) { %>
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>
@ -48,7 +50,7 @@
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
<div class="dropdown-menu dropdown-menu-right">
<a href="#" class="edit dropdown-item"><i class="dropdown-icon fe fe-edit"></i> <%- i18n('str', 'edit') %></a>
<!--<a href="#" class="logs dropdown-item"><i class="dropdown-icon fe fe-book"></i> <%- i18n('str', 'logs') %></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>

View File

@ -9,12 +9,25 @@ module.exports = Mn.View.extend({
tagName: 'tr',
ui: {
able: 'a.able',
edit: 'a.edit',
delete: 'a.delete',
host_link: '.host-link'
},
events: {
'click @ui.able': function (e) {
e.preventDefault();
let id = this.model.get('id');
App.Api.Nginx.ProxyHosts[this.model.get('enabled') ? 'disable' : 'enable'](id)
.then(() => {
return App.Api.Nginx.ProxyHosts.get(id)
.then(row => {
this.model.set(row);
});
});
},
'click @ui.edit': function (e) {
e.preventDefault();
App.Controller.showNginxProxyForm(this.model);

View File

@ -60,7 +60,7 @@
</select>
</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="ssl_forced" value="1"<%- ssl_forced ? ' checked' : '' %><%- certificate_id ? '' : ' disabled' %>>
@ -69,6 +69,33 @@
</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="http2_support" value="1"<%- http2_support ? ' checked' : '' %><%- certificate_id ? '' : ' disabled' %>>
<span class="custom-switch-indicator"></span>
<span class="custom-switch-description"><%- i18n('all-hosts', 'http2-support') %></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_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 -->
<div class="col-sm-12 col-md-12 letsencrypt">

View File

@ -22,6 +22,9 @@ module.exports = Mn.View.extend({
save: 'button.save',
certificate_select: 'select[name="certificate_id"]',
ssl_forced: 'input[name="ssl_forced"]',
hsts_enabled: 'input[name="hsts_enabled"]',
hsts_subdomains: 'input[name="hsts_subdomains"]',
http2_support: 'input[name="http2_support"]',
letsencrypt: '.letsencrypt'
},
@ -35,7 +38,44 @@ module.exports = Mn.View.extend({
}
let enabled = id === 'new' || parseInt(id, 10) > 0;
this.ui.ssl_forced.prop('disabled', !enabled).parents('.form-group').css('opacity', enabled ? 1 : 0.5);
let inputs = this.ui.ssl_forced.add(this.ui.http2_support);
inputs
.prop('disabled', !enabled)
.parents('.form-group')
.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) {
@ -50,12 +90,12 @@ module.exports = Mn.View.extend({
let data = this.ui.form.serializeJSON();
// Manipulate
data.block_exploits = !!data.block_exploits;
data.preserve_path = !!data.preserve_path;
if (typeof data.ssl_forced !== 'undefined' && data.ssl_forced === '1') {
data.ssl_forced = true;
}
data.block_exploits = !!data.block_exploits;
data.preserve_path = !!data.preserve_path;
data.http2_support = !!data.http2_support;
data.hsts_enabled = !!data.hsts_enabled;
data.hsts_subdomains = !!data.hsts_subdomains;
data.ssl_forced = !!data.ssl_forced;
if (typeof data.domain_names === 'string' && data.domain_names) {
data.domain_names = data.domain_names.split(',');
@ -77,7 +117,7 @@ module.exports = Mn.View.extend({
data.meta.letsencrypt_agree = data.meta.letsencrypt_agree === '1';
} else {
data.certificate_id = parseInt(data.certificate_id, 0);
data.certificate_id = parseInt(data.certificate_id, 10);
}
let method = App.Api.Nginx.RedirectionHosts.create;

View File

@ -31,7 +31,9 @@
<td>
<%
var o = isOnline();
if (o === true) { %>
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>
@ -45,7 +47,7 @@
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
<div class="dropdown-menu dropdown-menu-right">
<a href="#" class="edit dropdown-item"><i class="dropdown-icon fe fe-edit"></i> <%- i18n('str', 'edit') %></a>
<!--<a href="#" class="logs dropdown-item"><i class="dropdown-icon fe fe-book"></i> <%- i18n('str', 'logs') %></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>

View File

@ -9,12 +9,25 @@ module.exports = Mn.View.extend({
tagName: 'tr',
ui: {
able: 'a.able',
edit: 'a.edit',
delete: 'a.delete',
host_link: '.host-link'
},
events: {
'click @ui.able': function (e) {
e.preventDefault();
let id = this.model.get('id');
App.Api.Nginx.RedirectionHosts[this.model.get('enabled') ? 'disable' : 'enable'](id)
.then(() => {
return App.Api.Nginx.RedirectionHosts.get(id)
.then(row => {
this.model.set(row);
});
});
},
'click @ui.edit': function (e) {
e.preventDefault();
App.Controller.showNginxRedirectionForm(this.model);

View File

@ -27,7 +27,9 @@
<td>
<%
var o = isOnline();
if (o === true) { %>
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>
@ -41,6 +43,7 @@
<a href="#" data-toggle="dropdown" class="icon"><i class="fe fe-more-vertical"></i></a>
<div class="dropdown-menu dropdown-menu-right">
<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>

View File

@ -9,11 +9,24 @@ module.exports = Mn.View.extend({
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.Streams[this.model.get('enabled') ? 'disable' : 'enable'](id)
.then(() => {
return App.Api.Nginx.Streams.get(id)
.then(row => {
this.model.set(row);
});
});
},
'click @ui.edit': function (e) {
e.preventDefault();
App.Controller.showNginxStreamForm(this.model);

View File

@ -14,6 +14,8 @@
"save": "Save",
"cancel": "Cancel",
"close": "Close",
"enable": "Enable",
"disable": "Disable",
"sure": "Yes I'm Sure",
"disabled": "Disabled",
"choose-file": "Choose file",
@ -65,6 +67,7 @@
"details": "Details",
"enable-ssl": "Enable SSL",
"force-ssl": "Force SSL",
"http2-support": "HTTP/2 Support",
"domain-names": "Domain Names",
"cert-provider": "Certificate Provider",
"block-exploits": "Block Common Exploits",
@ -76,7 +79,9 @@
"no-ssl": "This host will not use HTTPS",
"advanced": "Advanced",
"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": {
"letsencrypt": "Let's Encrypt",
@ -92,6 +97,7 @@
"empty": "There are no Proxy Hosts",
"add": "Add Proxy Host",
"form-title": "{id, select, undefined{New} other{Edit}} Proxy Host",
"forward-scheme": "Scheme",
"forward-host": "Forward Hostname / IP",
"forward-port": "Forward Port",
"delete": "Delete Proxy Host",
@ -99,7 +105,8 @@
"help-title": "What is a Proxy Host?",
"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": "Allow Websocket HTTP Upgrades"
"allow-websocket-upgrade": "Websockets Support",
"ignore-invalid-upstream-ssl": "Ignore Invalid SSL"
},
"redirection-hosts": {
"title": "Redirection Hosts",
@ -210,6 +217,8 @@
"created": "Created {name}",
"updated": "Updated {name}",
"deleted": "Deleted {name}",
"enabled": "Enabled {name}",
"disabled": "Disabled {name}",
"meta-title": "Details for Event",
"view-meta": "View Details",
"date": "Date"

View File

@ -13,6 +13,10 @@ const model = Backbone.Model.extend({
domain_names: [],
certificate_id: 0,
ssl_forced: false,
http2_support: false,
hsts_enabled: false,
hsts_subdomains: false,
enabled: true,
meta: {},
advanced_config: '',
// The following are expansions:

View File

@ -11,15 +11,20 @@ const model = Backbone.Model.extend({
created_on: null,
modified_on: null,
domain_names: [],
forward_scheme: 'http',
forward_host: '',
forward_port: null,
access_list_id: 0,
certificate_id: 0,
ssl_forced: false,
hsts_enabled: false,
hsts_subdomains: false,
caching_enabled: false,
allow_websocket_upgrade: false,
block_exploits: false,
http2_support: false,
advanced_config: '',
enabled: true,
meta: {},
// The following are expansions:
owner: null,

View File

@ -15,8 +15,12 @@ const model = Backbone.Model.extend({
preserve_path: true,
certificate_id: 0,
ssl_forced: false,
hsts_enabled: false,
hsts_subdomains: false,
block_exploits: false,
http2_support: false,
advanced_config: '',
enabled: true,
meta: {},
// The following are expansions:
owner: null,

View File

@ -15,6 +15,7 @@ const model = Backbone.Model.extend({
forwarding_port: null,
tcp_forwarding: true,
udp_forwarding: false,
enabled: true,
meta: {},
// The following are expansions:
owner: null