diff --git a/.version b/.version index 8bbb6e4..c6436a8 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.10.1 +2.10.2 diff --git a/Jenkinsfile b/Jenkinsfile index cb597eb..862b247 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -91,6 +91,10 @@ pipeline { // 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 sh 'rm -rf test/results' diff --git a/README.md b/README.md index d5a7473..eefa11e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@
-
+
diff --git a/backend/doc/api.swagger.json b/backend/doc/api.swagger.json
index 06c0256..3fa19fc 100644
--- a/backend/doc/api.swagger.json
+++ b/backend/doc/api.swagger.json
@@ -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": {
"get": {
"operationId": "schema",
@@ -55,14 +259,10 @@
"get": {
"operationId": "refreshToken",
"summary": "Refresh your access token",
- "tags": [
- "Tokens"
- ],
+ "tags": ["Tokens"],
"security": [
{
- "BearerAuth": [
- "tokens"
- ]
+ "BearerAuth": ["tokens"]
}
],
"responses": {
@@ -104,19 +304,14 @@
"scope": {
"minLength": 1,
"type": "string",
- "enum": [
- "user"
- ]
+ "enum": ["user"]
},
"secret": {
"minLength": 1,
"type": "string"
}
},
- "required": [
- "identity",
- "secret"
- ],
+ "required": ["identity", "secret"],
"type": "object"
}
}
@@ -144,23 +339,17 @@
}
},
"summary": "Request a new access token from credentials",
- "tags": [
- "Tokens"
- ]
+ "tags": ["Tokens"]
}
},
"/settings": {
"get": {
"operationId": "getSettings",
"summary": "Get all settings",
- "tags": [
- "Settings"
- ],
+ "tags": ["Settings"],
"security": [
{
- "BearerAuth": [
- "settings"
- ]
+ "BearerAuth": ["settings"]
}
],
"responses": {
@@ -194,14 +383,10 @@
"get": {
"operationId": "getSetting",
"summary": "Get a setting",
- "tags": [
- "Settings"
- ],
+ "tags": ["Settings"],
"security": [
{
- "BearerAuth": [
- "settings"
- ]
+ "BearerAuth": ["settings"]
}
],
"parameters": [
@@ -244,14 +429,10 @@
"put": {
"operationId": "updateSetting",
"summary": "Update a setting",
- "tags": [
- "Settings"
- ],
+ "tags": ["Settings"],
"security": [
{
- "BearerAuth": [
- "settings"
- ]
+ "BearerAuth": ["settings"]
}
],
"parameters": [
@@ -305,14 +486,10 @@
"get": {
"operationId": "getUsers",
"summary": "Get all users",
- "tags": [
- "Users"
- ],
+ "tags": ["Users"],
"security": [
{
- "BearerAuth": [
- "users"
- ]
+ "BearerAuth": ["users"]
}
],
"parameters": [
@@ -322,9 +499,7 @@
"description": "Expansions",
"schema": {
"type": "string",
- "enum": [
- "permissions"
- ]
+ "enum": ["permissions"]
}
}
],
@@ -345,9 +520,7 @@
"name": "Jamie Curnow",
"nickname": "James",
"avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
- "roles": [
- "admin"
- ]
+ "roles": ["admin"]
}
]
},
@@ -362,9 +535,7 @@
"name": "Jamie Curnow",
"nickname": "James",
"avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
- "roles": [
- "admin"
- ],
+ "roles": ["admin"],
"permissions": {
"visibility": "all",
"proxy_hosts": "manage",
@@ -389,14 +560,10 @@
"post": {
"operationId": "createUser",
"summary": "Create a User",
- "tags": [
- "Users"
- ],
+ "tags": ["Users"],
"security": [
{
- "BearerAuth": [
- "users"
- ]
+ "BearerAuth": ["users"]
}
],
"parameters": [
@@ -426,9 +593,7 @@
"name": "Jamie Curnow",
"nickname": "James",
"avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
- "roles": [
- "admin"
- ],
+ "roles": ["admin"],
"permissions": {
"visibility": "all",
"proxy_hosts": "manage",
@@ -454,14 +619,10 @@
"get": {
"operationId": "getUser",
"summary": "Get a user",
- "tags": [
- "Users"
- ],
+ "tags": ["Users"],
"security": [
{
- "BearerAuth": [
- "users"
- ]
+ "BearerAuth": ["users"]
}
],
"parameters": [
@@ -501,9 +662,7 @@
"name": "Jamie Curnow",
"nickname": "James",
"avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
- "roles": [
- "admin"
- ]
+ "roles": ["admin"]
}
}
},
@@ -518,14 +677,10 @@
"put": {
"operationId": "updateUser",
"summary": "Update a User",
- "tags": [
- "Users"
- ],
+ "tags": ["Users"],
"security": [
{
- "BearerAuth": [
- "users"
- ]
+ "BearerAuth": ["users"]
}
],
"parameters": [
@@ -574,9 +729,7 @@
"name": "Jamie Curnow",
"nickname": "James",
"avatar": "//www.gravatar.com/avatar/6193176330f8d38747f038c170ddb193?default=mm",
- "roles": [
- "admin"
- ]
+ "roles": ["admin"]
}
}
},
@@ -591,14 +744,10 @@
"delete": {
"operationId": "deleteUser",
"summary": "Delete a User",
- "tags": [
- "Users"
- ],
+ "tags": ["Users"],
"security": [
{
- "BearerAuth": [
- "users"
- ]
+ "BearerAuth": ["users"]
}
],
"parameters": [
@@ -637,14 +786,10 @@
"put": {
"operationId": "updateUserAuth",
"summary": "Update a User's Authentication",
- "tags": [
- "Users"
- ],
+ "tags": ["Users"],
"security": [
{
- "BearerAuth": [
- "users"
- ]
+ "BearerAuth": ["users"]
}
],
"parameters": [
@@ -700,14 +845,10 @@
"put": {
"operationId": "updateUserPermissions",
"summary": "Update a User's Permissions",
- "tags": [
- "Users"
- ],
+ "tags": ["Users"],
"security": [
{
- "BearerAuth": [
- "users"
- ]
+ "BearerAuth": ["users"]
}
],
"parameters": [
@@ -755,14 +896,10 @@
"put": {
"operationId": "loginAsUser",
"summary": "Login as this user",
- "tags": [
- "Users"
- ],
+ "tags": ["Users"],
"security": [
{
- "BearerAuth": [
- "users"
- ]
+ "BearerAuth": ["users"]
}
],
"parameters": [
@@ -797,9 +934,7 @@
"name": "Jamie Curnow",
"nickname": "James",
"avatar": "//www.gravatar.com/avatar/3c8d73f45fd8763f827b964c76e6032a?default=mm",
- "roles": [
- "admin"
- ]
+ "roles": ["admin"]
}
}
}
@@ -807,11 +942,7 @@
"schema": {
"type": "object",
"description": "Login object",
- "required": [
- "expires",
- "token",
- "user"
- ],
+ "required": ["expires", "token", "user"],
"additionalProperties": false,
"properties": {
"expires": {
@@ -840,14 +971,10 @@
"get": {
"operationId": "reportsHosts",
"summary": "Report on Host Statistics",
- "tags": [
- "Reports"
- ],
+ "tags": ["Reports"],
"security": [
{
- "BearerAuth": [
- "reports"
- ]
+ "BearerAuth": ["reports"]
}
],
"responses": {
@@ -878,14 +1005,10 @@
"get": {
"operationId": "getAuditLog",
"summary": "Get Audit Log",
- "tags": [
- "Audit Log"
- ],
+ "tags": ["Audit Log"],
"security": [
{
- "BearerAuth": [
- "audit-log"
- ]
+ "BearerAuth": ["audit-log"]
}
],
"responses": {
@@ -925,10 +1048,7 @@
"type": "object",
"description": "Health object",
"additionalProperties": false,
- "required": [
- "status",
- "version"
- ],
+ "required": ["status", "version"],
"properties": {
"status": {
"type": "string",
@@ -944,11 +1064,7 @@
"revision": 0
},
"additionalProperties": false,
- "required": [
- "major",
- "minor",
- "revision"
- ],
+ "required": ["major", "minor", "revision"],
"properties": {
"major": {
"type": "integer",
@@ -969,10 +1085,7 @@
"TokenObject": {
"type": "object",
"description": "Token object",
- "required": [
- "expires",
- "token"
- ],
+ "required": ["expires", "token"],
"additionalProperties": false,
"properties": {
"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": {
"type": "object",
"description": "Setting object",
- "required": [
- "id",
- "name",
- "description",
- "value",
- "meta"
- ],
+ "required": ["id", "name", "description", "value", "meta"],
"additionalProperties": false,
"properties": {
"id": {
@@ -1057,17 +1301,7 @@
"UserObject": {
"type": "object",
"description": "User object",
- "required": [
- "id",
- "created_on",
- "modified_on",
- "is_disabled",
- "email",
- "name",
- "nickname",
- "avatar",
- "roles"
- ],
+ "required": ["id", "created_on", "modified_on", "is_disabled", "email", "name", "nickname", "avatar", "roles"],
"additionalProperties": false,
"properties": {
"id": {
@@ -1117,9 +1351,7 @@
},
"roles": {
"description": "Roles applied",
- "example": [
- "admin"
- ],
+ "example": ["admin"],
"type": "array",
"items": {
"type": "string"
@@ -1137,10 +1369,7 @@
"AuthObject": {
"type": "object",
"description": "Authentication Object",
- "required": [
- "type",
- "secret"
- ],
+ "required": ["type", "secret"],
"properties": {
"type": {
"type": "string",
@@ -1167,64 +1396,37 @@
"visibility": {
"type": "string",
"description": "Visibility Type",
- "enum": [
- "all",
- "user"
- ]
+ "enum": ["all", "user"]
},
"access_lists": {
"type": "string",
"description": "Access Lists Permissions",
- "enum": [
- "hidden",
- "view",
- "manage"
- ]
+ "enum": ["hidden", "view", "manage"]
},
"dead_hosts": {
"type": "string",
"description": "404 Hosts Permissions",
- "enum": [
- "hidden",
- "view",
- "manage"
- ]
+ "enum": ["hidden", "view", "manage"]
},
"proxy_hosts": {
"type": "string",
"description": "Proxy Hosts Permissions",
- "enum": [
- "hidden",
- "view",
- "manage"
- ]
+ "enum": ["hidden", "view", "manage"]
},
"redirection_hosts": {
"type": "string",
"description": "Redirection Permissions",
- "enum": [
- "hidden",
- "view",
- "manage"
- ]
+ "enum": ["hidden", "view", "manage"]
},
"streams": {
"type": "string",
"description": "Streams Permissions",
- "enum": [
- "hidden",
- "view",
- "manage"
- ]
+ "enum": ["hidden", "view", "manage"]
},
"certificates": {
"type": "string",
"description": "Certificates Permissions",
- "enum": [
- "hidden",
- "view",
- "manage"
- ]
+ "enum": ["hidden", "view", "manage"]
}
}
},
@@ -1251,4 +1453,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/docker/docker-compose.ci.yml b/docker/docker-compose.ci.yml
index c090e19..9f4edc0 100644
--- a/docker/docker-compose.ci.yml
+++ b/docker/docker-compose.ci.yml
@@ -33,6 +33,8 @@ services:
LE_STAGING: 'true'
FORCE_COLOR: 1
DB_SQLITE_FILE: '/data/mydb.sqlite'
+ PUID: 1000
+ PGID: 1000
volumes:
- npm_data:/data
expose:
diff --git a/docker/rootfs/bin/common.sh b/docker/rootfs/bin/common.sh
index b95ff94..0bc6468 100644
--- a/docker/rootfs/bin/common.sh
+++ b/docker/rootfs/bin/common.sh
@@ -9,6 +9,19 @@ RED='\E[1;31m'
RESET='\E[0m'
export CYAN BLUE YELLOW RED RESET
+PUID=${PUID:-0}
+PGID=${PGID:-0}
+
+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}"
}
diff --git a/docker/rootfs/etc/nginx/nginx.conf b/docker/rootfs/etc/nginx/nginx.conf
index 438c1bd..c2ee97c 100644
--- a/docker/rootfs/etc/nginx/nginx.conf
+++ b/docker/rootfs/etc/nginx/nginx.conf
@@ -1,6 +1,7 @@
# run nginx in foreground
daemon off;
pid /run/nginx/nginx.pid;
+user npmuser;
# Set number of worker processes automatically based on number of CPU cores.
worker_processes auto;
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/backend/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/backend/run
index b828764..e8ffa17 100755
--- a/docker/rootfs/etc/s6-overlay/s6-rc.d/backend/run
+++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/backend/run
@@ -5,15 +5,14 @@ set -e
. /bin/common.sh
+cd /app || exit 1
+
log_info 'Starting backend ...'
-if [ "$DEVELOPMENT" == "true" ]; then
- cd /app || exit 1
- # If yarn install fails: add --verbose --network-concurrency 1
+if [ "${DEVELOPMENT:-}" = 'true' ]; then
s6-setuidgid npmuser yarn install
exec s6-setuidgid npmuser bash -c 'export HOME=/tmp/npmuserhome;node --max_old_space_size=250 --abort_on_uncaught_exception node_modules/nodemon/bin/nodemon.js'
else
- cd /app || exit 1
while :
do
s6-setuidgid npmuser bash -c 'export HOME=/tmp/npmuserhome;node --abort_on_uncaught_exception --max_old_space_size=250 index.js'
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/frontend/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/frontend/run
index 7a80c25..1181c53 100755
--- a/docker/rootfs/etc/s6-overlay/s6-rc.d/frontend/run
+++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/frontend/run
@@ -5,15 +5,15 @@ set -e
# This service is DEVELOPMENT only.
-if [ "$DEVELOPMENT" == "true" ]; then
+if [ "$DEVELOPMENT" = 'true' ]; then
. /bin/common.sh
cd /app/frontend || exit 1
- log_info 'Starting frontend ...'
HOME=/tmp/npmuserhome
export HOME
mkdir -p /app/frontend/dist
- chown -R npmuser:npmuser /app/frontend/dist
- # If yarn install fails: add --verbose --network-concurrency 1
+ chown -R "$PUID:$PGID" /app/frontend/dist
+
+ log_info 'Starting frontend ...'
s6-setuidgid npmuser yarn install
exec s6-setuidgid npmuser yarn watch
else
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/nginx/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/nginx/run
index 044e4d3..fa8c1fc 100755
--- a/docker/rootfs/etc/s6-overlay/s6-rc.d/nginx/run
+++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/nginx/run
@@ -6,5 +6,4 @@ set -e
. /bin/common.sh
log_info 'Starting nginx ...'
-
exec s6-setuidgid npmuser nginx
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/10-npmuser.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/10-npmuser.sh
index f8da7b8..c5cf543 100755
--- a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/10-npmuser.sh
+++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/10-npmuser.sh
@@ -3,23 +3,18 @@
set -e
-PUID=${PUID:-911}
-PGID=${PGID:-911}
-
log_info 'Configuring npmuser ...'
-groupmod -g 1000 users || exit 1
-
if id -u npmuser; then
# user already exists
- usermod -u "${PUID}" npmuser || exit 1
+ usermod -u "$PUID" npmuser || exit 1
else
# Add npmuser user
- useradd -u "${PUID}" -U -d /tmp/npmuserhome -s /bin/false npmuser || exit 1
+ useradd -o -u "$PUID" -U -d /tmp/npmuserhome -s /bin/false npmuser || exit 1
fi
-usermod -G users npmuser || exit 1
-groupmod -o -g "${PGID}" npmuser || exit 1
+usermod -G "$PGID" npmuser || exit 1
+groupmod -o -g "$PGID" npmuser || exit 1
# Home for npmuser
mkdir -p /tmp/npmuserhome
-chown -R npmuser:npmuser /tmp/npmuserhome
+chown -R "$PUID:$PGID" /tmp/npmuserhome
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/30-ownership.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/30-ownership.sh
index 3f5c481..684166e 100755
--- a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/30-ownership.sh
+++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/30-ownership.sh
@@ -9,16 +9,16 @@ log_info 'Setting ownership ...'
chown root /tmp/nginx
# npmuser
-chown -R npmuser:npmuser /data
-chown -R npmuser:npmuser /etc/letsencrypt
-chown -R npmuser:npmuser /run/nginx
-chown -R npmuser:npmuser /tmp/nginx
-chown -R npmuser:npmuser /var/cache/nginx
-chown -R npmuser:npmuser /var/lib/logrotate
-chown -R npmuser:npmuser /var/lib/nginx
-chown -R npmuser:npmuser /var/log/nginx
+chown -R "$PUID:$PGID" /data \
+ /etc/letsencrypt \
+ /run/nginx \
+ /tmp/nginx \
+ /var/cache/nginx \
+ /var/lib/logrotate \
+ /var/lib/nginx \
+ /var/log/nginx
# Don't chown entire /etc/nginx folder as this causes crashes on some systems
-chown -R npmuser:npmuser /etc/nginx/nginx
-chown -R npmuser:npmuser /etc/nginx/nginx.conf
-chown -R npmuser:npmuser /etc/nginx/conf.d
+chown -R "$PUID:$PGID" /etc/nginx/nginx \
+ /etc/nginx/nginx.conf \
+ /etc/nginx/conf.d
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh
index bcd64d2..bc27eb1 100755
--- a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh
+++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/50-ipv6.sh
@@ -29,7 +29,7 @@ process_folder () {
done
# ensure the files are still owned by the npmuser
- chown -R npmuser:npmuser "$1"
+ chown -R "$PUID:$PGID" "$1"
}
process_folder /etc/nginx/conf.d
diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/90-banner.sh b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/90-banner.sh
index af51b46..7991ddf 100755
--- a/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/90-banner.sh
+++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/prepare/90-banner.sh
@@ -3,15 +3,15 @@
set -e
-echo
-echo "-------------------------------------
+echo "
+-------------------------------------
_ _ ____ __ __
| \ | | _ \| \/ |
| \| | |_) | |\/| |
| |\ | __/| | | |
|_| \_|_| |_| |_|
-------------------------------------
-User UID: $(id -u npmuser)
-User GID: $(id -g npmuser)
+User ID: $PUID
+Group ID: $PGID
-------------------------------------
"
diff --git a/docs/advanced-config/README.md b/docs/advanced-config/README.md
index 7cb8a3a..a0acdda 100644
--- a/docs/advanced-config/README.md
+++ b/docs/advanced-config/README.md
@@ -1,5 +1,26 @@
# 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
For those who have a few of their upstream services running in Docker on the same Docker
diff --git a/docs/setup/README.md b/docs/setup/README.md
index a78d79e..032b714 100644
--- a/docs/setup/README.md
+++ b/docs/setup/README.md
@@ -64,9 +64,6 @@ services:
# Add any other Stream port you want to expose
# - '21:21' # FTP
environment:
- # Unix user and group IDs, optional
- PUID: 1000
- PGID: 1000
# Mysql/Maria connection parameters:
DB_MYSQL_HOST: "db"
DB_MYSQL_PORT: 3306
@@ -90,7 +87,7 @@ services:
MYSQL_USER: 'npm'
MYSQL_PASSWORD: 'npm'
volumes:
- - ./data/mysql:/var/lib/mysql
+ - ./mysql:/var/lib/mysql
```
::: warning
diff --git a/test/cypress/integration/api/Hosts.spec.js b/test/cypress/integration/api/Hosts.spec.js
new file mode 100644
index 0000000..4652c8e
--- /dev/null
+++ b/test/cypress/integration/api/Hosts.spec.js
@@ -0,0 +1,48 @@
+///