Compare commits

...

30 Commits

Author SHA1 Message Date
824c837a38 Merge pull request #2906 from NginxProxyManager/develop
Fix certbot plugins install when using PUID/PGID
2023-05-10 14:40:15 +10:00
2a06384a4a Merge branch 'master' into develop 2023-05-10 14:40:06 +10:00
05307aa253 Fix certbot plugins install when using PUID/PGID 2023-05-10 14:39:08 +10:00
3d2406ac3d Merge pull request #2905 from NginxProxyManager/develop
v2.10.3
2023-05-10 14:09:04 +10:00
0127dc7f03 Bump version 2023-05-10 11:32:22 +10:00
4349d42636 Merge pull request #2904 from NginxProxyManager/s6-verbose
Fixes for s6 timeout at startup
2023-05-10 11:31:17 +10:00
4b6f9d9419 Remove s6 service timeout 2023-05-10 09:57:24 +10:00
c3f019c911 Test ipv6 disabled in ci 2023-05-09 08:19:09 +10:00
ecf0290203 Update s6-overlay 2023-05-09 08:15:44 +10:00
4f41fe0c95 Update s6-overlay 2023-05-05 08:46:54 +10:00
c3735fdbbb Missed a file that was explicit verbose 2023-05-04 12:30:27 +10:00
c432c34fb3 Small refactor of user/groups and add checks during startup. Only use -x in bash scripts when DEBUG=true set in env vars 2023-05-04 10:03:06 +10:00
a1245bc161 Split up ownership to indentify point of failure 2023-05-04 08:27:38 +10:00
db4ab1d548 Verbose debugging of s6 scripts 2023-05-03 16:01:27 +10:00
86ddd9c83c Merge pull request #2784 from NginxProxyManager/develop
v2.10.2
2023-03-31 09:37:08 +10:00
67208e43cc Merge branch 'master' into develop 2023-03-31 08:27:00 +10:00
ddf80302c6 Bump version 2023-03-31 08:25:45 +10:00
5f2576946d Merge pull request #2783 from NginxProxyManager/uidgid
Make PUID and PGID optional
2023-03-31 08:25:07 +10:00
9fe07fa6c3 Update documentation 2023-03-30 15:37:59 +10:00
d9b9af543e Fix text replacement whoops 2023-03-30 15:03:57 +10:00
eb2e2e0478 Throw in a docker restart during testing phase 2023-03-30 14:44:15 +10:00
9225d5d442 Tweak test 2023-03-30 13:00:22 +10:00
308a7149ed Tweak test 2023-03-30 12:55:20 +10:00
8a4a7d0caf Allow 201 as success in test result 2023-03-30 12:51:26 +10:00
5d03ede100 Add test for creating a host 2023-03-30 12:44:28 +10:00
4a86bb42cc Different approach, always create npmuser
even if the user id is zero, and then we'll always use it
2023-03-30 11:19:16 +10:00
dad8561ea1 Use numbers for permissions in case npmuser doesn't exist 2023-03-30 10:20:20 +10:00
56a92e5c0e Run as root by default
Optionally run as another user/group only if
the env vars are specified. Should give flexibility
to those who need to run processes as root and open ports
without having to request additional priveleges
2023-03-30 09:04:37 +10:00
9d672f5813 Own this nginx folder too 2023-03-29 14:04:48 +10:00
d5ed70dbb6 Own this nginx folder too 2023-03-29 14:03:58 +10:00
24 changed files with 600 additions and 266 deletions

View File

@ -1 +1 @@
2.10.1
2.10.3

4
Jenkinsfile vendored
View File

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

View File

@ -1,7 +1,7 @@
<p align="center">
<img src="https://nginxproxymanager.com/github.png">
<br><br>
<img src="https://img.shields.io/badge/version-2.10.1-green.svg?style=for-the-badge">
<img src="https://img.shields.io/badge/version-2.10.3-green.svg?style=for-the-badge">
<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">
</a>

View File

@ -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"]
}
}
},

View File

@ -131,7 +131,7 @@ const setupCertbotPlugins = () => {
});
if (plugins.length) {
const install_cmd = '. /opt/certbot/bin/activate && pip install --no-cache-dir --user ' + plugins.join(' ') + ' && deactivate';
const install_cmd = '. /opt/certbot/bin/activate && pip install --no-cache-dir ' + plugins.join(' ') + ' && deactivate';
promises.push(utils.exec(install_cmd));
}

View File

@ -10,9 +10,13 @@ ARG BUILD_VERSION
ARG BUILD_COMMIT
ARG BUILD_DATE
# See: https://github.com/just-containers/s6-overlay/blob/master/README.md
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=1 \
NODE_ENV=production \
NPM_BUILD_VERSION="${BUILD_VERSION}" \
NPM_BUILD_COMMIT="${BUILD_COMMIT}" \

View File

@ -1,9 +1,13 @@
FROM jc21/nginx-full:certbot-node
LABEL maintainer="Jamie Curnow <jc@jc21.com>"
ENV S6_LOGGING=0 \
SUPPRESS_NO_CONFIG_WARNING=1 \
S6_FIX_ATTRS_HIDDEN=1
# See: https://github.com/just-containers/s6-overlay/blob/master/README.md
ENV SUPPRESS_NO_CONFIG_WARNING=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
RUN echo "fs.file-max = 65535" > /etc/sysctl.conf \
&& apt-get update \

View File

@ -33,6 +33,9 @@ services:
LE_STAGING: 'true'
FORCE_COLOR: 1
DB_SQLITE_FILE: '/data/mydb.sqlite'
PUID: 1000
PGID: 1000
DISABLE_IPV6: 'true'
volumes:
- npm_data:/data
expose:

View File

@ -9,6 +9,24 @@ RED='\E[1;31m'
RESET='\E[0m'
export CYAN BLUE YELLOW RED RESET
PUID=${PUID:-0}
PGID=${PGID:-0}
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}"
}
@ -27,3 +45,10 @@ log_fatal () {
/run/s6/basedir/bin/halt
exit 1
}
# param $1: group_name
get_group_id () {
if [ "${1:-}" != '' ]; then
getent group "$1" | cut -d: -f3
fi
}

View File

@ -1,6 +1,7 @@
# run nginx in foreground
daemon off;
pid /run/nginx/nginx.pid;
user npm;
# Set number of worker processes automatically based on number of CPU cores.
worker_processes auto;

View File

@ -5,18 +5,17 @@ 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
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'
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
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'
s6-setuidgid "$PUID:$PGID" bash -c "export HOME=$NPMHOME;node --abort_on_uncaught_exception --max_old_space_size=250 index.js"
sleep 1
done
fi

View File

@ -5,17 +5,17 @@ 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
HOME=$NPMHOME
export HOME
mkdir -p /app/frontend/dist
chown -R npmuser:npmuser /app/frontend/dist
# If yarn install fails: add --verbose --network-concurrency 1
s6-setuidgid npmuser yarn install
exec s6-setuidgid npmuser yarn watch
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
exit 0
fi

View File

@ -6,5 +6,4 @@ set -e
. /bin/common.sh
log_info 'Starting nginx ...'
exec s6-setuidgid npmuser nginx
exec s6-setuidgid "$PUID:$PGID" nginx

View File

@ -9,7 +9,11 @@ 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
. /etc/s6-overlay/s6-rc.d/prepare/10-npmuser.sh
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

View File

@ -1,25 +0,0 @@
#!/command/with-contenv bash
# shellcheck shell=bash
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
else
# Add npmuser user
useradd -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
# Home for npmuser
mkdir -p /tmp/npmuserhome
chown -R npmuser:npmuser /tmp/npmuserhome

View 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"

View File

@ -8,16 +8,20 @@ log_info 'Setting ownership ...'
# root
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
# 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 npmuser:npmuser /etc/nginx/nginx.conf
chown -R npmuser:npmuser /etc/nginx/conf.d
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 -R "$PUID:$PGID" /opt/certbot

View File

@ -1,8 +1,11 @@
#!/bin/bash
#!/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
@ -28,8 +31,8 @@ process_folder () {
sed -E -i "$SED_REGEX" "$FILE"
done
# ensure the files are still owned by the npmuser
chown -R npmuser:npmuser "$1"
# ensure the files are still owned by the npm user
chown -R "$PUID:$PGID" "$1"
}
process_folder /etc/nginx/conf.d

View File

@ -2,16 +2,17 @@
# shellcheck shell=bash
set -e
set +x
echo
echo "-------------------------------------
echo "
-------------------------------------
_ _ ____ __ __
| \ | | _ \| \/ |
| \| | |_) | |\/| |
| |\ | __/| | | |
|_| \_|_| |_| |_|
-------------------------------------
User UID: $(id -u npmuser)
User GID: $(id -g npmuser)
User: $NPMUSER PUID:$PUID ID:$(id -u "$NPMUSER") GROUP:$(id -g "$NPMUSER")
Group: $NPMGROUP PGID:$PGID ID:$(get_group_id "$NPMGROUP")
-------------------------------------
"

View File

@ -8,7 +8,7 @@ BLUE='\E[1;34m'
GREEN='\E[1;32m'
RESET='\E[0m'
S6_OVERLAY_VERSION=3.1.4.1
S6_OVERLAY_VERSION=3.1.5.0
TARGETPLATFORM=${1:unspecified}
# Determine the correct binary file for the architecture given

View File

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

View File

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

View 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');
});
});
});

View File

@ -126,7 +126,7 @@ BackendApi.prototype._putPostJson = function(fn, path, data, returnOnError) {
logger('Response data:', data);
if (!returnOnError && data instanceof Error) {
reject(data);
} else if (!returnOnError && response.statusCode != 200) {
} else if (!returnOnError && (response.statusCode < 200 || response.statusCode >= 300)) {
if (typeof data === 'object' && typeof data.error === 'object' && typeof data.error.message !== 'undefined') {
reject(new Error(data.error.code + ': ' + data.error.message));
} else {