mirror of
https://github.com/NginxProxyManager/nginx-proxy-manager.git
synced 2025-06-18 18:16:26 +00:00
Compare commits
268 Commits
v2.9.22
...
e1698a903c
Author | SHA1 | Date | |
---|---|---|---|
e1698a903c | |||
3a22a0c09c | |||
82d2476855 | |||
79cd0c5294 | |||
09a03edfd7 | |||
35f0fe745d | |||
f1e433714e | |||
035eaed0a4 | |||
4b100a384d | |||
c5c5fa0a5a | |||
02aefa50cd | |||
4d91cfc397 | |||
79a453f2fe | |||
c62c09569d | |||
09bcf4010c | |||
6aeade6c98 | |||
8655b7d2db | |||
2d929dffa8 | |||
52eaa042d8 | |||
b35aa50b88 | |||
c575a706b5 | |||
587b97c2d3 | |||
317003beda | |||
5a761236c5 | |||
b135527347 | |||
abca9cc89c | |||
6721923601 | |||
a88f77c1a5 | |||
a5b21d0306 | |||
8eab8d71f2 | |||
c56e64bce2 | |||
d06572bb5f | |||
d40f9e06fc | |||
69ec017a53 | |||
fa67f257ef | |||
0dcd648c9d | |||
c989a282e3 | |||
5aff969c04 | |||
bfbf7519ec | |||
bf36c7966a | |||
63cd9ba08f | |||
e3d4882c3d | |||
3e1b73143e | |||
10ece3548d | |||
0503a6af75 | |||
55d765e785 | |||
1fb9a75a33 | |||
9c2e838d61 | |||
c55e47aacf | |||
40d81d6e44 | |||
1c84eaac02 | |||
577954ef8c | |||
f0c75641d8 | |||
e42e2acf12 | |||
eaa11fe460 | |||
5b53825ccb | |||
a94660120f | |||
39f4836485 | |||
58ae5a1559 | |||
2d490b01dc | |||
aec30207da | |||
209c1b3334 | |||
58138fbac4 | |||
da820db4e1 | |||
8fa3069a8d | |||
47b868bfc6 | |||
89a405f60c | |||
0353051436 | |||
a3630a6286 | |||
10d9760242 | |||
c722eb1cea | |||
0472abacd2 | |||
a2e85ceed8 | |||
cddd6fb985 | |||
db23c9a52f | |||
8646cb5a19 | |||
fe0c04610f | |||
a108a5d316 | |||
9f16dae2ff | |||
00264bcfb2 | |||
834fb1a361 | |||
1be87f48c1 | |||
9c54d1b718 | |||
f7d1c490b3 | |||
fe4bd9fed6 | |||
58ef9a688e | |||
d19ebf5925 | |||
96fc6a20bb | |||
e69684919c | |||
be39253a6f | |||
30772a48bd | |||
33c867895c | |||
a7fe687bae | |||
4028120f55 | |||
d1119ec63f | |||
4c906283df | |||
8ec0c76f51 | |||
c70f65d349 | |||
883a272b0a | |||
6aee2bbcba | |||
025fc9776b | |||
b699f05f47 | |||
f7c87f63bd | |||
e4ef095254 | |||
09d5e2c94f | |||
459b7a2223 | |||
9c813bcce3 | |||
b8596ac01c | |||
082c4e1008 | |||
2273eae6ee | |||
997e9d431b | |||
b3564b6d4b | |||
4e27cdabc4 | |||
965873adc5 | |||
5de95a8c90 | |||
fa557d8159 | |||
bc8211a6a9 | |||
1c498f84ad | |||
ea6e9757e3 | |||
1308ae42c2 | |||
7be548575b | |||
c6aab8d4e6 | |||
da55e93183 | |||
af475ab5d4 | |||
7d85463dae | |||
13d4f98fdb | |||
388fff84f2 | |||
49a765516c | |||
27bc8c4e33 | |||
881a067aff | |||
1975e4a151 | |||
4704bd6a38 | |||
ca56e0483f | |||
3b8cb86d72 | |||
5165de4a91 | |||
1ab3575c68 | |||
ccf9cce825 | |||
3ad2188f78 | |||
33dbffb974 | |||
289e438c59 | |||
e08a4d4490 | |||
d1d1819677 | |||
4e0768d56c | |||
3666364418 | |||
9052502a17 | |||
b608d3392d | |||
edb81ecce0 | |||
e24181936f | |||
940d06cac9 | |||
134902d127 | |||
2df4620d05 | |||
f41b1069ae | |||
004a93fbc3 | |||
2d9f04edcd | |||
53dbe258a5 | |||
e4ba22f0f8 | |||
3197de41de | |||
0f7be7987b | |||
853c48dff6 | |||
410c3484ab | |||
44e9f377f9 | |||
0f3b76f607 | |||
f426e64569 | |||
4867db078c | |||
6b565e628f | |||
881d70502b | |||
62e4edddf0 | |||
4b9c02cc0c | |||
5af834e40b | |||
9827c0b202 | |||
6f8db95249 | |||
fe93cb3474 | |||
fa851b61da | |||
3333a32612 | |||
9a79fce498 | |||
b1180f5077 | |||
5454352fe5 | |||
aee93a2f6f | |||
f38cb5b500 | |||
f1b7156c89 | |||
98465cf1b0 | |||
137e865b66 | |||
e740fb4064 | |||
f91f0ee8db | |||
1c9f751512 | |||
a602bdd514 | |||
f7b2be68cc | |||
ab4586fc6b | |||
a984a68065 | |||
52875fca6e | |||
63b50fcd95 | |||
5ab4aea03f | |||
d73135378e | |||
e19d685cb6 | |||
c8caaa56d9 | |||
11a98f4c12 | |||
4a85d4ac4e | |||
3138ba46ce | |||
cdd0b2e6d3 | |||
f458730d87 | |||
d20873dcbb | |||
d1e9407e4d | |||
63ee69f432 | |||
f39e527680 | |||
2dd4434ceb | |||
81054631f9 | |||
53d61bd626 | |||
847e879b3f | |||
ca6d112ecb | |||
cd882b07d0 | |||
4c59400731 | |||
824c837a38 | |||
2a06384a4a | |||
05307aa253 | |||
3d2406ac3d | |||
0127dc7f03 | |||
4349d42636 | |||
4b6f9d9419 | |||
c3f019c911 | |||
ecf0290203 | |||
4f41fe0c95 | |||
c3735fdbbb | |||
c432c34fb3 | |||
a1245bc161 | |||
db4ab1d548 | |||
4245e6231c | |||
6a035eaeab | |||
77057284d3 | |||
86ddd9c83c | |||
67208e43cc | |||
ddf80302c6 | |||
5f2576946d | |||
9fe07fa6c3 | |||
d9b9af543e | |||
eb2e2e0478 | |||
9225d5d442 | |||
308a7149ed | |||
8a4a7d0caf | |||
5d03ede100 | |||
4a86bb42cc | |||
dad8561ea1 | |||
56a92e5c0e | |||
9d672f5813 | |||
d5ed70dbb6 | |||
c197e66d62 | |||
91cf3c8873 | |||
7f5e0414ac | |||
d179887c15 | |||
35abb4d7ae | |||
61b290e220 | |||
e1bcef6e5c | |||
81f51f9e2d | |||
661953db25 | |||
065c2dac42 | |||
c40e48e678 | |||
124cb18e17 | |||
5ac9dc0758 | |||
9a799d51ce | |||
77eb618758 | |||
79fedfcea4 | |||
8fdb8ac853 | |||
4fdc80be01 | |||
f8e6c8d018 | |||
c3469de61b | |||
ea61b15a40 | |||
60175e6d8c | |||
2a07445005 | |||
dad3e1da7c |
21
.github/workflows/stale.yml
vendored
Normal file
21
.github/workflows/stale.yml
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
name: 'Close stale issues and PRs'
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '30 1 * * *'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
stale:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/stale@v9
|
||||||
|
with:
|
||||||
|
stale-issue-label: 'stale'
|
||||||
|
stale-pr-label: 'stale'
|
||||||
|
stale-issue-message: 'Issue is now considered stale. If you want to keep it open, please comment :+1:'
|
||||||
|
stale-pr-message: 'PR is now considered stale. If you want to keep it open, please comment :+1:'
|
||||||
|
close-issue-message: 'Issue was closed due to inactivity.'
|
||||||
|
close-pr-message: 'PR was closed due to inactivity.'
|
||||||
|
days-before-stale: 182
|
||||||
|
days-before-close: 365
|
||||||
|
operations-per-run: 50
|
252
Jenkinsfile
vendored
252
Jenkinsfile
vendored
@ -14,16 +14,14 @@ pipeline {
|
|||||||
ansiColor('xterm')
|
ansiColor('xterm')
|
||||||
}
|
}
|
||||||
environment {
|
environment {
|
||||||
IMAGE = "nginx-proxy-manager"
|
IMAGE = 'nginx-proxy-manager'
|
||||||
BUILD_VERSION = getVersion()
|
BUILD_VERSION = getVersion()
|
||||||
MAJOR_VERSION = "2"
|
MAJOR_VERSION = '2'
|
||||||
BRANCH_LOWER = "${BRANCH_NAME.toLowerCase().replaceAll('/', '-')}"
|
BRANCH_LOWER = "${BRANCH_NAME.toLowerCase().replaceAll('\\\\', '-').replaceAll('/', '-').replaceAll('\\.', '-')}"
|
||||||
COMPOSE_PROJECT_NAME = "npm_${BRANCH_LOWER}_${BUILD_NUMBER}"
|
COMPOSE_PROJECT_NAME = "npm_${BRANCH_LOWER}_${BUILD_NUMBER}"
|
||||||
COMPOSE_FILE = 'docker/docker-compose.ci.yml'
|
COMPOSE_FILE = 'docker/docker-compose.ci.yml'
|
||||||
COMPOSE_INTERACTIVE_NO_CLI = 1
|
COMPOSE_INTERACTIVE_NO_CLI = 1
|
||||||
BUILDX_NAME = "${COMPOSE_PROJECT_NAME}"
|
BUILDX_NAME = "${COMPOSE_PROJECT_NAME}"
|
||||||
DOCS_BUCKET = 'jc21-npm-site'
|
|
||||||
DOCS_CDN = 'EN1G6DEWZUTDT'
|
|
||||||
}
|
}
|
||||||
stages {
|
stages {
|
||||||
stage('Environment') {
|
stage('Environment') {
|
||||||
@ -62,99 +60,114 @@ pipeline {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Build and Test') {
|
stage('Builds') {
|
||||||
steps {
|
parallel {
|
||||||
script {
|
stage('Project') {
|
||||||
// Frontend and Backend
|
steps {
|
||||||
def shStatusCode = sh(label: 'Checking and Building', returnStatus: true, script: '''
|
script {
|
||||||
set -e
|
// Frontend and Backend
|
||||||
./scripts/ci/frontend-build > ${WORKSPACE}/tmp-sh-build 2>&1
|
def shStatusCode = sh(label: 'Checking and Building', returnStatus: true, script: '''
|
||||||
./scripts/ci/test-and-build > ${WORKSPACE}/tmp-sh-build 2>&1
|
set -e
|
||||||
''')
|
./scripts/ci/frontend-build > ${WORKSPACE}/tmp-sh-build 2>&1
|
||||||
shOutput = readFile "${env.WORKSPACE}/tmp-sh-build"
|
./scripts/ci/test-and-build > ${WORKSPACE}/tmp-sh-build 2>&1
|
||||||
if (shStatusCode != 0) {
|
''')
|
||||||
error "${shOutput}"
|
shOutput = readFile "${env.WORKSPACE}/tmp-sh-build"
|
||||||
|
if (shStatusCode != 0) {
|
||||||
|
error "${shOutput}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
post {
|
||||||
|
always {
|
||||||
|
sh 'rm -f ${WORKSPACE}/tmp-sh-build'
|
||||||
|
}
|
||||||
|
failure {
|
||||||
|
npmGithubPrComment("CI Error:\n\n```\n${shOutput}\n```", true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Docs') {
|
||||||
|
steps {
|
||||||
|
dir(path: 'docs') {
|
||||||
|
sh 'yarn install'
|
||||||
|
sh 'yarn build'
|
||||||
|
}
|
||||||
|
dir(path: 'docs/.vuepress/dist') {
|
||||||
|
sh 'tar -czf ../../docs.tgz *'
|
||||||
|
}
|
||||||
|
archiveArtifacts(artifacts: 'docs/docs.tgz', allowEmptyArchive: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Cypress') {
|
||||||
|
steps {
|
||||||
|
// Creating will also create the network prior to
|
||||||
|
// using it in parallel stages below and mitigating
|
||||||
|
// a race condition.
|
||||||
|
sh 'docker-compose build cypress-sqlite'
|
||||||
|
sh 'docker-compose build cypress-mysql'
|
||||||
|
sh 'docker-compose create cypress-sqlite'
|
||||||
|
sh 'docker-compose create cypress-mysql'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
post {
|
|
||||||
always {
|
|
||||||
sh 'rm -f ${WORKSPACE}/tmp-sh-build'
|
|
||||||
}
|
|
||||||
failure {
|
|
||||||
npmGithubPrComment("CI Error:\n\n```\n${shOutput}\n```", true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
stage('Integration Tests Sqlite') {
|
stage('Integration Tests') {
|
||||||
steps {
|
parallel {
|
||||||
// Bring up a stack
|
stage('Sqlite') {
|
||||||
sh 'docker-compose up -d fullstack-sqlite'
|
steps {
|
||||||
sh './scripts/wait-healthy $(docker-compose ps -q fullstack-sqlite) 120'
|
// Bring up a stack
|
||||||
|
sh 'docker-compose up -d fullstack-sqlite'
|
||||||
|
sh './scripts/wait-healthy $(docker-compose ps --all -q fullstack-sqlite) 120'
|
||||||
|
// Stop and Start it, as this will test it's ability to restart with existing data
|
||||||
|
sh 'docker-compose stop fullstack-sqlite'
|
||||||
|
sh 'docker-compose start fullstack-sqlite'
|
||||||
|
sh './scripts/wait-healthy $(docker-compose ps --all -q fullstack-sqlite) 120'
|
||||||
|
|
||||||
// Run tests
|
// Run tests
|
||||||
sh 'rm -rf test/results'
|
sh 'rm -rf test/results-sqlite'
|
||||||
sh 'docker-compose up cypress-sqlite'
|
sh 'docker-compose up cypress-sqlite'
|
||||||
// Get results
|
// Get results
|
||||||
sh 'docker cp -L "$(docker-compose ps -q cypress-sqlite):/test/results" test/'
|
sh 'docker cp -L "$(docker-compose ps --all -q cypress-sqlite):/test/results" test/results-sqlite'
|
||||||
}
|
|
||||||
post {
|
|
||||||
always {
|
|
||||||
// Dumps to analyze later
|
|
||||||
sh 'mkdir -p debug'
|
|
||||||
sh 'docker-compose logs fullstack-sqlite | gzip > debug/docker_fullstack_sqlite.log.gz'
|
|
||||||
sh 'docker-compose logs db | gzip > debug/docker_db.log.gz'
|
|
||||||
// Cypress videos and screenshot artifacts
|
|
||||||
dir(path: 'test/results') {
|
|
||||||
archiveArtifacts allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml'
|
|
||||||
}
|
}
|
||||||
junit 'test/results/junit/*'
|
post {
|
||||||
}
|
always {
|
||||||
}
|
// Dumps to analyze later
|
||||||
}
|
sh 'mkdir -p debug/sqlite'
|
||||||
stage('Integration Tests Mysql') {
|
sh 'docker-compose logs fullstack-sqlite > debug/sqlite/docker_fullstack_sqlite.log'
|
||||||
steps {
|
// Cypress videos and screenshot artifacts
|
||||||
// Bring up a stack
|
dir(path: 'test/results-sqlite') {
|
||||||
sh 'docker-compose up -d fullstack-mysql'
|
archiveArtifacts allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml'
|
||||||
sh './scripts/wait-healthy $(docker-compose ps -q fullstack-mysql) 120'
|
}
|
||||||
|
junit 'test/results-sqlite/junit/*'
|
||||||
// Run tests
|
}
|
||||||
sh 'rm -rf test/results'
|
|
||||||
sh 'docker-compose up cypress-mysql'
|
|
||||||
// Get results
|
|
||||||
sh 'docker cp -L "$(docker-compose ps -q cypress-mysql):/test/results" test/'
|
|
||||||
}
|
|
||||||
post {
|
|
||||||
always {
|
|
||||||
// Dumps to analyze later
|
|
||||||
sh 'mkdir -p debug'
|
|
||||||
sh 'docker-compose logs fullstack-mysql | gzip > debug/docker_fullstack_mysql.log.gz'
|
|
||||||
sh 'docker-compose logs db | gzip > debug/docker_db.log.gz'
|
|
||||||
// Cypress videos and screenshot artifacts
|
|
||||||
dir(path: 'test/results') {
|
|
||||||
archiveArtifacts allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml'
|
|
||||||
}
|
}
|
||||||
junit 'test/results/junit/*'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stage('Docs') {
|
|
||||||
when {
|
|
||||||
not {
|
|
||||||
equals expected: 'UNSTABLE', actual: currentBuild.result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
steps {
|
|
||||||
dir(path: 'docs') {
|
|
||||||
sh 'yarn install'
|
|
||||||
sh 'yarn build'
|
|
||||||
}
|
}
|
||||||
|
stage('Mysql') {
|
||||||
|
steps {
|
||||||
|
// Bring up a stack
|
||||||
|
sh 'docker-compose up -d fullstack-mysql'
|
||||||
|
sh './scripts/wait-healthy $(docker-compose ps --all -q fullstack-mysql) 120'
|
||||||
|
|
||||||
dir(path: 'docs/.vuepress/dist') {
|
// Run tests
|
||||||
sh 'tar -czf ../../docs.tgz *'
|
sh 'rm -rf test/results-mysql'
|
||||||
|
sh 'docker-compose up cypress-mysql'
|
||||||
|
// Get results
|
||||||
|
sh 'docker cp -L "$(docker-compose ps --all -q cypress-mysql):/test/results" test/results-mysql'
|
||||||
|
}
|
||||||
|
post {
|
||||||
|
always {
|
||||||
|
// Dumps to analyze later
|
||||||
|
sh 'mkdir -p debug/mysql'
|
||||||
|
sh 'docker-compose logs fullstack-mysql > debug/mysql/docker_fullstack_mysql.log'
|
||||||
|
sh 'docker-compose logs db > debug/mysql/docker_db.log'
|
||||||
|
// Cypress videos and screenshot artifacts
|
||||||
|
dir(path: 'test/results-mysql') {
|
||||||
|
archiveArtifacts allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml'
|
||||||
|
}
|
||||||
|
junit 'test/results-mysql/junit/*'
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
archiveArtifacts(artifacts: 'docs/docs.tgz', allowEmptyArchive: false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('MultiArch Build') {
|
stage('MultiArch Build') {
|
||||||
@ -170,31 +183,48 @@ pipeline {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Docs Deploy') {
|
stage('Docs / Comment') {
|
||||||
when {
|
parallel {
|
||||||
allOf {
|
stage('Master Docs') {
|
||||||
branch 'master'
|
when {
|
||||||
not {
|
allOf {
|
||||||
equals expected: 'UNSTABLE', actual: currentBuild.result
|
branch 'master'
|
||||||
|
not {
|
||||||
|
equals expected: 'UNSTABLE', actual: currentBuild.result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
npmDocsReleaseMaster()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
stage('Develop Docs') {
|
||||||
steps {
|
when {
|
||||||
npmDocsRelease("$DOCS_BUCKET", "$DOCS_CDN")
|
allOf {
|
||||||
}
|
branch 'develop'
|
||||||
}
|
not {
|
||||||
stage('PR Comment') {
|
equals expected: 'UNSTABLE', actual: currentBuild.result
|
||||||
when {
|
}
|
||||||
allOf {
|
}
|
||||||
changeRequest()
|
}
|
||||||
not {
|
steps {
|
||||||
equals expected: 'UNSTABLE', actual: currentBuild.result
|
npmDocsReleaseDevelop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
stage('PR Comment') {
|
||||||
steps {
|
when {
|
||||||
script {
|
allOf {
|
||||||
npmGithubPrComment("Docker Image for build ${BUILD_NUMBER} is available on [DockerHub](https://cloud.docker.com/repository/docker/jc21/${IMAGE}) as `jc21/${IMAGE}:github-${BRANCH_LOWER}`\n\n**Note:** ensure you backup your NPM instance before testing this PR image! Especially if this PR contains database changes.", true)
|
changeRequest()
|
||||||
|
not {
|
||||||
|
equals expected: 'UNSTABLE', actual: currentBuild.result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
npmGithubPrComment("Docker Image for build ${BUILD_NUMBER} is available on [DockerHub](https://cloud.docker.com/repository/docker/jc21/${IMAGE}) as `jc21/${IMAGE}:github-${BRANCH_LOWER}`\n\n**Note:** ensure you backup your NPM instance before testing this PR image! Especially if this PR contains database changes.", true)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -210,12 +240,12 @@ pipeline {
|
|||||||
sh 'figlet "SUCCESS"'
|
sh 'figlet "SUCCESS"'
|
||||||
}
|
}
|
||||||
failure {
|
failure {
|
||||||
archiveArtifacts(artifacts: 'debug/**.*', allowEmptyArchive: true)
|
archiveArtifacts(artifacts: 'debug/**/*.*', allowEmptyArchive: true)
|
||||||
juxtapose event: 'failure'
|
juxtapose event: 'failure'
|
||||||
sh 'figlet "FAILURE"'
|
sh 'figlet "FAILURE"'
|
||||||
}
|
}
|
||||||
unstable {
|
unstable {
|
||||||
archiveArtifacts(artifacts: 'debug/**.*', allowEmptyArchive: true)
|
archiveArtifacts(artifacts: 'debug/**/*.*', allowEmptyArchive: true)
|
||||||
juxtapose event: 'unstable'
|
juxtapose event: 'unstable'
|
||||||
sh 'figlet "UNSTABLE"'
|
sh 'figlet "UNSTABLE"'
|
||||||
}
|
}
|
||||||
|
26
README.md
26
README.md
@ -1,7 +1,7 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="https://nginxproxymanager.com/github.png">
|
<img src="https://nginxproxymanager.com/github.png">
|
||||||
<br><br>
|
<br><br>
|
||||||
<img src="https://img.shields.io/badge/version-2.9.22-green.svg?style=for-the-badge">
|
<img src="https://img.shields.io/badge/version-2.11.2-green.svg?style=for-the-badge">
|
||||||
<a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager">
|
<a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager">
|
||||||
<img src="https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge">
|
<img src="https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge">
|
||||||
</a>
|
</a>
|
||||||
@ -19,7 +19,7 @@ running at home or otherwise, including free SSL, without having to know too muc
|
|||||||
|
|
||||||
## Project Goal
|
## Project Goal
|
||||||
|
|
||||||
I created this project to fill a personal need to provide users with a easy way to accomplish reverse
|
I created this project to fill a personal need to provide users with an 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.
|
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
|
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.
|
so that the barrier for entry here is low.
|
||||||
@ -56,10 +56,10 @@ I won't go in to too much detail here but here are the basics for someone new to
|
|||||||
2. Create a docker-compose.yml file similar to this:
|
2. Create a docker-compose.yml file similar to this:
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
version: '3'
|
version: '3.8'
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
image: 'jc21/nginx-proxy-manager:latest'
|
image: 'docker.io/jc21/nginx-proxy-manager:latest'
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- '80:80'
|
- '80:80'
|
||||||
@ -70,6 +70,8 @@ services:
|
|||||||
- ./letsencrypt:/etc/letsencrypt
|
- ./letsencrypt:/etc/letsencrypt
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This is the bare minimum configuration required. See the [documentation](https://nginxproxymanager.com/setup/) for more.
|
||||||
|
|
||||||
3. Bring up your stack by running
|
3. Bring up your stack by running
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@ -96,7 +98,18 @@ Password: changeme
|
|||||||
Immediately after logging in with this default user you will be asked to modify your details and change your password.
|
Immediately after logging in with this default user you will be asked to modify your details and change your password.
|
||||||
|
|
||||||
|
|
||||||
## Contributors
|
## Contributing
|
||||||
|
|
||||||
|
All are welcome to create pull requests for this project, against the `develop` branch. Official releases are created from the `master` branch.
|
||||||
|
|
||||||
|
CI is used in this project. All PR's must pass before being considered. After passing,
|
||||||
|
docker builds for PR's are available on dockerhub for manual verifications.
|
||||||
|
|
||||||
|
Documentation within the `develop` branch is available for preview at
|
||||||
|
[https://develop.nginxproxymanager.com](https://develop.nginxproxymanager.com)
|
||||||
|
|
||||||
|
|
||||||
|
### Contributors
|
||||||
|
|
||||||
Special thanks to [all of our contributors](https://github.com/NginxProxyManager/nginx-proxy-manager/graphs/contributors).
|
Special thanks to [all of our contributors](https://github.com/NginxProxyManager/nginx-proxy-manager/graphs/contributors).
|
||||||
|
|
||||||
@ -105,5 +118,4 @@ Special thanks to [all of our contributors](https://github.com/NginxProxyManager
|
|||||||
|
|
||||||
1. [Found a bug?](https://github.com/NginxProxyManager/nginx-proxy-manager/issues)
|
1. [Found a bug?](https://github.com/NginxProxyManager/nginx-proxy-manager/issues)
|
||||||
2. [Discussions](https://github.com/NginxProxyManager/nginx-proxy-manager/discussions)
|
2. [Discussions](https://github.com/NginxProxyManager/nginx-proxy-manager/discussions)
|
||||||
3. [Development Gitter](https://gitter.im/nginx-proxy-manager/community)
|
3. [Reddit](https://reddit.com/r/nginxproxymanager)
|
||||||
4. [Reddit](https://reddit.com/r/nginxproxymanager)
|
|
||||||
|
@ -2,6 +2,7 @@ const express = require('express');
|
|||||||
const bodyParser = require('body-parser');
|
const bodyParser = require('body-parser');
|
||||||
const fileUpload = require('express-fileupload');
|
const fileUpload = require('express-fileupload');
|
||||||
const compression = require('compression');
|
const compression = require('compression');
|
||||||
|
const config = require('./lib/config');
|
||||||
const log = require('./logger').express;
|
const log = require('./logger').express;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -24,7 +25,7 @@ app.enable('trust proxy', ['loopback', 'linklocal', 'uniquelocal']);
|
|||||||
app.enable('strict routing');
|
app.enable('strict routing');
|
||||||
|
|
||||||
// pretty print JSON when not live
|
// pretty print JSON when not live
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (config.debug()) {
|
||||||
app.set('json spaces', 2);
|
app.set('json spaces', 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +66,7 @@ app.use(function (err, req, res, next) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'development' || (req.baseUrl + req.path).includes('nginx/certificates')) {
|
if (config.debug() || (req.baseUrl + req.path).includes('nginx/certificates')) {
|
||||||
payload.debug = {
|
payload.debug = {
|
||||||
stack: typeof err.stack !== 'undefined' && err.stack ? err.stack.split('\n') : null,
|
stack: typeof err.stack !== 'undefined' && err.stack ? err.stack.split('\n') : null,
|
||||||
previous: err.previous
|
previous: err.previous
|
||||||
@ -74,7 +75,7 @@ app.use(function (err, req, res, next) {
|
|||||||
|
|
||||||
// Not every error is worth logging - but this is good for now until it gets annoying.
|
// Not every error is worth logging - but this is good for now until it gets annoying.
|
||||||
if (typeof err.stack !== 'undefined' && err.stack) {
|
if (typeof err.stack !== 'undefined' && err.stack) {
|
||||||
if (process.env.NODE_ENV === 'development' || process.env.DEBUG) {
|
if (config.debug()) {
|
||||||
log.debug(err.stack);
|
log.debug(err.stack);
|
||||||
} else if (typeof err.public == 'undefined' || !err.public) {
|
} else if (typeof err.public == 'undefined' || !err.public) {
|
||||||
log.warn(err.message);
|
log.warn(err.message);
|
||||||
|
@ -1,33 +1,27 @@
|
|||||||
const config = require('config');
|
const config = require('./lib/config');
|
||||||
|
|
||||||
if (!config.has('database')) {
|
if (!config.has('database')) {
|
||||||
throw new Error('Database config does not exist! Please read the instructions: https://github.com/jc21/nginx-proxy-manager/blob/master/doc/INSTALL.md');
|
throw new Error('Database config does not exist! Please read the instructions: https://nginxproxymanager.com/setup/');
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateDbConfig() {
|
function generateDbConfig() {
|
||||||
if (config.database.engine === 'knex-native') {
|
const cfg = config.get('database');
|
||||||
return config.database.knex;
|
if (cfg.engine === 'knex-native') {
|
||||||
} else
|
return cfg.knex;
|
||||||
return {
|
}
|
||||||
client: config.database.engine,
|
return {
|
||||||
connection: {
|
client: cfg.engine,
|
||||||
host: config.database.host,
|
connection: {
|
||||||
user: config.database.user,
|
host: cfg.host,
|
||||||
password: config.database.password,
|
user: cfg.user,
|
||||||
database: config.database.name,
|
password: cfg.password,
|
||||||
port: config.database.port
|
database: cfg.name,
|
||||||
},
|
port: cfg.port
|
||||||
migrations: {
|
},
|
||||||
tableName: 'migrations'
|
migrations: {
|
||||||
}
|
tableName: 'migrations'
|
||||||
};
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports = require('knex')(generateDbConfig());
|
||||||
let data = generateDbConfig();
|
|
||||||
|
|
||||||
if (typeof config.database.version !== 'undefined') {
|
|
||||||
data.version = config.database.version;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = require('knex')(data);
|
|
||||||
|
@ -40,6 +40,210 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/nginx/proxy-hosts": {
|
||||||
|
"get": {
|
||||||
|
"operationId": "getProxyHosts",
|
||||||
|
"summary": "Get all proxy hosts",
|
||||||
|
"tags": ["Proxy Hosts"],
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"BearerAuth": ["users"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"in": "query",
|
||||||
|
"name": "expand",
|
||||||
|
"description": "Expansions",
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["access_list", "owner", "certificate"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "200 response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"examples": {
|
||||||
|
"default": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"created_on": "2023-03-30T01:12:23.000Z",
|
||||||
|
"modified_on": "2023-03-30T02:15:40.000Z",
|
||||||
|
"owner_user_id": 1,
|
||||||
|
"domain_names": ["aasdasdad"],
|
||||||
|
"forward_host": "asdasd",
|
||||||
|
"forward_port": 80,
|
||||||
|
"access_list_id": 0,
|
||||||
|
"certificate_id": 0,
|
||||||
|
"ssl_forced": 0,
|
||||||
|
"caching_enabled": 0,
|
||||||
|
"block_exploits": 0,
|
||||||
|
"advanced_config": "sdfsdfsdf",
|
||||||
|
"meta": {
|
||||||
|
"letsencrypt_agree": false,
|
||||||
|
"dns_challenge": false,
|
||||||
|
"nginx_online": false,
|
||||||
|
"nginx_err": "Command failed: /usr/sbin/nginx -t -g \"error_log off;\"\nnginx: [emerg] unknown directive \"sdfsdfsdf\" in /data/nginx/proxy_host/1.conf:37\nnginx: configuration file /etc/nginx/nginx.conf test failed\n"
|
||||||
|
},
|
||||||
|
"allow_websocket_upgrade": 0,
|
||||||
|
"http2_support": 0,
|
||||||
|
"forward_scheme": "http",
|
||||||
|
"enabled": 1,
|
||||||
|
"locations": [],
|
||||||
|
"hsts_enabled": 0,
|
||||||
|
"hsts_subdomains": 0,
|
||||||
|
"owner": {
|
||||||
|
"id": 1,
|
||||||
|
"created_on": "2023-03-30T01:11:50.000Z",
|
||||||
|
"modified_on": "2023-03-30T01:11:50.000Z",
|
||||||
|
"is_deleted": 0,
|
||||||
|
"is_disabled": 0,
|
||||||
|
"email": "admin@example.com",
|
||||||
|
"name": "Administrator",
|
||||||
|
"nickname": "Admin",
|
||||||
|
"avatar": "",
|
||||||
|
"roles": ["admin"]
|
||||||
|
},
|
||||||
|
"access_list": null,
|
||||||
|
"certificate": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"created_on": "2023-03-30T02:11:49.000Z",
|
||||||
|
"modified_on": "2023-03-30T02:11:49.000Z",
|
||||||
|
"owner_user_id": 1,
|
||||||
|
"domain_names": ["test.example.com"],
|
||||||
|
"forward_host": "1.1.1.1",
|
||||||
|
"forward_port": 80,
|
||||||
|
"access_list_id": 0,
|
||||||
|
"certificate_id": 0,
|
||||||
|
"ssl_forced": 0,
|
||||||
|
"caching_enabled": 0,
|
||||||
|
"block_exploits": 0,
|
||||||
|
"advanced_config": "",
|
||||||
|
"meta": {
|
||||||
|
"letsencrypt_agree": false,
|
||||||
|
"dns_challenge": false,
|
||||||
|
"nginx_online": true,
|
||||||
|
"nginx_err": null
|
||||||
|
},
|
||||||
|
"allow_websocket_upgrade": 0,
|
||||||
|
"http2_support": 0,
|
||||||
|
"forward_scheme": "http",
|
||||||
|
"enabled": 1,
|
||||||
|
"locations": [],
|
||||||
|
"hsts_enabled": 0,
|
||||||
|
"hsts_subdomains": 0,
|
||||||
|
"owner": {
|
||||||
|
"id": 1,
|
||||||
|
"created_on": "2023-03-30T01:11:50.000Z",
|
||||||
|
"modified_on": "2023-03-30T01:11:50.000Z",
|
||||||
|
"is_deleted": 0,
|
||||||
|
"is_disabled": 0,
|
||||||
|
"email": "admin@example.com",
|
||||||
|
"name": "Administrator",
|
||||||
|
"nickname": "Admin",
|
||||||
|
"avatar": "",
|
||||||
|
"roles": ["admin"]
|
||||||
|
},
|
||||||
|
"access_list": null,
|
||||||
|
"certificate": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ProxyHostsList"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"post": {
|
||||||
|
"operationId": "createProxyHost",
|
||||||
|
"summary": "Create a Proxy Host",
|
||||||
|
"tags": ["Proxy Hosts"],
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"BearerAuth": ["users"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"in": "body",
|
||||||
|
"name": "proxyhost",
|
||||||
|
"description": "Proxy Host Payload",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ProxyHostObject"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"201": {
|
||||||
|
"description": "201 response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"examples": {
|
||||||
|
"default": {
|
||||||
|
"value": {
|
||||||
|
"id": 3,
|
||||||
|
"created_on": "2023-03-30T02:31:27.000Z",
|
||||||
|
"modified_on": "2023-03-30T02:31:27.000Z",
|
||||||
|
"owner_user_id": 1,
|
||||||
|
"domain_names": ["test2.example.com"],
|
||||||
|
"forward_host": "1.1.1.1",
|
||||||
|
"forward_port": 80,
|
||||||
|
"access_list_id": 0,
|
||||||
|
"certificate_id": 0,
|
||||||
|
"ssl_forced": 0,
|
||||||
|
"caching_enabled": 0,
|
||||||
|
"block_exploits": 0,
|
||||||
|
"advanced_config": "",
|
||||||
|
"meta": {
|
||||||
|
"letsencrypt_agree": false,
|
||||||
|
"dns_challenge": false
|
||||||
|
},
|
||||||
|
"allow_websocket_upgrade": 0,
|
||||||
|
"http2_support": 0,
|
||||||
|
"forward_scheme": "http",
|
||||||
|
"enabled": 1,
|
||||||
|
"locations": [],
|
||||||
|
"hsts_enabled": 0,
|
||||||
|
"hsts_subdomains": 0,
|
||||||
|
"certificate": null,
|
||||||
|
"owner": {
|
||||||
|
"id": 1,
|
||||||
|
"created_on": "2023-03-30T01:11:50.000Z",
|
||||||
|
"modified_on": "2023-03-30T01:11:50.000Z",
|
||||||
|
"is_deleted": 0,
|
||||||
|
"is_disabled": 0,
|
||||||
|
"email": "admin@example.com",
|
||||||
|
"name": "Administrator",
|
||||||
|
"nickname": "Admin",
|
||||||
|
"avatar": "",
|
||||||
|
"roles": ["admin"]
|
||||||
|
},
|
||||||
|
"access_list": null,
|
||||||
|
"use_default_location": true,
|
||||||
|
"ipv6": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ProxyHostObject"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/schema": {
|
"/schema": {
|
||||||
"get": {
|
"get": {
|
||||||
"operationId": "schema",
|
"operationId": "schema",
|
||||||
@ -55,14 +259,10 @@
|
|||||||
"get": {
|
"get": {
|
||||||
"operationId": "refreshToken",
|
"operationId": "refreshToken",
|
||||||
"summary": "Refresh your access token",
|
"summary": "Refresh your access token",
|
||||||
"tags": [
|
"tags": ["Tokens"],
|
||||||
"Tokens"
|
|
||||||
],
|
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"BearerAuth": [
|
"BearerAuth": ["tokens"]
|
||||||
"tokens"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
@ -104,19 +304,14 @@
|
|||||||
"scope": {
|
"scope": {
|
||||||
"minLength": 1,
|
"minLength": 1,
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": ["user"]
|
||||||
"user"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"secret": {
|
"secret": {
|
||||||
"minLength": 1,
|
"minLength": 1,
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": ["identity", "secret"],
|
||||||
"identity",
|
|
||||||
"secret"
|
|
||||||
],
|
|
||||||
"type": "object"
|
"type": "object"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -144,23 +339,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"summary": "Request a new access token from credentials",
|
"summary": "Request a new access token from credentials",
|
||||||
"tags": [
|
"tags": ["Tokens"]
|
||||||
"Tokens"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/settings": {
|
"/settings": {
|
||||||
"get": {
|
"get": {
|
||||||
"operationId": "getSettings",
|
"operationId": "getSettings",
|
||||||
"summary": "Get all settings",
|
"summary": "Get all settings",
|
||||||
"tags": [
|
"tags": ["Settings"],
|
||||||
"Settings"
|
|
||||||
],
|
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"BearerAuth": [
|
"BearerAuth": ["settings"]
|
||||||
"settings"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
@ -194,14 +383,10 @@
|
|||||||
"get": {
|
"get": {
|
||||||
"operationId": "getSetting",
|
"operationId": "getSetting",
|
||||||
"summary": "Get a setting",
|
"summary": "Get a setting",
|
||||||
"tags": [
|
"tags": ["Settings"],
|
||||||
"Settings"
|
|
||||||
],
|
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"BearerAuth": [
|
"BearerAuth": ["settings"]
|
||||||
"settings"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@ -244,14 +429,10 @@
|
|||||||
"put": {
|
"put": {
|
||||||
"operationId": "updateSetting",
|
"operationId": "updateSetting",
|
||||||
"summary": "Update a setting",
|
"summary": "Update a setting",
|
||||||
"tags": [
|
"tags": ["Settings"],
|
||||||
"Settings"
|
|
||||||
],
|
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"BearerAuth": [
|
"BearerAuth": ["settings"]
|
||||||
"settings"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@ -305,14 +486,10 @@
|
|||||||
"get": {
|
"get": {
|
||||||
"operationId": "getUsers",
|
"operationId": "getUsers",
|
||||||
"summary": "Get all users",
|
"summary": "Get all users",
|
||||||
"tags": [
|
"tags": ["Users"],
|
||||||
"Users"
|
|
||||||
],
|
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"BearerAuth": [
|
"BearerAuth": ["users"]
|
||||||
"users"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@ -322,9 +499,7 @@
|
|||||||
"description": "Expansions",
|
"description": "Expansions",
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": ["permissions"]
|
||||||
"permissions"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -345,9 +520,7 @@
|
|||||||
"name": "Jamie Curnow",
|
"name": "Jamie Curnow",
|
||||||
"nickname": "James",
|
"nickname": "James",
|
||||||
"avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
|
"avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
|
||||||
"roles": [
|
"roles": ["admin"]
|
||||||
"admin"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -362,9 +535,7 @@
|
|||||||
"name": "Jamie Curnow",
|
"name": "Jamie Curnow",
|
||||||
"nickname": "James",
|
"nickname": "James",
|
||||||
"avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
|
"avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
|
||||||
"roles": [
|
"roles": ["admin"],
|
||||||
"admin"
|
|
||||||
],
|
|
||||||
"permissions": {
|
"permissions": {
|
||||||
"visibility": "all",
|
"visibility": "all",
|
||||||
"proxy_hosts": "manage",
|
"proxy_hosts": "manage",
|
||||||
@ -389,14 +560,10 @@
|
|||||||
"post": {
|
"post": {
|
||||||
"operationId": "createUser",
|
"operationId": "createUser",
|
||||||
"summary": "Create a User",
|
"summary": "Create a User",
|
||||||
"tags": [
|
"tags": ["Users"],
|
||||||
"Users"
|
|
||||||
],
|
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"BearerAuth": [
|
"BearerAuth": ["users"]
|
||||||
"users"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@ -426,9 +593,7 @@
|
|||||||
"name": "Jamie Curnow",
|
"name": "Jamie Curnow",
|
||||||
"nickname": "James",
|
"nickname": "James",
|
||||||
"avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
|
"avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
|
||||||
"roles": [
|
"roles": ["admin"],
|
||||||
"admin"
|
|
||||||
],
|
|
||||||
"permissions": {
|
"permissions": {
|
||||||
"visibility": "all",
|
"visibility": "all",
|
||||||
"proxy_hosts": "manage",
|
"proxy_hosts": "manage",
|
||||||
@ -454,14 +619,10 @@
|
|||||||
"get": {
|
"get": {
|
||||||
"operationId": "getUser",
|
"operationId": "getUser",
|
||||||
"summary": "Get a user",
|
"summary": "Get a user",
|
||||||
"tags": [
|
"tags": ["Users"],
|
||||||
"Users"
|
|
||||||
],
|
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"BearerAuth": [
|
"BearerAuth": ["users"]
|
||||||
"users"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@ -501,9 +662,7 @@
|
|||||||
"name": "Jamie Curnow",
|
"name": "Jamie Curnow",
|
||||||
"nickname": "James",
|
"nickname": "James",
|
||||||
"avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
|
"avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
|
||||||
"roles": [
|
"roles": ["admin"]
|
||||||
"admin"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -518,14 +677,10 @@
|
|||||||
"put": {
|
"put": {
|
||||||
"operationId": "updateUser",
|
"operationId": "updateUser",
|
||||||
"summary": "Update a User",
|
"summary": "Update a User",
|
||||||
"tags": [
|
"tags": ["Users"],
|
||||||
"Users"
|
|
||||||
],
|
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"BearerAuth": [
|
"BearerAuth": ["users"]
|
||||||
"users"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@ -574,9 +729,7 @@
|
|||||||
"name": "Jamie Curnow",
|
"name": "Jamie Curnow",
|
||||||
"nickname": "James",
|
"nickname": "James",
|
||||||
"avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
|
"avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
|
||||||
"roles": [
|
"roles": ["admin"]
|
||||||
"admin"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -591,14 +744,10 @@
|
|||||||
"delete": {
|
"delete": {
|
||||||
"operationId": "deleteUser",
|
"operationId": "deleteUser",
|
||||||
"summary": "Delete a User",
|
"summary": "Delete a User",
|
||||||
"tags": [
|
"tags": ["Users"],
|
||||||
"Users"
|
|
||||||
],
|
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"BearerAuth": [
|
"BearerAuth": ["users"]
|
||||||
"users"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@ -637,14 +786,10 @@
|
|||||||
"put": {
|
"put": {
|
||||||
"operationId": "updateUserAuth",
|
"operationId": "updateUserAuth",
|
||||||
"summary": "Update a User's Authentication",
|
"summary": "Update a User's Authentication",
|
||||||
"tags": [
|
"tags": ["Users"],
|
||||||
"Users"
|
|
||||||
],
|
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"BearerAuth": [
|
"BearerAuth": ["users"]
|
||||||
"users"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@ -700,14 +845,10 @@
|
|||||||
"put": {
|
"put": {
|
||||||
"operationId": "updateUserPermissions",
|
"operationId": "updateUserPermissions",
|
||||||
"summary": "Update a User's Permissions",
|
"summary": "Update a User's Permissions",
|
||||||
"tags": [
|
"tags": ["Users"],
|
||||||
"Users"
|
|
||||||
],
|
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"BearerAuth": [
|
"BearerAuth": ["users"]
|
||||||
"users"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@ -755,14 +896,10 @@
|
|||||||
"put": {
|
"put": {
|
||||||
"operationId": "loginAsUser",
|
"operationId": "loginAsUser",
|
||||||
"summary": "Login as this user",
|
"summary": "Login as this user",
|
||||||
"tags": [
|
"tags": ["Users"],
|
||||||
"Users"
|
|
||||||
],
|
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"BearerAuth": [
|
"BearerAuth": ["users"]
|
||||||
"users"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"parameters": [
|
"parameters": [
|
||||||
@ -797,9 +934,7 @@
|
|||||||
"name": "Jamie Curnow",
|
"name": "Jamie Curnow",
|
||||||
"nickname": "James",
|
"nickname": "James",
|
||||||
"avatar": "//www.gravatar.com/avatar/3c8d73f45fd8763f827b964c76e6032a?default=mm",
|
"avatar": "//www.gravatar.com/avatar/3c8d73f45fd8763f827b964c76e6032a?default=mm",
|
||||||
"roles": [
|
"roles": ["admin"]
|
||||||
"admin"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -807,11 +942,7 @@
|
|||||||
"schema": {
|
"schema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "Login object",
|
"description": "Login object",
|
||||||
"required": [
|
"required": ["expires", "token", "user"],
|
||||||
"expires",
|
|
||||||
"token",
|
|
||||||
"user"
|
|
||||||
],
|
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
"expires": {
|
"expires": {
|
||||||
@ -840,14 +971,10 @@
|
|||||||
"get": {
|
"get": {
|
||||||
"operationId": "reportsHosts",
|
"operationId": "reportsHosts",
|
||||||
"summary": "Report on Host Statistics",
|
"summary": "Report on Host Statistics",
|
||||||
"tags": [
|
"tags": ["Reports"],
|
||||||
"Reports"
|
|
||||||
],
|
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"BearerAuth": [
|
"BearerAuth": ["reports"]
|
||||||
"reports"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
@ -878,14 +1005,10 @@
|
|||||||
"get": {
|
"get": {
|
||||||
"operationId": "getAuditLog",
|
"operationId": "getAuditLog",
|
||||||
"summary": "Get Audit Log",
|
"summary": "Get Audit Log",
|
||||||
"tags": [
|
"tags": ["Audit Log"],
|
||||||
"Audit Log"
|
|
||||||
],
|
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"BearerAuth": [
|
"BearerAuth": ["audit-log"]
|
||||||
"audit-log"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
@ -925,10 +1048,7 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "Health object",
|
"description": "Health object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"required": [
|
"required": ["status", "version"],
|
||||||
"status",
|
|
||||||
"version"
|
|
||||||
],
|
|
||||||
"properties": {
|
"properties": {
|
||||||
"status": {
|
"status": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@ -944,11 +1064,7 @@
|
|||||||
"revision": 0
|
"revision": 0
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"required": [
|
"required": ["major", "minor", "revision"],
|
||||||
"major",
|
|
||||||
"minor",
|
|
||||||
"revision"
|
|
||||||
],
|
|
||||||
"properties": {
|
"properties": {
|
||||||
"major": {
|
"major": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
@ -969,10 +1085,7 @@
|
|||||||
"TokenObject": {
|
"TokenObject": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "Token object",
|
"description": "Token object",
|
||||||
"required": [
|
"required": ["expires", "token"],
|
||||||
"expires",
|
|
||||||
"token"
|
|
||||||
],
|
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
"expires": {
|
"expires": {
|
||||||
@ -988,16 +1101,147 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ProxyHostObject": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Proxy Host object",
|
||||||
|
"required": [
|
||||||
|
"id",
|
||||||
|
"created_on",
|
||||||
|
"modified_on",
|
||||||
|
"owner_user_id",
|
||||||
|
"domain_names",
|
||||||
|
"forward_host",
|
||||||
|
"forward_port",
|
||||||
|
"access_list_id",
|
||||||
|
"certificate_id",
|
||||||
|
"ssl_forced",
|
||||||
|
"caching_enabled",
|
||||||
|
"block_exploits",
|
||||||
|
"advanced_config",
|
||||||
|
"meta",
|
||||||
|
"allow_websocket_upgrade",
|
||||||
|
"http2_support",
|
||||||
|
"forward_scheme",
|
||||||
|
"enabled",
|
||||||
|
"locations",
|
||||||
|
"hsts_enabled",
|
||||||
|
"hsts_subdomains",
|
||||||
|
"certificate",
|
||||||
|
"use_default_location",
|
||||||
|
"ipv6"
|
||||||
|
],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Proxy Host ID",
|
||||||
|
"minimum": 1,
|
||||||
|
"example": 1
|
||||||
|
},
|
||||||
|
"created_on": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Created Date",
|
||||||
|
"example": "2020-01-30T09:36:08.000Z"
|
||||||
|
},
|
||||||
|
"modified_on": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Modified Date",
|
||||||
|
"example": "2020-01-30T09:41:04.000Z"
|
||||||
|
},
|
||||||
|
"owner_user_id": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 1,
|
||||||
|
"example": 1
|
||||||
|
},
|
||||||
|
"domain_names": {
|
||||||
|
"type": "array",
|
||||||
|
"minItems": 1,
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"forward_host": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"forward_port": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 1
|
||||||
|
},
|
||||||
|
"access_list_id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"certificate_id": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"ssl_forced": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"caching_enabled": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"block_exploits": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"advanced_config": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"allow_websocket_upgrade": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"http2_support": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"forward_scheme": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"enabled": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"locations": {
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"hsts_enabled": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"hsts_subdomains": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"certificate": {
|
||||||
|
"type": "object",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"owner": {
|
||||||
|
"type": "object",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"access_list": {
|
||||||
|
"type": "object",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"use_default_location": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"ipv6": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ProxyHostsList": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "Proxyn Hosts list",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/ProxyHostObject"
|
||||||
|
}
|
||||||
|
},
|
||||||
"SettingObject": {
|
"SettingObject": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "Setting object",
|
"description": "Setting object",
|
||||||
"required": [
|
"required": ["id", "name", "description", "value", "meta"],
|
||||||
"id",
|
|
||||||
"name",
|
|
||||||
"description",
|
|
||||||
"value",
|
|
||||||
"meta"
|
|
||||||
],
|
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": {
|
"id": {
|
||||||
@ -1057,17 +1301,7 @@
|
|||||||
"UserObject": {
|
"UserObject": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "User object",
|
"description": "User object",
|
||||||
"required": [
|
"required": ["id", "created_on", "modified_on", "is_disabled", "email", "name", "nickname", "avatar", "roles"],
|
||||||
"id",
|
|
||||||
"created_on",
|
|
||||||
"modified_on",
|
|
||||||
"is_disabled",
|
|
||||||
"email",
|
|
||||||
"name",
|
|
||||||
"nickname",
|
|
||||||
"avatar",
|
|
||||||
"roles"
|
|
||||||
],
|
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": {
|
"id": {
|
||||||
@ -1117,9 +1351,7 @@
|
|||||||
},
|
},
|
||||||
"roles": {
|
"roles": {
|
||||||
"description": "Roles applied",
|
"description": "Roles applied",
|
||||||
"example": [
|
"example": ["admin"],
|
||||||
"admin"
|
|
||||||
],
|
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@ -1137,10 +1369,7 @@
|
|||||||
"AuthObject": {
|
"AuthObject": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "Authentication Object",
|
"description": "Authentication Object",
|
||||||
"required": [
|
"required": ["type", "secret"],
|
||||||
"type",
|
|
||||||
"secret"
|
|
||||||
],
|
|
||||||
"properties": {
|
"properties": {
|
||||||
"type": {
|
"type": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@ -1167,64 +1396,37 @@
|
|||||||
"visibility": {
|
"visibility": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Visibility Type",
|
"description": "Visibility Type",
|
||||||
"enum": [
|
"enum": ["all", "user"]
|
||||||
"all",
|
|
||||||
"user"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"access_lists": {
|
"access_lists": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Access Lists Permissions",
|
"description": "Access Lists Permissions",
|
||||||
"enum": [
|
"enum": ["hidden", "view", "manage"]
|
||||||
"hidden",
|
|
||||||
"view",
|
|
||||||
"manage"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"dead_hosts": {
|
"dead_hosts": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "404 Hosts Permissions",
|
"description": "404 Hosts Permissions",
|
||||||
"enum": [
|
"enum": ["hidden", "view", "manage"]
|
||||||
"hidden",
|
|
||||||
"view",
|
|
||||||
"manage"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"proxy_hosts": {
|
"proxy_hosts": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Proxy Hosts Permissions",
|
"description": "Proxy Hosts Permissions",
|
||||||
"enum": [
|
"enum": ["hidden", "view", "manage"]
|
||||||
"hidden",
|
|
||||||
"view",
|
|
||||||
"manage"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"redirection_hosts": {
|
"redirection_hosts": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Redirection Permissions",
|
"description": "Redirection Permissions",
|
||||||
"enum": [
|
"enum": ["hidden", "view", "manage"]
|
||||||
"hidden",
|
|
||||||
"view",
|
|
||||||
"manage"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"streams": {
|
"streams": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Streams Permissions",
|
"description": "Streams Permissions",
|
||||||
"enum": [
|
"enum": ["hidden", "view", "manage"]
|
||||||
"hidden",
|
|
||||||
"view",
|
|
||||||
"manage"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"certificates": {
|
"certificates": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Certificates Permissions",
|
"description": "Certificates Permissions",
|
||||||
"enum": [
|
"enum": ["hidden", "view", "manage"]
|
||||||
"hidden",
|
|
||||||
"view",
|
|
||||||
"manage"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -3,9 +3,6 @@
|
|||||||
const logger = require('./logger').global;
|
const logger = require('./logger').global;
|
||||||
|
|
||||||
async function appStart () {
|
async function appStart () {
|
||||||
// Create config file db settings if environment variables have been set
|
|
||||||
await createDbConfigFromEnvironment();
|
|
||||||
|
|
||||||
const migrate = require('./migrate');
|
const migrate = require('./migrate');
|
||||||
const setup = require('./setup');
|
const setup = require('./setup');
|
||||||
const app = require('./app');
|
const app = require('./app');
|
||||||
@ -42,90 +39,6 @@ async function appStart () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createDbConfigFromEnvironment() {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const envMysqlHost = process.env.DB_MYSQL_HOST || null;
|
|
||||||
const envMysqlPort = process.env.DB_MYSQL_PORT || null;
|
|
||||||
const envMysqlUser = process.env.DB_MYSQL_USER || null;
|
|
||||||
const envMysqlName = process.env.DB_MYSQL_NAME || null;
|
|
||||||
let envSqliteFile = process.env.DB_SQLITE_FILE || null;
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const filename = (process.env.NODE_CONFIG_DIR || './config') + '/' + (process.env.NODE_ENV || 'default') + '.json';
|
|
||||||
let configData = {};
|
|
||||||
|
|
||||||
try {
|
|
||||||
configData = require(filename);
|
|
||||||
} catch (err) {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
if (configData.database && configData.database.engine && !configData.database.fromEnv) {
|
|
||||||
logger.info('Manual db configuration already exists, skipping config creation from environment variables');
|
|
||||||
resolve();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!envMysqlHost || !envMysqlPort || !envMysqlUser || !envMysqlName) && !envSqliteFile){
|
|
||||||
envSqliteFile = '/data/database.sqlite';
|
|
||||||
logger.info(`No valid environment variables for database provided, using default SQLite file '${envSqliteFile}'`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (envMysqlHost && envMysqlPort && envMysqlUser && envMysqlName) {
|
|
||||||
const newConfig = {
|
|
||||||
fromEnv: true,
|
|
||||||
engine: 'mysql',
|
|
||||||
host: envMysqlHost,
|
|
||||||
port: envMysqlPort,
|
|
||||||
user: envMysqlUser,
|
|
||||||
password: process.env.DB_MYSQL_PASSWORD,
|
|
||||||
name: envMysqlName,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (JSON.stringify(configData.database) === JSON.stringify(newConfig)) {
|
|
||||||
// Config is unchanged, skip overwrite
|
|
||||||
resolve();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info('Generating MySQL knex configuration from environment variables');
|
|
||||||
configData.database = newConfig;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
const newConfig = {
|
|
||||||
fromEnv: true,
|
|
||||||
engine: 'knex-native',
|
|
||||||
knex: {
|
|
||||||
client: 'sqlite3',
|
|
||||||
connection: {
|
|
||||||
filename: envSqliteFile
|
|
||||||
},
|
|
||||||
useNullAsDefault: true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (JSON.stringify(configData.database) === JSON.stringify(newConfig)) {
|
|
||||||
// Config is unchanged, skip overwrite
|
|
||||||
resolve();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info('Generating SQLite knex configuration');
|
|
||||||
configData.database = newConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write config
|
|
||||||
fs.writeFile(filename, JSON.stringify(configData, null, 2), (err) => {
|
|
||||||
if (err) {
|
|
||||||
logger.error('Could not write db config to config file: ' + filename);
|
|
||||||
reject(err);
|
|
||||||
} else {
|
|
||||||
logger.debug('Wrote db configuration to config file: ' + filename);
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
appStart();
|
appStart();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -204,7 +204,6 @@ const internalAccessList = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(internalNginx.reload)
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// Add to audit log
|
// Add to audit log
|
||||||
return internalAuditLog.add(access, {
|
return internalAuditLog.add(access, {
|
||||||
@ -227,7 +226,7 @@ const internalAccessList = {
|
|||||||
if (row.proxy_host_count) {
|
if (row.proxy_host_count) {
|
||||||
return internalNginx.bulkGenerateConfigs('proxy_host', row.proxy_hosts);
|
return internalNginx.bulkGenerateConfigs('proxy_host', row.proxy_hosts);
|
||||||
}
|
}
|
||||||
})
|
}).then(internalNginx.reload)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return internalAccessList.maskItems(row);
|
return internalAccessList.maskItems(row);
|
||||||
});
|
});
|
||||||
|
@ -1,22 +1,26 @@
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const https = require('https');
|
const https = require('https');
|
||||||
const tempWrite = require('temp-write');
|
const tempWrite = require('temp-write');
|
||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
const logger = require('../logger').ssl;
|
const logger = require('../logger').ssl;
|
||||||
const error = require('../lib/error');
|
const config = require('../lib/config');
|
||||||
const utils = require('../lib/utils');
|
const error = require('../lib/error');
|
||||||
const certificateModel = require('../models/certificate');
|
const utils = require('../lib/utils');
|
||||||
const dnsPlugins = require('../global/certbot-dns-plugins');
|
const certificateModel = require('../models/certificate');
|
||||||
const internalAuditLog = require('./audit-log');
|
const tokenModel = require('../models/token');
|
||||||
const internalNginx = require('./nginx');
|
const dnsPlugins = require('../global/certbot-dns-plugins.json');
|
||||||
const internalHost = require('./host');
|
const internalAuditLog = require('./audit-log');
|
||||||
const letsencryptStaging = process.env.NODE_ENV !== 'production';
|
const internalNginx = require('./nginx');
|
||||||
|
const internalHost = require('./host');
|
||||||
|
const certbot = require('../lib/certbot');
|
||||||
|
const archiver = require('archiver');
|
||||||
|
const path = require('path');
|
||||||
|
const { isArray } = require('lodash');
|
||||||
|
|
||||||
|
const letsencryptStaging = config.useLetsencryptStaging();
|
||||||
const letsencryptConfig = '/etc/letsencrypt.ini';
|
const letsencryptConfig = '/etc/letsencrypt.ini';
|
||||||
const certbotCommand = 'certbot';
|
const certbotCommand = 'certbot';
|
||||||
const archiver = require('archiver');
|
|
||||||
const path = require('path');
|
|
||||||
const { isArray } = require('lodash');
|
|
||||||
|
|
||||||
function omissions() {
|
function omissions() {
|
||||||
return ['is_deleted'];
|
return ['is_deleted'];
|
||||||
@ -24,10 +28,11 @@ function omissions() {
|
|||||||
|
|
||||||
const internalCertificate = {
|
const internalCertificate = {
|
||||||
|
|
||||||
allowedSslFiles: ['certificate', 'certificate_key', 'intermediate_certificate'],
|
allowedSslFiles: ['certificate', 'certificate_key', 'intermediate_certificate'],
|
||||||
intervalTimeout: 1000 * 60 * 60, // 1 hour
|
intervalTimeout: 1000 * 60 * 60, // 1 hour
|
||||||
interval: null,
|
interval: null,
|
||||||
intervalProcessing: false,
|
intervalProcessing: false,
|
||||||
|
renewBeforeExpirationBy: [30, 'days'],
|
||||||
|
|
||||||
initTimer: () => {
|
initTimer: () => {
|
||||||
logger.info('Let\'s Encrypt Renewal Timer initialized');
|
logger.info('Let\'s Encrypt Renewal Timer initialized');
|
||||||
@ -42,60 +47,51 @@ const internalCertificate = {
|
|||||||
processExpiringHosts: () => {
|
processExpiringHosts: () => {
|
||||||
if (!internalCertificate.intervalProcessing) {
|
if (!internalCertificate.intervalProcessing) {
|
||||||
internalCertificate.intervalProcessing = true;
|
internalCertificate.intervalProcessing = true;
|
||||||
logger.info('Renewing SSL certs close to expiry...');
|
logger.info('Renewing SSL certs expiring within ' + internalCertificate.renewBeforeExpirationBy[0] + ' ' + internalCertificate.renewBeforeExpirationBy[1] + ' ...');
|
||||||
|
|
||||||
const cmd = certbotCommand + ' renew --non-interactive --quiet ' +
|
const expirationThreshold = moment().add(internalCertificate.renewBeforeExpirationBy[0], internalCertificate.renewBeforeExpirationBy[1]).format('YYYY-MM-DD HH:mm:ss');
|
||||||
'--config "' + letsencryptConfig + '" ' +
|
|
||||||
'--preferred-challenges "dns,http" ' +
|
|
||||||
'--disable-hook-validation ' +
|
|
||||||
(letsencryptStaging ? '--staging' : '');
|
|
||||||
|
|
||||||
return utils.exec(cmd)
|
// Fetch all the letsencrypt certs from the db that will expire within the configured threshold
|
||||||
.then((result) => {
|
certificateModel
|
||||||
if (result) {
|
.query()
|
||||||
logger.info('Renew Result: ' + result);
|
.where('is_deleted', 0)
|
||||||
|
.andWhere('provider', 'letsencrypt')
|
||||||
|
.andWhere('expires_on', '<', expirationThreshold)
|
||||||
|
.then((certificates) => {
|
||||||
|
if (!certificates || !certificates.length) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return internalNginx.reload()
|
/**
|
||||||
.then(() => {
|
* Renews must be run sequentially or we'll get an error 'Another
|
||||||
logger.info('Renew Complete');
|
* instance of Certbot is already running.'
|
||||||
return result;
|
*/
|
||||||
});
|
let sequence = Promise.resolve();
|
||||||
})
|
|
||||||
.then(() => {
|
certificates.forEach(function (certificate) {
|
||||||
// Now go and fetch all the letsencrypt certs from the db and query the files and update expiry times
|
sequence = sequence.then(() =>
|
||||||
return certificateModel
|
internalCertificate
|
||||||
.query()
|
.renew(
|
||||||
.where('is_deleted', 0)
|
{
|
||||||
.andWhere('provider', 'letsencrypt')
|
can: () =>
|
||||||
.then((certificates) => {
|
Promise.resolve({
|
||||||
if (certificates && certificates.length) {
|
permission_visibility: 'all',
|
||||||
let promises = [];
|
}),
|
||||||
|
token: new tokenModel(),
|
||||||
certificates.map(function (certificate) {
|
},
|
||||||
promises.push(
|
{ id: certificate.id },
|
||||||
internalCertificate.getCertificateInfoFromFile('/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem')
|
)
|
||||||
.then((cert_info) => {
|
.catch((err) => {
|
||||||
return certificateModel
|
// Don't want to stop the train here, just log the error
|
||||||
.query()
|
logger.error(err.message);
|
||||||
.where('id', certificate.id)
|
}),
|
||||||
.andWhere('provider', 'letsencrypt')
|
);
|
||||||
.patch({
|
});
|
||||||
expires_on: moment(cert_info.dates.to, 'X').format('YYYY-MM-DD HH:mm:ss')
|
|
||||||
});
|
return sequence;
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
// Don't want to stop the train here, just log the error
|
|
||||||
logger.error(err.message);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
return Promise.all(promises);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
logger.info('Completed SSL cert renew process');
|
||||||
internalCertificate.intervalProcessing = false;
|
internalCertificate.intervalProcessing = false;
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
@ -833,6 +829,8 @@ const internalCertificate = {
|
|||||||
|
|
||||||
const cmd = certbotCommand + ' certonly ' +
|
const cmd = certbotCommand + ' certonly ' +
|
||||||
'--config "' + letsencryptConfig + '" ' +
|
'--config "' + letsencryptConfig + '" ' +
|
||||||
|
'--work-dir "/tmp/letsencrypt-lib" ' +
|
||||||
|
'--logs-dir "/tmp/letsencrypt-log" ' +
|
||||||
'--cert-name "npm-' + certificate.id + '" ' +
|
'--cert-name "npm-' + certificate.id + '" ' +
|
||||||
'--agree-tos ' +
|
'--agree-tos ' +
|
||||||
'--authenticator webroot ' +
|
'--authenticator webroot ' +
|
||||||
@ -852,45 +850,41 @@ const internalCertificate = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} certificate the certificate row
|
* @param {Object} certificate the certificate row
|
||||||
* @param {String} dns_provider the dns provider name (key used in `certbot-dns-plugins.js`)
|
* @param {String} dns_provider the dns provider name (key used in `certbot-dns-plugins.json`)
|
||||||
* @param {String | null} credentials the content of this providers credentials file
|
* @param {String | null} credentials the content of this providers credentials file
|
||||||
* @param {String} propagation_seconds the cloudflare api token
|
* @param {String} propagation_seconds
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
requestLetsEncryptSslWithDnsChallenge: (certificate) => {
|
requestLetsEncryptSslWithDnsChallenge: async (certificate) => {
|
||||||
const dns_plugin = dnsPlugins[certificate.meta.dns_provider];
|
await certbot.installPlugin(certificate.meta.dns_provider);
|
||||||
|
const dnsPlugin = dnsPlugins[certificate.meta.dns_provider];
|
||||||
if (!dns_plugin) {
|
logger.info(`Requesting Let'sEncrypt certificates via ${dnsPlugin.name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
|
||||||
throw Error(`Unknown DNS provider '${certificate.meta.dns_provider}'`);
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info(`Requesting Let'sEncrypt certificates via ${dns_plugin.display_name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
|
|
||||||
|
|
||||||
const credentialsLocation = '/etc/letsencrypt/credentials/credentials-' + certificate.id;
|
const credentialsLocation = '/etc/letsencrypt/credentials/credentials-' + certificate.id;
|
||||||
// Escape single quotes and backslashes
|
// Escape single quotes and backslashes
|
||||||
const escapedCredentials = certificate.meta.dns_provider_credentials.replaceAll('\'', '\\\'').replaceAll('\\', '\\\\');
|
const escapedCredentials = certificate.meta.dns_provider_credentials.replaceAll('\'', '\\\'').replaceAll('\\', '\\\\');
|
||||||
const credentialsCmd = 'mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + escapedCredentials + '\' > \'' + credentialsLocation + '\' && chmod 600 \'' + credentialsLocation + '\'';
|
const credentialsCmd = 'mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + escapedCredentials + '\' > \'' + credentialsLocation + '\' && chmod 600 \'' + credentialsLocation + '\'';
|
||||||
// we call `. /opt/certbot/bin/activate` (`.` is alternative to `source` in dash) to access certbot venv
|
|
||||||
let prepareCmd = '. /opt/certbot/bin/activate && pip install ' + dns_plugin.package_name + (dns_plugin.version_requirement || '') + ' ' + dns_plugin.dependencies + ' && deactivate';
|
|
||||||
|
|
||||||
// Whether the plugin has a --<name>-credentials argument
|
// Whether the plugin has a --<name>-credentials argument
|
||||||
const hasConfigArg = certificate.meta.dns_provider !== 'route53';
|
const hasConfigArg = certificate.meta.dns_provider !== 'route53';
|
||||||
|
|
||||||
let mainCmd = certbotCommand + ' certonly ' +
|
let mainCmd = certbotCommand + ' certonly ' +
|
||||||
'--config "' + letsencryptConfig + '" ' +
|
'--config "' + letsencryptConfig + '" ' +
|
||||||
|
'--work-dir "/tmp/letsencrypt-lib" ' +
|
||||||
|
'--logs-dir "/tmp/letsencrypt-log" ' +
|
||||||
'--cert-name "npm-' + certificate.id + '" ' +
|
'--cert-name "npm-' + certificate.id + '" ' +
|
||||||
'--agree-tos ' +
|
'--agree-tos ' +
|
||||||
'--email "' + certificate.meta.letsencrypt_email + '" ' +
|
'--email "' + certificate.meta.letsencrypt_email + '" ' +
|
||||||
'--domains "' + certificate.domain_names.join(',') + '" ' +
|
'--domains "' + certificate.domain_names.join(',') + '" ' +
|
||||||
'--authenticator ' + dns_plugin.full_plugin_name + ' ' +
|
'--authenticator ' + dnsPlugin.full_plugin_name + ' ' +
|
||||||
(
|
(
|
||||||
hasConfigArg
|
hasConfigArg
|
||||||
? '--' + dns_plugin.full_plugin_name + '-credentials "' + credentialsLocation + '"'
|
? '--' + dnsPlugin.full_plugin_name + '-credentials "' + credentialsLocation + '"'
|
||||||
: ''
|
: ''
|
||||||
) +
|
) +
|
||||||
(
|
(
|
||||||
certificate.meta.propagation_seconds !== undefined
|
certificate.meta.propagation_seconds !== undefined
|
||||||
? ' --' + dns_plugin.full_plugin_name + '-propagation-seconds ' + certificate.meta.propagation_seconds
|
? ' --' + dnsPlugin.full_plugin_name + '-propagation-seconds ' + certificate.meta.propagation_seconds
|
||||||
: ''
|
: ''
|
||||||
) +
|
) +
|
||||||
(letsencryptStaging ? ' --staging' : '');
|
(letsencryptStaging ? ' --staging' : '');
|
||||||
@ -900,24 +894,23 @@ const internalCertificate = {
|
|||||||
mainCmd = 'AWS_CONFIG_FILE=\'' + credentialsLocation + '\' ' + mainCmd;
|
mainCmd = 'AWS_CONFIG_FILE=\'' + credentialsLocation + '\' ' + mainCmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info('Command:', `${credentialsCmd} && ${prepareCmd} && ${mainCmd}`);
|
if (certificate.meta.dns_provider === 'duckdns') {
|
||||||
|
mainCmd = mainCmd + ' --dns-duckdns-no-txt-restore';
|
||||||
|
}
|
||||||
|
|
||||||
return utils.exec(credentialsCmd)
|
logger.info('Command:', `${credentialsCmd} && && ${mainCmd}`);
|
||||||
.then(() => {
|
|
||||||
return utils.exec(prepareCmd)
|
try {
|
||||||
.then(() => {
|
await utils.exec(credentialsCmd);
|
||||||
return utils.exec(mainCmd)
|
const result = await utils.exec(mainCmd);
|
||||||
.then(async (result) => {
|
logger.info(result);
|
||||||
logger.info(result);
|
return result;
|
||||||
return result;
|
} catch (err) {
|
||||||
});
|
// Don't fail if file does not exist
|
||||||
});
|
const delete_credentialsCmd = `rm -f '${credentialsLocation}' || true`;
|
||||||
}).catch(async (err) => {
|
await utils.exec(delete_credentialsCmd);
|
||||||
// Don't fail if file does not exist
|
throw err;
|
||||||
const delete_credentialsCmd = `rm -f '${credentialsLocation}' || true`;
|
}
|
||||||
await utils.exec(delete_credentialsCmd);
|
|
||||||
throw err;
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
@ -974,6 +967,8 @@ const internalCertificate = {
|
|||||||
|
|
||||||
const cmd = certbotCommand + ' renew --force-renewal ' +
|
const cmd = certbotCommand + ' renew --force-renewal ' +
|
||||||
'--config "' + letsencryptConfig + '" ' +
|
'--config "' + letsencryptConfig + '" ' +
|
||||||
|
'--work-dir "/tmp/letsencrypt-lib" ' +
|
||||||
|
'--logs-dir "/tmp/letsencrypt-log" ' +
|
||||||
'--cert-name "npm-' + certificate.id + '" ' +
|
'--cert-name "npm-' + certificate.id + '" ' +
|
||||||
'--preferred-challenges "dns,http" ' +
|
'--preferred-challenges "dns,http" ' +
|
||||||
'--no-random-sleep-on-renew ' +
|
'--no-random-sleep-on-renew ' +
|
||||||
@ -994,16 +989,18 @@ const internalCertificate = {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
renewLetsEncryptSslWithDnsChallenge: (certificate) => {
|
renewLetsEncryptSslWithDnsChallenge: (certificate) => {
|
||||||
const dns_plugin = dnsPlugins[certificate.meta.dns_provider];
|
const dnsPlugin = dnsPlugins[certificate.meta.dns_provider];
|
||||||
|
|
||||||
if (!dns_plugin) {
|
if (!dnsPlugin) {
|
||||||
throw Error(`Unknown DNS provider '${certificate.meta.dns_provider}'`);
|
throw Error(`Unknown DNS provider '${certificate.meta.dns_provider}'`);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(`Renewing Let'sEncrypt certificates via ${dns_plugin.display_name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
|
logger.info(`Renewing Let'sEncrypt certificates via ${dnsPlugin.name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
|
||||||
|
|
||||||
let mainCmd = certbotCommand + ' renew ' +
|
let mainCmd = certbotCommand + ' renew --force-renewal ' +
|
||||||
'--config "' + letsencryptConfig + '" ' +
|
'--config "' + letsencryptConfig + '" ' +
|
||||||
|
'--work-dir "/tmp/letsencrypt-lib" ' +
|
||||||
|
'--logs-dir "/tmp/letsencrypt-log" ' +
|
||||||
'--cert-name "npm-' + certificate.id + '" ' +
|
'--cert-name "npm-' + certificate.id + '" ' +
|
||||||
'--disable-hook-validation ' +
|
'--disable-hook-validation ' +
|
||||||
'--no-random-sleep-on-renew ' +
|
'--no-random-sleep-on-renew ' +
|
||||||
@ -1034,6 +1031,8 @@ const internalCertificate = {
|
|||||||
|
|
||||||
const mainCmd = certbotCommand + ' revoke ' +
|
const mainCmd = certbotCommand + ' revoke ' +
|
||||||
'--config "' + letsencryptConfig + '" ' +
|
'--config "' + letsencryptConfig + '" ' +
|
||||||
|
'--work-dir "/tmp/letsencrypt-lib" ' +
|
||||||
|
'--logs-dir "/tmp/letsencrypt-log" ' +
|
||||||
'--cert-path "/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem" ' +
|
'--cert-path "/etc/letsencrypt/live/npm-' + certificate.id + '/fullchain.pem" ' +
|
||||||
'--delete-after-revoke ' +
|
'--delete-after-revoke ' +
|
||||||
(letsencryptStaging ? '--staging' : '');
|
(letsencryptStaging ? '--staging' : '');
|
||||||
@ -1151,6 +1150,7 @@ const internalCertificate = {
|
|||||||
const options = {
|
const options = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
|
'User-Agent': 'Mozilla/5.0',
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
'Content-Length': Buffer.byteLength(formBody)
|
'Content-Length': Buffer.byteLength(formBody)
|
||||||
}
|
}
|
||||||
@ -1163,12 +1163,22 @@ const internalCertificate = {
|
|||||||
|
|
||||||
res.on('data', (chunk) => responseBody = responseBody + chunk);
|
res.on('data', (chunk) => responseBody = responseBody + chunk);
|
||||||
res.on('end', function () {
|
res.on('end', function () {
|
||||||
const parsedBody = JSON.parse(responseBody + '');
|
try {
|
||||||
if (res.statusCode !== 200) {
|
const parsedBody = JSON.parse(responseBody + '');
|
||||||
logger.warn(`Failed to test HTTP challenge for domain ${domain}`, res);
|
if (res.statusCode !== 200) {
|
||||||
|
logger.warn(`Failed to test HTTP challenge for domain ${domain} because HTTP status code ${res.statusCode} was returned: ${parsedBody.message}`);
|
||||||
|
resolve(undefined);
|
||||||
|
} else {
|
||||||
|
resolve(parsedBody);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
if (res.statusCode !== 200) {
|
||||||
|
logger.warn(`Failed to test HTTP challenge for domain ${domain} because HTTP status code ${res.statusCode} was returned`);
|
||||||
|
} else {
|
||||||
|
logger.warn(`Failed to test HTTP challenge for domain ${domain} because response failed to be parsed: ${err.message}`);
|
||||||
|
}
|
||||||
resolve(undefined);
|
resolve(undefined);
|
||||||
}
|
}
|
||||||
resolve(parsedBody);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1182,6 +1192,9 @@ const internalCertificate = {
|
|||||||
if (!result) {
|
if (!result) {
|
||||||
// Some error occurred while trying to get the data
|
// Some error occurred while trying to get the data
|
||||||
return 'failed';
|
return 'failed';
|
||||||
|
} else if (result.error) {
|
||||||
|
logger.info(`HTTP challenge test failed for domain ${domain} because error was returned: ${result.error.msg}`);
|
||||||
|
return `other:${result.error.msg}`;
|
||||||
} else if (`${result.responsecode}` === '200' && result.htmlresponse === 'Success') {
|
} else if (`${result.responsecode}` === '200' && result.htmlresponse === 'Success') {
|
||||||
// Server exists and has responded with the correct data
|
// Server exists and has responded with the correct data
|
||||||
return 'ok';
|
return 'ok';
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const logger = require('../logger').nginx;
|
const logger = require('../logger').nginx;
|
||||||
const utils = require('../lib/utils');
|
const config = require('../lib/config');
|
||||||
const error = require('../lib/error');
|
const utils = require('../lib/utils');
|
||||||
const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
|
const error = require('../lib/error');
|
||||||
|
|
||||||
const internalNginx = {
|
const internalNginx = {
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ const internalNginx = {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (debug_mode) {
|
if (config.debug()) {
|
||||||
logger.error('Nginx test failed:', valid_lines.join('\n'));
|
logger.error('Nginx test failed:', valid_lines.join('\n'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +101,7 @@ const internalNginx = {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
test: () => {
|
test: () => {
|
||||||
if (debug_mode) {
|
if (config.debug()) {
|
||||||
logger.info('Testing Nginx configuration');
|
logger.info('Testing Nginx configuration');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,7 +184,7 @@ const internalNginx = {
|
|||||||
generateConfig: (host_type, host) => {
|
generateConfig: (host_type, host) => {
|
||||||
const nice_host_type = internalNginx.getFileFriendlyHostType(host_type);
|
const nice_host_type = internalNginx.getFileFriendlyHostType(host_type);
|
||||||
|
|
||||||
if (debug_mode) {
|
if (config.debug()) {
|
||||||
logger.info('Generating ' + nice_host_type + ' Config:', JSON.stringify(host, null, 2));
|
logger.info('Generating ' + nice_host_type + ' Config:', JSON.stringify(host, null, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,7 +239,7 @@ const internalNginx = {
|
|||||||
.then((config_text) => {
|
.then((config_text) => {
|
||||||
fs.writeFileSync(filename, config_text, {encoding: 'utf8'});
|
fs.writeFileSync(filename, config_text, {encoding: 'utf8'});
|
||||||
|
|
||||||
if (debug_mode) {
|
if (config.debug()) {
|
||||||
logger.success('Wrote config:', filename, config_text);
|
logger.success('Wrote config:', filename, config_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,7 +249,7 @@ const internalNginx = {
|
|||||||
resolve(true);
|
resolve(true);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
if (debug_mode) {
|
if (config.debug()) {
|
||||||
logger.warn('Could not write ' + filename + ':', err.message);
|
logger.warn('Could not write ' + filename + ':', err.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,7 +268,7 @@ const internalNginx = {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
generateLetsEncryptRequestConfig: (certificate) => {
|
generateLetsEncryptRequestConfig: (certificate) => {
|
||||||
if (debug_mode) {
|
if (config.debug()) {
|
||||||
logger.info('Generating LetsEncrypt Request Config:', certificate);
|
logger.info('Generating LetsEncrypt Request Config:', certificate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,14 +292,14 @@ const internalNginx = {
|
|||||||
.then((config_text) => {
|
.then((config_text) => {
|
||||||
fs.writeFileSync(filename, config_text, {encoding: 'utf8'});
|
fs.writeFileSync(filename, config_text, {encoding: 'utf8'});
|
||||||
|
|
||||||
if (debug_mode) {
|
if (config.debug()) {
|
||||||
logger.success('Wrote config:', filename, config_text);
|
logger.success('Wrote config:', filename, config_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(true);
|
resolve(true);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
if (debug_mode) {
|
if (config.debug()) {
|
||||||
logger.warn('Could not write ' + filename + ':', err.message);
|
logger.warn('Could not write ' + filename + ':', err.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,8 +416,8 @@ const internalNginx = {
|
|||||||
* @param {string} config
|
* @param {string} config
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
advancedConfigHasDefaultLocation: function (config) {
|
advancedConfigHasDefaultLocation: function (cfg) {
|
||||||
return !!config.match(/^(?:.*;)?\s*?location\s*?\/\s*?{/im);
|
return !!cfg.match(/^(?:.*;)?\s*?location\s*?\/\s*?{/im);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -225,7 +225,7 @@ const internalProxyHost = {
|
|||||||
.query()
|
.query()
|
||||||
.where('is_deleted', 0)
|
.where('is_deleted', 0)
|
||||||
.andWhere('id', data.id)
|
.andWhere('id', data.id)
|
||||||
.allowGraph('[owner,access_list,access_list.[clients,items],certificate]')
|
.allowGraph('[owner,access_list.[clients,items],certificate]')
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
if (access_data.permission_visibility !== 'all') {
|
if (access_data.permission_visibility !== 'all') {
|
||||||
|
78
backend/lib/certbot.js
Normal file
78
backend/lib/certbot.js
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
const dnsPlugins = require('../global/certbot-dns-plugins.json');
|
||||||
|
const utils = require('./utils');
|
||||||
|
const error = require('./error');
|
||||||
|
const logger = require('../logger').certbot;
|
||||||
|
const batchflow = require('batchflow');
|
||||||
|
|
||||||
|
const CERTBOT_VERSION_REPLACEMENT = '$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')';
|
||||||
|
|
||||||
|
const certbot = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {array} pluginKeys
|
||||||
|
*/
|
||||||
|
installPlugins: async function (pluginKeys) {
|
||||||
|
let hasErrors = false;
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (pluginKeys.length === 0) {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
batchflow(pluginKeys).sequential()
|
||||||
|
.each((i, pluginKey, next) => {
|
||||||
|
certbot.installPlugin(pluginKey)
|
||||||
|
.then(() => {
|
||||||
|
next();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
hasErrors = true;
|
||||||
|
next(err);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.error((err) => {
|
||||||
|
logger.error(err.message);
|
||||||
|
})
|
||||||
|
.end(() => {
|
||||||
|
if (hasErrors) {
|
||||||
|
reject(new error.CommandError('Some plugins failed to install. Please check the logs above', 1));
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Installs a cerbot plugin given the key for the object from
|
||||||
|
* ../global/certbot-dns-plugins.json
|
||||||
|
*
|
||||||
|
* @param {string} pluginKey
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
installPlugin: async function (pluginKey) {
|
||||||
|
if (typeof dnsPlugins[pluginKey] === 'undefined') {
|
||||||
|
// throw Error(`Certbot plugin ${pluginKey} not found`);
|
||||||
|
throw new error.ItemNotFoundError(pluginKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
const plugin = dnsPlugins[pluginKey];
|
||||||
|
logger.start(`Installing ${pluginKey}...`);
|
||||||
|
|
||||||
|
plugin.version = plugin.version.replace(/{{certbot-version}}/g, CERTBOT_VERSION_REPLACEMENT);
|
||||||
|
plugin.dependencies = plugin.dependencies.replace(/{{certbot-version}}/g, CERTBOT_VERSION_REPLACEMENT);
|
||||||
|
|
||||||
|
const cmd = '. /opt/certbot/bin/activate && pip install --no-cache-dir ' + plugin.dependencies + ' ' + plugin.package_name + plugin.version + ' ' + ' && deactivate';
|
||||||
|
return utils.exec(cmd)
|
||||||
|
.then((result) => {
|
||||||
|
logger.complete(`Installed ${pluginKey}`);
|
||||||
|
return result;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = certbot;
|
184
backend/lib/config.js
Normal file
184
backend/lib/config.js
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const NodeRSA = require('node-rsa');
|
||||||
|
const logger = require('../logger').global;
|
||||||
|
|
||||||
|
const keysFile = '/data/keys.json';
|
||||||
|
|
||||||
|
let instance = null;
|
||||||
|
|
||||||
|
// 1. Load from config file first (not recommended anymore)
|
||||||
|
// 2. Use config env variables next
|
||||||
|
const configure = () => {
|
||||||
|
const filename = (process.env.NODE_CONFIG_DIR || './config') + '/' + (process.env.NODE_ENV || 'default') + '.json';
|
||||||
|
if (fs.existsSync(filename)) {
|
||||||
|
let configData;
|
||||||
|
try {
|
||||||
|
configData = require(filename);
|
||||||
|
} catch (err) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configData && configData.database) {
|
||||||
|
logger.info(`Using configuration from file: ${filename}`);
|
||||||
|
instance = configData;
|
||||||
|
instance.keys = getKeys();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const envMysqlHost = process.env.DB_MYSQL_HOST || null;
|
||||||
|
const envMysqlUser = process.env.DB_MYSQL_USER || null;
|
||||||
|
const envMysqlName = process.env.DB_MYSQL_NAME || null;
|
||||||
|
if (envMysqlHost && envMysqlUser && envMysqlName) {
|
||||||
|
// we have enough mysql creds to go with mysql
|
||||||
|
logger.info('Using MySQL configuration');
|
||||||
|
instance = {
|
||||||
|
database: {
|
||||||
|
engine: 'mysql',
|
||||||
|
host: envMysqlHost,
|
||||||
|
port: process.env.DB_MYSQL_PORT || 3306,
|
||||||
|
user: envMysqlUser,
|
||||||
|
password: process.env.DB_MYSQL_PASSWORD,
|
||||||
|
name: envMysqlName,
|
||||||
|
},
|
||||||
|
keys: getKeys(),
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const envSqliteFile = process.env.DB_SQLITE_FILE || '/data/database.sqlite';
|
||||||
|
logger.info(`Using Sqlite: ${envSqliteFile}`);
|
||||||
|
instance = {
|
||||||
|
database: {
|
||||||
|
engine: 'knex-native',
|
||||||
|
knex: {
|
||||||
|
client: 'sqlite3',
|
||||||
|
connection: {
|
||||||
|
filename: envSqliteFile
|
||||||
|
},
|
||||||
|
useNullAsDefault: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
keys: getKeys(),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const getKeys = () => {
|
||||||
|
// Get keys from file
|
||||||
|
if (!fs.existsSync(keysFile)) {
|
||||||
|
generateKeys();
|
||||||
|
} else if (process.env.DEBUG) {
|
||||||
|
logger.info('Keys file exists OK');
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return require(keysFile);
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('Could not read JWT key pair from config file: ' + keysFile, err);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateKeys = () => {
|
||||||
|
logger.info('Creating a new JWT key pair...');
|
||||||
|
// Now create the keys and save them in the config.
|
||||||
|
const key = new NodeRSA({ b: 2048 });
|
||||||
|
key.generateKeyPair();
|
||||||
|
|
||||||
|
const keys = {
|
||||||
|
key: key.exportKey('private').toString(),
|
||||||
|
pub: key.exportKey('public').toString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Write keys config
|
||||||
|
try {
|
||||||
|
fs.writeFileSync(keysFile, JSON.stringify(keys, null, 2));
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('Could not write JWT key pair to config file: ' + keysFile + ': ' . err.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
logger.info('Wrote JWT key pair to config file: ' + keysFile);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} key ie: 'database' or 'database.engine'
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
has: function(key) {
|
||||||
|
instance === null && configure();
|
||||||
|
const keys = key.split('.');
|
||||||
|
let level = instance;
|
||||||
|
let has = true;
|
||||||
|
keys.forEach((keyItem) =>{
|
||||||
|
if (typeof level[keyItem] === 'undefined') {
|
||||||
|
has = false;
|
||||||
|
} else {
|
||||||
|
level = level[keyItem];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return has;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a specific key from the top level
|
||||||
|
*
|
||||||
|
* @param {string} key
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
get: function (key) {
|
||||||
|
instance === null && configure();
|
||||||
|
if (key && typeof instance[key] !== 'undefined') {
|
||||||
|
return instance[key];
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this a sqlite configuration?
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
isSqlite: function () {
|
||||||
|
instance === null && configure();
|
||||||
|
return instance.database.knex && instance.database.knex.client === 'sqlite3';
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Are we running in debug mdoe?
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
debug: function () {
|
||||||
|
return !!process.env.DEBUG;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a public key
|
||||||
|
*
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
getPublicKey: function () {
|
||||||
|
instance === null && configure();
|
||||||
|
return instance.keys.pub;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a private key
|
||||||
|
*
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
getPrivateKey: function () {
|
||||||
|
instance === null && configure();
|
||||||
|
return instance.keys.key;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
useLetsencryptStaging: function () {
|
||||||
|
return !!process.env.LE_STAGING;
|
||||||
|
}
|
||||||
|
};
|
@ -82,7 +82,16 @@ module.exports = {
|
|||||||
this.message = message;
|
this.message = message;
|
||||||
this.public = false;
|
this.public = false;
|
||||||
this.status = 400;
|
this.status = 400;
|
||||||
}
|
},
|
||||||
|
|
||||||
|
CommandError: function (stdErr, code, previous) {
|
||||||
|
Error.captureStackTrace(this, this.constructor);
|
||||||
|
this.name = this.constructor.name;
|
||||||
|
this.previous = previous;
|
||||||
|
this.message = stdErr;
|
||||||
|
this.code = code;
|
||||||
|
this.public = false;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
_.forEach(module.exports, function (error) {
|
_.forEach(module.exports, function (error) {
|
||||||
|
@ -3,23 +3,27 @@ const exec = require('child_process').exec;
|
|||||||
const execFile = require('child_process').execFile;
|
const execFile = require('child_process').execFile;
|
||||||
const { Liquid } = require('liquidjs');
|
const { Liquid } = require('liquidjs');
|
||||||
const logger = require('../logger').global;
|
const logger = require('../logger').global;
|
||||||
|
const error = require('./error');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
||||||
/**
|
exec: async function(cmd, options = {}) {
|
||||||
* @param {String} cmd
|
logger.debug('CMD:', cmd);
|
||||||
* @returns {Promise}
|
|
||||||
*/
|
const { stdout, stderr } = await new Promise((resolve, reject) => {
|
||||||
exec: function (cmd) {
|
const child = exec(cmd, options, (isError, stdout, stderr) => {
|
||||||
return new Promise((resolve, reject) => {
|
if (isError) {
|
||||||
exec(cmd, function (err, stdout, /*stderr*/) {
|
reject(new error.CommandError(stderr, isError));
|
||||||
if (err && typeof err === 'object') {
|
|
||||||
reject(err);
|
|
||||||
} else {
|
} else {
|
||||||
resolve(stdout.trim());
|
resolve({ stdout, stderr });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
child.on('error', (e) => {
|
||||||
|
reject(new error.CommandError(stderr, 1, e));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
return stdout;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -28,7 +32,8 @@ module.exports = {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
execFile: function (cmd, args) {
|
execFile: function (cmd, args) {
|
||||||
logger.debug('CMD: ' + cmd + ' ' + (args ? args.join(' ') : ''));
|
// logger.debug('CMD: ' + cmd + ' ' + (args ? args.join(' ') : ''));
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
execFile(cmd, args, function (err, stdout, /*stderr*/) {
|
execFile(cmd, args, function (err, stdout, /*stderr*/) {
|
||||||
if (err && typeof err === 'object') {
|
if (err && typeof err === 'object') {
|
||||||
|
@ -5,7 +5,7 @@ const definitions = require('../../schema/definitions.json');
|
|||||||
RegExp.prototype.toJSON = RegExp.prototype.toString;
|
RegExp.prototype.toJSON = RegExp.prototype.toString;
|
||||||
|
|
||||||
const ajv = require('ajv')({
|
const ajv = require('ajv')({
|
||||||
verbose: true, //process.env.NODE_ENV === 'development',
|
verbose: true,
|
||||||
allErrors: true,
|
allErrors: true,
|
||||||
format: 'full', // strict regexes for format checks
|
format: 'full', // strict regexes for format checks
|
||||||
coerceTypes: true,
|
coerceTypes: true,
|
||||||
|
@ -7,6 +7,7 @@ module.exports = {
|
|||||||
access: new Signale({scope: 'Access '}),
|
access: new Signale({scope: 'Access '}),
|
||||||
nginx: new Signale({scope: 'Nginx '}),
|
nginx: new Signale({scope: 'Nginx '}),
|
||||||
ssl: new Signale({scope: 'SSL '}),
|
ssl: new Signale({scope: 'SSL '}),
|
||||||
|
certbot: new Signale({scope: 'Certbot '}),
|
||||||
import: new Signale({scope: 'Importer '}),
|
import: new Signale({scope: 'Importer '}),
|
||||||
setup: new Signale({scope: 'Setup '}),
|
setup: new Signale({scope: 'Setup '}),
|
||||||
ip_ranges: new Signale({scope: 'IP Ranges'})
|
ip_ranges: new Signale({scope: 'IP Ranges'})
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
const db = require('../db');
|
const db = require('../db');
|
||||||
const config = require('config');
|
const config = require('../lib/config');
|
||||||
const Model = require('objection').Model;
|
const Model = require('objection').Model;
|
||||||
|
|
||||||
Model.knex(db);
|
Model.knex(db);
|
||||||
|
|
||||||
module.exports = function () {
|
module.exports = function () {
|
||||||
if (config.database.knex && config.database.knex.client === 'sqlite3') {
|
if (config.isSqlite()) {
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
return Model.raw("datetime('now','localtime')");
|
return Model.raw("datetime('now','localtime')");
|
||||||
}
|
}
|
||||||
|
@ -6,44 +6,36 @@
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const jwt = require('jsonwebtoken');
|
const jwt = require('jsonwebtoken');
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
|
const config = require('../lib/config');
|
||||||
const error = require('../lib/error');
|
const error = require('../lib/error');
|
||||||
|
const logger = require('../logger').global;
|
||||||
const ALGO = 'RS256';
|
const ALGO = 'RS256';
|
||||||
|
|
||||||
let public_key = null;
|
|
||||||
let private_key = null;
|
|
||||||
|
|
||||||
function checkJWTKeyPair() {
|
|
||||||
if (!public_key || !private_key) {
|
|
||||||
let config = require('config');
|
|
||||||
public_key = config.get('jwt.pub');
|
|
||||||
private_key = config.get('jwt.key');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = function () {
|
module.exports = function () {
|
||||||
|
|
||||||
let token_data = {};
|
let token_data = {};
|
||||||
|
|
||||||
let self = {
|
const self = {
|
||||||
/**
|
/**
|
||||||
* @param {Object} payload
|
* @param {Object} payload
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
create: (payload) => {
|
create: (payload) => {
|
||||||
|
if (!config.getPrivateKey()) {
|
||||||
|
logger.error('Private key is empty!');
|
||||||
|
}
|
||||||
// sign with RSA SHA256
|
// sign with RSA SHA256
|
||||||
let options = {
|
const options = {
|
||||||
algorithm: ALGO,
|
algorithm: ALGO,
|
||||||
expiresIn: payload.expiresIn || '1d'
|
expiresIn: payload.expiresIn || '1d'
|
||||||
};
|
};
|
||||||
|
|
||||||
payload.jti = crypto.randomBytes(12)
|
payload.jti = crypto.randomBytes(12)
|
||||||
.toString('base64')
|
.toString('base64')
|
||||||
.substr(-8);
|
.substring(-8);
|
||||||
|
|
||||||
checkJWTKeyPair();
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
jwt.sign(payload, private_key, options, (err, token) => {
|
jwt.sign(payload, config.getPrivateKey(), options, (err, token) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
} else {
|
} else {
|
||||||
@ -62,13 +54,15 @@ module.exports = function () {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
load: function (token) {
|
load: function (token) {
|
||||||
|
if (!config.getPublicKey()) {
|
||||||
|
logger.error('Public key is empty!');
|
||||||
|
}
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
checkJWTKeyPair();
|
|
||||||
try {
|
try {
|
||||||
if (!token || token === null || token === 'null') {
|
if (!token || token === null || token === 'null') {
|
||||||
reject(new error.AuthError('Empty token'));
|
reject(new error.AuthError('Empty token'));
|
||||||
} else {
|
} else {
|
||||||
jwt.verify(token, public_key, {ignoreExpiration: false, algorithms: [ALGO]}, (err, result) => {
|
jwt.verify(token, config.getPublicKey(), {ignoreExpiration: false, algorithms: [ALGO]}, (err, result) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|
||||||
if (err.name === 'TokenExpiredError') {
|
if (err.name === 'TokenExpiredError') {
|
||||||
@ -132,7 +126,7 @@ module.exports = function () {
|
|||||||
* @returns {Integer}
|
* @returns {Integer}
|
||||||
*/
|
*/
|
||||||
getUserId: (default_value) => {
|
getUserId: (default_value) => {
|
||||||
let attrs = self.get('attrs');
|
const attrs = self.get('attrs');
|
||||||
if (attrs && typeof attrs.id !== 'undefined' && attrs.id) {
|
if (attrs && typeof attrs.id !== 'undefined' && attrs.id) {
|
||||||
return attrs.id;
|
return attrs.id;
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,7 @@
|
|||||||
"bcrypt": "^5.0.0",
|
"bcrypt": "^5.0.0",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
"config": "^3.3.1",
|
"express": "^4.19.2",
|
||||||
"express": "^4.17.3",
|
|
||||||
"express-fileupload": "^1.1.9",
|
"express-fileupload": "^1.1.9",
|
||||||
"gravatar": "^1.8.0",
|
"gravatar": "^1.8.0",
|
||||||
"json-schema-ref-parser": "^8.0.0",
|
"json-schema-ref-parser": "^8.0.0",
|
||||||
@ -22,7 +21,6 @@
|
|||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"mysql": "^2.18.1",
|
"mysql": "^2.18.1",
|
||||||
"node-rsa": "^1.0.8",
|
"node-rsa": "^1.0.8",
|
||||||
"nodemon": "^2.0.2",
|
|
||||||
"objection": "3.0.1",
|
"objection": "3.0.1",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
"signale": "1.4.0",
|
"signale": "1.4.0",
|
||||||
@ -36,8 +34,9 @@
|
|||||||
"author": "Jamie Curnow <jc@jc21.com>",
|
"author": "Jamie Curnow <jc@jc21.com>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^8.36.0",
|
||||||
"eslint-plugin-align-assignments": "^1.1.2",
|
"eslint-plugin-align-assignments": "^1.1.2",
|
||||||
|
"nodemon": "^2.0.2",
|
||||||
"prettier": "^2.0.4"
|
"prettier": "^2.0.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -172,7 +172,7 @@
|
|||||||
"description": "Domain Names separated by a comma",
|
"description": "Domain Names separated by a comma",
|
||||||
"example": "*.jc21.com,blog.jc21.com",
|
"example": "*.jc21.com,blog.jc21.com",
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"maxItems": 15,
|
"maxItems": 100,
|
||||||
"uniqueItems": true,
|
"uniqueItems": true,
|
||||||
"items": {
|
"items": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
49
backend/scripts/install-certbot-plugins
Executable file
49
backend/scripts/install-certbot-plugins
Executable file
@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/node
|
||||||
|
|
||||||
|
// Usage:
|
||||||
|
// Install all plugins defined in `certbot-dns-plugins.json`:
|
||||||
|
// ./install-certbot-plugins
|
||||||
|
// Install one or more specific plugins:
|
||||||
|
// ./install-certbot-plugins route53 cloudflare
|
||||||
|
//
|
||||||
|
// Usage with a running docker container:
|
||||||
|
// docker exec npm_core /command/s6-setuidgid 1000:1000 bash -c "/app/scripts/install-certbot-plugins"
|
||||||
|
//
|
||||||
|
|
||||||
|
const dnsPlugins = require('../global/certbot-dns-plugins.json');
|
||||||
|
const certbot = require('../lib/certbot');
|
||||||
|
const logger = require('../logger').certbot;
|
||||||
|
const batchflow = require('batchflow');
|
||||||
|
|
||||||
|
let hasErrors = false;
|
||||||
|
let failingPlugins = [];
|
||||||
|
|
||||||
|
let pluginKeys = Object.keys(dnsPlugins);
|
||||||
|
if (process.argv.length > 2) {
|
||||||
|
pluginKeys = process.argv.slice(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
batchflow(pluginKeys).sequential()
|
||||||
|
.each((i, pluginKey, next) => {
|
||||||
|
certbot.installPlugin(pluginKey)
|
||||||
|
.then(() => {
|
||||||
|
next();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
hasErrors = true;
|
||||||
|
failingPlugins.push(pluginKey);
|
||||||
|
next(err);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.error((err) => {
|
||||||
|
logger.error(err.message);
|
||||||
|
})
|
||||||
|
.end(() => {
|
||||||
|
if (hasErrors) {
|
||||||
|
logger.error('Some plugins failed to install. Please check the logs above. Failing plugins: ' + '\n - ' + failingPlugins.join('\n - '));
|
||||||
|
process.exit(1);
|
||||||
|
} else {
|
||||||
|
logger.complete('Plugins installed successfully');
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
});
|
101
backend/setup.js
101
backend/setup.js
@ -1,6 +1,4 @@
|
|||||||
const fs = require('fs');
|
const config = require('./lib/config');
|
||||||
const NodeRSA = require('node-rsa');
|
|
||||||
const config = require('config');
|
|
||||||
const logger = require('./logger').setup;
|
const logger = require('./logger').setup;
|
||||||
const certificateModel = require('./models/certificate');
|
const certificateModel = require('./models/certificate');
|
||||||
const userModel = require('./models/user');
|
const userModel = require('./models/user');
|
||||||
@ -8,64 +6,7 @@ const userPermissionModel = require('./models/user_permission');
|
|||||||
const utils = require('./lib/utils');
|
const utils = require('./lib/utils');
|
||||||
const authModel = require('./models/auth');
|
const authModel = require('./models/auth');
|
||||||
const settingModel = require('./models/setting');
|
const settingModel = require('./models/setting');
|
||||||
const dns_plugins = require('./global/certbot-dns-plugins');
|
const certbot = require('./lib/certbot');
|
||||||
const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new JWT RSA Keypair if not alread set on the config
|
|
||||||
*
|
|
||||||
* @returns {Promise}
|
|
||||||
*/
|
|
||||||
const setupJwt = () => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
// Now go and check if the jwt gpg keys have been created and if not, create them
|
|
||||||
if (!config.has('jwt') || !config.has('jwt.key') || !config.has('jwt.pub')) {
|
|
||||||
logger.info('Creating a new JWT key pair...');
|
|
||||||
|
|
||||||
// jwt keys are not configured properly
|
|
||||||
const filename = config.util.getEnv('NODE_CONFIG_DIR') + '/' + (config.util.getEnv('NODE_ENV') || 'default') + '.json';
|
|
||||||
let config_data = {};
|
|
||||||
|
|
||||||
try {
|
|
||||||
config_data = require(filename);
|
|
||||||
} catch (err) {
|
|
||||||
// do nothing
|
|
||||||
if (debug_mode) {
|
|
||||||
logger.debug(filename + ' config file could not be required');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now create the keys and save them in the config.
|
|
||||||
let key = new NodeRSA({ b: 2048 });
|
|
||||||
key.generateKeyPair();
|
|
||||||
|
|
||||||
config_data.jwt = {
|
|
||||||
key: key.exportKey('private').toString(),
|
|
||||||
pub: key.exportKey('public').toString(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Write config
|
|
||||||
fs.writeFile(filename, JSON.stringify(config_data, null, 2), (err) => {
|
|
||||||
if (err) {
|
|
||||||
logger.error('Could not write JWT key pair to config file: ' + filename);
|
|
||||||
reject(err);
|
|
||||||
} else {
|
|
||||||
logger.info('Wrote JWT key pair to config file: ' + filename);
|
|
||||||
delete require.cache[require.resolve('config')];
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// JWT key pair exists
|
|
||||||
if (debug_mode) {
|
|
||||||
logger.debug('JWT Keypair already exists');
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a default admin users if one doesn't already exist in the database
|
* Creates a default admin users if one doesn't already exist in the database
|
||||||
*
|
*
|
||||||
@ -119,8 +60,8 @@ const setupDefaultUser = () => {
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
logger.info('Initial admin setup completed');
|
logger.info('Initial admin setup completed');
|
||||||
});
|
});
|
||||||
} else if (debug_mode) {
|
} else if (config.debug()) {
|
||||||
logger.debug('Admin user setup not required');
|
logger.info('Admin user setup not required');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -151,8 +92,8 @@ const setupDefaultSettings = () => {
|
|||||||
logger.info('Default settings added');
|
logger.info('Default settings added');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (debug_mode) {
|
if (config.debug()) {
|
||||||
logger.debug('Default setting setup not required');
|
logger.info('Default setting setup not required');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -174,10 +115,9 @@ const setupCertbotPlugins = () => {
|
|||||||
|
|
||||||
certificates.map(function (certificate) {
|
certificates.map(function (certificate) {
|
||||||
if (certificate.meta && certificate.meta.dns_challenge === true) {
|
if (certificate.meta && certificate.meta.dns_challenge === true) {
|
||||||
const dns_plugin = dns_plugins[certificate.meta.dns_provider];
|
if (plugins.indexOf(certificate.meta.dns_provider) === -1) {
|
||||||
|
plugins.push(certificate.meta.dns_provider);
|
||||||
const packages_to_install = `${dns_plugin.package_name}${dns_plugin.version_requirement || ''} ${dns_plugin.dependencies}`;
|
}
|
||||||
if (plugins.indexOf(packages_to_install) === -1) plugins.push(packages_to_install);
|
|
||||||
|
|
||||||
// Make sure credentials file exists
|
// Make sure credentials file exists
|
||||||
const credentials_loc = '/etc/letsencrypt/credentials/credentials-' + certificate.id;
|
const credentials_loc = '/etc/letsencrypt/credentials/credentials-' + certificate.id;
|
||||||
@ -188,17 +128,15 @@ const setupCertbotPlugins = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (plugins.length) {
|
return certbot.installPlugins(plugins)
|
||||||
const install_cmd = '. /opt/certbot/bin/activate && pip install ' + plugins.join(' ') + ' && deactivate';
|
.then(() => {
|
||||||
promises.push(utils.exec(install_cmd));
|
if (promises.length) {
|
||||||
}
|
return Promise.all(promises)
|
||||||
|
.then(() => {
|
||||||
if (promises.length) {
|
logger.info('Added Certbot plugins ' + plugins.join(', '));
|
||||||
return Promise.all(promises)
|
});
|
||||||
.then(() => {
|
}
|
||||||
logger.info('Added Certbot plugins ' + plugins.join(', '));
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -225,8 +163,7 @@ const setupLogrotation = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
module.exports = function () {
|
module.exports = function () {
|
||||||
return setupJwt()
|
return setupDefaultUser()
|
||||||
.then(setupDefaultUser)
|
|
||||||
.then(setupDefaultSettings)
|
.then(setupDefaultSettings)
|
||||||
.then(setupCertbotPlugins)
|
.then(setupCertbotPlugins)
|
||||||
.then(setupLogrotation);
|
.then(setupLogrotation);
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
{% if ssl_forced == 1 or ssl_forced == true %}
|
{% if ssl_forced == 1 or ssl_forced == true %}
|
||||||
{% if hsts_enabled == 1 or hsts_enabled == true %}
|
{% if hsts_enabled == 1 or hsts_enabled == true %}
|
||||||
# HSTS (ngx_http_headers_module is required) (63072000 seconds = 2 years)
|
# HSTS (ngx_http_headers_module is required) (63072000 seconds = 2 years)
|
||||||
add_header Strict-Transport-Security "max-age=63072000;{% if hsts_subdomains == 1 or hsts_subdomains == true -%} includeSubDomains;{% endif %} preload" always;
|
add_header Strict-Transport-Security $hsts_header always;
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
3
backend/templates/_hsts_map.conf
Normal file
3
backend/templates/_hsts_map.conf
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
map $scheme $hsts_header {
|
||||||
|
https "max-age=63072000;{% if hsts_subdomains == 1 or hsts_subdomains == true -%} includeSubDomains;{% endif %} preload";
|
||||||
|
}
|
@ -5,9 +5,9 @@
|
|||||||
#listen [::]:80;
|
#listen [::]:80;
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if certificate -%}
|
{% if certificate -%}
|
||||||
listen 443 ssl{% if http2_support %} http2{% endif %};
|
listen 443 ssl{% if http2_support == 1 or http2_support == true %} http2{% endif %};
|
||||||
{% if ipv6 -%}
|
{% if ipv6 -%}
|
||||||
listen [::]:443 ssl{% if http2_support %} http2{% endif %};
|
listen [::]:443 ssl{% if http2_support == 1 or http2_support == true %} http2{% endif %};
|
||||||
{% else -%}
|
{% else -%}
|
||||||
#listen [::]:443;
|
#listen [::]:443;
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
{% include "_header_comment.conf" %}
|
{% include "_header_comment.conf" %}
|
||||||
|
|
||||||
{% if enabled %}
|
{% if enabled %}
|
||||||
|
|
||||||
|
{% include "_hsts_map.conf" %}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
{% include "_listen.conf" %}
|
{% include "_listen.conf" %}
|
||||||
{% include "_certificates.conf" %}
|
{% include "_certificates.conf" %}
|
||||||
|
@ -24,6 +24,12 @@ server {
|
|||||||
}
|
}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{%- if value == "444" %}
|
||||||
|
location / {
|
||||||
|
return 444;
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{%- if value == "redirect" %}
|
{%- if value == "redirect" %}
|
||||||
location / {
|
location / {
|
||||||
return 301 {{ meta.redirect }};
|
return 301 {{ meta.redirect }};
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
{% include "_header_comment.conf" %}
|
{% include "_header_comment.conf" %}
|
||||||
|
|
||||||
{% if enabled %}
|
{% if enabled %}
|
||||||
|
|
||||||
|
{% include "_hsts_map.conf" %}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
set $forward_scheme {{ forward_scheme }};
|
set $forward_scheme {{ forward_scheme }};
|
||||||
set $server "{{ forward_host }}";
|
set $server "{{ forward_host }}";
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
{% include "_header_comment.conf" %}
|
{% include "_header_comment.conf" %}
|
||||||
|
|
||||||
{% if enabled %}
|
{% if enabled %}
|
||||||
|
|
||||||
|
{% include "_hsts_map.conf" %}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
{% include "_listen.conf" %}
|
{% include "_listen.conf" %}
|
||||||
{% include "_certificates.conf" %}
|
{% include "_certificates.conf" %}
|
||||||
|
1056
backend/yarn.lock
1056
backend/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -3,20 +3,25 @@
|
|||||||
|
|
||||||
# This file assumes that the frontend has been built using ./scripts/frontend-build
|
# This file assumes that the frontend has been built using ./scripts/frontend-build
|
||||||
|
|
||||||
FROM jc21/nginx-full:certbot-node
|
FROM nginxproxymanager/nginx-full:certbot-node
|
||||||
|
|
||||||
ARG TARGETPLATFORM
|
ARG TARGETPLATFORM
|
||||||
ARG BUILD_VERSION
|
ARG BUILD_VERSION
|
||||||
ARG BUILD_COMMIT
|
ARG BUILD_COMMIT
|
||||||
ARG BUILD_DATE
|
ARG BUILD_DATE
|
||||||
|
|
||||||
|
# See: https://github.com/just-containers/s6-overlay/blob/master/README.md
|
||||||
ENV SUPPRESS_NO_CONFIG_WARNING=1 \
|
ENV SUPPRESS_NO_CONFIG_WARNING=1 \
|
||||||
S6_FIX_ATTRS_HIDDEN=1 \
|
|
||||||
S6_BEHAVIOUR_IF_STAGE2_FAILS=1 \
|
S6_BEHAVIOUR_IF_STAGE2_FAILS=1 \
|
||||||
|
S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0 \
|
||||||
|
S6_FIX_ATTRS_HIDDEN=1 \
|
||||||
|
S6_KILL_FINISH_MAXTIME=10000 \
|
||||||
|
S6_VERBOSITY=1 \
|
||||||
NODE_ENV=production \
|
NODE_ENV=production \
|
||||||
NPM_BUILD_VERSION="${BUILD_VERSION}" \
|
NPM_BUILD_VERSION="${BUILD_VERSION}" \
|
||||||
NPM_BUILD_COMMIT="${BUILD_COMMIT}" \
|
NPM_BUILD_COMMIT="${BUILD_COMMIT}" \
|
||||||
NPM_BUILD_DATE="${BUILD_DATE}"
|
NPM_BUILD_DATE="${BUILD_DATE}" \
|
||||||
|
NODE_OPTIONS="--openssl-legacy-provider"
|
||||||
|
|
||||||
RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \
|
RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \
|
||||||
&& apt-get update \
|
&& apt-get update \
|
||||||
@ -35,23 +40,17 @@ COPY frontend/dist /app/frontend
|
|||||||
COPY global /app/global
|
COPY global /app/global
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
RUN yarn install
|
RUN yarn install \
|
||||||
|
&& yarn cache clean
|
||||||
|
|
||||||
# add late to limit cache-busting by modifications
|
# add late to limit cache-busting by modifications
|
||||||
COPY docker/rootfs /
|
COPY docker/rootfs /
|
||||||
|
|
||||||
# Remove frontend service not required for prod, dev nginx config as well
|
# Remove frontend service not required for prod, dev nginx config as well
|
||||||
RUN rm -rf /etc/services.d/frontend /etc/nginx/conf.d/dev.conf
|
RUN rm -rf /etc/s6-overlay/s6-rc.d/user/contents.d/frontend /etc/nginx/conf.d/dev.conf \
|
||||||
|
&& chmod 644 /etc/logrotate.d/nginx-proxy-manager
|
||||||
|
|
||||||
# Change permission of logrotate config file
|
VOLUME [ "/data" ]
|
||||||
RUN chmod 644 /etc/logrotate.d/nginx-proxy-manager
|
|
||||||
|
|
||||||
# fix for pip installs
|
|
||||||
# https://github.com/NginxProxyManager/nginx-proxy-manager/issues/1769
|
|
||||||
RUN pip uninstall --yes setuptools \
|
|
||||||
&& pip install "setuptools==58.0.0"
|
|
||||||
|
|
||||||
VOLUME [ "/data", "/etc/letsencrypt" ]
|
|
||||||
ENTRYPOINT [ "/init" ]
|
ENTRYPOINT [ "/init" ]
|
||||||
|
|
||||||
LABEL org.label-schema.schema-version="1.0" \
|
LABEL org.label-schema.schema-version="1.0" \
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
FROM jc21/nginx-full:certbot-node
|
FROM nginxproxymanager/nginx-full:certbot-node
|
||||||
LABEL maintainer="Jamie Curnow <jc@jc21.com>"
|
LABEL maintainer="Jamie Curnow <jc@jc21.com>"
|
||||||
|
|
||||||
ENV S6_LOGGING=0 \
|
# See: https://github.com/just-containers/s6-overlay/blob/master/README.md
|
||||||
SUPPRESS_NO_CONFIG_WARNING=1 \
|
ENV SUPPRESS_NO_CONFIG_WARNING=1 \
|
||||||
S6_FIX_ATTRS_HIDDEN=1
|
S6_BEHAVIOUR_IF_STAGE2_FAILS=1 \
|
||||||
|
S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0 \
|
||||||
|
S6_FIX_ATTRS_HIDDEN=1 \
|
||||||
|
S6_KILL_FINISH_MAXTIME=10000 \
|
||||||
|
S6_VERBOSITY=2 \
|
||||||
|
NODE_OPTIONS="--openssl-legacy-provider"
|
||||||
|
|
||||||
RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \
|
RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \
|
||||||
&& apt-get update \
|
&& apt-get update \
|
||||||
|
@ -1,19 +1,21 @@
|
|||||||
# WARNING: This is a CI docker-compose file used for building and testing of the entire app, it should not be used for production.
|
# WARNING: This is a CI docker-compose file used for building and testing of the entire app, it should not be used for production.
|
||||||
version: "3"
|
version: '3.8'
|
||||||
services:
|
services:
|
||||||
|
|
||||||
fullstack-mysql:
|
fullstack-mysql:
|
||||||
image: ${IMAGE}:ci-${BUILD_NUMBER}
|
image: "${IMAGE}:ci-${BUILD_NUMBER}"
|
||||||
environment:
|
environment:
|
||||||
NODE_ENV: "development"
|
DEBUG: 'true'
|
||||||
|
LE_STAGING: 'true'
|
||||||
FORCE_COLOR: 1
|
FORCE_COLOR: 1
|
||||||
DB_MYSQL_HOST: "db"
|
DB_MYSQL_HOST: 'db'
|
||||||
DB_MYSQL_PORT: 3306
|
DB_MYSQL_PORT: '3306'
|
||||||
DB_MYSQL_USER: "npm"
|
DB_MYSQL_USER: 'npm'
|
||||||
DB_MYSQL_PASSWORD: "npm"
|
DB_MYSQL_PASSWORD: 'npm'
|
||||||
DB_MYSQL_NAME: "npm"
|
DB_MYSQL_NAME: 'npm'
|
||||||
volumes:
|
volumes:
|
||||||
- npm_data:/data
|
- npm_data_mysql:/data
|
||||||
|
- npm_le_mysql:/etc/letsencrypt
|
||||||
expose:
|
expose:
|
||||||
- 81
|
- 81
|
||||||
- 80
|
- 80
|
||||||
@ -21,60 +23,69 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "/bin/check-health"]
|
test: ["CMD", "/usr/bin/check-health"]
|
||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 3s
|
timeout: 3s
|
||||||
|
|
||||||
fullstack-sqlite:
|
fullstack-sqlite:
|
||||||
image: ${IMAGE}:ci-${BUILD_NUMBER}
|
image: "${IMAGE}:ci-${BUILD_NUMBER}"
|
||||||
environment:
|
environment:
|
||||||
NODE_ENV: "development"
|
DEBUG: 'true'
|
||||||
|
LE_STAGING: 'true'
|
||||||
FORCE_COLOR: 1
|
FORCE_COLOR: 1
|
||||||
DB_SQLITE_FILE: "/data/database.sqlite"
|
DB_SQLITE_FILE: '/data/mydb.sqlite'
|
||||||
|
PUID: 1000
|
||||||
|
PGID: 1000
|
||||||
|
DISABLE_IPV6: 'true'
|
||||||
volumes:
|
volumes:
|
||||||
- npm_data:/data
|
- npm_data_sqlite:/data
|
||||||
|
- npm_le_sqlite:/etc/letsencrypt
|
||||||
expose:
|
expose:
|
||||||
- 81
|
- 81
|
||||||
- 80
|
- 80
|
||||||
- 443
|
- 443
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "/bin/check-health"]
|
test: ["CMD", "/usr/bin/check-health"]
|
||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 3s
|
timeout: 3s
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: jc21/mariadb-aria
|
image: jc21/mariadb-aria
|
||||||
environment:
|
environment:
|
||||||
MYSQL_ROOT_PASSWORD: "npm"
|
MYSQL_ROOT_PASSWORD: 'npm'
|
||||||
MYSQL_DATABASE: "npm"
|
MYSQL_DATABASE: 'npm'
|
||||||
MYSQL_USER: "npm"
|
MYSQL_USER: 'npm'
|
||||||
MYSQL_PASSWORD: "npm"
|
MYSQL_PASSWORD: 'npm'
|
||||||
volumes:
|
volumes:
|
||||||
- db_data:/var/lib/mysql
|
- mysql_data:/var/lib/mysql
|
||||||
|
|
||||||
cypress-mysql:
|
cypress-mysql:
|
||||||
image: ${IMAGE}-cypress:ci-${BUILD_NUMBER}
|
image: "${IMAGE}-cypress:ci-${BUILD_NUMBER}"
|
||||||
build:
|
build:
|
||||||
context: ../test/
|
context: ../test/
|
||||||
dockerfile: cypress/Dockerfile
|
dockerfile: cypress/Dockerfile
|
||||||
environment:
|
environment:
|
||||||
CYPRESS_baseUrl: "http://fullstack-mysql:81"
|
CYPRESS_baseUrl: 'http://fullstack-mysql:81'
|
||||||
volumes:
|
volumes:
|
||||||
- cypress-logs:/results
|
- cypress_logs_mysql:/results
|
||||||
command: cypress run --browser chrome --config-file=${CYPRESS_CONFIG:-cypress/config/ci.json}
|
command: cypress run --browser chrome --config-file=${CYPRESS_CONFIG:-cypress/config/ci.json}
|
||||||
|
|
||||||
cypress-sqlite:
|
cypress-sqlite:
|
||||||
image: ${IMAGE}-cypress:ci-${BUILD_NUMBER}
|
image: "${IMAGE}-cypress:ci-${BUILD_NUMBER}"
|
||||||
build:
|
build:
|
||||||
context: ../test/
|
context: ../test/
|
||||||
dockerfile: cypress/Dockerfile
|
dockerfile: cypress/Dockerfile
|
||||||
environment:
|
environment:
|
||||||
CYPRESS_baseUrl: "http://fullstack-sqlite:81"
|
CYPRESS_baseUrl: "http://fullstack-sqlite:81"
|
||||||
volumes:
|
volumes:
|
||||||
- cypress-logs:/results
|
- cypress_logs_sqlite:/results
|
||||||
command: cypress run --browser chrome --config-file=${CYPRESS_CONFIG:-cypress/config/ci.json}
|
command: cypress run --browser chrome --config-file=${CYPRESS_CONFIG:-cypress/config/ci.json}
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
cypress-logs:
|
cypress_logs_mysql:
|
||||||
npm_data:
|
cypress_logs_sqlite:
|
||||||
db_data:
|
npm_data_mysql:
|
||||||
|
npm_data_sqlite:
|
||||||
|
npm_le_sqlite:
|
||||||
|
npm_le_mysql:
|
||||||
|
mysql_data:
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# WARNING: This is a DEVELOPMENT docker-compose file, it should not be used for production.
|
# WARNING: This is a DEVELOPMENT docker-compose file, it should not be used for production.
|
||||||
version: "3.5"
|
version: '3.8'
|
||||||
services:
|
services:
|
||||||
|
|
||||||
npm:
|
npm:
|
||||||
image: nginxproxymanager:dev
|
image: nginxproxymanager:dev
|
||||||
container_name: npm_core
|
container_name: npm_core
|
||||||
@ -14,14 +15,19 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- nginx_proxy_manager
|
- nginx_proxy_manager
|
||||||
environment:
|
environment:
|
||||||
NODE_ENV: "development"
|
PUID: 1000
|
||||||
|
PGID: 1000
|
||||||
FORCE_COLOR: 1
|
FORCE_COLOR: 1
|
||||||
DEVELOPMENT: "true"
|
# specifically for dev:
|
||||||
DB_MYSQL_HOST: "db"
|
DEBUG: 'true'
|
||||||
DB_MYSQL_PORT: 3306
|
DEVELOPMENT: 'true'
|
||||||
DB_MYSQL_USER: "npm"
|
LE_STAGING: 'true'
|
||||||
DB_MYSQL_PASSWORD: "npm"
|
# db:
|
||||||
DB_MYSQL_NAME: "npm"
|
DB_MYSQL_HOST: 'db'
|
||||||
|
DB_MYSQL_PORT: '3306'
|
||||||
|
DB_MYSQL_USER: 'npm'
|
||||||
|
DB_MYSQL_PASSWORD: 'npm'
|
||||||
|
DB_MYSQL_NAME: 'npm'
|
||||||
# DB_SQLITE_FILE: "/data/database.sqlite"
|
# DB_SQLITE_FILE: "/data/database.sqlite"
|
||||||
# DISABLE_IPV6: "true"
|
# DISABLE_IPV6: "true"
|
||||||
volumes:
|
volumes:
|
||||||
@ -42,10 +48,10 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- nginx_proxy_manager
|
- nginx_proxy_manager
|
||||||
environment:
|
environment:
|
||||||
MYSQL_ROOT_PASSWORD: "npm"
|
MYSQL_ROOT_PASSWORD: 'npm'
|
||||||
MYSQL_DATABASE: "npm"
|
MYSQL_DATABASE: 'npm'
|
||||||
MYSQL_USER: "npm"
|
MYSQL_USER: 'npm'
|
||||||
MYSQL_PASSWORD: "npm"
|
MYSQL_PASSWORD: 'npm'
|
||||||
volumes:
|
volumes:
|
||||||
- db_data:/var/lib/mysql
|
- db_data:/var/lib/mysql
|
||||||
|
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# This command reads the `DISABLE_IPV6` env var and will either enable
|
|
||||||
# or disable ipv6 in all nginx configs based on this setting.
|
|
||||||
|
|
||||||
# Lowercase
|
|
||||||
DISABLE_IPV6=$(echo "${DISABLE_IPV6:-}" | tr '[:upper:]' '[:lower:]')
|
|
||||||
|
|
||||||
CYAN='\E[1;36m'
|
|
||||||
BLUE='\E[1;34m'
|
|
||||||
YELLOW='\E[1;33m'
|
|
||||||
RED='\E[1;31m'
|
|
||||||
RESET='\E[0m'
|
|
||||||
|
|
||||||
FOLDER=$1
|
|
||||||
if [ "$FOLDER" == "" ]; then
|
|
||||||
echo -e "${RED}❯ $0 requires a absolute folder path as the first argument!${RESET}"
|
|
||||||
echo -e "${YELLOW} ie: $0 /data/nginx${RESET}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
FILES=$(find "$FOLDER" -type f -name "*.conf")
|
|
||||||
if [ "$DISABLE_IPV6" == "true" ] || [ "$DISABLE_IPV6" == "on" ] || [ "$DISABLE_IPV6" == "1" ] || [ "$DISABLE_IPV6" == "yes" ]; then
|
|
||||||
# IPV6 is disabled
|
|
||||||
echo "Disabling IPV6 in hosts"
|
|
||||||
echo -e "${BLUE}❯ ${CYAN}Disabling IPV6 in hosts: ${YELLOW}${FOLDER}${RESET}"
|
|
||||||
|
|
||||||
# Iterate over configs and run the regex
|
|
||||||
for FILE in $FILES
|
|
||||||
do
|
|
||||||
echo -e " ${BLUE}❯ ${YELLOW}${FILE}${RESET}"
|
|
||||||
sed -E -i 's/^([^#]*)listen \[::\]/\1#listen [::]/g' "$FILE"
|
|
||||||
done
|
|
||||||
|
|
||||||
else
|
|
||||||
# IPV6 is enabled
|
|
||||||
echo -e "${BLUE}❯ ${CYAN}Enabling IPV6 in hosts: ${YELLOW}${FOLDER}${RESET}"
|
|
||||||
|
|
||||||
# Iterate over configs and run the regex
|
|
||||||
for FILE in $FILES
|
|
||||||
do
|
|
||||||
echo -e " ${BLUE}❯ ${YELLOW}${FILE}${RESET}"
|
|
||||||
sed -E -i 's/^(\s*)#listen \[::\]/\1listen [::]/g' "$FILE"
|
|
||||||
done
|
|
||||||
|
|
||||||
fi
|
|
@ -1,5 +1,6 @@
|
|||||||
/data/logs/*_access.log /data/logs/*/access.log {
|
/data/logs/*_access.log /data/logs/*/access.log {
|
||||||
create 0644 root root
|
su npm npm
|
||||||
|
create 0644
|
||||||
weekly
|
weekly
|
||||||
rotate 4
|
rotate 4
|
||||||
missingok
|
missingok
|
||||||
@ -12,7 +13,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/data/logs/*_error.log /data/logs/*/error.log {
|
/data/logs/*_error.log /data/logs/*/error.log {
|
||||||
create 0644 root root
|
su npm npm
|
||||||
|
create 0644
|
||||||
weekly
|
weekly
|
||||||
rotate 10
|
rotate 10
|
||||||
missingok
|
missingok
|
||||||
|
@ -32,6 +32,7 @@ server {
|
|||||||
server_name localhost;
|
server_name localhost;
|
||||||
access_log /data/logs/fallback_access.log standard;
|
access_log /data/logs/fallback_access.log standard;
|
||||||
error_log /dev/null crit;
|
error_log /dev/null crit;
|
||||||
|
include conf.d/include/ssl-ciphers.conf;
|
||||||
ssl_reject_handshake on;
|
ssl_reject_handshake on;
|
||||||
|
|
||||||
return 444;
|
return 444;
|
||||||
|
@ -1,3 +1,10 @@
|
|||||||
|
set $test "";
|
||||||
if ($scheme = "http") {
|
if ($scheme = "http") {
|
||||||
|
set $test "H";
|
||||||
|
}
|
||||||
|
if ($request_uri = /.well-known/acme-challenge/test-challenge) {
|
||||||
|
set $test "${test}T";
|
||||||
|
}
|
||||||
|
if ($test = H) {
|
||||||
return 301 https://$host$request_uri;
|
return 301 https://$host$request_uri;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# run nginx in foreground
|
# run nginx in foreground
|
||||||
daemon off;
|
daemon off;
|
||||||
|
pid /run/nginx/nginx.pid;
|
||||||
user root;
|
user npm;
|
||||||
|
|
||||||
# Set number of worker processes automatically based on number of CPU cores.
|
# Set number of worker processes automatically based on number of CPU cores.
|
||||||
worker_processes auto;
|
worker_processes auto;
|
||||||
|
@ -3,17 +3,19 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
echo "❯ Starting backend ..."
|
. /usr/bin/common.sh
|
||||||
if [ "$DEVELOPMENT" == "true" ]; then
|
|
||||||
cd /app || exit 1
|
cd /app || exit 1
|
||||||
# If yarn install fails: add --verbose --network-concurrency 1
|
|
||||||
yarn install
|
log_info 'Starting backend ...'
|
||||||
node --max_old_space_size=250 --abort_on_uncaught_exception node_modules/nodemon/bin/nodemon.js
|
|
||||||
|
if [ "${DEVELOPMENT:-}" = 'true' ]; then
|
||||||
|
s6-setuidgid "$PUID:$PGID" yarn install
|
||||||
|
exec s6-setuidgid "$PUID:$PGID" bash -c "export HOME=$NPMHOME;node --max_old_space_size=250 --abort_on_uncaught_exception node_modules/nodemon/bin/nodemon.js"
|
||||||
else
|
else
|
||||||
cd /app || exit 1
|
|
||||||
while :
|
while :
|
||||||
do
|
do
|
||||||
node --abort_on_uncaught_exception --max_old_space_size=250 index.js
|
s6-setuidgid "$PUID:$PGID" bash -c "export HOME=$NPMHOME;node --abort_on_uncaught_exception --max_old_space_size=250 index.js"
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
54
docker/rootfs/etc/s6-overlay/s6-rc.d/cs-crowdsec-bouncer/script.sh
Executable file
54
docker/rootfs/etc/s6-overlay/s6-rc.d/cs-crowdsec-bouncer/script.sh
Executable file
@ -0,0 +1,54 @@
|
|||||||
|
#!/command/with-contenv bash
|
||||||
|
|
||||||
|
set -e # Exit immediately if a command exits with a non-zero status.
|
||||||
|
|
||||||
|
function set_properties() {
|
||||||
|
sed -i "s,^$1=.*,$1=$2,g" "${3}"
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Deploy Crowdsec Openresty Bouncer.."
|
||||||
|
if [ -n "${CROWDSEC_OPENRESTY_BOUNCER}" ]; then
|
||||||
|
while IFS= read -r line
|
||||||
|
do
|
||||||
|
if ! [[ "$line" != "^#" ]] || [[ "$line" != "^\n" ]]; then
|
||||||
|
name=$(echo "$line" | cut -d "=" -f1)
|
||||||
|
value=$(echo "$line" | cut -d "=" -f2)
|
||||||
|
if grep -q "${name}" /defaults/crowdsec/crowdsec-openresty-bouncer.conf ; then
|
||||||
|
set_properties "${name}" "${value}" "/defaults/crowdsec/crowdsec-openresty-bouncer.conf"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done <<< "${CROWDSEC_OPENRESTY_BOUNCER}"
|
||||||
|
else
|
||||||
|
mkdir -p /data/crowdsec/templates
|
||||||
|
sed -i 's|/defaults/crowdsec|/data/crowdsec|' /etc/nginx/conf.d/crowdsec_openresty.conf
|
||||||
|
|
||||||
|
if [ -f /data/crowdsec/crowdsec-openresty-bouncer.conf ]; then
|
||||||
|
echo "Patch crowdsec-openresty-bouncer.conf .."
|
||||||
|
sed "s/=.*//g" /data/crowdsec/crowdsec-openresty-bouncer.conf > /tmp/crowdsec.conf.raw
|
||||||
|
sed "s/=.*//g" /defaults/crowdsec/crowdsec-openresty-bouncer.conf > /tmp/crowdsec-openresty-bouncer.conf.raw
|
||||||
|
if grep -vf /tmp/crowdsec.conf.raw /tmp/crowdsec-openresty-bouncer.conf.raw ; then
|
||||||
|
grep -vf /tmp/crowdsec.conf.raw /tmp/crowdsec-openresty-bouncer.conf.raw > /tmp/config.newvals
|
||||||
|
cp /data/crowdsec/crowdsec-openresty-bouncer.conf /data/crowdsec/crowdsec-openresty-bouncer.conf.bak
|
||||||
|
grep -f /tmp/config.newvals /defaults/crowdsec/crowdsec-openresty-bouncer.conf >> /data/crowdsec/crowdsec-openresty-bouncer.conf
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Deploy new crowdsec-openresty-bouncer.conf .."
|
||||||
|
cp /defaults/crowdsec/crowdsec-openresty-bouncer.conf /data/crowdsec/crowdsec-openresty-bouncer.conf
|
||||||
|
fi
|
||||||
|
#Make sure the config location is where we get the config from instead of /default/
|
||||||
|
sed -i 's|/defaults/crowdsec|/data/crowdsec|' /data/crowdsec/crowdsec-openresty-bouncer.conf
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
#Make sure we only copy files that don't exist in /data/crowdsec.
|
||||||
|
if [ -d "/data/crowdsec/templates" ]; then
|
||||||
|
echo "Deploy Crowdsec Templates .."
|
||||||
|
cd /defaults/crowdsec/templates/
|
||||||
|
for file in *.html
|
||||||
|
do
|
||||||
|
if [ ! -e "/data/crowdsec/templates/${file}" ]
|
||||||
|
then
|
||||||
|
cp -r "/defaults/crowdsec/templates/${file}" "/data/crowdsec/templates/"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
@ -0,0 +1 @@
|
|||||||
|
oneshot
|
@ -0,0 +1,2 @@
|
|||||||
|
# shellcheck shell=bash
|
||||||
|
/etc/s6-overlay/s6-rc.d/cs-crowdsec-bouncer/script.sh
|
@ -5,11 +5,17 @@ set -e
|
|||||||
|
|
||||||
# This service is DEVELOPMENT only.
|
# This service is DEVELOPMENT only.
|
||||||
|
|
||||||
if [ "$DEVELOPMENT" == "true" ]; then
|
if [ "$DEVELOPMENT" = 'true' ]; then
|
||||||
|
. /usr/bin/common.sh
|
||||||
cd /app/frontend || exit 1
|
cd /app/frontend || exit 1
|
||||||
# If yarn install fails: add --verbose --network-concurrency 1
|
HOME=$NPMHOME
|
||||||
yarn install
|
export HOME
|
||||||
yarn watch
|
mkdir -p /app/frontend/dist
|
||||||
|
chown -R "$PUID:$PGID" /app/frontend/dist
|
||||||
|
|
||||||
|
log_info 'Starting frontend ...'
|
||||||
|
s6-setuidgid "$PUID:$PGID" yarn install
|
||||||
|
exec s6-setuidgid "$PUID:$PGID" yarn watch
|
||||||
else
|
else
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
@ -3,5 +3,7 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
echo "❯ Starting nginx ..."
|
. /usr/bin/common.sh
|
||||||
exec nginx
|
|
||||||
|
log_info 'Starting nginx ...'
|
||||||
|
exec s6-setuidgid "$PUID:$PGID" nginx
|
||||||
|
22
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/00-all.sh
Executable file
22
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/00-all.sh
Executable file
@ -0,0 +1,22 @@
|
|||||||
|
#!/command/with-contenv bash
|
||||||
|
# shellcheck shell=bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
. /usr/bin/common.sh
|
||||||
|
|
||||||
|
if [ "$(id -u)" != "0" ]; then
|
||||||
|
log_fatal "This docker container must be run as root, do not specify a user.\nYou can specify PUID and PGID env vars to run processes as that user and group after initialization."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$DEBUG" = "true" ]; then
|
||||||
|
set -x
|
||||||
|
fi
|
||||||
|
|
||||||
|
. /etc/s6-overlay/s6-rc.d/prepare/10-usergroup.sh
|
||||||
|
. /etc/s6-overlay/s6-rc.d/prepare/20-paths.sh
|
||||||
|
. /etc/s6-overlay/s6-rc.d/prepare/30-ownership.sh
|
||||||
|
. /etc/s6-overlay/s6-rc.d/prepare/40-dynamic.sh
|
||||||
|
. /etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh
|
||||||
|
. /etc/s6-overlay/s6-rc.d/prepare/60-secrets.sh
|
||||||
|
. /etc/s6-overlay/s6-rc.d/prepare/90-banner.sh
|
40
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/10-usergroup.sh
Executable file
40
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/10-usergroup.sh
Executable file
@ -0,0 +1,40 @@
|
|||||||
|
#!/command/with-contenv bash
|
||||||
|
# shellcheck shell=bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
log_info "Configuring $NPMUSER user ..."
|
||||||
|
|
||||||
|
if id -u "$NPMUSER" 2>/dev/null; then
|
||||||
|
# user already exists
|
||||||
|
usermod -u "$PUID" "$NPMUSER"
|
||||||
|
else
|
||||||
|
# Add user
|
||||||
|
useradd -o -u "$PUID" -U -d "$NPMHOME" -s /bin/false "$NPMUSER"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Configuring $NPMGROUP group ..."
|
||||||
|
if [ "$(get_group_id "$NPMGROUP")" = '' ]; then
|
||||||
|
# Add group. This will not set the id properly if it's already taken
|
||||||
|
groupadd -f -g "$PGID" "$NPMGROUP"
|
||||||
|
else
|
||||||
|
groupmod -o -g "$PGID" "$NPMGROUP"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set the group ID and check it
|
||||||
|
groupmod -o -g "$PGID" "$NPMGROUP"
|
||||||
|
if [ "$(get_group_id "$NPMGROUP")" != "$PGID" ]; then
|
||||||
|
echo "ERROR: Unable to set group id properly"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set the group against the user and check it
|
||||||
|
usermod -G "$PGID" "$NPMGROUP"
|
||||||
|
if [ "$(id -g "$NPMUSER")" != "$PGID" ] ; then
|
||||||
|
echo "ERROR: Unable to set group against the user properly"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Home for user
|
||||||
|
mkdir -p "$NPMHOME"
|
||||||
|
chown -R "$PUID:$PGID" "$NPMHOME"
|
41
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/20-paths.sh
Executable file
41
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/20-paths.sh
Executable file
@ -0,0 +1,41 @@
|
|||||||
|
#!/command/with-contenv bash
|
||||||
|
# shellcheck shell=bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
log_info 'Checking paths ...'
|
||||||
|
|
||||||
|
# Ensure /data is mounted
|
||||||
|
if [ ! -d '/data' ]; then
|
||||||
|
log_fatal '/data is not mounted! Check your docker configuration.'
|
||||||
|
fi
|
||||||
|
# Ensure /etc/letsencrypt is mounted
|
||||||
|
if [ ! -d '/etc/letsencrypt' ]; then
|
||||||
|
log_fatal '/etc/letsencrypt is not mounted! Check your docker configuration.'
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create required folders
|
||||||
|
mkdir -p \
|
||||||
|
/data/nginx \
|
||||||
|
/data/custom_ssl \
|
||||||
|
/data/logs \
|
||||||
|
/data/access \
|
||||||
|
/data/nginx/default_host \
|
||||||
|
/data/nginx/default_www \
|
||||||
|
/data/nginx/proxy_host \
|
||||||
|
/data/nginx/redirection_host \
|
||||||
|
/data/nginx/stream \
|
||||||
|
/data/nginx/dead_host \
|
||||||
|
/data/nginx/temp \
|
||||||
|
/data/letsencrypt-acme-challenge \
|
||||||
|
/run/nginx \
|
||||||
|
/tmp/nginx/body \
|
||||||
|
/var/log/nginx \
|
||||||
|
/var/lib/nginx/cache/public \
|
||||||
|
/var/lib/nginx/cache/private \
|
||||||
|
/var/cache/nginx/proxy_temp
|
||||||
|
|
||||||
|
touch /var/log/nginx/error.log || true
|
||||||
|
chmod 777 /var/log/nginx/error.log || true
|
||||||
|
chmod -R 777 /var/cache/nginx || true
|
||||||
|
chmod 644 /etc/logrotate.d/nginx-proxy-manager
|
28
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/30-ownership.sh
Executable file
28
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/30-ownership.sh
Executable file
@ -0,0 +1,28 @@
|
|||||||
|
#!/command/with-contenv bash
|
||||||
|
# shellcheck shell=bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
log_info 'Setting ownership ...'
|
||||||
|
|
||||||
|
# root
|
||||||
|
chown root /tmp/nginx
|
||||||
|
|
||||||
|
# npm user and group
|
||||||
|
chown -R "$PUID:$PGID" /data
|
||||||
|
chown -R "$PUID:$PGID" /etc/letsencrypt
|
||||||
|
chown -R "$PUID:$PGID" /run/nginx
|
||||||
|
chown -R "$PUID:$PGID" /tmp/nginx
|
||||||
|
chown -R "$PUID:$PGID" /var/cache/nginx
|
||||||
|
chown -R "$PUID:$PGID" /var/lib/logrotate
|
||||||
|
chown -R "$PUID:$PGID" /var/lib/nginx
|
||||||
|
chown -R "$PUID:$PGID" /var/log/nginx
|
||||||
|
|
||||||
|
# Don't chown entire /etc/nginx folder as this causes crashes on some systems
|
||||||
|
chown -R "$PUID:$PGID" /etc/nginx/nginx
|
||||||
|
chown -R "$PUID:$PGID" /etc/nginx/nginx.conf
|
||||||
|
chown -R "$PUID:$PGID" /etc/nginx/conf.d
|
||||||
|
|
||||||
|
# Prevents errors when installing python certbot plugins when non-root
|
||||||
|
chown "$PUID:$PGID" /opt/certbot /opt/certbot/bin
|
||||||
|
find /opt/certbot/lib/python*/site-packages -not -user "$PUID" -execdir chown "$PUID:$PGID" {} \+
|
17
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/40-dynamic.sh
Executable file
17
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/40-dynamic.sh
Executable file
@ -0,0 +1,17 @@
|
|||||||
|
#!/command/with-contenv bash
|
||||||
|
# shellcheck shell=bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
log_info 'Dynamic resolvers ...'
|
||||||
|
|
||||||
|
DISABLE_IPV6=$(echo "${DISABLE_IPV6:-}" | tr '[:upper:]' '[:lower:]')
|
||||||
|
|
||||||
|
# Dynamically generate resolvers file, if resolver is IPv6, enclose in `[]`
|
||||||
|
# thanks @tfmm
|
||||||
|
if [ "$DISABLE_IPV6" == "true" ] || [ "$DISABLE_IPV6" == "on" ] || [ "$DISABLE_IPV6" == "1" ] || [ "$DISABLE_IPV6" == "yes" ];
|
||||||
|
then
|
||||||
|
echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf) ipv6=off valid=10s;" > /etc/nginx/conf.d/include/resolvers.conf
|
||||||
|
else
|
||||||
|
echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf) valid=10s;" > /etc/nginx/conf.d/include/resolvers.conf
|
||||||
|
fi
|
39
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh
Executable file
39
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh
Executable file
@ -0,0 +1,39 @@
|
|||||||
|
#!/command/with-contenv bash
|
||||||
|
# shellcheck shell=bash
|
||||||
|
|
||||||
|
# This command reads the `DISABLE_IPV6` env var and will either enable
|
||||||
|
# or disable ipv6 in all nginx configs based on this setting.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
log_info 'IPv6 ...'
|
||||||
|
|
||||||
|
# Lowercase
|
||||||
|
DISABLE_IPV6=$(echo "${DISABLE_IPV6:-}" | tr '[:upper:]' '[:lower:]')
|
||||||
|
|
||||||
|
process_folder () {
|
||||||
|
FILES=$(find "$1" -type f -name "*.conf")
|
||||||
|
SED_REGEX=
|
||||||
|
|
||||||
|
if [ "$DISABLE_IPV6" == "true" ] || [ "$DISABLE_IPV6" == "on" ] || [ "$DISABLE_IPV6" == "1" ] || [ "$DISABLE_IPV6" == "yes" ]; then
|
||||||
|
# IPV6 is disabled
|
||||||
|
echo "Disabling IPV6 in hosts in: $1"
|
||||||
|
SED_REGEX='s/^([^#]*)listen \[::\]/\1#listen [::]/g'
|
||||||
|
else
|
||||||
|
# IPV6 is enabled
|
||||||
|
echo "Enabling IPV6 in hosts in: $1"
|
||||||
|
SED_REGEX='s/^(\s*)#listen \[::\]/\1listen [::]/g'
|
||||||
|
fi
|
||||||
|
|
||||||
|
for FILE in $FILES
|
||||||
|
do
|
||||||
|
echo "- ${FILE}"
|
||||||
|
echo "$(sed -E "$SED_REGEX" "$FILE")" > $FILE
|
||||||
|
done
|
||||||
|
|
||||||
|
# ensure the files are still owned by the npm user
|
||||||
|
chown -R "$PUID:$PGID" "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
process_folder /etc/nginx/conf.d
|
||||||
|
process_folder /data/nginx
|
30
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/60-secrets.sh
Executable file
30
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/60-secrets.sh
Executable file
@ -0,0 +1,30 @@
|
|||||||
|
#!/command/with-contenv bash
|
||||||
|
# shellcheck shell=bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# in s6, environmental variables are written as text files for s6 to monitor
|
||||||
|
# search through full-path filenames for files ending in "__FILE"
|
||||||
|
log_info 'Docker secrets ...'
|
||||||
|
|
||||||
|
for FILENAME in $(find /var/run/s6/container_environment/ | grep "__FILE$"); do
|
||||||
|
echo "[secret-init] Evaluating ${FILENAME##*/} ..."
|
||||||
|
|
||||||
|
# set SECRETFILE to the contents of the full-path textfile
|
||||||
|
SECRETFILE=$(cat "${FILENAME}")
|
||||||
|
# if SECRETFILE exists / is not null
|
||||||
|
if [[ -f "${SECRETFILE}" ]]; then
|
||||||
|
# strip the appended "__FILE" from environmental variable name ...
|
||||||
|
STRIPFILE=$(echo "${FILENAME}" | sed "s/__FILE//g")
|
||||||
|
# echo "[secret-init] Set STRIPFILE to ${STRIPFILE}" # DEBUG - rm for prod!
|
||||||
|
|
||||||
|
# ... and set value to contents of secretfile
|
||||||
|
# since s6 uses text files, this is effectively "export ..."
|
||||||
|
printf $(cat "${SECRETFILE}") > "${STRIPFILE}"
|
||||||
|
# echo "[secret-init] Set ${STRIPFILE##*/} to $(cat ${STRIPFILE})" # DEBUG - rm for prod!"
|
||||||
|
echo "Success: ${STRIPFILE##*/} set from ${FILENAME##*/}"
|
||||||
|
|
||||||
|
else
|
||||||
|
echo "Cannot find secret in ${FILENAME}"
|
||||||
|
fi
|
||||||
|
done
|
18
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/90-banner.sh
Executable file
18
docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/90-banner.sh
Executable file
@ -0,0 +1,18 @@
|
|||||||
|
#!/command/with-contenv bash
|
||||||
|
# shellcheck shell=bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set +x
|
||||||
|
|
||||||
|
echo "
|
||||||
|
-------------------------------------
|
||||||
|
_ _ ____ __ __
|
||||||
|
| \ | | _ \| \/ |
|
||||||
|
| \| | |_) | |\/| |
|
||||||
|
| |\ | __/| | | |
|
||||||
|
|_| \_|_| |_| |_|
|
||||||
|
-------------------------------------
|
||||||
|
User: $NPMUSER PUID:$PUID ID:$(id -u "$NPMUSER") GROUP:$(id -g "$NPMUSER")
|
||||||
|
Group: $NPMGROUP PGID:$PGID ID:$(get_group_id "$NPMGROUP")
|
||||||
|
-------------------------------------
|
||||||
|
"
|
@ -1,93 +0,0 @@
|
|||||||
#!/command/with-contenv bash
|
|
||||||
# shellcheck shell=bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
DATA_PATH=/data
|
|
||||||
|
|
||||||
# Ensure /data is mounted
|
|
||||||
if [ ! -d "$DATA_PATH" ]; then
|
|
||||||
echo '--------------------------------------'
|
|
||||||
echo "ERROR: $DATA_PATH is not mounted! Check your docker configuration."
|
|
||||||
echo '--------------------------------------'
|
|
||||||
/run/s6/basedir/bin/halt
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "❯ Checking folder structure ..."
|
|
||||||
|
|
||||||
# Create required folders
|
|
||||||
mkdir -p /tmp/nginx/body \
|
|
||||||
/run/nginx \
|
|
||||||
/var/log/nginx \
|
|
||||||
/data/nginx \
|
|
||||||
/data/custom_ssl \
|
|
||||||
/data/logs \
|
|
||||||
/data/access \
|
|
||||||
/data/nginx/default_host \
|
|
||||||
/data/nginx/default_www \
|
|
||||||
/data/nginx/proxy_host \
|
|
||||||
/data/nginx/redirection_host \
|
|
||||||
/data/nginx/stream \
|
|
||||||
/data/nginx/dead_host \
|
|
||||||
/data/nginx/temp \
|
|
||||||
/var/lib/nginx/cache/public \
|
|
||||||
/var/lib/nginx/cache/private \
|
|
||||||
/var/cache/nginx/proxy_temp \
|
|
||||||
/data/letsencrypt-acme-challenge
|
|
||||||
|
|
||||||
touch /var/log/nginx/error.log && chmod 777 /var/log/nginx/error.log && chmod -R 777 /var/cache/nginx
|
|
||||||
chown root /tmp/nginx
|
|
||||||
|
|
||||||
# Dynamically generate resolvers file, if resolver is IPv6, enclose in `[]`
|
|
||||||
# thanks @tfmm
|
|
||||||
if [ "$DISABLE_IPV6" == "true" ] || [ "$DISABLE_IPV6" == "on" ] || [ "$DISABLE_IPV6" == "1" ] || [ "$DISABLE_IPV6" == "yes" ];
|
|
||||||
then
|
|
||||||
echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf) ipv6=off valid=10s;" > /etc/nginx/conf.d/include/resolvers.conf
|
|
||||||
else
|
|
||||||
echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf) valid=10s;" > /etc/nginx/conf.d/include/resolvers.conf
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Changing ownership of /data/logs to $(id -u):$(id -g)"
|
|
||||||
chown -R "$(id -u):$(id -g)" /data/logs
|
|
||||||
|
|
||||||
# Handle IPV6 settings
|
|
||||||
/bin/handle-ipv6-setting /etc/nginx/conf.d
|
|
||||||
/bin/handle-ipv6-setting /data/nginx
|
|
||||||
|
|
||||||
# ref: https://github.com/linuxserver/docker-baseimage-alpine/blob/master/root/etc/cont-init.d/01-envfile
|
|
||||||
|
|
||||||
# in s6, environmental variables are written as text files for s6 to monitor
|
|
||||||
# search through full-path filenames for files ending in "__FILE"
|
|
||||||
echo "❯ Secrets-init ..."
|
|
||||||
for FILENAME in $(find /var/run/s6/container_environment/ | grep "__FILE$"); do
|
|
||||||
echo "[secret-init] Evaluating ${FILENAME##*/} ..."
|
|
||||||
|
|
||||||
# set SECRETFILE to the contents of the full-path textfile
|
|
||||||
SECRETFILE=$(cat "${FILENAME}")
|
|
||||||
# if SECRETFILE exists / is not null
|
|
||||||
if [[ -f "${SECRETFILE}" ]]; then
|
|
||||||
# strip the appended "__FILE" from environmental variable name ...
|
|
||||||
STRIPFILE=$(echo "${FILENAME}" | sed "s/__FILE//g")
|
|
||||||
# echo "[secret-init] Set STRIPFILE to ${STRIPFILE}" # DEBUG - rm for prod!
|
|
||||||
|
|
||||||
# ... and set value to contents of secretfile
|
|
||||||
# since s6 uses text files, this is effectively "export ..."
|
|
||||||
printf $(cat "${SECRETFILE}") > "${STRIPFILE}"
|
|
||||||
# echo "[secret-init] Set ${STRIPFILE##*/} to $(cat ${STRIPFILE})" # DEBUG - rm for prod!"
|
|
||||||
echo "[secret-init] Success! ${STRIPFILE##*/} set from ${FILENAME##*/}"
|
|
||||||
|
|
||||||
else
|
|
||||||
echo "[secret-init] cannot find secret in ${FILENAME}"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "-------------------------------------
|
|
||||||
_ _ ____ __ __
|
|
||||||
| \ | | _ \| \/ |
|
|
||||||
| \| | |_) | |\/| |
|
|
||||||
| |\ | __/| | | |
|
|
||||||
|_| \_|_| |_| |_|
|
|
||||||
-------------------------------------
|
|
||||||
"
|
|
@ -1,2 +1,2 @@
|
|||||||
# shellcheck shell=bash
|
# shellcheck shell=bash
|
||||||
/etc/s6-overlay/s6-rc.d/prepare/script.sh
|
/etc/s6-overlay/s6-rc.d/prepare/00-all.sh
|
||||||
|
58
docker/rootfs/usr/bin/common.sh
Normal file
58
docker/rootfs/usr/bin/common.sh
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
CYAN='\E[1;36m'
|
||||||
|
BLUE='\E[1;34m'
|
||||||
|
YELLOW='\E[1;33m'
|
||||||
|
RED='\E[1;31m'
|
||||||
|
RESET='\E[0m'
|
||||||
|
export CYAN BLUE YELLOW RED RESET
|
||||||
|
|
||||||
|
PUID=${PUID:-0}
|
||||||
|
PGID=${PGID:-0}
|
||||||
|
|
||||||
|
# If changing the username and group name below,
|
||||||
|
# ensure all references to this user is also changed.
|
||||||
|
# See docker/rootfs/etc/logrotate.d/nginx-proxy-manager
|
||||||
|
# and docker/rootfs/etc/nginx/nginx.conf
|
||||||
|
NPMUSER=npm
|
||||||
|
NPMGROUP=npm
|
||||||
|
NPMHOME=/tmp/npmuserhome
|
||||||
|
export NPMUSER NPMGROUP NPMHOME
|
||||||
|
|
||||||
|
if [[ "$PUID" -ne '0' ]] && [ "$PGID" = '0' ]; then
|
||||||
|
# set group id to same as user id,
|
||||||
|
# the user probably forgot to specify the group id and
|
||||||
|
# it would be rediculous to intentionally use the root group
|
||||||
|
# for a non-root user
|
||||||
|
PGID=$PUID
|
||||||
|
fi
|
||||||
|
|
||||||
|
export PUID PGID
|
||||||
|
|
||||||
|
log_info () {
|
||||||
|
echo -e "${BLUE}❯ ${CYAN}$1${RESET}"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_error () {
|
||||||
|
echo -e "${RED}❯ $1${RESET}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# The `run` file will only execute 1 line so this helps keep things
|
||||||
|
# logically separated
|
||||||
|
|
||||||
|
log_fatal () {
|
||||||
|
echo -e "${RED}--------------------------------------${RESET}"
|
||||||
|
echo -e "${RED}ERROR: $1${RESET}"
|
||||||
|
echo -e "${RED}--------------------------------------${RESET}"
|
||||||
|
/run/s6/basedir/bin/halt
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# param $1: group_name
|
||||||
|
get_group_id () {
|
||||||
|
if [ "${1:-}" != '' ]; then
|
||||||
|
getent group "$1" | cut -d: -f3
|
||||||
|
fi
|
||||||
|
}
|
@ -8,8 +8,8 @@ BLUE='\E[1;34m'
|
|||||||
GREEN='\E[1;32m'
|
GREEN='\E[1;32m'
|
||||||
RESET='\E[0m'
|
RESET='\E[0m'
|
||||||
|
|
||||||
S6_OVERLAY_VERSION=3.1.4.1
|
S6_OVERLAY_VERSION=3.1.5.0
|
||||||
TARGETPLATFORM=${1:unspecified}
|
TARGETPLATFORM=${1:-linux/amd64}
|
||||||
|
|
||||||
# Determine the correct binary file for the architecture given
|
# Determine the correct binary file for the architecture given
|
||||||
case $TARGETPLATFORM in
|
case $TARGETPLATFORM in
|
||||||
|
10
docs/.gitignore
vendored
10
docs/.gitignore
vendored
@ -1,3 +1,13 @@
|
|||||||
.vuepress/dist
|
.vuepress/dist
|
||||||
node_modules
|
node_modules
|
||||||
ts
|
ts
|
||||||
|
.temp
|
||||||
|
.cache
|
||||||
|
|
||||||
|
.yarn/*
|
||||||
|
!.yarn/releases
|
||||||
|
!.yarn/plugins
|
||||||
|
!.yarn/sdks
|
||||||
|
!.yarn/versions
|
||||||
|
*.gz
|
||||||
|
*.tgz
|
@ -1,82 +1,120 @@
|
|||||||
module.exports = {
|
import { defineUserConfig } from 'vuepress';
|
||||||
locales: {
|
import { defaultTheme } from 'vuepress'
|
||||||
"/": {
|
import { googleAnalyticsPlugin } from '@vuepress/plugin-google-analytics';
|
||||||
lang: "en-US",
|
import { searchPlugin } from '@vuepress/plugin-search'
|
||||||
title: "Nginx Proxy Manager",
|
import { sitemapPlugin } from 'vuepress-plugin-sitemap2';
|
||||||
description: "Expose your services easily and securely"
|
import zoomingPlugin from 'vuepress-plugin-zooming';
|
||||||
}
|
|
||||||
},
|
export default defineUserConfig({
|
||||||
head: [
|
locales: {
|
||||||
["link", { rel: "icon", href: "/icon.png" }],
|
"/": {
|
||||||
["meta", { name: "description", content: "Docker container and built in Web Application for managing Nginx proxy hosts with a simple, powerful interface, providing free SSL support via Let's Encrypt" }],
|
lang: "en-US",
|
||||||
["meta", { property: "og:title", content: "Nginx Proxy Manager" }],
|
title: "Nginx Proxy Manager",
|
||||||
["meta", { property: "og:description", content: "Docker container and built in Web Application for managing Nginx proxy hosts with a simple, powerful interface, providing free SSL support via Let's Encrypt"}],
|
description: "Expose your services easily and securely",
|
||||||
["meta", { property: "og:type", content: "website" }],
|
},
|
||||||
["meta", { property: "og:url", content: "https://nginxproxymanager.com/" }],
|
},
|
||||||
["meta", { property: "og:image", content: "https://nginxproxymanager.com/icon.png" }],
|
head: [
|
||||||
["meta", { name: "twitter:card", content: "summary"}],
|
["link", { rel: "icon", href: "/icon.png" }],
|
||||||
["meta", { name: "twitter:title", content: "Nginx Proxy Manager"}],
|
["meta", { name: "description", content: "Docker container and built in Web Application for managing Nginx proxy hosts with a simple, powerful interface, providing free SSL support via Let's Encrypt" }],
|
||||||
["meta", { name: "twitter:description", content: "Docker container and built in Web Application for managing Nginx proxy hosts with a simple, powerful interface, providing free SSL support via Let's Encrypt"}],
|
["meta", { property: "og:title", content: "Nginx Proxy Manager" }],
|
||||||
["meta", { name: "twitter:image", content: "https://nginxproxymanager.com/icon.png"}],
|
["meta", { property: "og:description", content: "Docker container and built in Web Application for managing Nginx proxy hosts with a simple, powerful interface, providing free SSL support via Let's Encrypt"}],
|
||||||
["meta", { name: "twitter:alt", content: "Nginx Proxy Manager"}],
|
["meta", { property: "og:type", content: "website" }],
|
||||||
],
|
["meta", { property: "og:url", content: "https://nginxproxymanager.com/" }],
|
||||||
themeConfig: {
|
["meta", { property: "og:image", content: "https://nginxproxymanager.com/icon.png" }],
|
||||||
logo: "/icon.png",
|
["meta", { name: "twitter:card", content: "summary"}],
|
||||||
// the GitHub repo path
|
["meta", { name: "twitter:title", content: "Nginx Proxy Manager"}],
|
||||||
repo: "jc21/nginx-proxy-manager",
|
["meta", { name: "twitter:description", content: "Docker container and built in Web Application for managing Nginx proxy hosts with a simple, powerful interface, providing free SSL support via Let's Encrypt"}],
|
||||||
// the label linking to the repo
|
["meta", { name: "twitter:image", content: "https://nginxproxymanager.com/icon.png"}],
|
||||||
repoLabel: "GitHub",
|
["meta", { name: "twitter:alt", content: "Nginx Proxy Manager"}],
|
||||||
// if your docs are not at the root of the repo:
|
],
|
||||||
docsDir: "docs",
|
theme: defaultTheme({
|
||||||
// defaults to false, set to true to enable
|
logo: '/icon.png',
|
||||||
editLinks: true,
|
repo: "jc21/nginx-proxy-manager",
|
||||||
locales: {
|
docsRepo: 'https://github.com/jc21/nginx-proxy-manager',
|
||||||
"/": {
|
docsBranch: 'develop',
|
||||||
// text for the language dropdown
|
docsDir: 'docs',
|
||||||
selectText: "Languages",
|
editLinkPattern: ':repo/edit/:branch/:path',
|
||||||
// label for this locale in the language dropdown
|
locales: {
|
||||||
label: "English",
|
'/': {
|
||||||
// Custom text for edit link. Defaults to "Edit this page"
|
label: 'English',
|
||||||
editLinkText: "Edit this page on GitHub",
|
selectLanguageText: 'Languages',
|
||||||
// Custom navbar values
|
selectLanguageName: 'English',
|
||||||
nav: [{ text: "Setup", link: "/setup/" }],
|
editLinkText: 'Edit this page on GitHub',
|
||||||
// Custom sidebar values
|
navbar: [
|
||||||
sidebar: [
|
{ text: 'Setup', link: '/setup/' }
|
||||||
"/",
|
],
|
||||||
["/guide/", "Guide"],
|
sidebar: {
|
||||||
["/screenshots/", "Screenshots"],
|
'/': [
|
||||||
["/setup/", "Setup Instructions"],
|
{
|
||||||
["/advanced-config/", "Advanced Configuration"],
|
text: 'Home',
|
||||||
["/upgrading/", "Upgrading"],
|
link: '/'
|
||||||
["/faq/", "Frequently Asked Questions"],
|
},
|
||||||
["/third-party/", "Third Party"]
|
{
|
||||||
]
|
text: 'Guide',
|
||||||
}
|
link: '/guide/',
|
||||||
}
|
collapsible: true,
|
||||||
},
|
},
|
||||||
plugins: [
|
{
|
||||||
[
|
text: 'Screenshots',
|
||||||
"@vuepress/google-analytics",
|
link: '/screenshots/',
|
||||||
{
|
collapsible: true,
|
||||||
ga: "UA-99675467-4"
|
},
|
||||||
}
|
{
|
||||||
],
|
text: 'Setup Instructions',
|
||||||
[
|
link: '/setup/',
|
||||||
"sitemap",
|
collapsible: true,
|
||||||
{
|
},
|
||||||
hostname: "https://nginxproxymanager.com"
|
{
|
||||||
}
|
text: 'Advanced Configuration',
|
||||||
],
|
link: '/advanced-config/',
|
||||||
[
|
collapsible: true,
|
||||||
'vuepress-plugin-zooming',
|
},
|
||||||
{
|
{
|
||||||
selector: '.zooming',
|
text: 'Upgrading',
|
||||||
delay: 1000,
|
link: '/upgrading/',
|
||||||
options: {
|
collapsible: true,
|
||||||
bgColor: 'black',
|
},
|
||||||
zIndex: 10000,
|
{
|
||||||
},
|
text: 'Frequently Asked Questions',
|
||||||
},
|
link: '/faq/',
|
||||||
],
|
collapsible: true,
|
||||||
]
|
},
|
||||||
};
|
{
|
||||||
|
text: 'Third Party',
|
||||||
|
link: '/third-party/',
|
||||||
|
collapsible: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
markdown: {
|
||||||
|
code: {
|
||||||
|
lineNumbers: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
googleAnalyticsPlugin({
|
||||||
|
id: 'UA-99675467-4'
|
||||||
|
}),
|
||||||
|
sitemapPlugin({
|
||||||
|
hostname: "https://nginxproxymanager.com",
|
||||||
|
}),
|
||||||
|
zoomingPlugin({
|
||||||
|
selector: '.zooming',
|
||||||
|
delay: 1000,
|
||||||
|
options: {
|
||||||
|
bgColor: 'black',
|
||||||
|
zIndex: 10000,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
searchPlugin({
|
||||||
|
locales: {
|
||||||
|
'/': {
|
||||||
|
placeholder: 'Search',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
258
docs/.vuepress/styles/index.scss
Normal file
258
docs/.vuepress/styles/index.scss
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
:root {
|
||||||
|
// brand colors
|
||||||
|
--c-brand: #f15833;
|
||||||
|
--c-brand-light: #f15833;
|
||||||
|
|
||||||
|
// background colors
|
||||||
|
--c-bg: #ffffff;
|
||||||
|
--c-bg-light: #f3f4f5;
|
||||||
|
--c-bg-lighter: #eeeeee;
|
||||||
|
--c-bg-dark: #ebebec;
|
||||||
|
--c-bg-darker: #e6e6e6;
|
||||||
|
--c-bg-navbar: var(--c-bg);
|
||||||
|
--c-bg-sidebar: var(--c-bg);
|
||||||
|
--c-bg-arrow: #cccccc;
|
||||||
|
|
||||||
|
// text colors
|
||||||
|
--c-text: #663015;
|
||||||
|
--c-text-accent: var(--c-brand);
|
||||||
|
--c-text-light: #863f1c;
|
||||||
|
--c-text-lighter: #b65626;
|
||||||
|
--c-text-lightest: #f15833;
|
||||||
|
--c-text-quote: #999999;
|
||||||
|
|
||||||
|
// border colors
|
||||||
|
--c-border: #eaecef;
|
||||||
|
--c-border-dark: #dfe2e5;
|
||||||
|
|
||||||
|
// custom container colors
|
||||||
|
--c-tip: #42b983;
|
||||||
|
--c-tip-bg: var(--c-bg-light);
|
||||||
|
--c-tip-title: var(--c-text);
|
||||||
|
--c-tip-text: var(--c-text);
|
||||||
|
--c-tip-text-accent: var(--c-text-accent);
|
||||||
|
--c-warning: #ffc310;
|
||||||
|
--c-warning-bg: #fffae3;
|
||||||
|
--c-warning-bg-light: #fff3ba;
|
||||||
|
--c-warning-bg-lighter: #fff0b0;
|
||||||
|
--c-warning-border-dark: #f7dc91;
|
||||||
|
--c-warning-details-bg: #fff5ca;
|
||||||
|
--c-warning-title: #f1b300;
|
||||||
|
--c-warning-text: #746000;
|
||||||
|
--c-warning-text-accent: #edb100;
|
||||||
|
--c-warning-text-light: #c1971c;
|
||||||
|
--c-warning-text-quote: #ccab49;
|
||||||
|
--c-danger: #f11e37;
|
||||||
|
--c-danger-bg: #ffe0e0;
|
||||||
|
--c-danger-bg-light: #ffcfde;
|
||||||
|
--c-danger-bg-lighter: #ffc9c9;
|
||||||
|
--c-danger-border-dark: #f1abab;
|
||||||
|
--c-danger-details-bg: #ffd4d4;
|
||||||
|
--c-danger-title: #ed1e2c;
|
||||||
|
--c-danger-text: #660000;
|
||||||
|
--c-danger-text-accent: #bd1a1a;
|
||||||
|
--c-danger-text-light: #b5474d;
|
||||||
|
--c-danger-text-quote: #c15b5b;
|
||||||
|
--c-details-bg: #eeeeee;
|
||||||
|
|
||||||
|
// badge component colors
|
||||||
|
--c-badge-tip: var(--c-tip);
|
||||||
|
--c-badge-warning: #ecc808;
|
||||||
|
--c-badge-warning-text: var(--c-bg);
|
||||||
|
--c-badge-danger: #dc2626;
|
||||||
|
--c-badge-danger-text: var(--c-bg);
|
||||||
|
|
||||||
|
// transition vars
|
||||||
|
--t-color: 0.3s ease;
|
||||||
|
--t-transform: 0.3s ease;
|
||||||
|
|
||||||
|
// code blocks vars
|
||||||
|
--code-bg-color: #282c34;
|
||||||
|
--code-hl-bg-color: rgba(0, 0, 0, 0.66);
|
||||||
|
--code-ln-color: #9e9e9e;
|
||||||
|
--code-ln-wrapper-width: 3.5rem;
|
||||||
|
|
||||||
|
// font vars
|
||||||
|
--font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
|
||||||
|
Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
|
||||||
|
--font-family-code: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||||
|
|
||||||
|
// layout vars
|
||||||
|
--navbar-height: 3.6rem;
|
||||||
|
--navbar-padding-v: 0.7rem;
|
||||||
|
--navbar-padding-h: 1.5rem;
|
||||||
|
--sidebar-width: 20rem;
|
||||||
|
--sidebar-width-mobile: calc(var(--sidebar-width) * 0.82);
|
||||||
|
--content-width: 740px;
|
||||||
|
--homepage-width: 960px;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark {
|
||||||
|
// brand colors
|
||||||
|
--c-brand: #f15833;
|
||||||
|
--c-brand-light: #f15833;
|
||||||
|
|
||||||
|
// background colors
|
||||||
|
--c-bg: #22272e;
|
||||||
|
--c-bg-light: #2b313a;
|
||||||
|
--c-bg-lighter: #262c34;
|
||||||
|
--c-bg-dark: #343b44;
|
||||||
|
--c-bg-darker: #37404c;
|
||||||
|
|
||||||
|
// text colors
|
||||||
|
--c-text: #adbac7;
|
||||||
|
--c-text-light: #96a7b7;
|
||||||
|
--c-text-lighter: #8b9eb0;
|
||||||
|
--c-text-lightest: #8094a8;
|
||||||
|
|
||||||
|
// border colors
|
||||||
|
--c-border: #3e4c5a;
|
||||||
|
--c-border-dark: #34404c;
|
||||||
|
|
||||||
|
// custom container colors
|
||||||
|
--c-tip: #318a62;
|
||||||
|
--c-warning: #e0ad15;
|
||||||
|
--c-warning-bg: #2d2f2d;
|
||||||
|
--c-warning-bg-light: #423e2a;
|
||||||
|
--c-warning-bg-lighter: #44442f;
|
||||||
|
--c-warning-border-dark: #957c35;
|
||||||
|
--c-warning-details-bg: #39392d;
|
||||||
|
--c-warning-title: #fdca31;
|
||||||
|
--c-warning-text: #d8d96d;
|
||||||
|
--c-warning-text-accent: #ffbf00;
|
||||||
|
--c-warning-text-light: #ddb84b;
|
||||||
|
--c-warning-text-quote: #ccab49;
|
||||||
|
--c-danger: #fc1e38;
|
||||||
|
--c-danger-bg: #39232c;
|
||||||
|
--c-danger-bg-light: #4b2b35;
|
||||||
|
--c-danger-bg-lighter: #553040;
|
||||||
|
--c-danger-border-dark: #a25151;
|
||||||
|
--c-danger-details-bg: #482936;
|
||||||
|
--c-danger-title: #fc2d3b;
|
||||||
|
--c-danger-text: #ea9ca0;
|
||||||
|
--c-danger-text-accent: #fd3636;
|
||||||
|
--c-danger-text-light: #d9777c;
|
||||||
|
--c-danger-text-quote: #d56b6b;
|
||||||
|
--c-details-bg: #323843;
|
||||||
|
|
||||||
|
// badge component colors
|
||||||
|
--c-badge-warning: var(--c-warning);
|
||||||
|
--c-badge-warning-text: #3c2e05;
|
||||||
|
--c-badge-danger: var(--c-danger);
|
||||||
|
--c-badge-danger-text: #401416;
|
||||||
|
|
||||||
|
// code blocks vars
|
||||||
|
--code-hl-bg-color: #363b46;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// plugin-back-to-top
|
||||||
|
.back-to-top {
|
||||||
|
--back-to-top-color: var(--c-brand);
|
||||||
|
--back-to-top-color-hover: var(--c-brand-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
// plugin-docsearch
|
||||||
|
.DocSearch {
|
||||||
|
--docsearch-primary-color: var(--c-brand);
|
||||||
|
--docsearch-text-color: var(--c-text);
|
||||||
|
--docsearch-highlight-color: var(--c-brand);
|
||||||
|
--docsearch-muted-color: var(--c-text-quote);
|
||||||
|
--docsearch-container-background: rgba(9, 10, 17, 0.8);
|
||||||
|
--docsearch-modal-background: var(--c-bg-light);
|
||||||
|
--docsearch-searchbox-background: var(--c-bg-lighter);
|
||||||
|
--docsearch-searchbox-focus-background: var(--c-bg);
|
||||||
|
--docsearch-searchbox-shadow: inset 0 0 0 2px var(--c-brand);
|
||||||
|
--docsearch-hit-color: var(--c-text-light);
|
||||||
|
--docsearch-hit-active-color: var(--c-bg);
|
||||||
|
--docsearch-hit-background: var(--c-bg);
|
||||||
|
--docsearch-hit-shadow: 0 1px 3px 0 var(--c-border-dark);
|
||||||
|
--docsearch-footer-background: var(--c-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// dark plugin-docsearch
|
||||||
|
html.dark .DocSearch {
|
||||||
|
--docsearch-logo-color: var(--c-text);
|
||||||
|
--docsearch-modal-shadow: inset 1px 1px 0 0 #2c2e40, 0 3px 8px 0 #000309;
|
||||||
|
--docsearch-key-shadow: inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d,
|
||||||
|
0 2px 2px 0 rgba(3, 4, 9, 0.3);
|
||||||
|
--docsearch-key-gradient: linear-gradient(-225deg, #444950, #1c1e21);
|
||||||
|
--docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, 0.5),
|
||||||
|
0 -4px 8px 0 rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// plugin-external-link-icon
|
||||||
|
.external-link-icon {
|
||||||
|
--external-link-icon-color: var(--c-text-quote);
|
||||||
|
}
|
||||||
|
|
||||||
|
// plugin-medium-zoom
|
||||||
|
.medium-zoom-overlay {
|
||||||
|
--medium-zoom-bg-color: var(--c-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// plugin-nprogress
|
||||||
|
#nprogress {
|
||||||
|
--nprogress-color: var(--c-brand);
|
||||||
|
}
|
||||||
|
|
||||||
|
// plugin-pwa-popup
|
||||||
|
.pwa-popup {
|
||||||
|
--pwa-popup-text-color: var(--c-text);
|
||||||
|
--pwa-popup-bg-color: var(--c-bg);
|
||||||
|
--pwa-popup-border-color: var(--c-brand);
|
||||||
|
--pwa-popup-shadow: 0 4px 16px var(--c-brand);
|
||||||
|
--pwa-popup-btn-text-color: var(--c-bg);
|
||||||
|
--pwa-popup-btn-bg-color: var(--c-brand);
|
||||||
|
--pwa-popup-btn-hover-bg-color: var(--c-brand-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
// plugin-search
|
||||||
|
.search-box {
|
||||||
|
--search-bg-color: var(--c-bg);
|
||||||
|
--search-accent-color: var(--c-brand);
|
||||||
|
--search-text-color: var(--c-text);
|
||||||
|
--search-border-color: var(--c-border);
|
||||||
|
|
||||||
|
--search-item-text-color: var(--c-text-lighter);
|
||||||
|
--search-item-focus-bg-color: var(--c-bg-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.home .hero img {
|
||||||
|
max-width: 500px !important;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 80%
|
||||||
|
}
|
||||||
|
|
||||||
|
#main-title {
|
||||||
|
display: none
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main-title {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero {
|
||||||
|
margin: 150px 25px 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Nerd Font';
|
||||||
|
src: url("/nerd-font.woff2") format("woff2");
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: 'Nerd Font', source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||||
|
}
|
@ -1,23 +0,0 @@
|
|||||||
.home .hero img
|
|
||||||
max-width: 500px !important
|
|
||||||
min-width: 300px
|
|
||||||
width: 100%
|
|
||||||
|
|
||||||
.center
|
|
||||||
margin 0 auto;
|
|
||||||
width: 80%
|
|
||||||
|
|
||||||
#main-title
|
|
||||||
display: none
|
|
||||||
|
|
||||||
.hero
|
|
||||||
margin: 150px 25px 70px
|
|
||||||
|
|
||||||
@font-face
|
|
||||||
font-family: 'Nerd Font';
|
|
||||||
src: url("/nerd-font.woff2") format("woff2");
|
|
||||||
font-weight: 400;
|
|
||||||
font-style: normal
|
|
||||||
|
|
||||||
code
|
|
||||||
font-family: 'Nerd Font', source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
|
|
@ -1,4 +0,0 @@
|
|||||||
$accentColor = #f15833
|
|
||||||
$textColor = #663015
|
|
||||||
$borderColor = #eaecef
|
|
||||||
$codeBgColor = #282c34
|
|
893
docs/.yarn/releases/yarn-4.0.2.cjs
vendored
Normal file
893
docs/.yarn/releases/yarn-4.0.2.cjs
vendored
Normal file
File diff suppressed because one or more lines are too long
3
docs/.yarnrc.yml
Normal file
3
docs/.yarnrc.yml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
nodeLinker: node-modules
|
||||||
|
|
||||||
|
yarnPath: .yarn/releases/yarn-4.0.2.cjs
|
@ -1,8 +1,10 @@
|
|||||||
---
|
---
|
||||||
home: true
|
home: true
|
||||||
heroImage: /logo.png
|
heroImage: /logo.png
|
||||||
actionText: Get Started →
|
actions:
|
||||||
actionLink: /guide/
|
- text: Get Started
|
||||||
|
link: /guide/
|
||||||
|
type: primary
|
||||||
footer: MIT Licensed | Copyright © 2016-present jc21.com
|
footer: MIT Licensed | Copyright © 2016-present jc21.com
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -1,5 +1,26 @@
|
|||||||
# Advanced Configuration
|
# Advanced Configuration
|
||||||
|
|
||||||
|
## Running processes as a user/group
|
||||||
|
|
||||||
|
By default, the services (nginx etc) will run as `root` user inside the docker container.
|
||||||
|
You can change this behaviour by setting the following environment variables.
|
||||||
|
Not only will they run the services as this user/group, they will change the ownership
|
||||||
|
on the `data` and `letsencrypt` folders at startup.
|
||||||
|
|
||||||
|
```yml
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: 'jc21/nginx-proxy-manager:latest'
|
||||||
|
environment:
|
||||||
|
PUID: 1000
|
||||||
|
PGID: 1000
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
This may have the side effect of a failed container start due to permission denied trying
|
||||||
|
to open port 80 on some systems. The only course to fix that is to remove the variables
|
||||||
|
and run as the default root user.
|
||||||
|
|
||||||
## Best Practice: Use a Docker network
|
## Best Practice: Use a Docker network
|
||||||
|
|
||||||
For those who have a few of their upstream services running in Docker on the same Docker
|
For those who have a few of their upstream services running in Docker on the same Docker
|
||||||
@ -25,7 +46,7 @@ networks:
|
|||||||
Let's look at a Portainer example:
|
Let's look at a Portainer example:
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
version: '3'
|
version: '3.8'
|
||||||
services:
|
services:
|
||||||
|
|
||||||
portainer:
|
portainer:
|
||||||
@ -55,19 +76,19 @@ feature by adding the following to the service in your `docker-compose.yml` file
|
|||||||
|
|
||||||
```yml
|
```yml
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "/bin/check-health"]
|
test: ["CMD", "/usr/bin/check-health"]
|
||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 3s
|
timeout: 3s
|
||||||
```
|
```
|
||||||
|
|
||||||
## Docker Secrets
|
## Docker File Secrets
|
||||||
|
|
||||||
This image supports the use of Docker secrets to import from file and keep sensitive usernames or passwords from being passed or preserved in plaintext.
|
This image supports the use of Docker secrets to import from files and keep sensitive usernames or passwords from being passed or preserved in plaintext.
|
||||||
|
|
||||||
You can set any environment variable from a file by appending `__FILE` (double-underscore FILE) to the environmental variable name.
|
You can set any environment variable from a file by appending `__FILE` (double-underscore FILE) to the environmental variable name.
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
version: "3.7"
|
version: '3.8'
|
||||||
|
|
||||||
secrets:
|
secrets:
|
||||||
# Secrets are single-line text files where the sole content is the secret
|
# Secrets are single-line text files where the sole content is the secret
|
||||||
@ -96,9 +117,7 @@ services:
|
|||||||
# DB_MYSQL_PASSWORD: "npm" # use secret instead
|
# DB_MYSQL_PASSWORD: "npm" # use secret instead
|
||||||
DB_MYSQL_PASSWORD__FILE: /run/secrets/MYSQL_PWD
|
DB_MYSQL_PASSWORD__FILE: /run/secrets/MYSQL_PWD
|
||||||
DB_MYSQL_NAME: "npm"
|
DB_MYSQL_NAME: "npm"
|
||||||
# If you would rather use Sqlite uncomment this
|
# If you would rather use Sqlite, remove all DB_MYSQL_* lines above
|
||||||
# and remove all DB_MYSQL_* lines above
|
|
||||||
# DB_SQLITE_FILE: "/data/database.sqlite"
|
|
||||||
# Uncomment this if IPv6 is not enabled on your host
|
# Uncomment this if IPv6 is not enabled on your host
|
||||||
# DISABLE_IPV6: 'true'
|
# DISABLE_IPV6: 'true'
|
||||||
volumes:
|
volumes:
|
||||||
@ -108,6 +127,7 @@ services:
|
|||||||
- MYSQL_PWD
|
- MYSQL_PWD
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: jc21/mariadb-aria
|
image: jc21/mariadb-aria
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@ -118,8 +138,9 @@ services:
|
|||||||
MYSQL_USER: "npm"
|
MYSQL_USER: "npm"
|
||||||
# MYSQL_PASSWORD: "npm" # use secret instead
|
# MYSQL_PASSWORD: "npm" # use secret instead
|
||||||
MYSQL_PASSWORD__FILE: /run/secrets/MYSQL_PWD
|
MYSQL_PASSWORD__FILE: /run/secrets/MYSQL_PWD
|
||||||
|
MARIADB_AUTO_UPGRADE: '1'
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/mysql:/var/lib/mysql
|
- ./mysql:/var/lib/mysql
|
||||||
secrets:
|
secrets:
|
||||||
- DB_ROOT_PWD
|
- DB_ROOT_PWD
|
||||||
- MYSQL_PWD
|
- MYSQL_PWD
|
||||||
@ -173,3 +194,17 @@ value by specifying it as a Docker environment variable. The default if not spec
|
|||||||
X_FRAME_OPTIONS: "sameorigin"
|
X_FRAME_OPTIONS: "sameorigin"
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Customising logrotate settings
|
||||||
|
|
||||||
|
By default, NPM rotates the access- and error logs weekly and keeps 4 and 10 log files respectively.
|
||||||
|
Depending on the usage, this can lead to large log files, especially access logs.
|
||||||
|
You can customise the logrotate configuration through a mount (if your custom config is `logrotate.custom`):
|
||||||
|
|
||||||
|
```yml
|
||||||
|
volumes:
|
||||||
|
...
|
||||||
|
- ./logrotate.custom:/etc/logrotate.d/nginx-proxy-manager
|
||||||
|
```
|
||||||
|
|
||||||
|
For reference, the default configuration can be found [here](https://github.com/NginxProxyManager/nginx-proxy-manager/blob/develop/docker/rootfs/etc/logrotate.d/nginx-proxy-manager).
|
||||||
|
@ -3,775 +3,21 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"dependencies": {
|
"devDependencies": {
|
||||||
"@vuepress/plugin-google-analytics": "^1.5.3",
|
"vuepress": "^2.0.0-rc.0"
|
||||||
"abbrev": "^1.1.1",
|
|
||||||
"accepts": "^1.3.7",
|
|
||||||
"acorn": "^7.4.0",
|
|
||||||
"agentkeepalive": "^4.1.3",
|
|
||||||
"ajv": "^6.12.3",
|
|
||||||
"ajv-errors": "^1.0.1",
|
|
||||||
"ajv-keywords": "^3.5.2",
|
|
||||||
"algoliasearch": "^4.3.1",
|
|
||||||
"alphanum-sort": "^1.0.2",
|
|
||||||
"ansi-colors": "^4.1.1",
|
|
||||||
"ansi-escapes": "^4.3.1",
|
|
||||||
"ansi-html": "^0.0.8",
|
|
||||||
"ansi-regex": "^5.0.0",
|
|
||||||
"ansi-styles": "^4.2.1",
|
|
||||||
"anymatch": "^3.1.1",
|
|
||||||
"aproba": "^2.0.0",
|
|
||||||
"argparse": "^1.0.10",
|
|
||||||
"arr-diff": "^4.0.0",
|
|
||||||
"arr-flatten": "^1.1.0",
|
|
||||||
"arr-union": "^3.1.0",
|
|
||||||
"array-flatten": "^3.0.0",
|
|
||||||
"array-union": "^2.1.0",
|
|
||||||
"array-uniq": "^2.1.0",
|
|
||||||
"array-unique": "^0.3.2",
|
|
||||||
"asn1": "^0.2.4",
|
|
||||||
"asn1.js": "^5.4.1",
|
|
||||||
"assert": "^2.0.0",
|
|
||||||
"assert-plus": "^1.0.0",
|
|
||||||
"assign-symbols": "^2.0.2",
|
|
||||||
"async": "^3.2.0",
|
|
||||||
"async-each": "^1.0.3",
|
|
||||||
"async-limiter": "^2.0.0",
|
|
||||||
"asynckit": "^0.4.0",
|
|
||||||
"atob": "^2.1.2",
|
|
||||||
"autocomplete.js": "^0.37.1",
|
|
||||||
"autoprefixer": "^9.8.6",
|
|
||||||
"aws-sign2": "^0.7.0",
|
|
||||||
"aws4": "^1.10.0",
|
|
||||||
"babel-loader": "^8.1.0",
|
|
||||||
"babel-plugin-dynamic-import-node": "^2.3.3",
|
|
||||||
"babel-plugin-module-resolver": "^4.0.0",
|
|
||||||
"balanced-match": "^1.0.0",
|
|
||||||
"base": "^3.0.0",
|
|
||||||
"base64-js": "^1.3.1",
|
|
||||||
"batch": "^0.6.1",
|
|
||||||
"bcrypt-pbkdf": "^1.0.2",
|
|
||||||
"big.js": "^5.2.2",
|
|
||||||
"binary-extensions": "^2.1.0",
|
|
||||||
"bluebird": "^3.7.2",
|
|
||||||
"bn.js": "^5.1.2",
|
|
||||||
"body-parser": "^1.19.0",
|
|
||||||
"bonjour": "^3.5.0",
|
|
||||||
"boolbase": "^1.0.0",
|
|
||||||
"brace-expansion": "^1.1.11",
|
|
||||||
"braces": "^3.0.2",
|
|
||||||
"brorand": "^1.1.0",
|
|
||||||
"browserify-aes": "^1.2.0",
|
|
||||||
"browserify-cipher": "^1.0.1",
|
|
||||||
"browserify-des": "^1.0.2",
|
|
||||||
"browserify-rsa": "^4.0.1",
|
|
||||||
"browserify-sign": "^4.2.1",
|
|
||||||
"browserify-zlib": "^0.2.0",
|
|
||||||
"browserslist": "^4.13.0",
|
|
||||||
"buffer": "^5.6.0",
|
|
||||||
"buffer-from": "^1.1.1",
|
|
||||||
"buffer-indexof": "^1.1.1",
|
|
||||||
"buffer-json": "^2.0.0",
|
|
||||||
"buffer-xor": "^2.0.2",
|
|
||||||
"builtin-status-codes": "^3.0.0",
|
|
||||||
"bytes": "^3.1.0",
|
|
||||||
"cac": "^6.6.1",
|
|
||||||
"cacache": "^15.0.5",
|
|
||||||
"cache-base": "^4.0.0",
|
|
||||||
"cache-loader": "^4.1.0",
|
|
||||||
"call-me-maybe": "^1.0.1",
|
|
||||||
"caller-callsite": "^4.1.0",
|
|
||||||
"caller-path": "^3.0.0",
|
|
||||||
"callsites": "^3.1.0",
|
|
||||||
"camel-case": "^4.1.1",
|
|
||||||
"camelcase": "^6.0.0",
|
|
||||||
"caniuse-api": "^3.0.0",
|
|
||||||
"caniuse-lite": "^1.0.30001111",
|
|
||||||
"caseless": "^0.12.0",
|
|
||||||
"chalk": "^4.1.0",
|
|
||||||
"chokidar": "^3.4.1",
|
|
||||||
"chownr": "^2.0.0",
|
|
||||||
"chrome-trace-event": "^1.0.2",
|
|
||||||
"ci-info": "^2.0.0",
|
|
||||||
"cipher-base": "^1.0.4",
|
|
||||||
"class-utils": "^0.3.6",
|
|
||||||
"clean-css": "^4.2.3",
|
|
||||||
"clipboard": "^2.0.6",
|
|
||||||
"cliui": "^6.0.0",
|
|
||||||
"coa": "^2.0.2",
|
|
||||||
"code-point-at": "^1.1.0",
|
|
||||||
"collection-visit": "^1.0.0",
|
|
||||||
"color": "^3.1.2",
|
|
||||||
"color-convert": "^2.0.1",
|
|
||||||
"color-name": "^1.1.4",
|
|
||||||
"color-string": "^1.5.3",
|
|
||||||
"combined-stream": "^1.0.8",
|
|
||||||
"commander": "^6.0.0",
|
|
||||||
"commondir": "^1.0.1",
|
|
||||||
"component-emitter": "^1.3.0",
|
|
||||||
"compressible": "^2.0.18",
|
|
||||||
"compression": "^1.7.4",
|
|
||||||
"concat-map": "^0.0.1",
|
|
||||||
"concat-stream": "^2.0.0",
|
|
||||||
"connect-history-api-fallback": "^1.6.0",
|
|
||||||
"consola": "^2.15.0",
|
|
||||||
"console-browserify": "^1.2.0",
|
|
||||||
"consolidate": "^0.15.1",
|
|
||||||
"constants-browserify": "^1.0.0",
|
|
||||||
"content-disposition": "^0.5.3",
|
|
||||||
"content-type": "^1.0.4",
|
|
||||||
"convert-source-map": "^1.7.0",
|
|
||||||
"cookie": "^0.4.1",
|
|
||||||
"cookie-signature": "^1.1.0",
|
|
||||||
"copy-concurrently": "^1.0.5",
|
|
||||||
"copy-descriptor": "^0.1.1",
|
|
||||||
"copy-webpack-plugin": "^6.0.3",
|
|
||||||
"core-js": "^3.6.5",
|
|
||||||
"core-util-is": "^1.0.2",
|
|
||||||
"cosmiconfig": "^7.0.0",
|
|
||||||
"create-ecdh": "^4.0.4",
|
|
||||||
"create-hash": "^1.2.0",
|
|
||||||
"create-hmac": "^1.1.7",
|
|
||||||
"cross-spawn": "^7.0.3",
|
|
||||||
"crypto-browserify": "^3.12.0",
|
|
||||||
"css": "^3.0.0",
|
|
||||||
"css-color-names": "^1.0.1",
|
|
||||||
"css-declaration-sorter": "^5.1.2",
|
|
||||||
"css-loader": "^4.2.0",
|
|
||||||
"css-parse": "^2.0.0",
|
|
||||||
"css-select": "^2.1.0",
|
|
||||||
"css-select-base-adapter": "^0.1.1",
|
|
||||||
"css-tree": "^1.0.0-alpha.39",
|
|
||||||
"css-unit-converter": "^1.1.2",
|
|
||||||
"css-what": "^5.0.1",
|
|
||||||
"cssesc": "^3.0.0",
|
|
||||||
"cssnano": "^4.1.10",
|
|
||||||
"cssnano-preset-default": "^4.0.7",
|
|
||||||
"cssnano-util-get-arguments": "^4.0.0",
|
|
||||||
"cssnano-util-get-match": "^4.0.0",
|
|
||||||
"cssnano-util-raw-cache": "^4.0.1",
|
|
||||||
"cssnano-util-same-parent": "^4.0.1",
|
|
||||||
"csso": "^4.0.3",
|
|
||||||
"cyclist": "^1.0.1",
|
|
||||||
"dashdash": "^1.14.1",
|
|
||||||
"de-indent": "^1.0.2",
|
|
||||||
"debug": "^4.1.1",
|
|
||||||
"decamelize": "^4.0.0",
|
|
||||||
"decode-uri-component": "^0.2.0",
|
|
||||||
"deep-equal": "^2.0.3",
|
|
||||||
"deepmerge": "^4.2.2",
|
|
||||||
"default-gateway": "^6.0.1",
|
|
||||||
"define-properties": "^1.1.3",
|
|
||||||
"define-property": "^2.0.2",
|
|
||||||
"del": "^5.1.0",
|
|
||||||
"delayed-stream": "^1.0.0",
|
|
||||||
"delegate": "^3.2.0",
|
|
||||||
"depd": "^2.0.0",
|
|
||||||
"des.js": "^1.0.1",
|
|
||||||
"destroy": "^1.0.4",
|
|
||||||
"detect-node": "^2.0.4",
|
|
||||||
"diacritics": "^1.3.0",
|
|
||||||
"diffie-hellman": "^5.0.3",
|
|
||||||
"dir-glob": "^3.0.1",
|
|
||||||
"dns-equal": "^1.0.0",
|
|
||||||
"dns-packet": "^5.2.1",
|
|
||||||
"dns-txt": "^2.0.2",
|
|
||||||
"docsearch.js": "^2.6.3",
|
|
||||||
"dom-converter": "^0.2.0",
|
|
||||||
"dom-serializer": "^1.0.1",
|
|
||||||
"dom-walk": "^0.1.2",
|
|
||||||
"domain-browser": "^4.16.0",
|
|
||||||
"domelementtype": "^2.0.1",
|
|
||||||
"domhandler": "^3.0.0",
|
|
||||||
"domutils": "^2.1.0",
|
|
||||||
"dot-prop": "^5.2.0",
|
|
||||||
"duplexify": "^4.1.1",
|
|
||||||
"ecc-jsbn": "^0.2.0",
|
|
||||||
"ee-first": "^1.1.1",
|
|
||||||
"electron-to-chromium": "^1.3.522",
|
|
||||||
"elliptic": "^6.5.3",
|
|
||||||
"emoji-regex": "^9.0.0",
|
|
||||||
"emojis-list": "^3.0.0",
|
|
||||||
"encodeurl": "^1.0.2",
|
|
||||||
"end-of-stream": "^1.4.4",
|
|
||||||
"enhanced-resolve": "^4.3.0",
|
|
||||||
"entities": "^2.0.3",
|
|
||||||
"envify": "^4.1.0",
|
|
||||||
"envinfo": "^7.7.2",
|
|
||||||
"errno": "^0.1.7",
|
|
||||||
"error-ex": "^1.3.2",
|
|
||||||
"es-abstract": "^1.17.6",
|
|
||||||
"es-to-primitive": "^1.2.1",
|
|
||||||
"es6-promise": "^4.2.8",
|
|
||||||
"escape-html": "^1.0.3",
|
|
||||||
"escape-string-regexp": "^4.0.0",
|
|
||||||
"eslint-scope": "^5.1.0",
|
|
||||||
"esprima": "^4.0.1",
|
|
||||||
"esrecurse": "^4.2.1",
|
|
||||||
"estraverse": "^5.2.0",
|
|
||||||
"esutils": "^2.0.3",
|
|
||||||
"etag": "^1.8.1",
|
|
||||||
"eventemitter3": "^4.0.4",
|
|
||||||
"events": "^3.2.0",
|
|
||||||
"eventsource": "^2.0.2",
|
|
||||||
"evp_bytestokey": "^1.0.3",
|
|
||||||
"execa": "^4.0.3",
|
|
||||||
"expand-brackets": "^4.0.0",
|
|
||||||
"express": "^4.17.1",
|
|
||||||
"extend": "^3.0.2",
|
|
||||||
"extend-shallow": "^3.0.2",
|
|
||||||
"extglob": "^3.0.0",
|
|
||||||
"extsprintf": "^1.4.0",
|
|
||||||
"fast-deep-equal": "^3.1.3",
|
|
||||||
"fast-glob": "^3.2.4",
|
|
||||||
"fast-json-stable-stringify": "^2.1.0",
|
|
||||||
"faye-websocket": "^0.11.3",
|
|
||||||
"figgy-pudding": "^3.5.2",
|
|
||||||
"figures": "^3.2.0",
|
|
||||||
"file-loader": "^6.0.0",
|
|
||||||
"fill-range": "^7.0.1",
|
|
||||||
"finalhandler": "^1.1.2",
|
|
||||||
"find-babel-config": "^1.2.0",
|
|
||||||
"find-cache-dir": "^3.3.1",
|
|
||||||
"find-up": "^4.1.0",
|
|
||||||
"flush-write-stream": "^2.0.0",
|
|
||||||
"follow-redirects": "^1.12.1",
|
|
||||||
"for-in": "^1.0.2",
|
|
||||||
"foreach": "^2.0.5",
|
|
||||||
"forever-agent": "^0.6.1",
|
|
||||||
"form-data": "^3.0.0",
|
|
||||||
"forwarded": "^0.1.2",
|
|
||||||
"fragment-cache": "^0.2.1",
|
|
||||||
"fresh": "^0.5.2",
|
|
||||||
"from2": "^2.3.0",
|
|
||||||
"fs-extra": "^9.0.1",
|
|
||||||
"fs-write-stream-atomic": "^1.0.10",
|
|
||||||
"fs.realpath": "^1.0.0",
|
|
||||||
"function-bind": "^1.1.1",
|
|
||||||
"gensync": "^1.0.0-beta.1",
|
|
||||||
"get-caller-file": "^2.0.5",
|
|
||||||
"get-stream": "^5.1.0",
|
|
||||||
"get-value": "^3.0.1",
|
|
||||||
"getpass": "^0.1.7",
|
|
||||||
"glob": "^7.1.6",
|
|
||||||
"glob-parent": "^5.1.1",
|
|
||||||
"glob-to-regexp": "^0.4.1",
|
|
||||||
"global": "^4.4.0",
|
|
||||||
"globals": "^13.1.0",
|
|
||||||
"globby": "^11.0.1",
|
|
||||||
"good-listener": "^1.2.2",
|
|
||||||
"graceful-fs": "^4.2.4",
|
|
||||||
"gray-matter": "^4.0.2",
|
|
||||||
"handle-thing": "^2.0.1",
|
|
||||||
"har-schema": "^2.0.0",
|
|
||||||
"har-validator": "^5.1.5",
|
|
||||||
"has": "^1.0.3",
|
|
||||||
"has-ansi": "^4.0.0",
|
|
||||||
"has-flag": "^4.0.0",
|
|
||||||
"has-symbols": "^1.0.1",
|
|
||||||
"has-value": "^2.0.2",
|
|
||||||
"has-values": "^2.0.1",
|
|
||||||
"hash-base": "^3.1.0",
|
|
||||||
"hash-sum": "^2.0.0",
|
|
||||||
"hash.js": "^1.1.7",
|
|
||||||
"he": "^1.2.0",
|
|
||||||
"hex-color-regex": "^1.1.0",
|
|
||||||
"hmac-drbg": "^1.0.1",
|
|
||||||
"hogan.js": "^3.0.2",
|
|
||||||
"hpack.js": "^2.1.6",
|
|
||||||
"hsl-regex": "^1.0.0",
|
|
||||||
"hsla-regex": "^1.0.0",
|
|
||||||
"html-comment-regex": "^1.1.2",
|
|
||||||
"html-entities": "^1.3.1",
|
|
||||||
"html-minifier": "^4.0.0",
|
|
||||||
"html-tags": "^3.1.0",
|
|
||||||
"htmlparser2": "^4.1.0",
|
|
||||||
"http-deceiver": "^1.2.7",
|
|
||||||
"http-errors": "^1.8.0",
|
|
||||||
"http-parser-js": "^0.5.2",
|
|
||||||
"http-proxy": "^1.18.1",
|
|
||||||
"http-proxy-middleware": "^1.0.5",
|
|
||||||
"http-signature": "^1.3.4",
|
|
||||||
"https-browserify": "^1.0.0",
|
|
||||||
"iconv-lite": "^0.6.2",
|
|
||||||
"icss-replace-symbols": "^1.1.0",
|
|
||||||
"icss-utils": "^4.1.1",
|
|
||||||
"ieee754": "^1.1.13",
|
|
||||||
"iferr": "^1.0.2",
|
|
||||||
"ignore": "^5.1.8",
|
|
||||||
"immediate": "^3.3.0",
|
|
||||||
"import-cwd": "^3.0.0",
|
|
||||||
"import-fresh": "^3.2.1",
|
|
||||||
"import-from": "^3.0.0",
|
|
||||||
"import-local": "^3.0.2",
|
|
||||||
"imurmurhash": "^0.1.4",
|
|
||||||
"indexes-of": "^1.0.1",
|
|
||||||
"infer-owner": "^1.0.4",
|
|
||||||
"inflight": "^1.0.6",
|
|
||||||
"inherits": "^2.0.4",
|
|
||||||
"internal-ip": "^6.1.0",
|
|
||||||
"invariant": "^2.2.4",
|
|
||||||
"invert-kv": "^3.0.1",
|
|
||||||
"ip": "^1.1.5",
|
|
||||||
"ip-regex": "^4.1.0",
|
|
||||||
"ipaddr.js": "^1.9.1",
|
|
||||||
"is-absolute-url": "^3.0.3",
|
|
||||||
"is-accessor-descriptor": "^3.0.1",
|
|
||||||
"is-arguments": "^1.0.4",
|
|
||||||
"is-arrayish": "^0.3.2",
|
|
||||||
"is-binary-path": "^2.1.0",
|
|
||||||
"is-buffer": "^2.0.4",
|
|
||||||
"is-callable": "^1.2.0",
|
|
||||||
"is-color-stop": "^1.1.0",
|
|
||||||
"is-data-descriptor": "^2.0.0",
|
|
||||||
"is-date-object": "^1.0.2",
|
|
||||||
"is-descriptor": "^3.0.0",
|
|
||||||
"is-directory": "^0.3.1",
|
|
||||||
"is-extendable": "^1.0.1",
|
|
||||||
"is-extglob": "^2.1.1",
|
|
||||||
"is-fullwidth-code-point": "^3.0.0",
|
|
||||||
"is-glob": "^4.0.1",
|
|
||||||
"is-number": "^7.0.0",
|
|
||||||
"is-obj": "^2.0.0",
|
|
||||||
"is-path-cwd": "^2.2.0",
|
|
||||||
"is-path-in-cwd": "^3.0.0",
|
|
||||||
"is-path-inside": "^3.0.2",
|
|
||||||
"is-plain-obj": "^2.1.0",
|
|
||||||
"is-plain-object": "^4.1.1",
|
|
||||||
"is-regex": "^1.1.1",
|
|
||||||
"is-resolvable": "^1.1.0",
|
|
||||||
"is-stream": "^2.0.0",
|
|
||||||
"is-svg": "^4.2.1",
|
|
||||||
"is-symbol": "^1.0.3",
|
|
||||||
"is-typedarray": "^1.0.0",
|
|
||||||
"is-windows": "^1.0.2",
|
|
||||||
"is-wsl": "^2.2.0",
|
|
||||||
"isarray": "^2.0.5",
|
|
||||||
"isexe": "^2.0.0",
|
|
||||||
"isobject": "^4.0.0",
|
|
||||||
"isstream": "^0.1.2",
|
|
||||||
"javascript-stringify": "^2.0.1",
|
|
||||||
"js-levenshtein": "^1.1.6",
|
|
||||||
"js-tokens": "^6.0.0",
|
|
||||||
"js-yaml": "^3.14.0",
|
|
||||||
"jsbn": "^1.1.0",
|
|
||||||
"jsesc": "^3.0.1",
|
|
||||||
"json-parse-better-errors": "^1.0.2",
|
|
||||||
"json-schema": "^0.4.0",
|
|
||||||
"json-schema-traverse": "^0.4.1",
|
|
||||||
"json-stringify-safe": "^5.0.1",
|
|
||||||
"json3": "^3.3.3",
|
|
||||||
"json5": "^2.1.3",
|
|
||||||
"jsonfile": "^6.0.1",
|
|
||||||
"jsprim": "^2.0.0",
|
|
||||||
"killable": "^1.0.1",
|
|
||||||
"kind-of": "^6.0.3",
|
|
||||||
"last-call-webpack-plugin": "^3.0.0",
|
|
||||||
"lcid": "^3.1.1",
|
|
||||||
"linkify-it": "^3.0.2",
|
|
||||||
"load-script": "^1.0.0",
|
|
||||||
"loader-runner": "^4.0.0",
|
|
||||||
"loader-utils": "^2.0.0",
|
|
||||||
"locate-path": "^5.0.0",
|
|
||||||
"lodash": "^4.17.19",
|
|
||||||
"lodash._reinterpolate": "^3.0.0",
|
|
||||||
"lodash.chunk": "^4.2.0",
|
|
||||||
"lodash.clonedeep": "^4.5.0",
|
|
||||||
"lodash.debounce": "^4.0.8",
|
|
||||||
"lodash.kebabcase": "^4.1.1",
|
|
||||||
"lodash.memoize": "^4.1.2",
|
|
||||||
"lodash.padstart": "^4.6.1",
|
|
||||||
"lodash.sortby": "^4.7.0",
|
|
||||||
"lodash.template": "^4.5.0",
|
|
||||||
"lodash.templatesettings": "^4.2.0",
|
|
||||||
"lodash.uniq": "^4.5.0",
|
|
||||||
"loglevel": "^1.6.8",
|
|
||||||
"loose-envify": "^1.4.0",
|
|
||||||
"lower-case": "^2.0.1",
|
|
||||||
"lru-cache": "^6.0.0",
|
|
||||||
"make-dir": "^3.1.0",
|
|
||||||
"mamacro": "^0.0.7",
|
|
||||||
"map-age-cleaner": "^0.1.3",
|
|
||||||
"map-cache": "^0.2.2",
|
|
||||||
"map-visit": "^1.0.0",
|
|
||||||
"markdown-it": "^12.3.2",
|
|
||||||
"markdown-it-anchor": "^5.3.0",
|
|
||||||
"markdown-it-chain": "^1.3.0",
|
|
||||||
"markdown-it-container": "^3.0.0",
|
|
||||||
"markdown-it-emoji": "^1.4.0",
|
|
||||||
"markdown-it-table-of-contents": "^0.4.4",
|
|
||||||
"md5.js": "^1.3.5",
|
|
||||||
"mdn-data": "^2.0.11",
|
|
||||||
"mdurl": "^1.0.1",
|
|
||||||
"media-typer": "^1.1.0",
|
|
||||||
"mem": "^6.1.0",
|
|
||||||
"memory-fs": "^0.5.0",
|
|
||||||
"merge-descriptors": "^1.0.1",
|
|
||||||
"merge-source-map": "^1.1.0",
|
|
||||||
"merge2": "^1.4.1",
|
|
||||||
"methods": "^1.1.2",
|
|
||||||
"micromatch": "^4.0.2",
|
|
||||||
"miller-rabin": "^4.0.1",
|
|
||||||
"mime": "^2.4.6",
|
|
||||||
"mime-db": "^1.44.0",
|
|
||||||
"mime-types": "^2.1.27",
|
|
||||||
"mimic-fn": "^3.1.0",
|
|
||||||
"min-document": "^2.19.0",
|
|
||||||
"mini-css-extract-plugin": "^0.9.0",
|
|
||||||
"minimalistic-assert": "^1.0.1",
|
|
||||||
"minimalistic-crypto-utils": "^1.0.1",
|
|
||||||
"minimatch": "^3.0.4",
|
|
||||||
"minimist": "^1.2.5",
|
|
||||||
"mississippi": "^4.0.0",
|
|
||||||
"mixin-deep": "^2.0.1",
|
|
||||||
"mkdirp": "^1.0.4",
|
|
||||||
"move-concurrently": "^1.0.1",
|
|
||||||
"ms": "^2.1.2",
|
|
||||||
"multicast-dns": "^7.2.2",
|
|
||||||
"multicast-dns-service-types": "^1.1.0",
|
|
||||||
"nanomatch": "^1.2.13",
|
|
||||||
"negotiator": "^0.6.2",
|
|
||||||
"neo-async": "^2.6.2",
|
|
||||||
"nice-try": "^2.0.1",
|
|
||||||
"no-case": "^3.0.3",
|
|
||||||
"node-forge": "^1.0.0",
|
|
||||||
"node-libs-browser": "^2.2.1",
|
|
||||||
"node-releases": "^1.1.60",
|
|
||||||
"nopt": "^4.0.3",
|
|
||||||
"normalize-path": "^3.0.0",
|
|
||||||
"normalize-range": "^0.1.2",
|
|
||||||
"normalize-url": "^5.1.0",
|
|
||||||
"npm-run-path": "^4.0.1",
|
|
||||||
"nprogress": "^0.2.0",
|
|
||||||
"nth-check": "^2.0.1",
|
|
||||||
"num2fraction": "^1.2.2",
|
|
||||||
"number-is-nan": "^2.0.0",
|
|
||||||
"oauth-sign": "^0.9.0",
|
|
||||||
"object-assign": "^4.1.1",
|
|
||||||
"object-copy": "^1.0.0",
|
|
||||||
"object-inspect": "^1.8.0",
|
|
||||||
"object-is": "^1.1.2",
|
|
||||||
"object-keys": "^1.1.1",
|
|
||||||
"object-visit": "^1.0.1",
|
|
||||||
"object.assign": "^4.1.0",
|
|
||||||
"object.getownpropertydescriptors": "^2.1.0",
|
|
||||||
"object.pick": "^1.3.0",
|
|
||||||
"object.values": "^1.1.1",
|
|
||||||
"obuf": "^1.1.2",
|
|
||||||
"on-finished": "^2.3.0",
|
|
||||||
"on-headers": "^1.0.2",
|
|
||||||
"once": "^1.4.0",
|
|
||||||
"opencollective-postinstall": "^2.0.3",
|
|
||||||
"opn": "^6.0.0",
|
|
||||||
"optimize-css-assets-webpack-plugin": "^5.0.3",
|
|
||||||
"original": "^1.0.2",
|
|
||||||
"os-browserify": "^0.3.0",
|
|
||||||
"os-locale": "^5.0.0",
|
|
||||||
"p-defer": "^3.0.0",
|
|
||||||
"p-finally": "^2.0.1",
|
|
||||||
"p-is-promise": "^3.0.0",
|
|
||||||
"p-limit": "^3.0.2",
|
|
||||||
"p-locate": "^4.1.0",
|
|
||||||
"p-map": "^4.0.0",
|
|
||||||
"p-retry": "^4.2.0",
|
|
||||||
"p-try": "^2.2.0",
|
|
||||||
"pako": "^1.0.11",
|
|
||||||
"parallel-transform": "^1.2.0",
|
|
||||||
"param-case": "^3.0.3",
|
|
||||||
"parse-asn1": "^5.1.5",
|
|
||||||
"parse-json": "^5.0.1",
|
|
||||||
"parseurl": "^1.3.3",
|
|
||||||
"pascalcase": "^1.0.0",
|
|
||||||
"path-browserify": "^1.0.1",
|
|
||||||
"path-dirname": "^1.0.2",
|
|
||||||
"path-exists": "^4.0.0",
|
|
||||||
"path-is-absolute": "^2.0.0",
|
|
||||||
"path-is-inside": "^1.0.2",
|
|
||||||
"path-key": "^3.1.1",
|
|
||||||
"path-parse": "^1.0.6",
|
|
||||||
"path-to-regexp": "^6.1.0",
|
|
||||||
"path-type": "^4.0.0",
|
|
||||||
"pbkdf2": "^3.1.1",
|
|
||||||
"performance-now": "^2.1.0",
|
|
||||||
"pify": "^5.0.0",
|
|
||||||
"pinkie": "^2.0.4",
|
|
||||||
"pinkie-promise": "^2.0.1",
|
|
||||||
"pkg-dir": "^4.2.0",
|
|
||||||
"pkg-up": "^3.1.0",
|
|
||||||
"portfinder": "^1.0.28",
|
|
||||||
"posix-character-classes": "^1.0.0",
|
|
||||||
"postcss": "^8.2.10",
|
|
||||||
"postcss-calc": "^7.0.2",
|
|
||||||
"postcss-colormin": "^4.0.3",
|
|
||||||
"postcss-convert-values": "^4.0.1",
|
|
||||||
"postcss-discard-comments": "^4.0.2",
|
|
||||||
"postcss-discard-duplicates": "^4.0.2",
|
|
||||||
"postcss-discard-empty": "^4.0.1",
|
|
||||||
"postcss-discard-overridden": "^4.0.1",
|
|
||||||
"postcss-load-config": "^2.1.0",
|
|
||||||
"postcss-loader": "^3.0.0",
|
|
||||||
"postcss-merge-longhand": "^4.0.11",
|
|
||||||
"postcss-merge-rules": "^4.0.3",
|
|
||||||
"postcss-minify-font-values": "^4.0.2",
|
|
||||||
"postcss-minify-gradients": "^4.0.2",
|
|
||||||
"postcss-minify-params": "^4.0.2",
|
|
||||||
"postcss-minify-selectors": "^4.0.2",
|
|
||||||
"postcss-modules-extract-imports": "^2.0.0",
|
|
||||||
"postcss-modules-local-by-default": "^3.0.3",
|
|
||||||
"postcss-modules-scope": "^2.2.0",
|
|
||||||
"postcss-modules-values": "^3.0.0",
|
|
||||||
"postcss-normalize-charset": "^4.0.1",
|
|
||||||
"postcss-normalize-display-values": "^4.0.2",
|
|
||||||
"postcss-normalize-positions": "^4.0.2",
|
|
||||||
"postcss-normalize-repeat-style": "^4.0.2",
|
|
||||||
"postcss-normalize-string": "^4.0.2",
|
|
||||||
"postcss-normalize-timing-functions": "^4.0.2",
|
|
||||||
"postcss-normalize-unicode": "^4.0.1",
|
|
||||||
"postcss-normalize-url": "^4.0.1",
|
|
||||||
"postcss-normalize-whitespace": "^4.0.2",
|
|
||||||
"postcss-ordered-values": "^4.1.2",
|
|
||||||
"postcss-reduce-initial": "^4.0.3",
|
|
||||||
"postcss-reduce-transforms": "^4.0.2",
|
|
||||||
"postcss-safe-parser": "^4.0.2",
|
|
||||||
"postcss-selector-parser": "^6.0.2",
|
|
||||||
"postcss-svgo": "^4.0.2",
|
|
||||||
"postcss-unique-selectors": "^4.0.1",
|
|
||||||
"postcss-value-parser": "^4.1.0",
|
|
||||||
"prepend-http": "^3.0.1",
|
|
||||||
"prettier": "^2.0.5",
|
|
||||||
"pretty-error": "^2.1.1",
|
|
||||||
"pretty-time": "^1.1.0",
|
|
||||||
"prismjs": "^1.20.0",
|
|
||||||
"private": "^0.1.8",
|
|
||||||
"process": "^0.11.10",
|
|
||||||
"process-nextick-args": "^2.0.1",
|
|
||||||
"promise-inflight": "^1.0.1",
|
|
||||||
"proxy-addr": "^2.0.6",
|
|
||||||
"prr": "^1.0.1",
|
|
||||||
"pseudomap": "^1.0.2",
|
|
||||||
"psl": "^1.8.0",
|
|
||||||
"public-encrypt": "^4.0.3",
|
|
||||||
"pump": "^3.0.0",
|
|
||||||
"pumpify": "^2.0.1",
|
|
||||||
"punycode": "^2.1.1",
|
|
||||||
"q": "^1.5.1",
|
|
||||||
"qs": "^6.9.4",
|
|
||||||
"query-string": "^6.13.1",
|
|
||||||
"querystring": "^0.2.0",
|
|
||||||
"querystring-es3": "^0.2.1",
|
|
||||||
"querystringify": "^2.1.1",
|
|
||||||
"randombytes": "^2.1.0",
|
|
||||||
"randomfill": "^1.0.4",
|
|
||||||
"range-parser": "^1.2.1",
|
|
||||||
"raw-body": "^2.4.1",
|
|
||||||
"readable-stream": "^3.6.0",
|
|
||||||
"readdirp": "^3.4.0",
|
|
||||||
"reduce": "^1.0.2",
|
|
||||||
"regenerate": "^1.4.1",
|
|
||||||
"regenerate-unicode-properties": "^8.2.0",
|
|
||||||
"regenerator-runtime": "^0.13.7",
|
|
||||||
"regenerator-transform": "^0.14.5",
|
|
||||||
"regex-not": "^1.0.2",
|
|
||||||
"regexp.prototype.flags": "^1.3.0",
|
|
||||||
"regexpu-core": "^4.7.0",
|
|
||||||
"regjsgen": "^0.5.2",
|
|
||||||
"regjsparser": "^0.6.4",
|
|
||||||
"relateurl": "^0.2.7",
|
|
||||||
"remove-trailing-separator": "^1.1.0",
|
|
||||||
"renderkid": "^2.0.3",
|
|
||||||
"repeat-element": "^1.1.3",
|
|
||||||
"repeat-string": "^1.6.1",
|
|
||||||
"request": "^2.88.2",
|
|
||||||
"require-directory": "^2.1.1",
|
|
||||||
"require-main-filename": "^2.0.0",
|
|
||||||
"requires-port": "^1.0.0",
|
|
||||||
"reselect": "^4.0.0",
|
|
||||||
"resolve": "^1.17.0",
|
|
||||||
"resolve-cwd": "^3.0.0",
|
|
||||||
"resolve-from": "^5.0.0",
|
|
||||||
"resolve-url": "^0.2.1",
|
|
||||||
"ret": "^0.3.1",
|
|
||||||
"retry": "^0.12.0",
|
|
||||||
"rgb-regex": "^1.0.1",
|
|
||||||
"rgba-regex": "^1.0.0",
|
|
||||||
"rimraf": "^3.0.2",
|
|
||||||
"ripemd160": "^2.0.2",
|
|
||||||
"run-queue": "^2.0.1",
|
|
||||||
"safe-buffer": "^5.2.1",
|
|
||||||
"safe-regex": "^2.1.1",
|
|
||||||
"safer-buffer": "^2.1.2",
|
|
||||||
"sax": "^1.2.4",
|
|
||||||
"schema-utils": "^2.7.0",
|
|
||||||
"section-matter": "^1.0.0",
|
|
||||||
"select": "^1.1.2",
|
|
||||||
"select-hose": "^2.0.0",
|
|
||||||
"selfsigned": "^1.10.7",
|
|
||||||
"semver": "^7.3.2",
|
|
||||||
"send": "^0.17.1",
|
|
||||||
"serialize-javascript": "^4.0.0",
|
|
||||||
"serve-index": "^1.9.1",
|
|
||||||
"serve-static": "^1.14.1",
|
|
||||||
"set-blocking": "^2.0.0",
|
|
||||||
"set-value": "^4.0.1",
|
|
||||||
"setimmediate": "^1.0.5",
|
|
||||||
"setprototypeof": "^1.2.0",
|
|
||||||
"sha.js": "^2.4.11",
|
|
||||||
"shebang-command": "^2.0.0",
|
|
||||||
"shebang-regex": "^3.0.0",
|
|
||||||
"signal-exit": "^3.0.3",
|
|
||||||
"simple-swizzle": "^0.2.2",
|
|
||||||
"sitemap": "^6.2.0",
|
|
||||||
"slash": "^3.0.0",
|
|
||||||
"smoothscroll-polyfill": "^0.4.4",
|
|
||||||
"snapdragon": "^0.12.0",
|
|
||||||
"snapdragon-node": "^3.0.0",
|
|
||||||
"snapdragon-util": "^5.0.1",
|
|
||||||
"sockjs": "^0.3.21",
|
|
||||||
"sockjs-client": "^1.5.0",
|
|
||||||
"sort-keys": "^4.0.0",
|
|
||||||
"source-list-map": "^2.0.1",
|
|
||||||
"source-map": "^0.7.3",
|
|
||||||
"source-map-resolve": "^0.6.0",
|
|
||||||
"source-map-support": "^0.5.19",
|
|
||||||
"source-map-url": "^0.4.0",
|
|
||||||
"spdy": "^4.0.2",
|
|
||||||
"spdy-transport": "^3.0.0",
|
|
||||||
"split-string": "^6.1.0",
|
|
||||||
"sprintf-js": "^1.1.2",
|
|
||||||
"sshpk": "^1.16.1",
|
|
||||||
"ssri": "^8.0.0",
|
|
||||||
"stable": "^0.1.8",
|
|
||||||
"stack-utils": "^2.0.2",
|
|
||||||
"static-extend": "^0.1.2",
|
|
||||||
"statuses": "^2.0.0",
|
|
||||||
"std-env": "^2.2.1",
|
|
||||||
"stream-browserify": "^3.0.0",
|
|
||||||
"stream-each": "^1.2.3",
|
|
||||||
"stream-http": "^3.1.1",
|
|
||||||
"stream-shift": "^1.0.1",
|
|
||||||
"strict-uri-encode": "^2.0.0",
|
|
||||||
"string-width": "^4.2.0",
|
|
||||||
"string.prototype.trimleft": "^2.1.2",
|
|
||||||
"string.prototype.trimright": "^2.1.2",
|
|
||||||
"string_decoder": "^1.3.0",
|
|
||||||
"strip-ansi": "^6.0.0",
|
|
||||||
"strip-bom-string": "^1.0.0",
|
|
||||||
"strip-eof": "^2.0.0",
|
|
||||||
"stylehacks": "^4.0.3",
|
|
||||||
"stylus": "^0.54.8",
|
|
||||||
"stylus-loader": "^3.0.2",
|
|
||||||
"supports-color": "^7.1.0",
|
|
||||||
"svg-tags": "^1.0.0",
|
|
||||||
"svgo": "^1.3.2",
|
|
||||||
"tapable": "^1.1.3",
|
|
||||||
"terser": "^5.0.0",
|
|
||||||
"terser-webpack-plugin": "^4.0.0",
|
|
||||||
"text-table": "^0.2.0",
|
|
||||||
"through": "^2.3.8",
|
|
||||||
"through2": "^4.0.2",
|
|
||||||
"thunky": "^1.1.0",
|
|
||||||
"timers-browserify": "^2.0.11",
|
|
||||||
"timsort": "^0.3.0",
|
|
||||||
"tiny-emitter": "^2.1.0",
|
|
||||||
"to-arraybuffer": "^1.0.1",
|
|
||||||
"to-factory": "^1.0.0",
|
|
||||||
"to-fast-properties": "^3.0.1",
|
|
||||||
"to-object-path": "^0.3.0",
|
|
||||||
"to-regex": "^3.0.2",
|
|
||||||
"to-regex-range": "^5.0.1",
|
|
||||||
"toidentifier": "^1.0.0",
|
|
||||||
"toml": "^3.0.0",
|
|
||||||
"toposort": "^2.0.2",
|
|
||||||
"tough-cookie": "^4.0.0",
|
|
||||||
"tr46": "^2.0.2",
|
|
||||||
"tslib": "^2.0.0",
|
|
||||||
"tty-browserify": "^0.0.1",
|
|
||||||
"tunnel-agent": "^0.6.0",
|
|
||||||
"tweetnacl": "^1.0.3",
|
|
||||||
"type-fest": "^0.16.0",
|
|
||||||
"type-is": "^1.6.18",
|
|
||||||
"typedarray": "^0.0.6",
|
|
||||||
"uc.micro": "^1.0.6",
|
|
||||||
"uglify-js": "^3.10.1",
|
|
||||||
"unicode-canonical-property-names-ecmascript": "^1.0.4",
|
|
||||||
"unicode-match-property-ecmascript": "^1.0.4",
|
|
||||||
"unicode-match-property-value-ecmascript": "^1.2.0",
|
|
||||||
"unicode-property-aliases-ecmascript": "^1.1.0",
|
|
||||||
"union-value": "^2.0.1",
|
|
||||||
"uniq": "^1.0.1",
|
|
||||||
"uniqs": "^2.0.0",
|
|
||||||
"unique-filename": "^1.1.1",
|
|
||||||
"unique-slug": "^2.0.2",
|
|
||||||
"universalify": "^2.0.0",
|
|
||||||
"unpipe": "^1.0.0",
|
|
||||||
"unquote": "^1.1.1",
|
|
||||||
"unset-value": "^1.0.0",
|
|
||||||
"upath": "^1.2.0",
|
|
||||||
"upper-case": "^2.0.1",
|
|
||||||
"uri-js": "^4.2.2",
|
|
||||||
"urix": "^0.1.0",
|
|
||||||
"url": "^0.11.0",
|
|
||||||
"url-loader": "^4.1.0",
|
|
||||||
"url-parse": "^1.4.7",
|
|
||||||
"use": "^3.1.1",
|
|
||||||
"util": "^0.12.3",
|
|
||||||
"util-deprecate": "^1.0.2",
|
|
||||||
"util.promisify": "^1.0.1",
|
|
||||||
"utila": "^0.4.0",
|
|
||||||
"utils-merge": "^1.0.1",
|
|
||||||
"uuid": "^8.3.0",
|
|
||||||
"vary": "^1.1.2",
|
|
||||||
"vendors": "^1.0.4",
|
|
||||||
"verror": "^1.10.0",
|
|
||||||
"vm-browserify": "^1.1.2",
|
|
||||||
"vue": "^2.6.11",
|
|
||||||
"vue-hot-reload-api": "^2.3.4",
|
|
||||||
"vue-loader": "^15.9.3",
|
|
||||||
"vue-router": "^3.4.0",
|
|
||||||
"vue-server-renderer": "^2.6.11",
|
|
||||||
"vue-style-loader": "^4.1.2",
|
|
||||||
"vue-template-compiler": "^2.6.11",
|
|
||||||
"vue-template-es2015-compiler": "^1.9.1",
|
|
||||||
"vuepress": "^1.5.3",
|
|
||||||
"vuepress-html-webpack-plugin": "^3.2.0",
|
|
||||||
"vuepress-plugin-container": "^2.1.4",
|
|
||||||
"vuepress-plugin-sitemap": "^2.3.1",
|
|
||||||
"vuepress-plugin-smooth-scroll": "^0.0.9",
|
|
||||||
"vuepress-plugin-zooming": "^1.1.7",
|
|
||||||
"watchpack": "^1.7.4",
|
|
||||||
"wbuf": "^1.7.3",
|
|
||||||
"webidl-conversions": "^6.1.0",
|
|
||||||
"webpack": "^4.44.1",
|
|
||||||
"webpack-chain": "^6.5.1",
|
|
||||||
"webpack-dev-middleware": "^3.7.2",
|
|
||||||
"webpack-dev-server": "^3.11.0",
|
|
||||||
"webpack-log": "^3.0.1",
|
|
||||||
"webpack-merge": "^5.1.1",
|
|
||||||
"webpack-sources": "^1.4.3",
|
|
||||||
"webpackbar": "^4.0.0",
|
|
||||||
"websocket-driver": "^0.7.4",
|
|
||||||
"websocket-extensions": "^0.1.4",
|
|
||||||
"whatwg-url": "^8.1.0",
|
|
||||||
"when": "^3.7.8",
|
|
||||||
"which": "^2.0.2",
|
|
||||||
"which-module": "^2.0.0",
|
|
||||||
"worker-farm": "^1.7.0",
|
|
||||||
"wrap-ansi": "^7.0.0",
|
|
||||||
"wrappy": "^1.0.2",
|
|
||||||
"ws": "^7.3.1",
|
|
||||||
"xmlbuilder": "^15.1.1",
|
|
||||||
"xtend": "^4.0.2",
|
|
||||||
"y18n": "^4.0.0",
|
|
||||||
"yallist": "^4.0.0",
|
|
||||||
"yargs": "^15.4.1",
|
|
||||||
"yargs-parser": "^18.1.3",
|
|
||||||
"zepto": "^1.2.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {},
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vuepress dev",
|
"dev": "vuepress dev",
|
||||||
"build": "vuepress build"
|
"build": "vuepress build"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC"
|
"license": "ISC",
|
||||||
|
"packageManager": "yarn@4.0.2",
|
||||||
|
"dependencies": {
|
||||||
|
"@vuepress/plugin-google-analytics": "2.0.0-rc.0",
|
||||||
|
"@vuepress/plugin-search": "2.0.0-rc.0",
|
||||||
|
"@vuepress/theme-default": "^2.0.0-rc.0",
|
||||||
|
"vuepress-plugin-sitemap2": "^2.0.0-rc.5",
|
||||||
|
"vuepress-plugin-zooming": "^1.1.8"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
Create a `docker-compose.yml` file:
|
Create a `docker-compose.yml` file:
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
version: "3"
|
version: '3.8'
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
image: 'jc21/nginx-proxy-manager:latest'
|
image: 'jc21/nginx-proxy-manager:latest'
|
||||||
@ -35,7 +35,7 @@ services:
|
|||||||
Then:
|
Then:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker-compose up -d
|
docker compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
## Using MySQL / MariaDB Database
|
## Using MySQL / MariaDB Database
|
||||||
@ -51,7 +51,7 @@ are going to use.
|
|||||||
Here is an example of what your `docker-compose.yml` will look like when using a MariaDB container:
|
Here is an example of what your `docker-compose.yml` will look like when using a MariaDB container:
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
version: "3"
|
version: '3.8'
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
image: 'jc21/nginx-proxy-manager:latest'
|
image: 'jc21/nginx-proxy-manager:latest'
|
||||||
@ -64,6 +64,7 @@ services:
|
|||||||
# Add any other Stream port you want to expose
|
# Add any other Stream port you want to expose
|
||||||
# - '21:21' # FTP
|
# - '21:21' # FTP
|
||||||
environment:
|
environment:
|
||||||
|
# Mysql/Maria connection parameters:
|
||||||
DB_MYSQL_HOST: "db"
|
DB_MYSQL_HOST: "db"
|
||||||
DB_MYSQL_PORT: 3306
|
DB_MYSQL_PORT: 3306
|
||||||
DB_MYSQL_USER: "npm"
|
DB_MYSQL_USER: "npm"
|
||||||
@ -85,8 +86,9 @@ services:
|
|||||||
MYSQL_DATABASE: 'npm'
|
MYSQL_DATABASE: 'npm'
|
||||||
MYSQL_USER: 'npm'
|
MYSQL_USER: 'npm'
|
||||||
MYSQL_PASSWORD: 'npm'
|
MYSQL_PASSWORD: 'npm'
|
||||||
|
MARIADB_AUTO_UPGRADE: '1'
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/mysql:/var/lib/mysql
|
- ./mysql:/var/lib/mysql
|
||||||
```
|
```
|
||||||
|
|
||||||
::: warning
|
::: warning
|
||||||
@ -118,13 +120,12 @@ Please note that the `jc21/mariadb-aria:latest` image might have some problems o
|
|||||||
|
|
||||||
After the app is running for the first time, the following will happen:
|
After the app is running for the first time, the following will happen:
|
||||||
|
|
||||||
1. The database will initialize with table structures
|
1. JWT keys will be generated and saved in the data folder
|
||||||
2. GPG keys will be generated and saved in the configuration file
|
2. The database will initialize with table structures
|
||||||
3. A default admin user will be created
|
3. A default admin user will be created
|
||||||
|
|
||||||
This process can take a couple of minutes depending on your machine.
|
This process can take a couple of minutes depending on your machine.
|
||||||
|
|
||||||
|
|
||||||
## Default Administrator User
|
## Default Administrator User
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -134,49 +135,3 @@ Password: changeme
|
|||||||
|
|
||||||
Immediately after logging in with this default user you will be asked to modify your details and change your password.
|
Immediately after logging in with this default user you will be asked to modify your details and change your password.
|
||||||
|
|
||||||
## Configuration File
|
|
||||||
|
|
||||||
::: warning
|
|
||||||
|
|
||||||
This section is meant for advanced users
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
If you would like more control over the database settings you can define a custom config JSON file.
|
|
||||||
|
|
||||||
|
|
||||||
Here's an example for `sqlite` configuration as it is generated from the environment variables:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"database": {
|
|
||||||
"engine": "knex-native",
|
|
||||||
"knex": {
|
|
||||||
"client": "sqlite3",
|
|
||||||
"connection": {
|
|
||||||
"filename": "/data/database.sqlite"
|
|
||||||
},
|
|
||||||
"useNullAsDefault": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
You can modify the `knex` object with your custom configuration, but note that not all knex clients might be installed in the image.
|
|
||||||
|
|
||||||
Once you've created your configuration file you can mount it to `/app/config/production.json` inside you container using:
|
|
||||||
|
|
||||||
```
|
|
||||||
[...]
|
|
||||||
services:
|
|
||||||
app:
|
|
||||||
image: 'jc21/nginx-proxy-manager:latest'
|
|
||||||
[...]
|
|
||||||
volumes:
|
|
||||||
- ./config.json:/app/config/production.json
|
|
||||||
[...]
|
|
||||||
[...]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note:** After the first run of the application, the config file will be altered to include generated encryption keys unique to your installation.
|
|
||||||
These keys affect the login and session management of the application. If these keys change for any reason, all users will be logged out.
|
|
||||||
|
2
docs/third-party/README.md
vendored
2
docs/third-party/README.md
vendored
@ -7,7 +7,7 @@ Known integrations:
|
|||||||
|
|
||||||
- [HomeAssistant Hass.io plugin](https://github.com/hassio-addons/addon-nginx-proxy-manager)
|
- [HomeAssistant Hass.io plugin](https://github.com/hassio-addons/addon-nginx-proxy-manager)
|
||||||
- [UnRaid / Synology](https://github.com/jlesage/docker-nginx-proxy-manager)
|
- [UnRaid / Synology](https://github.com/jlesage/docker-nginx-proxy-manager)
|
||||||
- [Proxmox Scripts](https://github.com/ej52/proxmox-scripts/tree/main/lxc/nginx-proxy-manager)
|
- [Proxmox Scripts](https://github.com/ej52/proxmox-scripts/tree/main/apps/nginx-proxy-manager)
|
||||||
- [nginxproxymanagerGraf](https://github.com/ma-karai/nginxproxymanagerGraf)
|
- [nginxproxymanagerGraf](https://github.com/ma-karai/nginxproxymanagerGraf)
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,3 +9,4 @@ This project will automatically update any databases or other requirements so yo
|
|||||||
any crazy instructions. These steps above will pull the latest updates and recreate the docker
|
any crazy instructions. These steps above will pull the latest updates and recreate the docker
|
||||||
containers.
|
containers.
|
||||||
|
|
||||||
|
See the [list of releases](https://github.com/NginxProxyManager/nginx-proxy-manager/releases) for any upgrade steps specific to each release.
|
||||||
|
13843
docs/yarn.lock
13843
docs/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -10,6 +10,7 @@
|
|||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
<meta name="HandheldFriendly" content="True">
|
<meta name="HandheldFriendly" content="True">
|
||||||
<meta name="MobileOptimized" content="320">
|
<meta name="MobileOptimized" content="320">
|
||||||
|
<meta name="robots" content="noindex">
|
||||||
<title><%- title %></title>
|
<title><%- title %></title>
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="/images/favicons/apple-touch-icon.png">
|
<link rel="apple-touch-icon" sizes="180x180" href="/images/favicons/apple-touch-icon.png">
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="/images/favicons/favicon-32x32.png">
|
<link rel="icon" type="image/png" sizes="32x32" href="/images/favicons/favicon-32x32.png">
|
||||||
|
@ -74,7 +74,7 @@
|
|||||||
<option
|
<option
|
||||||
value="<%- plugin_name %>"
|
value="<%- plugin_name %>"
|
||||||
<%- getDnsProvider() === plugin_name ? 'selected' : '' %>
|
<%- getDnsProvider() === plugin_name ? 'selected' : '' %>
|
||||||
><%- plugin_info.display_name %></option>
|
><%- plugin_info.name %></option>
|
||||||
<% }); %>
|
<% }); %>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -11,7 +11,7 @@ require('selectize');
|
|||||||
|
|
||||||
function sortProvidersAlphabetically(obj) {
|
function sortProvidersAlphabetically(obj) {
|
||||||
return Object.entries(obj)
|
return Object.entries(obj)
|
||||||
.sort((a,b) => a[1].display_name.toLowerCase() > b[1].display_name.toLowerCase())
|
.sort((a,b) => a[1].name.toLowerCase() > b[1].name.toLowerCase())
|
||||||
.reduce((result, entry) => {
|
.reduce((result, entry) => {
|
||||||
result[entry[0]] = entry[1];
|
result[entry[0]] = entry[1];
|
||||||
return result;
|
return result;
|
||||||
@ -265,7 +265,7 @@ module.exports = Mn.View.extend({
|
|||||||
this.ui.domain_names.selectize({
|
this.ui.domain_names.selectize({
|
||||||
delimiter: ',',
|
delimiter: ',',
|
||||||
persist: false,
|
persist: false,
|
||||||
maxOptions: 15,
|
maxOptions: 100,
|
||||||
create: function (input) {
|
create: function (input) {
|
||||||
return {
|
return {
|
||||||
value: input,
|
value: input,
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<%- i18n('ssl', provider) %><% if (meta.dns_provider) { %> - <%- dns_providers[meta.dns_provider].display_name %><% } %>
|
<%- i18n('ssl', provider) %><% if (meta.dns_provider) { %> - <%- dns_providers[meta.dns_provider].name %><% } %>
|
||||||
</td>
|
</td>
|
||||||
<td class="<%- isExpired() ? 'text-danger' : '' %>">
|
<td class="<%- isExpired() ? 'text-danger' : '' %>">
|
||||||
<%- formatDbDate(expires_on, 'Do MMMM YYYY, h:mm a') %>
|
<%- formatDbDate(expires_on, 'Do MMMM YYYY, h:mm a') %>
|
||||||
|
@ -114,7 +114,7 @@
|
|||||||
<option
|
<option
|
||||||
value="<%- plugin_name %>"
|
value="<%- plugin_name %>"
|
||||||
<%- getDnsProvider() === plugin_name ? 'selected' : '' %>
|
<%- getDnsProvider() === plugin_name ? 'selected' : '' %>
|
||||||
><%- plugin_info.display_name %></option>
|
><%- plugin_info.name %></option>
|
||||||
<% }); %>
|
<% }); %>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -233,7 +233,7 @@ module.exports = Mn.View.extend({
|
|||||||
this.ui.domain_names.selectize({
|
this.ui.domain_names.selectize({
|
||||||
delimiter: ',',
|
delimiter: ',',
|
||||||
persist: false,
|
persist: false,
|
||||||
maxOptions: 15,
|
maxOptions: 100,
|
||||||
create: function (input) {
|
create: function (input) {
|
||||||
return {
|
return {
|
||||||
value: input,
|
value: input,
|
||||||
|
@ -182,7 +182,7 @@
|
|||||||
<option
|
<option
|
||||||
value="<%- plugin_name %>"
|
value="<%- plugin_name %>"
|
||||||
<%- getDnsProvider() === plugin_name ? 'selected' : '' %>
|
<%- getDnsProvider() === plugin_name ? 'selected' : '' %>
|
||||||
><%- plugin_info.display_name %></option>
|
><%- plugin_info.name %></option>
|
||||||
<% }); %>
|
<% }); %>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -271,7 +271,7 @@ module.exports = Mn.View.extend({
|
|||||||
this.ui.domain_names.selectize({
|
this.ui.domain_names.selectize({
|
||||||
delimiter: ',',
|
delimiter: ',',
|
||||||
persist: false,
|
persist: false,
|
||||||
maxOptions: 15,
|
maxOptions: 100,
|
||||||
create: function (input) {
|
create: function (input) {
|
||||||
return {
|
return {
|
||||||
value: input,
|
value: input,
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
<div class="col-sm-4 col-md-4">
|
<div class="col-sm-4 col-md-4">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label"><%- i18n('proxy-hosts', 'forward-port') %> <span class="form-required">*</span></label>
|
<label class="form-label"><%- i18n('proxy-hosts', 'forward-port') %> <span class="form-required">*</span></label>
|
||||||
<input name="forward_port" type="number" class="form-control text-monospace model" placeholder="80" value="<%- forward_port %>" required>
|
<input name="forward_port" type="number" class="form-control text-monospace model" placeholder="80" min="1" max="65535" value="<%- forward_port %>" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -161,7 +161,7 @@
|
|||||||
<option
|
<option
|
||||||
value="<%- plugin_name %>"
|
value="<%- plugin_name %>"
|
||||||
<%- getDnsProvider() === plugin_name ? 'selected' : '' %>
|
<%- getDnsProvider() === plugin_name ? 'selected' : '' %>
|
||||||
><%- plugin_info.display_name %></option>
|
><%- plugin_info.name %></option>
|
||||||
<% }); %>
|
<% }); %>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -235,7 +235,7 @@ module.exports = Mn.View.extend({
|
|||||||
this.ui.domain_names.selectize({
|
this.ui.domain_names.selectize({
|
||||||
delimiter: ',',
|
delimiter: ',',
|
||||||
persist: false,
|
persist: false,
|
||||||
maxOptions: 15,
|
maxOptions: 100,
|
||||||
create: function (input) {
|
create: function (input) {
|
||||||
return {
|
return {
|
||||||
value: input,
|
value: input,
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<div class="col-sm-12 col-md-12">
|
<div class="col-sm-12 col-md-12">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label"><%- i18n('streams', 'incoming-port') %> <span class="form-required">*</span></label>
|
<label class="form-label"><%- i18n('streams', 'incoming-port') %> <span class="form-required">*</span></label>
|
||||||
<input name="incoming_port" type="number" class="form-control text-monospace" placeholder="eg: 8080" value="<%- incoming_port %>" required>
|
<input name="incoming_port" type="number" class="form-control text-monospace" placeholder="eg: 8080" min="1" max="65535" value="<%- incoming_port %>" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-8 col-md-8">
|
<div class="col-sm-8 col-md-8">
|
||||||
@ -21,7 +21,7 @@
|
|||||||
<div class="col-sm-4 col-md-4">
|
<div class="col-sm-4 col-md-4">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label"><%- i18n('streams', 'forwarding-port') %> <span class="form-required">*</span></label>
|
<label class="form-label"><%- i18n('streams', 'forwarding-port') %> <span class="form-required">*</span></label>
|
||||||
<input name="forwarding_port" type="number" class="form-control text-monospace" placeholder="eg: 80" value="<%- forwarding_port %>" required>
|
<input name="forwarding_port" type="number" class="form-control text-monospace" placeholder="eg: 80" min="1" max="65535" value="<%- forwarding_port %>" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6 col-md-6">
|
<div class="col-sm-6 col-md-6">
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12 col-md-12">
|
<div class="col-sm-12 col-md-12">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="form-label"><%- description %></div>
|
<div class="form-label"><%- i18n('settings', 'default-site-description') %></div>
|
||||||
<div class="custom-controls-stacked">
|
<div class="custom-controls-stacked">
|
||||||
<label class="custom-control custom-radio">
|
<label class="custom-control custom-radio">
|
||||||
<input class="custom-control-input" name="value" value="congratulations" type="radio" required <%- value === 'congratulations' ? 'checked' : '' %>>
|
<input class="custom-control-input" name="value" value="congratulations" type="radio" required <%- value === 'congratulations' ? 'checked' : '' %>>
|
||||||
@ -18,6 +18,10 @@
|
|||||||
<input class="custom-control-input" name="value" value="404" type="radio" required <%- value === '404' ? 'checked' : '' %>>
|
<input class="custom-control-input" name="value" value="404" type="radio" required <%- value === '404' ? 'checked' : '' %>>
|
||||||
<div class="custom-control-label"><%- i18n('settings', 'default-site-404') %></div>
|
<div class="custom-control-label"><%- i18n('settings', 'default-site-404') %></div>
|
||||||
</label>
|
</label>
|
||||||
|
<label class="custom-control custom-radio">
|
||||||
|
<input class="custom-control-input" name="value" value="444" type="radio" required <%- value === '444' ? 'checked' : '' %>>
|
||||||
|
<div class="custom-control-label"><%- i18n('settings', 'default-site-444') %></div>
|
||||||
|
</label>
|
||||||
<label class="custom-control custom-radio">
|
<label class="custom-control custom-radio">
|
||||||
<input class="custom-control-input" name="value" value="redirect" type="radio" required <%- value === 'redirect' ? 'checked' : '' %>>
|
<input class="custom-control-input" name="value" value="redirect" type="radio" required <%- value === 'redirect' ? 'checked' : '' %>>
|
||||||
<div class="custom-control-label"><%- i18n('settings', 'default-site-redirect') %></div>
|
<div class="custom-control-label"><%- i18n('settings', 'default-site-redirect') %></div>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<td>
|
<td>
|
||||||
<div><%- name %></div>
|
<div><%- i18n('settings', 'default-site') %></div>
|
||||||
<div class="small text-muted">
|
<div class="small text-muted">
|
||||||
<%- description %>
|
<%- i18n('settings', 'default-site-description') %>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
@ -60,7 +60,7 @@
|
|||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"fork-me": "Fork me on Github",
|
"fork-me": "Fork me on Github",
|
||||||
"copy": "© 2022 <a href=\"{url}\" target=\"_blank\">jc21.com</a>.",
|
"copy": "© 2024 <a href=\"{url}\" target=\"_blank\">jc21.com</a>.",
|
||||||
"theme": "Theme by <a href=\"{url}\" target=\"_blank\">Tabler</a>"
|
"theme": "Theme by <a href=\"{url}\" target=\"_blank\">Tabler</a>"
|
||||||
},
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
@ -285,8 +285,10 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"title": "Settings",
|
"title": "Settings",
|
||||||
"default-site": "Default Site",
|
"default-site": "Default Site",
|
||||||
|
"default-site-description": "What to show when Nginx is hit with an unknown Host",
|
||||||
"default-site-congratulations": "Congratulations Page",
|
"default-site-congratulations": "Congratulations Page",
|
||||||
"default-site-404": "404 Page",
|
"default-site-404": "404 Page",
|
||||||
|
"default-site-444": "No Response (444)",
|
||||||
"default-site-html": "Custom Page",
|
"default-site-html": "Custom Page",
|
||||||
"default-site-redirect": "Redirect"
|
"default-site-redirect": "Redirect"
|
||||||
}
|
}
|
||||||
|
@ -27,10 +27,10 @@
|
|||||||
"messageformat-loader": "^0.8.1",
|
"messageformat-loader": "^0.8.1",
|
||||||
"mini-css-extract-plugin": "^0.9.0",
|
"mini-css-extract-plugin": "^0.9.0",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"node-sass": "^6.0.1",
|
"node-sass": "^9.0.0",
|
||||||
"nodemon": "^2.0.2",
|
"nodemon": "^2.0.2",
|
||||||
"numeral": "^2.0.6",
|
"numeral": "^2.0.6",
|
||||||
"sass-loader": "10.2.0",
|
"sass-loader": "^10.0.0",
|
||||||
"style-loader": "^1.1.3",
|
"style-loader": "^1.1.3",
|
||||||
"tabler-ui": "git+https://github.com/tabler/tabler.git#00f78ad823311bc3ad974ac3e5b0126198f0a813",
|
"tabler-ui": "git+https://github.com/tabler/tabler.git#00f78ad823311bc3ad974ac3e5b0126198f0a813",
|
||||||
"underscore": "^1.12.1",
|
"underscore": "^1.12.1",
|
||||||
|
1276
frontend/yarn.lock
1276
frontend/yarn.lock
File diff suppressed because it is too large
Load Diff
21
global/README.md
Normal file
21
global/README.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# certbot-dns-plugins
|
||||||
|
|
||||||
|
This file contains info about available Certbot DNS plugins.
|
||||||
|
This only works for plugins which use the standard argument structure, so:
|
||||||
|
--authenticator <plugin-name> --<plugin-name>-credentials <FILE> --<plugin-name>-propagation-seconds <number>
|
||||||
|
|
||||||
|
File Structure:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"cloudflare": {
|
||||||
|
"display_name": "Name displayed to the user",
|
||||||
|
"package_name": "Package name in PyPi repo",
|
||||||
|
"version_requirement": "Optional package version requirements (e.g. ==1.3 or >=1.2,<2.0, see https://www.python.org/dev/peps/pep-0440/#version-specifiers)",
|
||||||
|
"dependencies": "Additional dependencies, space separated (as you would pass it to pip install)",
|
||||||
|
"credentials": "Template of the credentials file",
|
||||||
|
"full_plugin_name": "The full plugin name as used in the commandline with certbot, e.g. 'dns-njalla'"
|
||||||
|
},
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
@ -1,563 +0,0 @@
|
|||||||
/**
|
|
||||||
* This file contains info about available Certbot DNS plugins.
|
|
||||||
* This only works for plugins which use the standard argument structure, so:
|
|
||||||
* --authenticator <plugin-name> --<plugin-name>-credentials <FILE> --<plugin-name>-propagation-seconds <number>
|
|
||||||
*
|
|
||||||
* File Structure:
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
* cloudflare: {
|
|
||||||
* display_name: "Name displayed to the user",
|
|
||||||
* package_name: "Package name in PyPi repo",
|
|
||||||
* version_requirement: "Optional package version requirements (e.g. ==1.3 or >=1.2,<2.0, see https://www.python.org/dev/peps/pep-0440/#version-specifiers)",
|
|
||||||
* dependencies: "Additional dependencies, space separated (as you would pass it to pip install)",
|
|
||||||
* credentials: `Template of the credentials file`,
|
|
||||||
* full_plugin_name: "The full plugin name as used in the commandline with certbot, e.g. 'dns-njalla'",
|
|
||||||
* },
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
//####################################################//
|
|
||||||
acmedns: {
|
|
||||||
display_name: 'ACME-DNS',
|
|
||||||
package_name: 'certbot-dns-acmedns',
|
|
||||||
version_requirement: '~=0.1.0',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_acmedns_api_url = http://acmedns-server/
|
|
||||||
dns_acmedns_registration_file = /data/acme-registration.json`,
|
|
||||||
full_plugin_name: 'dns-acmedns',
|
|
||||||
},
|
|
||||||
aliyun: {
|
|
||||||
display_name: 'Aliyun',
|
|
||||||
package_name: 'certbot-dns-aliyun',
|
|
||||||
version_requirement: '~=0.38.1',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_aliyun_access_key = 12345678
|
|
||||||
dns_aliyun_access_key_secret = 1234567890abcdef1234567890abcdef`,
|
|
||||||
full_plugin_name: 'dns-aliyun',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
azure: {
|
|
||||||
display_name: 'Azure',
|
|
||||||
package_name: 'certbot-dns-azure',
|
|
||||||
version_requirement: '~=1.2.0',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `# This plugin supported API authentication using either Service Principals or utilizing a Managed Identity assigned to the virtual machine.
|
|
||||||
# Regardless which authentication method used, the identity will need the “DNS Zone Contributor” role assigned to it.
|
|
||||||
# As multiple Azure DNS Zones in multiple resource groups can exist, the config file needs a mapping of zone to resource group ID. Multiple zones -> ID mappings can be listed by using the key dns_azure_zoneX where X is a unique number. At least 1 zone mapping is required.
|
|
||||||
|
|
||||||
# Using a service principal (option 1)
|
|
||||||
dns_azure_sp_client_id = 912ce44a-0156-4669-ae22-c16a17d34ca5
|
|
||||||
dns_azure_sp_client_secret = E-xqXU83Y-jzTI6xe9fs2YC~mck3ZzUih9
|
|
||||||
dns_azure_tenant_id = ed1090f3-ab18-4b12-816c-599af8a88cf7
|
|
||||||
|
|
||||||
# Using used assigned MSI (option 2)
|
|
||||||
# dns_azure_msi_client_id = 912ce44a-0156-4669-ae22-c16a17d34ca5
|
|
||||||
|
|
||||||
# Using system assigned MSI (option 3)
|
|
||||||
# dns_azure_msi_system_assigned = true
|
|
||||||
|
|
||||||
# Zones (at least one always required)
|
|
||||||
dns_azure_zone1 = example.com:/subscriptions/c135abce-d87d-48df-936c-15596c6968a5/resourceGroups/dns1
|
|
||||||
dns_azure_zone2 = example.org:/subscriptions/99800903-fb14-4992-9aff-12eaf2744622/resourceGroups/dns2`,
|
|
||||||
full_plugin_name: 'dns-azure',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
cloudflare: {
|
|
||||||
display_name: 'Cloudflare',
|
|
||||||
package_name: 'certbot-dns-cloudflare',
|
|
||||||
version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version
|
|
||||||
dependencies: 'cloudflare',
|
|
||||||
credentials: `# Cloudflare API token
|
|
||||||
dns_cloudflare_api_token = 0123456789abcdef0123456789abcdef01234567`,
|
|
||||||
full_plugin_name: 'dns-cloudflare',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
cloudns: {
|
|
||||||
display_name: 'ClouDNS',
|
|
||||||
package_name: 'certbot-dns-cloudns',
|
|
||||||
version_requirement: '~=0.4.0',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `# Target user ID (see https://www.cloudns.net/api-settings/)
|
|
||||||
dns_cloudns_auth_id=1234
|
|
||||||
# Alternatively, one of the following two options can be set:
|
|
||||||
# dns_cloudns_sub_auth_id=1234
|
|
||||||
# dns_cloudns_sub_auth_user=foobar
|
|
||||||
|
|
||||||
# API password
|
|
||||||
dns_cloudns_auth_password=password1`,
|
|
||||||
full_plugin_name: 'dns-cloudns',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
cloudxns: {
|
|
||||||
display_name: 'CloudXNS',
|
|
||||||
package_name: 'certbot-dns-cloudxns',
|
|
||||||
version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_cloudxns_api_key = 1234567890abcdef1234567890abcdef
|
|
||||||
dns_cloudxns_secret_key = 1122334455667788`,
|
|
||||||
full_plugin_name: 'dns-cloudxns',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
constellix: {
|
|
||||||
display_name: 'Constellix',
|
|
||||||
package_name: 'certbot-dns-constellix',
|
|
||||||
version_requirement: '~=0.2.1',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_constellix_apikey = 5fb4e76f-ac91-43e5-f982458bc595
|
|
||||||
dns_constellix_secretkey = 47d99fd0-32e7-4e07-85b46d08e70b
|
|
||||||
dns_constellix_endpoint = https://api.dns.constellix.com/v1`,
|
|
||||||
full_plugin_name: 'dns-constellix',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
corenetworks: {
|
|
||||||
display_name: 'Core Networks',
|
|
||||||
package_name: 'certbot-dns-corenetworks',
|
|
||||||
version_requirement: '~=0.1.4',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_corenetworks_username = asaHB12r
|
|
||||||
dns_corenetworks_password = secure_password`,
|
|
||||||
full_plugin_name: 'dns-corenetworks',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
cpanel: {
|
|
||||||
display_name: 'cPanel',
|
|
||||||
package_name: 'certbot-dns-cpanel',
|
|
||||||
version_requirement: '~=0.2.2',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `cpanel_url = https://cpanel.example.com:2083
|
|
||||||
cpanel_username = user
|
|
||||||
cpanel_password = hunter2`,
|
|
||||||
full_plugin_name: 'cpanel',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
desec: {
|
|
||||||
display_name: 'deSEC',
|
|
||||||
package_name: 'certbot-dns-desec',
|
|
||||||
version_requirement: '~=1.2.1',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_desec_token = YOUR_DESEC_API_TOKEN
|
|
||||||
dns_desec_endpoint = https://desec.io/api/v1/`,
|
|
||||||
full_plugin_name: 'dns-desec',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
duckdns: {
|
|
||||||
display_name: 'DuckDNS',
|
|
||||||
package_name: 'certbot-dns-duckdns',
|
|
||||||
version_requirement: '~=0.9',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: 'dns_duckdns_token=your-duckdns-token',
|
|
||||||
full_plugin_name: 'dns-duckdns',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
digitalocean: {
|
|
||||||
display_name: 'DigitalOcean',
|
|
||||||
package_name: 'certbot-dns-digitalocean',
|
|
||||||
version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version
|
|
||||||
dependencies: '',
|
|
||||||
credentials: 'dns_digitalocean_token = 0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff',
|
|
||||||
full_plugin_name: 'dns-digitalocean',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
directadmin: {
|
|
||||||
display_name: 'DirectAdmin',
|
|
||||||
package_name: 'certbot-dns-directadmin',
|
|
||||||
version_requirement: '~=0.0.23',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `directadmin_url = https://my.directadminserver.com:2222
|
|
||||||
directadmin_username = username
|
|
||||||
directadmin_password = aSuperStrongPassword`,
|
|
||||||
full_plugin_name: 'directadmin',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
dnsimple: {
|
|
||||||
display_name: 'DNSimple',
|
|
||||||
package_name: 'certbot-dns-dnsimple',
|
|
||||||
version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version
|
|
||||||
dependencies: '',
|
|
||||||
credentials: 'dns_dnsimple_token = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw',
|
|
||||||
full_plugin_name: 'dns-dnsimple',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
dnsmadeeasy: {
|
|
||||||
display_name: 'DNS Made Easy',
|
|
||||||
package_name: 'certbot-dns-dnsmadeeasy',
|
|
||||||
version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_dnsmadeeasy_api_key = 1c1a3c91-4770-4ce7-96f4-54c0eb0e457a
|
|
||||||
dns_dnsmadeeasy_secret_key = c9b5625f-9834-4ff8-baba-4ed5f32cae55`,
|
|
||||||
full_plugin_name: 'dns-dnsmadeeasy',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
dnspod: {
|
|
||||||
display_name: 'DNSPod',
|
|
||||||
package_name: 'certbot-dns-dnspod',
|
|
||||||
version_requirement: '~=0.1.0',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_dnspod_email = "email@example.com"
|
|
||||||
dns_dnspod_api_token = "id,key"`,
|
|
||||||
full_plugin_name: 'dns-dnspod',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
domainoffensive: {
|
|
||||||
display_name: 'DomainOffensive (do.de)',
|
|
||||||
package_name: 'certbot-dns-do',
|
|
||||||
version_requirement: '~=0.31.0',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: 'dns_do_api_token = YOUR_DO_DE_AUTH_TOKEN',
|
|
||||||
full_plugin_name: 'dns-do',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
domeneshop: {
|
|
||||||
display_name: 'Domeneshop',
|
|
||||||
package_name: 'certbot-dns-domeneshop',
|
|
||||||
version_requirement: '~=0.2.8',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_domeneshop_client_token=YOUR_DOMENESHOP_CLIENT_TOKEN
|
|
||||||
dns_domeneshop_client_secret=YOUR_DOMENESHOP_CLIENT_SECRET`,
|
|
||||||
full_plugin_name: 'dns-domeneshop',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
dynu: {
|
|
||||||
display_name: 'Dynu',
|
|
||||||
package_name: 'certbot-dns-dynu',
|
|
||||||
version_requirement: '~=0.0.1',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: 'dns_dynu_auth_token = YOUR_DYNU_AUTH_TOKEN',
|
|
||||||
full_plugin_name: 'dns-dynu',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
eurodns: {
|
|
||||||
display_name: 'EuroDNS',
|
|
||||||
package_name: 'certbot-dns-eurodns',
|
|
||||||
version_requirement: '~=0.0.4',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_eurodns_applicationId = myuser
|
|
||||||
dns_eurodns_apiKey = mysecretpassword
|
|
||||||
dns_eurodns_endpoint = https://rest-api.eurodns.com/user-api-gateway/proxy`,
|
|
||||||
full_plugin_name: 'dns-eurodns',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
gandi: {
|
|
||||||
display_name: 'Gandi Live DNS',
|
|
||||||
package_name: 'certbot_plugin_gandi',
|
|
||||||
version_requirement: '~=1.3.2',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `# live dns v5 api key
|
|
||||||
dns_gandi_api_key=APIKEY
|
|
||||||
|
|
||||||
# optional organization id, remove it if not used
|
|
||||||
dns_gandi_sharing_id=SHARINGID`,
|
|
||||||
full_plugin_name: 'dns-gandi',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
godaddy: {
|
|
||||||
display_name: 'GoDaddy',
|
|
||||||
package_name: 'certbot-dns-godaddy',
|
|
||||||
version_requirement: '~=0.2.0',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_godaddy_secret = 0123456789abcdef0123456789abcdef01234567
|
|
||||||
dns_godaddy_key = abcdef0123456789abcdef01234567abcdef0123`,
|
|
||||||
full_plugin_name: 'dns-godaddy',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
google: {
|
|
||||||
display_name: 'Google',
|
|
||||||
package_name: 'certbot-dns-google',
|
|
||||||
version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `{
|
|
||||||
"type": "service_account",
|
|
||||||
...
|
|
||||||
}`,
|
|
||||||
full_plugin_name: 'dns-google',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
googledomains: {
|
|
||||||
display_name: 'GoogleDomainsDNS',
|
|
||||||
package_name: 'certbot-dns-google-domains',
|
|
||||||
version_requirement: '~=0.1.5',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_google_domains_access_token = 0123456789abcdef0123456789abcdef01234567
|
|
||||||
dns_google_domains_zone = "example.com"`,
|
|
||||||
full_plugin_name: 'dns-google-domains',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
hetzner: {
|
|
||||||
display_name: 'Hetzner',
|
|
||||||
package_name: 'certbot-dns-hetzner',
|
|
||||||
version_requirement: '~=1.0.4',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: 'dns_hetzner_api_token = 0123456789abcdef0123456789abcdef',
|
|
||||||
full_plugin_name: 'dns-hetzner',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
infomaniak: {
|
|
||||||
display_name: 'Infomaniak',
|
|
||||||
package_name: 'certbot-dns-infomaniak',
|
|
||||||
version_requirement: '~=0.1.12',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: 'dns_infomaniak_token = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
|
|
||||||
full_plugin_name: 'dns-infomaniak',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
inwx: {
|
|
||||||
display_name: 'INWX',
|
|
||||||
package_name: 'certbot-dns-inwx',
|
|
||||||
version_requirement: '~=2.1.2',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_inwx_url = https://api.domrobot.com/xmlrpc/
|
|
||||||
dns_inwx_username = your_username
|
|
||||||
dns_inwx_password = your_password
|
|
||||||
dns_inwx_shared_secret = your_shared_secret optional`,
|
|
||||||
full_plugin_name: 'dns-inwx',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
ionos: {
|
|
||||||
display_name: 'IONOS',
|
|
||||||
package_name: 'certbot-dns-ionos',
|
|
||||||
version_requirement: '==2022.11.24',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_ionos_prefix = myapikeyprefix
|
|
||||||
dns_ionos_secret = verysecureapikeysecret
|
|
||||||
dns_ionos_endpoint = https://api.hosting.ionos.com`,
|
|
||||||
full_plugin_name: 'dns-ionos',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
ispconfig: {
|
|
||||||
display_name: 'ISPConfig',
|
|
||||||
package_name: 'certbot-dns-ispconfig',
|
|
||||||
version_requirement: '~=0.2.0',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_ispconfig_username = myremoteuser
|
|
||||||
dns_ispconfig_password = verysecureremoteuserpassword
|
|
||||||
dns_ispconfig_endpoint = https://localhost:8080`,
|
|
||||||
full_plugin_name: 'dns-ispconfig',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
isset: {
|
|
||||||
display_name: 'Isset',
|
|
||||||
package_name: 'certbot-dns-isset',
|
|
||||||
version_requirement: '~=0.0.3',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_isset_endpoint="https://customer.isset.net/api"
|
|
||||||
dns_isset_token="<token>"`,
|
|
||||||
full_plugin_name: 'dns-isset',
|
|
||||||
},
|
|
||||||
joker: {
|
|
||||||
display_name: 'Joker',
|
|
||||||
package_name: 'certbot-dns-joker',
|
|
||||||
version_requirement: '~=1.1.0',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_joker_username = <Dynamic DNS Authentication Username>
|
|
||||||
dns_joker_password = <Dynamic DNS Authentication Password>
|
|
||||||
dns_joker_domain = <Dynamic DNS Domain>`,
|
|
||||||
full_plugin_name: 'dns-joker',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
linode: {
|
|
||||||
display_name: 'Linode',
|
|
||||||
package_name: 'certbot-dns-linode',
|
|
||||||
version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_linode_key = 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ64
|
|
||||||
dns_linode_version = [<blank>|3|4]`,
|
|
||||||
full_plugin_name: 'dns-linode',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
loopia: {
|
|
||||||
display_name: 'Loopia',
|
|
||||||
package_name: 'certbot-dns-loopia',
|
|
||||||
version_requirement: '~=1.0.0',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_loopia_user = user@loopiaapi
|
|
||||||
dns_loopia_password = abcdef0123456789abcdef01234567abcdef0123`,
|
|
||||||
full_plugin_name: 'dns-loopia',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
luadns: {
|
|
||||||
display_name: 'LuaDNS',
|
|
||||||
package_name: 'certbot-dns-luadns',
|
|
||||||
version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_luadns_email = user@example.com
|
|
||||||
dns_luadns_token = 0123456789abcdef0123456789abcdef`,
|
|
||||||
full_plugin_name: 'dns-luadns',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
namecheap: {
|
|
||||||
display_name: 'Namecheap',
|
|
||||||
package_name: 'certbot-dns-namecheap',
|
|
||||||
version_requirement: '~=1.0.0',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_namecheap_username = 123456
|
|
||||||
dns_namecheap_api_key = 0123456789abcdef0123456789abcdef01234567`,
|
|
||||||
full_plugin_name: 'dns-namecheap',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
netcup: {
|
|
||||||
display_name: 'netcup',
|
|
||||||
package_name: 'certbot-dns-netcup',
|
|
||||||
version_requirement: '~=1.0.0',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_netcup_customer_id = 123456
|
|
||||||
dns_netcup_api_key = 0123456789abcdef0123456789abcdef01234567
|
|
||||||
dns_netcup_api_password = abcdef0123456789abcdef01234567abcdef0123`,
|
|
||||||
full_plugin_name: 'dns-netcup',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
njalla: {
|
|
||||||
display_name: 'Njalla',
|
|
||||||
package_name: 'certbot-dns-njalla',
|
|
||||||
version_requirement: '~=1.0.0',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: 'dns_njalla_token = 0123456789abcdef0123456789abcdef01234567',
|
|
||||||
full_plugin_name: 'dns-njalla',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
nsone: {
|
|
||||||
display_name: 'NS1',
|
|
||||||
package_name: 'certbot-dns-nsone',
|
|
||||||
version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version
|
|
||||||
dependencies: '',
|
|
||||||
credentials: 'dns_nsone_api_key = MDAwMDAwMDAwMDAwMDAw',
|
|
||||||
full_plugin_name: 'dns-nsone',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
oci: {
|
|
||||||
display_name: 'Oracle Cloud Infrastructure DNS',
|
|
||||||
package_name: 'certbot-dns-oci',
|
|
||||||
package_version: '0.3.6',
|
|
||||||
dependencies: 'oci',
|
|
||||||
credentials: `[DEFAULT]
|
|
||||||
user = ocid1.user.oc1...
|
|
||||||
fingerprint = xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx
|
|
||||||
tenancy = ocid1.tenancy.oc1...
|
|
||||||
region = us-ashburn-1
|
|
||||||
key_file = ~/.oci/oci_api_key.pem`,
|
|
||||||
full_plugin_name: 'dns-oci',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
online: {
|
|
||||||
display_name: 'Online',
|
|
||||||
package_name: 'certbot-dns-online',
|
|
||||||
version_requirement: '~=0.0.8',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: 'dns_online_token=0123456789abcdef0123456789abcdef01234567',
|
|
||||||
full_plugin_name: 'dns-online',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
ovh: {
|
|
||||||
display_name: 'OVH',
|
|
||||||
package_name: 'certbot-dns-ovh',
|
|
||||||
version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_ovh_endpoint = ovh-eu
|
|
||||||
dns_ovh_application_key = MDAwMDAwMDAwMDAw
|
|
||||||
dns_ovh_application_secret = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
|
|
||||||
dns_ovh_consumer_key = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw`,
|
|
||||||
full_plugin_name: 'dns-ovh',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
porkbun: {
|
|
||||||
display_name: 'Porkbun',
|
|
||||||
package_name: 'certbot-dns-porkbun',
|
|
||||||
version_requirement: '~=0.2',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_porkbun_key=your-porkbun-api-key
|
|
||||||
dns_porkbun_secret=your-porkbun-api-secret`,
|
|
||||||
full_plugin_name: 'dns-porkbun',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
powerdns: {
|
|
||||||
display_name: 'PowerDNS',
|
|
||||||
package_name: 'certbot-dns-powerdns',
|
|
||||||
version_requirement: '~=0.2.0',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_powerdns_api_url = https://api.mypowerdns.example.org
|
|
||||||
dns_powerdns_api_key = AbCbASsd!@34`,
|
|
||||||
full_plugin_name: 'dns-powerdns',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
regru: {
|
|
||||||
display_name: 'reg.ru',
|
|
||||||
package_name: 'certbot-regru',
|
|
||||||
version_requirement: '~=1.0.2',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_username=username
|
|
||||||
dns_password=password`,
|
|
||||||
full_plugin_name: 'dns',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
rfc2136: {
|
|
||||||
display_name: 'RFC 2136',
|
|
||||||
package_name: 'certbot-dns-rfc2136',
|
|
||||||
version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `# Target DNS server
|
|
||||||
dns_rfc2136_server = 192.0.2.1
|
|
||||||
# Target DNS port
|
|
||||||
dns_rfc2136_port = 53
|
|
||||||
# TSIG key name
|
|
||||||
dns_rfc2136_name = keyname.
|
|
||||||
# TSIG key secret
|
|
||||||
dns_rfc2136_secret = 4q4wM/2I180UXoMyN4INVhJNi8V9BCV+jMw2mXgZw/CSuxUT8C7NKKFs AmKd7ak51vWKgSl12ib86oQRPkpDjg==
|
|
||||||
# TSIG key algorithm
|
|
||||||
dns_rfc2136_algorithm = HMAC-SHA512`,
|
|
||||||
full_plugin_name: 'dns-rfc2136',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
route53: {
|
|
||||||
display_name: 'Route 53 (Amazon)',
|
|
||||||
package_name: 'certbot-dns-route53',
|
|
||||||
version_requirement: '==$(certbot --version | grep -Eo \'[0-9](\\.[0-9]+)+\')', // official plugin, use certbot version
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `[default]
|
|
||||||
aws_access_key_id=AKIAIOSFODNN7EXAMPLE
|
|
||||||
aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY`,
|
|
||||||
full_plugin_name: 'dns-route53',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
transip: {
|
|
||||||
display_name: 'TransIP',
|
|
||||||
package_name: 'certbot-dns-transip',
|
|
||||||
version_requirement: '~=0.4.3',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_transip_username = my_username
|
|
||||||
dns_transip_key_file = /etc/letsencrypt/transip-rsa.key`,
|
|
||||||
full_plugin_name: 'dns-transip',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
tencentcloud: {
|
|
||||||
display_name: 'Tencent Cloud',
|
|
||||||
package_name: 'certbot-dns-tencentcloud',
|
|
||||||
version_requirement: '~=2.0.0',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_tencentcloud_secret_id = TENCENT_CLOUD_SECRET_ID
|
|
||||||
dns_tencentcloud_secret_key = TENCENT_CLOUD_SECRET_KEY`,
|
|
||||||
full_plugin_name: 'dns-tencentcloud',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
vultr: {
|
|
||||||
display_name: 'Vultr',
|
|
||||||
package_name: 'certbot-dns-vultr',
|
|
||||||
version_requirement: '~=1.0.3',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: 'dns_vultr_key = YOUR_VULTR_API_KEY',
|
|
||||||
full_plugin_name: 'dns-vultr',
|
|
||||||
},
|
|
||||||
//####################################################//
|
|
||||||
websupportsk: {
|
|
||||||
display_name: 'Websupport.sk',
|
|
||||||
package_name: 'certbot-dns-websupportsk',
|
|
||||||
version_requirement: '~=0.1.6',
|
|
||||||
dependencies: '',
|
|
||||||
credentials: `dns_websupportsk_api_key = <api_key>
|
|
||||||
dns_websupportsk_secret = <secret>
|
|
||||||
dns_websupportsk_domain = example.com`,
|
|
||||||
full_plugin_name: 'dns-websupportsk',
|
|
||||||
},
|
|
||||||
};
|
|
458
global/certbot-dns-plugins.json
Normal file
458
global/certbot-dns-plugins.json
Normal file
@ -0,0 +1,458 @@
|
|||||||
|
{
|
||||||
|
"acmedns": {
|
||||||
|
"name": "ACME-DNS",
|
||||||
|
"package_name": "certbot-dns-acmedns",
|
||||||
|
"version": "~=0.1.0",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_acmedns_api_url = http://acmedns-server/\ndns_acmedns_registration_file = /data/acme-registration.json",
|
||||||
|
"full_plugin_name": "dns-acmedns"
|
||||||
|
},
|
||||||
|
"aliyun": {
|
||||||
|
"name": "Aliyun",
|
||||||
|
"package_name": "certbot-dns-aliyun",
|
||||||
|
"version": "~=0.38.1",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_aliyun_access_key = 12345678\ndns_aliyun_access_key_secret = 1234567890abcdef1234567890abcdef",
|
||||||
|
"full_plugin_name": "dns-aliyun"
|
||||||
|
},
|
||||||
|
"azure": {
|
||||||
|
"name": "Azure",
|
||||||
|
"package_name": "certbot-dns-azure",
|
||||||
|
"version": "~=1.2.0",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "# This plugin supported API authentication using either Service Principals or utilizing a Managed Identity assigned to the virtual machine.\n# Regardless which authentication method used, the identity will need the “DNS Zone Contributor” role assigned to it.\n# As multiple Azure DNS Zones in multiple resource groups can exist, the config file needs a mapping of zone to resource group ID. Multiple zones -> ID mappings can be listed by using the key dns_azure_zoneX where X is a unique number. At least 1 zone mapping is required.\n\n# Using a service principal (option 1)\ndns_azure_sp_client_id = 912ce44a-0156-4669-ae22-c16a17d34ca5\ndns_azure_sp_client_secret = E-xqXU83Y-jzTI6xe9fs2YC~mck3ZzUih9\ndns_azure_tenant_id = ed1090f3-ab18-4b12-816c-599af8a88cf7\n\n# Using used assigned MSI (option 2)\n# dns_azure_msi_client_id = 912ce44a-0156-4669-ae22-c16a17d34ca5\n\n# Using system assigned MSI (option 3)\n# dns_azure_msi_system_assigned = true\n\n# Zones (at least one always required)\ndns_azure_zone1 = example.com:/subscriptions/c135abce-d87d-48df-936c-15596c6968a5/resourceGroups/dns1\ndns_azure_zone2 = example.org:/subscriptions/99800903-fb14-4992-9aff-12eaf2744622/resourceGroups/dns2",
|
||||||
|
"full_plugin_name": "dns-azure"
|
||||||
|
},
|
||||||
|
"bunny": {
|
||||||
|
"name": "bunny.net",
|
||||||
|
"package_name": "certbot-dns-bunny",
|
||||||
|
"version": "~=0.0.9",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "# Bunny API token used by Certbot (see https://dash.bunny.net/account/settings)\ndns_bunny_api_key = xxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
|
||||||
|
"full_plugin_name": "dns-bunny"
|
||||||
|
},
|
||||||
|
"cloudflare": {
|
||||||
|
"name": "Cloudflare",
|
||||||
|
"package_name": "certbot-dns-cloudflare",
|
||||||
|
"version": "=={{certbot-version}}",
|
||||||
|
"dependencies": "cloudflare acme=={{certbot-version}}",
|
||||||
|
"credentials": "# Cloudflare API token\ndns_cloudflare_api_token = 0123456789abcdef0123456789abcdef01234567",
|
||||||
|
"full_plugin_name": "dns-cloudflare"
|
||||||
|
},
|
||||||
|
"cloudns": {
|
||||||
|
"name": "ClouDNS",
|
||||||
|
"package_name": "certbot-dns-cloudns",
|
||||||
|
"version": "~=0.6.0",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "# Target user ID (see https://www.cloudns.net/api-settings/)\n\tdns_cloudns_auth_id=1234\n\t# Alternatively, one of the following two options can be set:\n\t# dns_cloudns_sub_auth_id=1234\n\t# dns_cloudns_sub_auth_user=foobar\n\n\t# API password\n\tdns_cloudns_auth_password=password1",
|
||||||
|
"full_plugin_name": "dns-cloudns"
|
||||||
|
},
|
||||||
|
"cloudxns": {
|
||||||
|
"name": "CloudXNS",
|
||||||
|
"package_name": "certbot-dns-cloudxns",
|
||||||
|
"version": "~=1.32.0",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_cloudxns_api_key = 1234567890abcdef1234567890abcdef\ndns_cloudxns_secret_key = 1122334455667788",
|
||||||
|
"full_plugin_name": "dns-cloudxns"
|
||||||
|
},
|
||||||
|
"constellix": {
|
||||||
|
"name": "Constellix",
|
||||||
|
"package_name": "certbot-dns-constellix",
|
||||||
|
"version": "~=0.2.1",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_constellix_apikey = 5fb4e76f-ac91-43e5-f982458bc595\ndns_constellix_secretkey = 47d99fd0-32e7-4e07-85b46d08e70b\ndns_constellix_endpoint = https://api.dns.constellix.com/v1",
|
||||||
|
"full_plugin_name": "dns-constellix"
|
||||||
|
},
|
||||||
|
"corenetworks": {
|
||||||
|
"name": "Core Networks",
|
||||||
|
"package_name": "certbot-dns-corenetworks",
|
||||||
|
"version": "~=0.1.4",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_corenetworks_username = asaHB12r\ndns_corenetworks_password = secure_password",
|
||||||
|
"full_plugin_name": "dns-corenetworks"
|
||||||
|
},
|
||||||
|
"cpanel": {
|
||||||
|
"name": "cPanel",
|
||||||
|
"package_name": "certbot-dns-cpanel",
|
||||||
|
"version": "~=0.2.2",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "cpanel_url = https://cpanel.example.com:2083\ncpanel_username = user\ncpanel_password = hunter2",
|
||||||
|
"full_plugin_name": "cpanel"
|
||||||
|
},
|
||||||
|
"desec": {
|
||||||
|
"name": "deSEC",
|
||||||
|
"package_name": "certbot-dns-desec",
|
||||||
|
"version": "~=1.2.1",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_desec_token = YOUR_DESEC_API_TOKEN\ndns_desec_endpoint = https://desec.io/api/v1/",
|
||||||
|
"full_plugin_name": "dns-desec"
|
||||||
|
},
|
||||||
|
"duckdns": {
|
||||||
|
"name": "DuckDNS",
|
||||||
|
"package_name": "certbot-dns-duckdns",
|
||||||
|
"version": "~=1.0",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_duckdns_token=your-duckdns-token",
|
||||||
|
"full_plugin_name": "dns-duckdns"
|
||||||
|
},
|
||||||
|
"digitalocean": {
|
||||||
|
"name": "DigitalOcean",
|
||||||
|
"package_name": "certbot-dns-digitalocean",
|
||||||
|
"version": "=={{certbot-version}}",
|
||||||
|
"dependencies": "acme=={{certbot-version}}",
|
||||||
|
"credentials": "dns_digitalocean_token = 0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff",
|
||||||
|
"full_plugin_name": "dns-digitalocean"
|
||||||
|
},
|
||||||
|
"directadmin": {
|
||||||
|
"name": "DirectAdmin",
|
||||||
|
"package_name": "certbot-dns-directadmin",
|
||||||
|
"version": "~=0.0.23",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "directadmin_url = https://my.directadminserver.com:2222\ndirectadmin_username = username\ndirectadmin_password = aSuperStrongPassword",
|
||||||
|
"full_plugin_name": "directadmin"
|
||||||
|
},
|
||||||
|
"dnsimple": {
|
||||||
|
"name": "DNSimple",
|
||||||
|
"package_name": "certbot-dns-dnsimple",
|
||||||
|
"version": "=={{certbot-version}}",
|
||||||
|
"dependencies": "acme=={{certbot-version}}",
|
||||||
|
"credentials": "dns_dnsimple_token = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw",
|
||||||
|
"full_plugin_name": "dns-dnsimple"
|
||||||
|
},
|
||||||
|
"dnsmadeeasy": {
|
||||||
|
"name": "DNS Made Easy",
|
||||||
|
"package_name": "certbot-dns-dnsmadeeasy",
|
||||||
|
"version": "=={{certbot-version}}",
|
||||||
|
"dependencies": "acme=={{certbot-version}}",
|
||||||
|
"credentials": "dns_dnsmadeeasy_api_key = 1c1a3c91-4770-4ce7-96f4-54c0eb0e457a\ndns_dnsmadeeasy_secret_key = c9b5625f-9834-4ff8-baba-4ed5f32cae55",
|
||||||
|
"full_plugin_name": "dns-dnsmadeeasy"
|
||||||
|
},
|
||||||
|
"dnsmulti": {
|
||||||
|
"name": "DnsMulti",
|
||||||
|
"package_name": "certbot-dns-multi",
|
||||||
|
"version": "~=4.9",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "# See https://go-acme.github.io/lego/dns/#dns-providers for list of providers and their settings\n# Example provider configuration for DreamHost\n# dns_multi_provider = dreamhost\n# DREAMHOST_API_KEY = ABCDEFG1234",
|
||||||
|
"full_plugin_name": "dns-multi"
|
||||||
|
},
|
||||||
|
"dnspod": {
|
||||||
|
"name": "DNSPod",
|
||||||
|
"package_name": "certbot-dns-dnspod",
|
||||||
|
"version": "~=0.1.0",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_dnspod_email = \"email@example.com\"\ndns_dnspod_api_token = \"id,key\"",
|
||||||
|
"full_plugin_name": "dns-dnspod"
|
||||||
|
},
|
||||||
|
"domainoffensive": {
|
||||||
|
"name": "DomainOffensive (do.de)",
|
||||||
|
"package_name": "certbot-dns-do",
|
||||||
|
"version": "~=0.31.0",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_do_api_token = YOUR_DO_DE_AUTH_TOKEN",
|
||||||
|
"full_plugin_name": "dns-do"
|
||||||
|
},
|
||||||
|
"domeneshop": {
|
||||||
|
"name": "Domeneshop",
|
||||||
|
"package_name": "certbot-dns-domeneshop",
|
||||||
|
"version": "~=0.2.8",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_domeneshop_client_token=YOUR_DOMENESHOP_CLIENT_TOKEN\ndns_domeneshop_client_secret=YOUR_DOMENESHOP_CLIENT_SECRET",
|
||||||
|
"full_plugin_name": "dns-domeneshop"
|
||||||
|
},
|
||||||
|
"dynu": {
|
||||||
|
"name": "Dynu",
|
||||||
|
"package_name": "certbot-dns-dynu",
|
||||||
|
"version": "~=0.0.1",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_dynu_auth_token = YOUR_DYNU_AUTH_TOKEN",
|
||||||
|
"full_plugin_name": "dns-dynu"
|
||||||
|
},
|
||||||
|
"easydns": {
|
||||||
|
"name": "easyDNS",
|
||||||
|
"package_name": "certbot-dns-easydns",
|
||||||
|
"version": "~=0.1.2",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_easydns_usertoken = YOUR_EASYDNS_USERTOKEN\ndns_easydns_userkey = YOUR_EASYDNS_USERKEY\ndns_easydns_endpoint = https://rest.easydns.net",
|
||||||
|
"full_plugin_name": "dns-easydns"
|
||||||
|
},
|
||||||
|
"eurodns": {
|
||||||
|
"name": "EuroDNS",
|
||||||
|
"package_name": "certbot-dns-eurodns",
|
||||||
|
"version": "~=0.0.4",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_eurodns_applicationId = myuser\ndns_eurodns_apiKey = mysecretpassword\ndns_eurodns_endpoint = https://rest-api.eurodns.com/user-api-gateway/proxy",
|
||||||
|
"full_plugin_name": "dns-eurodns"
|
||||||
|
},
|
||||||
|
"freedns": {
|
||||||
|
"name": "FreeDNS",
|
||||||
|
"package_name": "certbot-dns-freedns",
|
||||||
|
"version": "~=0.1.0",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_freedns_username = myremoteuser\ndns_freedns_password = verysecureremoteuserpassword",
|
||||||
|
"full_plugin_name": "dns-freedns"
|
||||||
|
},
|
||||||
|
"gandi": {
|
||||||
|
"name": "Gandi Live DNS",
|
||||||
|
"package_name": "certbot_plugin_gandi",
|
||||||
|
"version": "~=1.5.0",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "# Gandi personal access token\ndns_gandi_token=PERSONAL_ACCESS_TOKEN",
|
||||||
|
"full_plugin_name": "dns-gandi"
|
||||||
|
},
|
||||||
|
"godaddy": {
|
||||||
|
"name": "GoDaddy",
|
||||||
|
"package_name": "certbot-dns-godaddy",
|
||||||
|
"version": "==2.8.0",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_godaddy_secret = 0123456789abcdef0123456789abcdef01234567\ndns_godaddy_key = abcdef0123456789abcdef01234567abcdef0123",
|
||||||
|
"full_plugin_name": "dns-godaddy"
|
||||||
|
},
|
||||||
|
"google": {
|
||||||
|
"name": "Google",
|
||||||
|
"package_name": "certbot-dns-google",
|
||||||
|
"version": "=={{certbot-version}}",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "{\n\"type\": \"service_account\",\n...\n}",
|
||||||
|
"full_plugin_name": "dns-google"
|
||||||
|
},
|
||||||
|
"googledomains": {
|
||||||
|
"name": "GoogleDomainsDNS",
|
||||||
|
"package_name": "certbot-dns-google-domains",
|
||||||
|
"version": "~=0.1.5",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_google_domains_access_token = 0123456789abcdef0123456789abcdef01234567\ndns_google_domains_zone = \"example.com\"",
|
||||||
|
"full_plugin_name": "dns-google-domains"
|
||||||
|
},
|
||||||
|
"he": {
|
||||||
|
"name": "Hurricane Electric",
|
||||||
|
"package_name": "certbot-dns-he",
|
||||||
|
"version": "~=1.0.0",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_he_user = Me\ndns_he_pass = my HE password",
|
||||||
|
"full_plugin_name": "dns-he"
|
||||||
|
},
|
||||||
|
"hetzner": {
|
||||||
|
"name": "Hetzner",
|
||||||
|
"package_name": "certbot-dns-hetzner",
|
||||||
|
"version": "~=1.0.4",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_hetzner_api_token = 0123456789abcdef0123456789abcdef",
|
||||||
|
"full_plugin_name": "dns-hetzner"
|
||||||
|
},
|
||||||
|
"infomaniak": {
|
||||||
|
"name": "Infomaniak",
|
||||||
|
"package_name": "certbot-dns-infomaniak",
|
||||||
|
"version": "~=0.2.2",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_infomaniak_token = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
|
||||||
|
"full_plugin_name": "dns-infomaniak"
|
||||||
|
},
|
||||||
|
"inwx": {
|
||||||
|
"name": "INWX",
|
||||||
|
"package_name": "certbot-dns-inwx",
|
||||||
|
"version": "~=2.1.2",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_inwx_url = https://api.domrobot.com/xmlrpc/\ndns_inwx_username = your_username\ndns_inwx_password = your_password\ndns_inwx_shared_secret = your_shared_secret optional",
|
||||||
|
"full_plugin_name": "dns-inwx"
|
||||||
|
},
|
||||||
|
"ionos": {
|
||||||
|
"name": "IONOS",
|
||||||
|
"package_name": "certbot-dns-ionos",
|
||||||
|
"version": "==2022.11.24",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_ionos_prefix = myapikeyprefix\ndns_ionos_secret = verysecureapikeysecret\ndns_ionos_endpoint = https://api.hosting.ionos.com",
|
||||||
|
"full_plugin_name": "dns-ionos"
|
||||||
|
},
|
||||||
|
"ispconfig": {
|
||||||
|
"name": "ISPConfig",
|
||||||
|
"package_name": "certbot-dns-ispconfig",
|
||||||
|
"version": "~=0.2.0",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_ispconfig_username = myremoteuser\ndns_ispconfig_password = verysecureremoteuserpassword\ndns_ispconfig_endpoint = https://localhost:8080",
|
||||||
|
"full_plugin_name": "dns-ispconfig"
|
||||||
|
},
|
||||||
|
"isset": {
|
||||||
|
"name": "Isset",
|
||||||
|
"package_name": "certbot-dns-isset",
|
||||||
|
"version": "~=0.0.3",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_isset_endpoint=\"https://customer.isset.net/api\"\ndns_isset_token=\"<token>\"",
|
||||||
|
"full_plugin_name": "dns-isset"
|
||||||
|
},
|
||||||
|
"joker": {
|
||||||
|
"name": "Joker",
|
||||||
|
"package_name": "certbot-dns-joker",
|
||||||
|
"version": "~=1.1.0",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_joker_username = <Dynamic DNS Authentication Username>\ndns_joker_password = <Dynamic DNS Authentication Password>\ndns_joker_domain = <Dynamic DNS Domain>",
|
||||||
|
"full_plugin_name": "dns-joker"
|
||||||
|
},
|
||||||
|
"linode": {
|
||||||
|
"name": "Linode",
|
||||||
|
"package_name": "certbot-dns-linode",
|
||||||
|
"version": "=={{certbot-version}}",
|
||||||
|
"dependencies": "acme=={{certbot-version}}",
|
||||||
|
"credentials": "dns_linode_key = 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ64\ndns_linode_version = [<blank>|3|4]",
|
||||||
|
"full_plugin_name": "dns-linode"
|
||||||
|
},
|
||||||
|
"loopia": {
|
||||||
|
"name": "Loopia",
|
||||||
|
"package_name": "certbot-dns-loopia",
|
||||||
|
"version": "~=1.0.0",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_loopia_user = user@loopiaapi\ndns_loopia_password = abcdef0123456789abcdef01234567abcdef0123",
|
||||||
|
"full_plugin_name": "dns-loopia"
|
||||||
|
},
|
||||||
|
"luadns": {
|
||||||
|
"name": "LuaDNS",
|
||||||
|
"package_name": "certbot-dns-luadns",
|
||||||
|
"version": "=={{certbot-version}}",
|
||||||
|
"dependencies": "acme=={{certbot-version}}",
|
||||||
|
"credentials": "dns_luadns_email = user@example.com\ndns_luadns_token = 0123456789abcdef0123456789abcdef",
|
||||||
|
"full_plugin_name": "dns-luadns"
|
||||||
|
},
|
||||||
|
"namecheap": {
|
||||||
|
"name": "Namecheap",
|
||||||
|
"package_name": "certbot-dns-namecheap",
|
||||||
|
"version": "~=1.0.0",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_namecheap_username = 123456\ndns_namecheap_api_key = 0123456789abcdef0123456789abcdef01234567",
|
||||||
|
"full_plugin_name": "dns-namecheap"
|
||||||
|
},
|
||||||
|
"netcup": {
|
||||||
|
"name": "netcup",
|
||||||
|
"package_name": "certbot-dns-netcup",
|
||||||
|
"version": "~=1.0.0",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_netcup_customer_id = 123456\ndns_netcup_api_key = 0123456789abcdef0123456789abcdef01234567\ndns_netcup_api_password = abcdef0123456789abcdef01234567abcdef0123",
|
||||||
|
"full_plugin_name": "dns-netcup"
|
||||||
|
},
|
||||||
|
"njalla": {
|
||||||
|
"name": "Njalla",
|
||||||
|
"package_name": "certbot-dns-njalla",
|
||||||
|
"version": "~=1.0.0",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_njalla_token = 0123456789abcdef0123456789abcdef01234567",
|
||||||
|
"full_plugin_name": "dns-njalla"
|
||||||
|
},
|
||||||
|
"nsone": {
|
||||||
|
"name": "NS1",
|
||||||
|
"package_name": "certbot-dns-nsone",
|
||||||
|
"version": "=={{certbot-version}}",
|
||||||
|
"dependencies": "acme=={{certbot-version}}",
|
||||||
|
"credentials": "dns_nsone_api_key = MDAwMDAwMDAwMDAwMDAw",
|
||||||
|
"full_plugin_name": "dns-nsone"
|
||||||
|
},
|
||||||
|
"oci": {
|
||||||
|
"name": "Oracle Cloud Infrastructure DNS",
|
||||||
|
"package_name": "certbot-dns-oci",
|
||||||
|
"version": "~=0.3.6",
|
||||||
|
"dependencies": "oci",
|
||||||
|
"credentials": "[DEFAULT]\nuser = ocid1.user.oc1...\nfingerprint = xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx\ntenancy = ocid1.tenancy.oc1...\nregion = us-ashburn-1\nkey_file = ~/.oci/oci_api_key.pem",
|
||||||
|
"full_plugin_name": "dns-oci"
|
||||||
|
},
|
||||||
|
"ovh": {
|
||||||
|
"name": "OVH",
|
||||||
|
"package_name": "certbot-dns-ovh",
|
||||||
|
"version": "=={{certbot-version}}",
|
||||||
|
"dependencies": "acme=={{certbot-version}}",
|
||||||
|
"credentials": "dns_ovh_endpoint = ovh-eu\ndns_ovh_application_key = MDAwMDAwMDAwMDAw\ndns_ovh_application_secret = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw\ndns_ovh_consumer_key = MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw",
|
||||||
|
"full_plugin_name": "dns-ovh"
|
||||||
|
},
|
||||||
|
"plesk": {
|
||||||
|
"name": "Plesk",
|
||||||
|
"package_name": "certbot-dns-plesk",
|
||||||
|
"version": "~=0.3.0",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_plesk_username = your-username\ndns_plesk_password = secret\ndns_plesk_api_url = https://plesk-api-host:8443",
|
||||||
|
"full_plugin_name": "dns-plesk"
|
||||||
|
},
|
||||||
|
"porkbun": {
|
||||||
|
"name": "Porkbun",
|
||||||
|
"package_name": "certbot-dns-porkbun",
|
||||||
|
"version": "~=0.2",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_porkbun_key=your-porkbun-api-key\ndns_porkbun_secret=your-porkbun-api-secret",
|
||||||
|
"full_plugin_name": "dns-porkbun"
|
||||||
|
},
|
||||||
|
"powerdns": {
|
||||||
|
"name": "PowerDNS",
|
||||||
|
"package_name": "certbot-dns-powerdns",
|
||||||
|
"version": "~=0.2.1",
|
||||||
|
"dependencies": "PyYAML==5.3.1",
|
||||||
|
"credentials": "dns_powerdns_api_url = https://api.mypowerdns.example.org\ndns_powerdns_api_key = AbCbASsd!@34",
|
||||||
|
"full_plugin_name": "dns-powerdns"
|
||||||
|
},
|
||||||
|
"regru": {
|
||||||
|
"name": "reg.ru",
|
||||||
|
"package_name": "certbot-regru",
|
||||||
|
"version": "~=1.0.2",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_username=username\ndns_password=password",
|
||||||
|
"full_plugin_name": "dns"
|
||||||
|
},
|
||||||
|
"rfc2136": {
|
||||||
|
"name": "RFC 2136",
|
||||||
|
"package_name": "certbot-dns-rfc2136",
|
||||||
|
"version": "=={{certbot-version}}",
|
||||||
|
"dependencies": "acme=={{certbot-version}}",
|
||||||
|
"credentials": "# Target DNS server\ndns_rfc2136_server = 192.0.2.1\n# Target DNS port\ndns_rfc2136_port = 53\n# TSIG key name\ndns_rfc2136_name = keyname.\n# TSIG key secret\ndns_rfc2136_secret = 4q4wM/2I180UXoMyN4INVhJNi8V9BCV+jMw2mXgZw/CSuxUT8C7NKKFs AmKd7ak51vWKgSl12ib86oQRPkpDjg==\n# TSIG key algorithm\ndns_rfc2136_algorithm = HMAC-SHA512",
|
||||||
|
"full_plugin_name": "dns-rfc2136"
|
||||||
|
},
|
||||||
|
"route53": {
|
||||||
|
"name": "Route 53 (Amazon)",
|
||||||
|
"package_name": "certbot-dns-route53",
|
||||||
|
"version": "=={{certbot-version}}",
|
||||||
|
"dependencies": "acme=={{certbot-version}}",
|
||||||
|
"credentials": "[default]\naws_access_key_id=AKIAIOSFODNN7EXAMPLE\naws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
|
||||||
|
"full_plugin_name": "dns-route53"
|
||||||
|
},
|
||||||
|
"strato": {
|
||||||
|
"name": "Strato",
|
||||||
|
"package_name": "certbot-dns-strato",
|
||||||
|
"version": "~=0.2.1",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_strato_username = user\ndns_strato_password = pass\n# uncomment if youre using two factor authentication:\n# dns_strato_totp_devicename = 2fa_device\n# dns_strato_totp_secret = 2fa_secret\n#\n# uncomment if domain name contains special characters\n# insert domain display name as seen on your account page here\n# dns_strato_domain_display_name = my-punicode-url.de\n#\n# if youre not using strato.de or another special endpoint you can customise it below\n# you will probably only need to adjust the host, but you can also change the complete endpoint url\n# dns_strato_custom_api_scheme = https\n# dns_strato_custom_api_host = www.strato.de\n# dns_strato_custom_api_port = 443\n# dns_strato_custom_api_path = \"/apps/CustomerService\"",
|
||||||
|
"full_plugin_name": "dns-strato"
|
||||||
|
},
|
||||||
|
"timeweb": {
|
||||||
|
"name": "Timeweb Cloud",
|
||||||
|
"package_name": "certbot-dns-timeweb",
|
||||||
|
"version": "~=1.0.1",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_timeweb_api_key = XXXXXXXXXXXXXXXXXXX",
|
||||||
|
"full_plugin_name": "dns-timeweb"
|
||||||
|
},
|
||||||
|
"transip": {
|
||||||
|
"name": "TransIP",
|
||||||
|
"package_name": "certbot-dns-transip",
|
||||||
|
"version": "~=0.5.2",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_transip_username = my_username\ndns_transip_key_file = /etc/letsencrypt/transip-rsa.key",
|
||||||
|
"full_plugin_name": "dns-transip"
|
||||||
|
},
|
||||||
|
"tencentcloud": {
|
||||||
|
"name": "Tencent Cloud",
|
||||||
|
"package_name": "certbot-dns-tencentcloud",
|
||||||
|
"version": "~=2.0.2",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_tencentcloud_secret_id = TENCENT_CLOUD_SECRET_ID\ndns_tencentcloud_secret_key = TENCENT_CLOUD_SECRET_KEY",
|
||||||
|
"full_plugin_name": "dns-tencentcloud"
|
||||||
|
},
|
||||||
|
"vultr": {
|
||||||
|
"name": "Vultr",
|
||||||
|
"package_name": "certbot-dns-vultr",
|
||||||
|
"version": "~=1.1.0",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_vultr_key = YOUR_VULTR_API_KEY",
|
||||||
|
"full_plugin_name": "dns-vultr"
|
||||||
|
},
|
||||||
|
"websupport": {
|
||||||
|
"name": "Websupport.sk",
|
||||||
|
"package_name": "certbot-dns-websupport",
|
||||||
|
"version": "~=2.0.1",
|
||||||
|
"dependencies": "",
|
||||||
|
"credentials": "dns_websupport_identifier = <api_key>\ndns_websupport_secret_key = <secret>",
|
||||||
|
"full_plugin_name": "dns-websupport"
|
||||||
|
}
|
||||||
|
}
|
@ -3,14 +3,22 @@
|
|||||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
. "$DIR/../.common.sh"
|
. "$DIR/../.common.sh"
|
||||||
|
|
||||||
DOCKER_IMAGE=jc21/nginx-full:certbot-node
|
DOCKER_IMAGE=nginxproxymanager/nginx-full:certbot-node
|
||||||
|
|
||||||
# Ensure docker exists
|
# Ensure docker exists
|
||||||
if hash docker 2>/dev/null; then
|
if hash docker 2>/dev/null; then
|
||||||
docker pull "${DOCKER_IMAGE}"
|
docker pull "${DOCKER_IMAGE}"
|
||||||
cd "${DIR}/../.."
|
cd "${DIR}/../.."
|
||||||
echo -e "${BLUE}❯ ${CYAN}Building Frontend ...${RESET}"
|
echo -e "${BLUE}❯ ${CYAN}Building Frontend ...${RESET}"
|
||||||
docker run --rm -e CI=true -v "$(pwd)/frontend:/app/frontend" -v "$(pwd)/global:/app/global" -w /app/frontend "$DOCKER_IMAGE" sh -c "yarn install && yarn build && yarn build && chown -R $(id -u):$(id -g) /app/frontend"
|
|
||||||
|
docker run --rm \
|
||||||
|
-e CI=true \
|
||||||
|
-e NODE_OPTIONS=--openssl-legacy-provider \
|
||||||
|
-v "$(pwd)/frontend:/app/frontend" \
|
||||||
|
-v "$(pwd)/global:/app/global" \
|
||||||
|
-w /app/frontend "$DOCKER_IMAGE" \
|
||||||
|
sh -c "yarn install && yarn build && yarn build && chown -R $(id -u):$(id -g) /app/frontend"
|
||||||
|
|
||||||
echo -e "${BLUE}❯ ${GREEN}Building Frontend Complete${RESET}"
|
echo -e "${BLUE}❯ ${GREEN}Building Frontend Complete${RESET}"
|
||||||
else
|
else
|
||||||
echo -e "${RED}❯ docker command is not available${RESET}"
|
echo -e "${RED}❯ docker command is not available${RESET}"
|
||||||
|
@ -1,23 +1,31 @@
|
|||||||
#!/bin/bash -e
|
#!/bin/bash -e
|
||||||
|
|
||||||
DOCKER_IMAGE=jc21/nginx-full:certbot-node
|
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
. "$DIR/../.common.sh"
|
||||||
|
|
||||||
|
DOCKER_IMAGE=nginxproxymanager/nginx-full:certbot-node
|
||||||
docker pull "${DOCKER_IMAGE}"
|
docker pull "${DOCKER_IMAGE}"
|
||||||
|
|
||||||
# Test
|
# Test
|
||||||
|
echo -e "${BLUE}❯ ${CYAN}Testing backend ...${RESET}"
|
||||||
docker run --rm \
|
docker run --rm \
|
||||||
-v "$(pwd)/backend:/app" \
|
-v "$(pwd)/backend:/app" \
|
||||||
-v "$(pwd)/global:/app/global" \
|
-v "$(pwd)/global:/app/global" \
|
||||||
-w /app \
|
-w /app \
|
||||||
"${DOCKER_IMAGE}" \
|
"${DOCKER_IMAGE}" \
|
||||||
sh -c 'yarn install && yarn eslint . && rm -rf node_modules'
|
sh -c 'yarn install && yarn eslint . && rm -rf node_modules'
|
||||||
|
echo -e "${BLUE}❯ ${GREEN}Testing Complete${RESET}"
|
||||||
|
|
||||||
# Build
|
# Build
|
||||||
docker build --pull --no-cache --squash --compress \
|
echo -e "${BLUE}❯ ${CYAN}Building ...${RESET}"
|
||||||
|
docker build --pull --no-cache --compress \
|
||||||
-t "${IMAGE}:ci-${BUILD_NUMBER}" \
|
-t "${IMAGE}:ci-${BUILD_NUMBER}" \
|
||||||
-f docker/Dockerfile \
|
-f docker/Dockerfile \
|
||||||
|
--progress=plain \
|
||||||
--build-arg TARGETPLATFORM=linux/amd64 \
|
--build-arg TARGETPLATFORM=linux/amd64 \
|
||||||
--build-arg BUILDPLATFORM=linux/amd64 \
|
--build-arg BUILDPLATFORM=linux/amd64 \
|
||||||
--build-arg BUILD_VERSION="${BUILD_VERSION}" \
|
--build-arg BUILD_VERSION="${BUILD_VERSION}" \
|
||||||
--build-arg BUILD_COMMIT="${BUILD_COMMIT}" \
|
--build-arg BUILD_COMMIT="${BUILD_COMMIT}" \
|
||||||
--build-arg BUILD_DATE="$(date '+%Y-%m-%d %T %Z')" \
|
--build-arg BUILD_DATE="$(date '+%Y-%m-%d %T %Z')" \
|
||||||
.
|
.
|
||||||
|
echo -e "${BLUE}❯ ${GREEN}Building Complete${RESET}"
|
||||||
|
@ -7,7 +7,7 @@ DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|||||||
if hash docker 2>/dev/null; then
|
if hash docker 2>/dev/null; then
|
||||||
cd "${DIR}/.."
|
cd "${DIR}/.."
|
||||||
echo -e "${BLUE}❯ ${CYAN}Building Docs ...${RESET}"
|
echo -e "${BLUE}❯ ${CYAN}Building Docs ...${RESET}"
|
||||||
docker run --rm -e CI=true -v "$(pwd)/docs:/app/docs" -w /app/docs node:alpine sh -c "yarn install && yarn build && chown -R $(id -u):$(id -g) /app/docs"
|
docker run --rm -e CI=true -v "$(pwd)/docs:/app/docs" -w /app/docs node:alpine sh -c "yarn set version berry && yarn install && yarn build && chown -R $(id -u):$(id -g) /app/docs"
|
||||||
echo -e "${BLUE}❯ ${GREEN}Building Docs Complete${RESET}"
|
echo -e "${BLUE}❯ ${GREEN}Building Docs Complete${RESET}"
|
||||||
else
|
else
|
||||||
echo -e "${RED}❯ docker command is not available${RESET}"
|
echo -e "${RED}❯ docker command is not available${RESET}"
|
||||||
|
48
test/cypress/integration/api/Hosts.spec.js
Normal file
48
test/cypress/integration/api/Hosts.spec.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/// <reference types="Cypress" />
|
||||||
|
|
||||||
|
describe('Hosts endpoints', () => {
|
||||||
|
let token;
|
||||||
|
|
||||||
|
before(() => {
|
||||||
|
cy.getToken().then((tok) => {
|
||||||
|
token = tok;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should be able to create a http host', function() {
|
||||||
|
cy.task('backendApiPost', {
|
||||||
|
token: token,
|
||||||
|
path: '/api/nginx/proxy-hosts',
|
||||||
|
data: {
|
||||||
|
domain_names: ['test.example.com'],
|
||||||
|
forward_scheme: 'http',
|
||||||
|
forward_host: '1.1.1.1',
|
||||||
|
forward_port: 80,
|
||||||
|
access_list_id: '0',
|
||||||
|
certificate_id: 0,
|
||||||
|
meta: {
|
||||||
|
letsencrypt_agree: false,
|
||||||
|
dns_challenge: false
|
||||||
|
},
|
||||||
|
advanced_config: '',
|
||||||
|
locations: [],
|
||||||
|
block_exploits: false,
|
||||||
|
caching_enabled: false,
|
||||||
|
allow_websocket_upgrade: false,
|
||||||
|
http2_support: false,
|
||||||
|
hsts_enabled: false,
|
||||||
|
hsts_subdomains: false,
|
||||||
|
ssl_forced: false
|
||||||
|
}
|
||||||
|
}).then((data) => {
|
||||||
|
cy.validateSwaggerSchema('post', 201, '/nginx/proxy-hosts', data);
|
||||||
|
expect(data).to.have.property('id');
|
||||||
|
expect(data.id).to.be.greaterThan(0);
|
||||||
|
expect(data).to.have.property('enabled');
|
||||||
|
expect(data.enabled).to.be.greaterThan(0);
|
||||||
|
expect(data).to.have.property('meta');
|
||||||
|
expect(typeof data.meta.nginx_online).to.be.equal('undefined');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user