Compare commits

...

435 Commits
dev ... master

Author SHA1 Message Date
nicolas.dorier
28590c301b
Fix crash running /app/force_renew
Some checks failed
Build and publish Docker image / Build and publish Docker image (push) Has been cancelled
2024-01-26 16:07:49 +09:00
nicolas.dorier
3badf7e255
Match all tags starting by v
Some checks failed
Build and publish Docker image / Build and publish Docker image (push) Has been cancelled
2023-12-15 10:25:00 +09:00
nicolas.dorier
c9b35d80be
BTCPayServer modifications
Some checks failed
Build and publish Docker image / Build and publish Docker image (push) Has been cancelled
2023-12-15 10:02:25 +09:00
Nicolas Duchon
796e6319b4
docs: update test status badge 2023-12-10 19:03:19 +01:00
Nicolas Duchon
f4ba86995f
Merge pull request #1072 from nginx-proxy/1064-test-update
test: update wildcard location enumeration test
2023-12-10 19:01:53 +01:00
Nicolas Duchon
2161ae5c4e
Merge pull request #967 from langfingaz/patch-1
add 5s:30s to -wait
2023-12-10 18:58:04 +01:00
Nicolas Duchon
2be93248eb
test: update wildcard location enumeration test 2023-12-10 18:54:37 +01:00
Nicolas Duchon
97b25e7974
Merge pull request #1064 from egormkn/main
fix: prevent infinite loop in enumerate_wildcard_locations
2023-12-10 18:53:50 +01:00
Nicolas Duchon
223972299a
Merge pull request #1069 from nginx-proxy/dependabot/github_actions/docker/setup-buildx-action-3
ci: bump docker/setup-buildx-action from 2 to 3
2023-12-08 18:49:56 +01:00
Nicolas Duchon
f0f44355d0
Merge pull request #1070 from nginx-proxy/dependabot/docker/alpine-3.19.0
build: bump alpine from 3.18.5 to 3.19.0
2023-12-08 18:49:25 +01:00
dependabot[bot]
89e7b9ca7f
build: bump alpine from 3.18.5 to 3.19.0
Bumps alpine from 3.18.5 to 3.19.0.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-08 15:02:44 +00:00
dependabot[bot]
ce3d3bec2f
ci: bump docker/setup-buildx-action from 2 to 3
Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 2 to 3.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-08 15:02:39 +00:00
Nicolas Duchon
1f46460711
Merge pull request #1067 from nginx-proxy/dependabot/docker/nginxproxy/docker-gen-0.10.7
build: bump nginxproxy/docker-gen from 0.10.6 to 0.10.7
2023-12-08 02:06:25 +01:00
Nicolas Duchon
79ca7e9f2b
Merge pull request #1066 from nginx-proxy/dependabot/docker/alpine-3.18.5
build: bump alpine from 3.18.3 to 3.18.5
2023-12-08 02:06:14 +01:00
dependabot[bot]
c57f1b0e6c
build: bump nginxproxy/docker-gen from 0.10.6 to 0.10.7
Bumps nginxproxy/docker-gen from 0.10.6 to 0.10.7.

---
updated-dependencies:
- dependency-name: nginxproxy/docker-gen
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-08 00:56:43 +00:00
Egor Makarenko
71d8369a06 fix: prevent infinite loop in enumerate_wildcard_locations 2023-12-08 01:55:59 +01:00
dependabot[bot]
cd035306e9
build: bump alpine from 3.18.3 to 3.18.5
Bumps alpine from 3.18.3 to 3.18.5.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-08 00:55:54 +00:00
Nicolas Duchon
3f7b759559
Merge pull request #1068 from nginx-proxy/boulder-release-2023-12-04
test: bump boulder to release-2023-12-04
2023-12-08 01:54:57 +01:00
Nicolas Duchon
2bb8f21355
test: bump boulder to release-2023-12-04 2023-12-08 01:37:46 +01:00
Nicolas Duchon
14d9de3be5
Merge pull request #1056 from nginx-proxy/dependabot/github_actions/docker/login-action-3
ci: bump docker/login-action from 2 to 3
2023-12-08 01:33:01 +01:00
Nicolas Duchon
86bc1c5c55
Merge pull request #1058 from nginx-proxy/dependabot/github_actions/docker/setup-qemu-action-3
ci: bump docker/setup-qemu-action from 2 to 3
2023-12-08 01:14:59 +01:00
dependabot[bot]
ca4ceff390
ci: bump docker/login-action from 2 to 3
Bumps [docker/login-action](https://github.com/docker/login-action) from 2 to 3.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-08 00:14:48 +00:00
Nicolas Duchon
3ace972cb4
Merge pull request #1057 from nginx-proxy/dependabot/github_actions/docker/build-push-action-5
ci: bump docker/build-push-action from 4 to 5
2023-12-08 01:14:40 +01:00
Nicolas Duchon
55f83904ce
Merge pull request #1055 from nginx-proxy/dependabot/github_actions/docker/metadata-action-5
ci: bump docker/metadata-action from 4 to 5
2023-12-08 01:13:56 +01:00
Nicolas Duchon
d291afe1b9
Merge pull request #1052 from nginx-proxy/dependabot/github_actions/actions/checkout-4
ci: bump actions/checkout from 3 to 4
2023-12-08 01:13:36 +01:00
dependabot[bot]
b49175269f
ci: bump docker/setup-qemu-action from 2 to 3
Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 2 to 3.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-18 15:37:36 +00:00
dependabot[bot]
7bf5a3b8e7
ci: bump docker/build-push-action from 4 to 5
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 4 to 5.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v4...v5)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-18 15:37:33 +00:00
dependabot[bot]
687a6a69a5
ci: bump docker/metadata-action from 4 to 5
Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 4 to 5.
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Upgrade guide](https://github.com/docker/metadata-action/blob/master/UPGRADE.md)
- [Commits](https://github.com/docker/metadata-action/compare/v4...v5)

---
updated-dependencies:
- dependency-name: docker/metadata-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-18 15:37:28 +00:00
dependabot[bot]
775ddcee79
ci: bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-04 15:39:45 +00:00
Nicolas Duchon
9fbd6684ef
Merge pull request #1048 from nginx-proxy/dependabot/docker/alpine-3.18.3
build: bump alpine from 3.17.3 to 3.18.3
2023-08-08 19:55:48 +02:00
dependabot[bot]
5814766cdc
build: bump alpine from 3.17.3 to 3.18.3
Bumps alpine from 3.17.3 to 3.18.3.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-08 15:55:37 +00:00
Nicolas Duchon
1fc38f882b
refactor: move to correctly namespaced labels (#1046) 2023-08-01 21:45:39 +02:00
Zvezdin
ca1f5a4e69
docs: clarify configuration requirements for standalone certificates (#1045)
* fix(Standalone-certificates.md): Clarify configuration requirements

* fix(Standalone-certificates.md): remove extra space

* Update docs/Standalone-certificates.md

Co-authored-by: Nicolas Duchon <nicolas.duchon@gmail.com>
2023-08-01 21:43:50 +02:00
Nicolas Duchon
347e3effbd
Merge pull request #1038 from nginx-proxy/dependabot/docker/nginxproxy/docker-gen-0.10.6
build: bump nginxproxy/docker-gen from 0.10.5 to 0.10.6
2023-07-12 21:48:38 +02:00
dependabot[bot]
f79468ef99
build: bump nginxproxy/docker-gen from 0.10.5 to 0.10.6
Bumps nginxproxy/docker-gen from 0.10.5 to 0.10.6.

---
updated-dependencies:
- dependency-name: nginxproxy/docker-gen
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-12 15:08:48 +00:00
Nicolas Duchon
6106556bf6
Merge pull request #1037 from nginx-proxy/dependabot/docker/nginxproxy/docker-gen-0.10.5
build: bump nginxproxy/docker-gen from 0.10.4 to 0.10.5
2023-07-04 08:20:24 +02:00
dependabot[bot]
648dc3be10
build: bump nginxproxy/docker-gen from 0.10.4 to 0.10.5
Bumps nginxproxy/docker-gen from 0.10.4 to 0.10.5.

---
updated-dependencies:
- dependency-name: nginxproxy/docker-gen
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-04 06:14:21 +00:00
Nicolas Duchon
63176c28ea ci: rename build / publish workflow
+ enable gha caching on docker/build-push-action
2023-05-08 16:48:19 +02:00
Nicolas Duchon
af2d179181
Merge pull request #1031 from nginx-proxy/dependabot/github_actions/docker/build-push-action-4
ci: Bump docker/build-push-action from 3 to 4
2023-04-30 16:28:09 +02:00
dependabot[bot]
545a5e4042
ci: Bump docker/build-push-action from 3 to 4
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 3 to 4.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-30 13:58:31 +00:00
Nicolas Duchon
033fc1de14
Merge pull request #1030 from nginx-proxy/dependabot-gh-actions
ci: add GitHub Actions to Dependabot config
2023-04-30 15:58:09 +02:00
Nicolas Duchon
2fe556cc10
ci: add GitHub Actions to Dependabot config 2023-04-30 15:38:22 +02:00
Nicolas Duchon
b4faf2143e
ci: only trigger test workflow on push for main branch 2023-04-30 15:37:13 +02:00
Nicolas Duchon
36195de745
Merge pull request #1025 from nginx-proxy/dependabot/docker/nginxproxy/docker-gen-0.10.4
build: Bump nginxproxy/docker-gen from 0.10.3 to 0.10.4
2023-04-18 08:02:56 +02:00
dependabot[bot]
dcddbde102
build: Bump nginxproxy/docker-gen from 0.10.3 to 0.10.4
Bumps nginxproxy/docker-gen from 0.10.3 to 0.10.4.

---
updated-dependencies:
- dependency-name: nginxproxy/docker-gen
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-18 05:54:21 +00:00
Nicolas Duchon
defde434bd
Merge pull request #1018 from nginx-proxy/dependabot/docker/alpine-3.17.3
build: Bump alpine from 3.17.2 to 3.17.3
2023-03-30 01:38:19 +02:00
dependabot[bot]
c01ed304b2
build: Bump alpine from 3.17.2 to 3.17.3
Bumps alpine from 3.17.2 to 3.17.3.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-29 23:06:48 +00:00
Nicolas Duchon
2aa5d67fde
Merge pull request #1019 from nginx-proxy/dependabot/docker/nginxproxy/docker-gen-0.10.3
build: Bump nginxproxy/docker-gen from 0.10.2 to 0.10.3
2023-03-30 01:05:40 +02:00
dependabot[bot]
f661555bbe
build: Bump nginxproxy/docker-gen from 0.10.2 to 0.10.3
Bumps nginxproxy/docker-gen from 0.10.2 to 0.10.3.

---
updated-dependencies:
- dependency-name: nginxproxy/docker-gen
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-29 19:10:56 +00:00
Nicolas Duchon
a16a97fe11
fix: restrict private key permissions (#1016)
* fix: restrict private file permissions by default

* fix: check perms of /etc/acme.sh private keys

* fix: typo
2023-03-27 19:03:21 +02:00
Nicolas Duchon
0383e3497f
Merge pull request #1011 from nginx-proxy/dependabot/docker/nginxproxy/docker-gen-0.10.2
build: Bump nginxproxy/docker-gen from 0.10.1 to 0.10.2
2023-03-21 07:27:38 +01:00
dependabot[bot]
f92821cd18
build: Bump nginxproxy/docker-gen from 0.10.1 to 0.10.2
Bumps nginxproxy/docker-gen from 0.10.1 to 0.10.2.

---
updated-dependencies:
- dependency-name: nginxproxy/docker-gen
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-21 06:17:10 +00:00
Nicolas Duchon
736b9302c1
Merge pull request #1001 from nginx-proxy/dependabot/docker/nginxproxy/docker-gen-0.10.1
build: Bump nginxproxy/docker-gen from 0.10.0 to 0.10.1
2023-02-28 08:39:29 +01:00
dependabot[bot]
6b2e1b69ca
build: Bump nginxproxy/docker-gen from 0.10.0 to 0.10.1
Bumps nginxproxy/docker-gen from 0.10.0 to 0.10.1.

---
updated-dependencies:
- dependency-name: nginxproxy/docker-gen
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-28 07:25:06 +00:00
Nicolas Duchon
280d9c2df1
Merge pull request #996 from nginx-proxy/dependabot/docker/alpine-3.17.2
build: Bump alpine from 3.17.1 to 3.17.2
2023-02-28 07:55:35 +01:00
dependabot[bot]
1281586e8b
build: Bump alpine from 3.17.1 to 3.17.2
Bumps alpine from 3.17.1 to 3.17.2.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-13 15:57:29 +00:00
Nicolas Duchon
bcace8178d
Merge pull request #994 from nginx-proxy/dependabot/docker/nginxproxy/docker-gen-0.10.0
build: Bump nginxproxy/docker-gen from 0.9.4 to 0.10.0
2023-02-09 08:27:39 +01:00
dependabot[bot]
7cb1667fea
build: Bump nginxproxy/docker-gen from 0.9.4 to 0.10.0
Bumps nginxproxy/docker-gen from 0.9.4 to 0.10.0.

---
updated-dependencies:
- dependency-name: nginxproxy/docker-gen
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-09 07:19:17 +00:00
Nicolas Duchon
66150b7ada
Merge pull request #993 from nginx-proxy/dependabot/docker/nginxproxy/docker-gen-0.9.4
build: bump nginxproxy/docker-gen from 0.9.3 to 0.9.4
2023-01-23 23:41:31 +01:00
dependabot[bot]
266033b664
chore(deps): Bump nginxproxy/docker-gen from 0.9.3 to 0.9.4
Bumps nginxproxy/docker-gen from 0.9.3 to 0.9.4.

---
updated-dependencies:
- dependency-name: nginxproxy/docker-gen
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-23 22:23:00 +00:00
Nicolas Duchon
7ffea7407a
ci: set Dependabot commit prefix 2023-01-23 23:21:15 +01:00
Nicolas Duchon
4d08e2fd2e
Merge pull request #991 from nginx-proxy/boulder-2023-01-09
ci: bump Boulder to release-2023-01-09
2023-01-17 08:47:58 +01:00
Nicolas Duchon
9f0b07407b
ci: bump Boulder to release-2023-01-09 2023-01-17 08:37:41 +01:00
Nicolas Duchon
439d7ccaa0
Merge pull request #990 from nginx-proxy/dependabot/docker/nginxproxy/docker-gen-0.9.3
chore(deps): Bump nginxproxy/docker-gen from 0.9.2 to 0.9.3
2023-01-17 08:21:03 +01:00
dependabot[bot]
065a69cad0
chore(deps): Bump nginxproxy/docker-gen from 0.9.2 to 0.9.3
Bumps nginxproxy/docker-gen from 0.9.2 to 0.9.3.

---
updated-dependencies:
- dependency-name: nginxproxy/docker-gen
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-17 07:13:17 +00:00
Nicolas Duchon
d543f880e4
Merge pull request #988 from nginx-proxy/dependabot/docker/alpine-3.17.1
chore(deps): Bump alpine from 3.17.0 to 3.17.1
2023-01-17 08:00:37 +01:00
dependabot[bot]
f1baa7756b
chore(deps): Bump alpine from 3.17.0 to 3.17.1
Bumps alpine from 3.17.0 to 3.17.1.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-10 15:07:19 +00:00
Nicolas Duchon
f293c8a8c1
Merge pull request #986 from nginx-proxy/boulder-2022-12-19
ci: bump Boulder to release-2022-12-19
2022-12-26 22:58:38 +01:00
Nicolas Duchon
015ac5ffa7
ci: bump Boulder to release-2022-12-19 2022-12-26 21:16:44 +01:00
Nicolas Duchon
cd6fe5e016
Merge pull request #984 from nginx-proxy/dependabot/docker/nginxproxy/docker-gen-0.9.2
chore(deps): Bump nginxproxy/docker-gen from 0.9.1 to 0.9.2
2022-12-23 19:31:20 +01:00
dependabot[bot]
b359a18da8
chore(deps): Bump nginxproxy/docker-gen from 0.9.1 to 0.9.2
Bumps nginxproxy/docker-gen from 0.9.1 to 0.9.2.

---
updated-dependencies:
- dependency-name: nginxproxy/docker-gen
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-23 18:23:03 +00:00
Nicolas Duchon
09a78d88e4
Merge pull request #977 from nginx-proxy/dependabot/docker/alpine-3.17.0
chore(deps): Bump alpine from 3.16.3 to 3.17.0
2022-12-23 19:16:18 +01:00
Nicolas Duchon
87c27d22a6
feat: use EAB if available no matter the ACME CI (#981)
* ci: setup Pebble with docker-compose + .env file

* refactor: move acme.sh hooks further down the file

* feat: user EAB with other CAs than Zero SSL

* tests: ACME External Account Binding (EAB)

* ci: add local Pebble EAB testing
2022-12-06 12:28:48 +01:00
Nicolas Duchon
68005e6938
Revert "fix: register ACME account with EAB first (#976)"
This reverts commit 77cc9d5241.
2022-12-03 22:15:40 +01:00
Jeroen
77cc9d5241
fix: register ACME account with EAB first (#976)
* check EAB_KID and HMAC_KEY before registering the ACME account using the provided email.

* Update letsencrypt_service

copy/past error

Co-authored-by: root <root@jump20dst.int.cvovolt.be>
2022-12-03 20:50:23 +01:00
Nicolas Duchon
23c5a2379c
test: fix private_keys test 2022-12-03 20:18:15 +01:00
Nicolas Duchon
e0c9b981ab
Merge pull request #980 from nginx-proxy/fix-boulder
ci: fix boulder integration tests
2022-12-03 19:40:24 +01:00
Nicolas Duchon
e320cc603b
ci: use actions/checkout@v3 2022-12-03 19:33:11 +01:00
Nicolas Duchon
7599550805
ci: fix integration test with Boulder 2022-12-03 19:27:05 +01:00
Nicolas Duchon
a0c0e2bddf
ci: update Actions versions 2022-12-03 14:45:13 +01:00
Nicolas Duchon
da853dd5ef
style: linting on CI yaml files 2022-12-03 14:43:43 +01:00
dependabot[bot]
6c106b0fdd
chore(deps): Bump alpine from 3.15.4 to 3.17.0
Bumps alpine from 3.15.4 to 3.17.0.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-02 15:53:23 +00:00
Nicolas Duchon
c80eaa3894
Merge pull request #963 from nginx-proxy/dependabot/docker/alpine-3.16.2
chore(deps): Bump alpine from 3.15.4 to 3.16.2
2022-12-02 16:52:42 +01:00
Nicolas Duchon
e70545ceb1
chore(deps): Bump alpine from 3.15.4 to 3.16.3 2022-12-02 16:31:28 +01:00
dependabot[bot]
00d70af4ae
chore(deps): Bump alpine from 3.15.4 to 3.16.2
Bumps alpine from 3.15.4 to 3.16.2.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-02 15:30:23 +00:00
Nicolas Duchon
aa08ed4bce
Merge pull request #978 from nginx-proxy/dependabot/docker/nginxproxy/docker-gen-0.9.1
chore(deps): Bump nginxproxy/docker-gen from 0.9.0 to 0.9.1
2022-12-02 16:29:44 +01:00
dependabot[bot]
5cee98e5d1
chore(deps): Bump nginxproxy/docker-gen from 0.9.0 to 0.9.1
Bumps nginxproxy/docker-gen from 0.9.0 to 0.9.1.

---
updated-dependencies:
- dependency-name: nginxproxy/docker-gen
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-02 15:01:00 +00:00
langfingaz
bb39d28a3e
add 5s:30s to -wait
In the [Advanced Usage doc](https://github.com/nginx-proxy/acme-companion/blob/main/docs/Advanced-usage.md#step-2---docker-gen), the there is `5s:30s` specified after `-wait`. In this doc it was missing. For completeness I have added it here.
2022-09-28 09:41:53 +00:00
Nicolas Duchon
b9627aa786
Merge pull request #946 from RodrigoTomeES/patch-1
docs: moved DEFAULT_EMAIL env to acme-companion
2022-04-12 18:30:39 +02:00
Rodrigo
26e1b4b382
docs: moved DEFAULT_EMAIL env to acme-companion
https://github.com/nginx-proxy/acme-companion#step-2---acme-companion
2022-04-11 23:29:30 +02:00
Nicolas Duchon
38fdd400b3
Merge pull request #945 from nginx-proxy/dependabot/docker/nginxproxy/docker-gen-0.9.0
chore(deps): Bump nginxproxy/docker-gen from 0.8.4 to 0.9.0
2022-04-10 14:18:41 +02:00
dependabot[bot]
9550d0d930
chore(deps): Bump nginxproxy/docker-gen from 0.8.4 to 0.9.0
Bumps nginxproxy/docker-gen from 0.8.4 to 0.9.0.

---
updated-dependencies:
- dependency-name: nginxproxy/docker-gen
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-10 12:06:45 +00:00
Nicolas Duchon
bec640e11c
Merge pull request #944 from nginx-proxy/dependabot/docker/alpine-3.15.4
chore(deps): Bump alpine from 3.15.3 to 3.15.4
2022-04-10 10:34:04 +02:00
dependabot[bot]
c2ef510f29
chore(deps): Bump alpine from 3.15.3 to 3.15.4
Bumps alpine from 3.15.3 to 3.15.4.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-05 15:08:52 +00:00
Nicolas Duchon
4786de5419
Merge pull request #939 from nginx-proxy/dependabot/docker/alpine-3.15.3
chore(deps): Bump alpine from 3.15.2 to 3.15.3
2022-03-29 22:24:09 +02:00
dependabot[bot]
722010f106
chore(deps): Bump alpine from 3.15.2 to 3.15.3
Bumps alpine from 3.15.2 to 3.15.3.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-29 15:05:31 +00:00
Nicolas Duchon
313722118c
Merge pull request #931 from nginx-proxy/dependabot/docker/nginxproxy/docker-gen-0.8.4
chore(deps): Bump nginxproxy/docker-gen from 0.8.3 to 0.8.4
2022-03-25 11:03:01 +01:00
dependabot[bot]
55f5697db8
chore(deps): Bump nginxproxy/docker-gen from 0.8.3 to 0.8.4
Bumps nginxproxy/docker-gen from 0.8.3 to 0.8.4.

---
updated-dependencies:
- dependency-name: nginxproxy/docker-gen
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-24 17:26:25 +00:00
Nicolas Duchon
fd181afa6c
Merge pull request #937 from nginx-proxy/dependabot/docker/alpine-3.15.2
chore(deps): Bump alpine from 3.15.0 to 3.15.2
2022-03-24 18:25:43 +01:00
dependabot[bot]
c4a9bf4102
chore(deps): Bump alpine from 3.15.0 to 3.15.2
Bumps alpine from 3.15.0 to 3.15.2.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-24 15:06:46 +00:00
Nicolas Duchon
fe6589a7a1
Merge pull request #930 from nginx-proxy/license-in-image
Include license in Docker image
2022-03-22 18:56:08 +01:00
Nicolas Duchon
0fd5ac4d59
chore: include license in Docker image 2022-03-09 14:04:32 +01:00
Nicolas Duchon
379639c2cf
ci: use path context 2022-03-05 17:05:41 +01:00
Nicolas Duchon
190ff4da1e
Merge pull request #928 from nginx-proxy/dependabot/docker/nginxproxy/docker-gen-0.8.3
chore(deps): Bump nginxproxy/docker-gen from 0.8.2 to 0.8.3
2022-03-04 23:05:51 +01:00
dependabot[bot]
61858ff7e9
chore(deps): Bump nginxproxy/docker-gen from 0.8.2 to 0.8.3
Bumps nginxproxy/docker-gen from 0.8.2 to 0.8.3.

---
updated-dependencies:
- dependency-name: nginxproxy/docker-gen
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-04 15:03:56 +00:00
Nicolas Duchon
328bebe682
ci: publish Docker images to ghcr.io 2022-03-04 10:44:51 +01:00
Nicolas Duchon
7c86871bd0
fix: typo in pre/post hooks test file 2022-03-02 19:55:22 +01:00
Nicolas Duchon
b3f4e40c05
Merge pull request #898 from TreeN0de/TreeN0de-pre/post-hook
Adding pre- and post-hook
2022-03-02 19:42:29 +01:00
Nicolas Duchon
b4ccbf1e8f
docs: per-container Pre-Hooks and Post-Hooks 2022-03-02 19:29:25 +01:00
Nicolas Duchon
16f7e198df
tests: per-container Pre-Hooks and Post-Hooks 2022-03-02 19:29:25 +01:00
Nicolas Duchon
084766d0eb
tests: change run_nginx_container() parameter handling 2022-03-02 19:29:25 +01:00
Nicolas Duchon
b9e7d59bed
feat: per-container Pre-Hooks and Post-Hooks 2022-03-02 18:46:15 +01:00
Nicolas Duchon
312fe57bb8
fix: remove unnecessary echo triggering SC2116 2022-03-02 17:42:54 +01:00
TreeN0de
c26ee284cd
tests: acme.sh pre and post hooks 2022-02-25 20:00:08 +01:00
TreeN0de
e3419dffb8
tests: change run_le_container() parameter handling 2022-02-25 20:00:08 +01:00
TreeN0de
281aa02052
docs: acme.sh pre and post hooks 2022-02-25 20:00:08 +01:00
TreeN0de
9da38f37b4
feat: acme.sh pre and post hooks 2022-02-25 19:07:22 +01:00
Nicolas Duchon
4b638eb6fe
Merge pull request #923 from marc-hg/change_request/add-email-env-to-docker-compose-docs
Added DEFAULT_EMAIL env variable to docker-compose docs
2022-02-18 15:10:52 +01:00
marc-hg
d7685271c8 Added DEFAULT_EMAIL env variable to docker-compose docs 2022-02-17 22:05:34 +01:00
Nicolas Duchon
a858475771
Merge pull request #919 from nginx-proxy/dependabot/docker/nginxproxy/docker-gen-0.8.2
chore(deps): Bump nginxproxy/docker-gen from 0.8.0 to 0.8.2
2022-02-02 15:27:50 +01:00
dependabot[bot]
d2ad39b6eb
chore(deps): Bump nginxproxy/docker-gen from 0.8.0 to 0.8.2
Bumps nginxproxy/docker-gen from 0.8.0 to 0.8.2.

---
updated-dependencies:
- dependency-name: nginxproxy/docker-gen
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-02 14:12:48 +00:00
Nicolas Duchon
d3d0c36d69
Merge pull request #906 from nginx-proxy/docker-gen-from-image
Get the docker-gen binary from upstream docker image instead of building.
2022-02-02 15:11:31 +01:00
Nicolas Duchon
3336e69426
ci: remove the nginx-proxy-pull job 2022-02-02 14:29:39 +01:00
Nicolas Duchon
f7df1ec2d9
build: get docker-gen binary from upstream image 2022-02-02 14:29:38 +01:00
Nicolas Duchon
53798f6d31
ci: replace maintainer label w/ OCI authors label 2022-01-14 15:57:57 +01:00
Nicolas Duchon
a20c4d32fe
ci: use git describe for OCI image version label 2022-01-14 15:56:24 +01:00
Nicolas Duchon
3e6252e9fa
ci: upgrade to docker/metadata-action@v3 2022-01-14 15:55:40 +01:00
Nicolas Duchon
2ce86ff988
docs: update maintainers list on license 2022-01-11 19:41:34 +01:00
Nicolas Duchon
4b23f7f44e
Merge pull request #911 from nginx-proxy/dependabot/docker/golang-1.17.6-alpine
chore(deps): Bump golang from 1.17.5-alpine to 1.17.6-alpine
2022-01-07 16:39:19 +01:00
dependabot[bot]
972469f504
chore(deps): Bump golang from 1.17.5-alpine to 1.17.6-alpine
Bumps golang from 1.17.5-alpine to 1.17.6-alpine.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-07 15:06:19 +00:00
Nicolas Duchon
44bd2fa437
Merge pull request #904 from nginx-proxy/dhparam-rfc7919
Use RFC 7919 DH groups + Remove DH generation
2021-12-16 03:07:08 +01:00
Nicolas Duchon
9b935a09a5
style: coherent coding style on check_dh_group
+ removal of an unneeded local keyword
2021-12-16 02:00:04 +01:00
Nicolas Duchon
57108ba21b
Merge pull request #843 from nginx-proxy/fix-761
Trim whitespaces from environment variables
2021-12-14 23:47:03 +01:00
Nicolas Duchon
e0aaa93eb6
refactor: better check_dh_group() logic
Replaces existing group if it does not match the DHPARAM_BITS key size.
2021-12-14 23:34:15 +01:00
Nicolas Duchon
f39edbe667
docs: updates for the use of RFC7919 DH groups
Both in nginx-proxy/acme-companion and nginx-proxy/nginx-proxy
2021-12-14 23:34:14 +01:00
Nicolas Duchon
67d4194fb6
feat: use pre generated RFC7919 DH groups
Co-authored-by: polarathene <5098581+polarathene@users.noreply.github.com>
2021-12-14 23:32:39 +01:00
Nicolas Duchon
2f7ac9708a
style: add comments to docker-gen template 2021-12-12 19:41:46 +01:00
Nicolas Duchon
8fdd196d4f
fix: trim whitespaces from per-container env vars 2021-12-12 19:41:24 +01:00
Nicolas Duchon
ec93ec882c
Merge pull request #903 from nginx-proxy/dependabot/docker/golang-1.17.5-alpine
chore(deps): Bump golang from 1.17.4-alpine to 1.17.5-alpine
2021-12-10 16:55:31 +01:00
dependabot[bot]
c886e48436
chore(deps): Bump golang from 1.17.4-alpine to 1.17.5-alpine
Bumps golang from 1.17.4-alpine to 1.17.5-alpine.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-10 15:05:12 +00:00
Nicolas Duchon
b72df952d6
Merge pull request #842 from nginx-proxy/fix-841
Prevent endless loop of wildcard enumeration
2021-12-08 15:52:52 +01:00
Nicolas Duchon
dddd7a5a82
fix: prevent endless loop of wildcard enumeration 2021-12-08 15:37:15 +01:00
Nicolas Duchon
2b5fd94271
Merge pull request #897 from nginx-proxy/dependabot/docker/golang-1.17.4-alpine
chore(deps): Bump golang from 1.16.7-alpine to 1.17.4-alpine
2021-12-08 11:44:14 +01:00
dependabot[bot]
0d03393a10
chore(deps): Bump golang from 1.16.7-alpine to 1.17.4-alpine
Bumps golang from 1.16.7-alpine to 1.17.4-alpine.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-08 10:34:07 +00:00
Nicolas Duchon
13a51f9afe
Merge pull request #886 from nginx-proxy/dockergen-0.8.0
Bump dockergen version from 0.7.6 to 0.8.0
2021-12-08 11:32:23 +01:00
Nicolas Duchon
14b4b00675
build: bump dockergen from 0.7.6 to 0.8.0 2021-12-08 11:20:15 +01:00
Nicolas Duchon
eb8710002d
Merge pull request #892 from nginx-proxy/dependabot/docker/alpine-3.14.3
chore(deps): Bump alpine from 3.13.5 to 3.14.3
2021-12-08 02:52:55 +01:00
dependabot[bot]
d6f0b91e68
chore(deps): Bump alpine from 3.13.5 to 3.14.3
Bumps alpine from 3.13.5 to 3.14.3.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-08 01:34:20 +00:00
Nicolas Duchon
ef67cf1e48
Merge pull request #900 from nginx-proxy/acme.sh-2.9.0
Bump acme.sh version to 2.9.0
2021-12-08 02:32:05 +01:00
Nicolas Duchon
fe38f076c2
build: acme.sh 2.8.8 -> 2.9.0 2021-12-08 02:15:50 +01:00
Nicolas Duchon
406acbb6d5
Merge pull request #899 from nginx-proxy/fix-tests
Fix test suite
2021-12-08 02:03:22 +01:00
Nicolas Duchon
5558a714a8
chore: update .gitignore (supplemental test files) 2021-12-08 01:44:45 +01:00
Nicolas Duchon
eff86377eb
tests: fix Docker volumes on nginx-proxy setup 2021-12-08 01:43:58 +01:00
Nicolas Duchon
746704cf30
tests: use pre generated dhparam.pem on companion 2021-12-08 01:42:27 +01:00
Nicolas Duchon
70424c5d60
tests: remove DHPARAM_BITS=256 on nginx-proxy
Using a low DH parameters size isn't needed anymore.
This specific size isn't supported and will cause the container to exit.
2021-10-28 12:00:39 +02:00
Nicolas Duchon
1785bc5394
docs: explain port 80 and 443 requirements 2021-10-01 10:31:54 +02:00
Nicolas Duchon
04f77ecc9c
Merge pull request #875 from HPPinata/patch-2
Add `DEFAULT_KEY_SIZE` variable to the documentation
2021-09-30 18:20:32 +02:00
HPPinata
8164a4641c
Add DEFAULT_KEY_SIZE variable to the documentation
Useful but undocumented environment variable to avoid setting LETSENCRYPT_KEYSIZE on every container manually
2021-09-26 20:53:55 +02:00
Nicolas Duchon
7f1b75460d
Merge pull request #857 from JMoVS/patch-1
Fix small missing \ to make it copy pasteable
2021-09-01 08:57:40 +02:00
JMoVS
3b9103d085
Fix small missing \ to make it copy pasteable
A \ was missing so pasting resulted in premature and incomplete execution.
2021-08-31 23:52:30 +02:00
Nicolas Duchon
1a6cc79086
Merge pull request #844 from nginx-proxy/dependabot/docker/golang-1.16.7-alpine
chore(deps): Bump golang from 1.16.6-alpine to 1.16.7-alpine
2021-08-08 14:11:09 +02:00
dependabot[bot]
eda87994e9
chore(deps): Bump golang from 1.16.6-alpine to 1.16.7-alpine
Bumps golang from 1.16.6-alpine to 1.16.7-alpine.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-06 15:03:15 +00:00
Nicolas Duchon
68a792e9ff
docs: update the issue template 2021-07-14 21:20:37 +02:00
Nicolas Duchon
ad9b2c9b55
Merge pull request #835 from nginx-proxy/dependabot/docker/golang-1.16.6-alpine
chore(deps): Bump golang from 1.16.5-alpine to 1.16.6-alpine
2021-07-14 17:14:46 +02:00
dependabot[bot]
f9493c4886
chore(deps): Bump golang from 1.16.5-alpine to 1.16.6-alpine
Bumps golang from 1.16.5-alpine to 1.16.6-alpine.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-14 15:05:00 +00:00
Nicolas Duchon
53e6ee0845
Merge pull request #828 from nginx-proxy/tests-workflow_dispatch
Enable workflow_dispatch on tests
2021-07-07 16:38:51 +02:00
Nicolas Duchon
0758306abd
ci: enable workflow_dispatch on tests 2021-07-07 16:21:58 +02:00
Nicolas Duchon
027c3cf3bf
docs: jwilder/docker-gen -> nginxproxy/docker-gen 2021-06-16 17:12:30 +02:00
Nicolas Duchon
b7e1362115
tests: jwilder/docker-gen -> nginxproxy/docker-gen 2021-06-16 17:12:25 +02:00
Nicolas Duchon
cb6a3f4495
build: jwilder/docker-gen -> nginxproxy/docker-gen 2021-06-16 17:12:05 +02:00
Nicolas Duchon
0a7153e646
Merge pull request #821 from nginx-proxy/dependabot/docker/golang-1.16.5-alpine
chore(deps): Bump golang from 1.16.3-alpine to 1.16.5-alpine
2021-06-16 00:08:37 +02:00
Nicolas Duchon
5341b3fac0
ci: update Dependabot label 2021-06-15 23:56:13 +02:00
dependabot[bot]
9bacd370ef
chore(deps): Bump golang from 1.16.3-alpine to 1.16.5-alpine
Bumps golang from 1.16.3-alpine to 1.16.5-alpine.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-04 06:24:01 +00:00
Nicolas Duchon
cd3b51bd7e
Merge pull request #790 from umireon/patch-1
Fix typo
2021-05-06 22:41:36 +02:00
Kaito Udagawa
eec7dacc1e
Fix typo
avanced to advanced
2021-05-07 02:01:19 +09:00
dependabot[bot]
8837acfa8d
Merge pull request #774 from nginx-proxy/dependabot/docker/golang-1.16.3-alpine 2021-04-26 16:53:20 +00:00
dependabot[bot]
616d585dc1
chore(deps): Bump alpine from 3.13.4 to 3.13.5 (#783) 2021-04-26 15:37:52 +00:00
dependabot[bot]
057752279c
chore(deps): Bump golang from 1.15.8-alpine to 1.16.3-alpine
Bumps golang from 1.15.8-alpine to 1.16.3-alpine.

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-26 15:21:36 +00:00
Nicolas Duchon
1b0104200e
Merge pull request #785 from nginx-proxy/dockergen-0.7.6
Bump docker-gen from 0.7.4 to 0.7.6
2021-04-26 17:20:54 +02:00
Nicolas Duchon
140e487827
ci: don't run default_cert test on GitHub Actions
For some yet unknown reason, this test is fine on local
but has a very high rate of failure on GitHub Actions
2021-04-26 17:11:32 +02:00
Nicolas Duchon
61e5156ed3
ci: fix tests w/ upstream nginx-proxy & docker-gen 2021-04-26 16:10:56 +02:00
Nicolas Duchon
03787386d1
chore: bump docker-gen from 0.7.4 to 0.7.6 2021-04-26 16:04:22 +02:00
Nicolas Duchon
d56d1aa9fd
ci: rebuild and push the latest image on schedule
Rebuild and push the last commit of the default branch every monday at 00:00 UTC
2021-04-06 13:46:17 +02:00
Nicolas Duchon
ddb6ebd8a4
doc: typo 2021-04-05 21:27:53 +02:00
Nicolas Duchon
a1440194ea
docs: replace microbadger's image info badge
MicroBadger.com will shutdown on 1st July 2021
2021-04-05 17:09:07 +02:00
Nicolas Duchon
4bdb229e51
ci: double the tests timeout duration 2021-04-05 14:37:14 +02:00
Nicolas Duchon
ab930cba5f
chore: nginx-proxy default branch renamed to main (#778) 2021-04-05 14:31:53 +02:00
Nicolas Duchon
595d52c7e3
chore: rename default branch master -> main 2021-04-05 12:48:49 +02:00
Nicolas Duchon
35a0b89a61
Merge pull request #777 from nginx-proxy/projects-name-change
chore: projects name change
2021-04-05 00:40:30 +02:00
Nicolas Duchon
e9abac8b12
chore: project name change 2021-04-05 00:27:52 +02:00
Nicolas Duchon
11a534ecc1
chore: use the nginxproxy/nginx-proxy registry 2021-04-05 00:21:31 +02:00
Nicolas Duchon
c8c1f69e89
ci: start pushing to nginxproxy/acme-companion 2021-04-02 16:22:32 +02:00
Nicolas Duchon
a503be2667
docs: don't redirect users to the project's wiki
The wiki contains outdated infos and need updating.
2021-04-01 15:39:15 +02:00
dependabot[bot]
f30084d1fd
chore(docker): Bump alpine from 3.13.3 to 3.13.4 (#773)
Bumps alpine from 3.13.3 to 3.13.4.

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-01 10:19:36 +02:00
Nicolas Duchon
53d2ac18a5
Merge pull request #772 from nginx-proxy/doc-volume
Update volume-related docs and examples
2021-03-26 15:30:22 +01:00
Nicolas Duchon
a5e3b3b6b1
docs: discourage use of anonymous volumes 2021-03-26 15:25:53 +01:00
Nicolas Duchon
90de0cec71
docs: user named volumes in all examples 2021-03-26 15:25:46 +01:00
dependabot[bot]
3a037b3a18
Bump alpine from 3.13.2 to 3.13.3 (#771) 2021-03-26 10:56:34 +00:00
Nicolas Duchon
1bfbb9bb11
docs: add a warning about external examples
As they might not include the additional volume needed by version >= 2.0
2021-03-16 22:58:45 +01:00
Nicolas Duchon
df13071394
Merge pull request #767 from LoganK/master
Fix unintentional file globbing during wildcard lookup
2021-03-16 22:51:08 +01:00
Logan Kennelly
ffffdc86bd Fix unintentional file globbing during wildcard lookup
Matching globs are common because the script runs in the certs
directory.

The test uses a suffix match as the test domains don't include
subdomains, although such cases should probably be considered.

Fix the le3.wtf test. The existing add_location_configuration modifies
"default"; a second add is not necessary.

Fixes #763
2021-03-15 18:14:22 -07:00
dependabot[bot]
87432987b2
Bump alpine from 3.13.1 to 3.13.2 (#757) 2021-03-02 00:43:47 +00:00
dependabot[bot]
74f901417f
Bump golang from 1.15.7-alpine to 1.15.8-alpine (#753) 2021-02-05 09:04:46 +00:00
dependabot[bot]
b966ed76e9
Bump alpine from 3.13.0 to 3.13.1 (#752) 2021-01-31 17:12:46 +00:00
dependabot[bot]
d0965158b5
Bump golang from 1.15.6-alpine to 1.15.7-alpine (#749) 2021-01-31 17:12:29 +00:00
dependabot[bot]
acb3c404b8
Merge pull request #748 from nginx-proxy/dependabot/docker/alpine-3.13.0 2021-01-15 10:06:03 +00:00
dependabot[bot]
4e4ccf9a80
Bump alpine from 3.12.3 to 3.13.0
Bumps alpine from 3.12.3 to 3.13.0.

Signed-off-by: dependabot[bot] <support@github.com>
2021-01-15 06:53:06 +00:00
Nicolas Duchon
5be695c3b1
Merge pull request #747 from bradym/linkTypoFix
Fix broken data persistence link in README.md
2021-01-12 21:16:18 +01:00
Brady Mitchell
60c537b942
Fix broken data persistence link in README.md 2021-01-12 13:04:34 -07:00
Nicolas Duchon
5d890e76d6
Merge pull request #744 from danil-smirnov/parametrize-time-to-wait
Making time to wait configurable
2021-01-04 10:04:26 +01:00
Danil Smirnov
a8b921109b Documentation fix 2020-12-29 15:14:24 +02:00
Danil Smirnov
42ddbcb580 Renaming and adding documentation 2020-12-29 15:09:03 +02:00
Danil Smirnov
b47755b875 Making time to wait configurable 2020-12-29 13:13:45 +02:00
Nicolas Duchon
b91ec66d7e
Merge pull request #743 from buchdag/gha-pebble
Use Pebble for tests.
2020-12-28 22:05:14 +01:00
Nicolas Duchon
cc2fa069c0
Enable local tests with both Boulder and Pebble 2020-12-28 16:28:18 +01:00
Nicolas Duchon
203f7a0bc9
CI/CD: Upgrade Boulder to release-2020-12-14 2020-12-28 16:27:16 +01:00
Nicolas Duchon
dc4255d8fa
CI/CD: Refactor the run_nginx_container() function 2020-12-28 11:32:04 +01:00
Nicolas Duchon
2222adc954
CI/CD: Enable Pebble random nonce rejection 2020-12-28 11:31:47 +01:00
Nicolas Duchon
38782a6acc
CI/CD: Upgrade Pebble to v2.3.1 2020-12-28 11:31:40 +01:00
Nicolas Duchon
2a5e34100e
CI/CD: Re-order integration tests strategy matrix 2020-12-28 11:31:31 +01:00
Nicolas Duchon
b77392a5c8
CI/CD: Test against both Boulder and Pebble 2020-12-28 11:31:20 +01:00
Nicolas Duchon
3f65c8c6c1
CI/CD: Test against Pebble 2020-12-28 11:31:12 +01:00
Nicolas Duchon
c610b59006
Add CA_BUNDLE environment variable
Allows acme.sh to use an alternative trusted root CA
2020-12-28 11:31:01 +01:00
Nicolas Duchon
a428655084
Remove most markdown from bug_report.md 2020-12-27 00:28:28 +01:00
Nicolas Duchon
ad072092c9
Update CI badge for GitHub Actions 2020-12-25 23:31:40 +01:00
Nicolas Duchon
ed07a99ca0
Use patch level releases in Dockerfile's FROM (#741)
Now that we use dependabot we can update dependencies more often.
2020-12-25 23:28:20 +01:00
Nicolas Duchon
e427c1c582 Add issue template 2020-12-25 20:42:45 +01:00
Nicolas Duchon
be654b5d27
Add OCSP Must-Staple extension feature (#740) 2020-12-25 18:23:33 +01:00
Nicolas Duchon
5e40af0d6d
Merge pull request #738 from buchdag/github-actions
Migrate CI to GitHub Actions as Travis CI is ending their free OSS builds
2020-12-23 19:51:40 +01:00
Nicolas Duchon
3ac0f260e9
CI/CD: Patch the companion's Dockerfile for tests 2020-12-23 19:15:11 +01:00
Nicolas Duchon
3d35839f31
CI/CD: Don't run tests for doc-only changes 2020-12-23 10:39:04 +01:00
Nicolas Duchon
cc66f42bae
Remove default volumes from Dockerfile (#736) 2020-12-23 00:32:56 +01:00
Nicolas Duchon
000c279bb6
CI/CD: Use in-container artifact cleaning script 2020-12-22 00:07:15 +01:00
Nicolas Duchon
992928816d
CI/CD: Fix certs_san test 2020-12-21 23:56:18 +01:00
Nicolas Duchon
cf09ccbb80
CI/CD: Remove Travis stuff from containers-logs.sh 2020-12-21 23:31:52 +01:00
Nicolas Duchon
4692095e40
CI/CD: Use patched nginx-proxy & docker-gen images 2020-12-21 23:31:51 +01:00
Kevin Marilleau
a6aaac7d72
CI/CD: split tests 2020-12-21 22:07:41 +01:00
Kevin Marilleau
17323b40a6
CI/CD: Split TestsJobs into Docker SPecifications Testing and Integration Testing. 2020-12-21 22:05:41 +01:00
Kevin Marilleau
cfff67c726
CI/CD: Remove Docker Compose Update. 2020-12-21 22:05:40 +01:00
Kevin Marilleau
cd3c9681dd
Move CI/CD from Travis to Github Actions. 2020-12-21 22:04:55 +01:00
Nicolas Duchon
a717c35c13
[skip ci] typo 2020-12-21 21:52:07 +01:00
Nicolas Duchon
0de1170d6f
[skip ci] Use on.push.paths for Dockerhub workflow 2020-12-21 21:50:15 +01:00
Nicolas Duchon
491be0c76d
[skip ci] safelist branches builds on Travis CI 2020-12-21 21:35:09 +01:00
Nicolas Duchon
63e190551e
[skip ci] include more details about v2.0 changes 2020-12-21 12:35:22 +01:00
Nicolas Duchon
0d95965f78
[skip ci] Merge pull request #734 from spiral-dev/patch-1
Fix document location
2020-12-19 16:13:31 +01:00
RB
3ca59ebda3
Fix document location
Document now points to correct location of data persistence
2020-12-19 15:22:01 +01:00
Nicolas Duchon
1752a420fa
Merge pull request #727 from buchdag/ci-fix
Fix LETSENCRYPT_KEYSIZE feature and tests
2020-12-08 23:58:58 +01:00
Nicolas Duchon
2cc81fcffe
[skip ci] Merge pull request #726 from bgarret/patch-1
Improve the Zero SSL documentation
2020-12-08 12:46:23 +01:00
Benoit Garret
1d62687cc6
Improve the Zero SSL documentation 2020-12-08 12:27:09 +01:00
Nicolas Duchon
a95a64eaac
Merge pull request #724 from bgarret/master
Allow ZEROSSL_API_KEY on the letsencrypt container
2020-12-08 10:36:27 +01:00
Benoit Garret
5f0bde1be4 Allow ZEROSSL_API_KEY on the letsencrypt container 2020-12-07 21:57:50 +01:00
Nicolas Duchon
6bfdd87109
Fix private keys types 2020-12-07 21:10:14 +01:00
Nicolas Duchon
082ad32f49
Fix the private_keys test 2020-12-07 21:05:32 +01:00
Nicolas Duchon
39682fa3e6
Test any image passed as argument to test/run.sh 2020-12-07 21:04:29 +01:00
Nicolas Duchon
6a4541ade9
Fix expected empty test output
They still require an (empty) expected-std-out.txt file
2020-12-07 19:40:41 +01:00
Nicolas Duchon
73b7186d6d
Raise back docker-gen debounce a bit 2020-12-07 00:57:22 +01:00
Nicolas Duchon
3d0cd89668
Remove array variable indirection hack
Replaced by declare -n builtin of Bash 4.3
2020-12-07 00:56:09 +01:00
Nicolas Duchon
1e3d4cf6c2
Update acme.sh to 2.8.8 (#723) 2020-12-06 11:56:56 +01:00
Nicolas Duchon
bc9327e873
Use Dependabot to maintain Docker dependencies 2020-11-30 19:01:50 +01:00
Nicolas Duchon
fb4d58e41e
[skip ci] doc typos 2020-11-30 15:20:57 +01:00
Nicolas Duchon
849c676f15
Merge pull request #719 from nginx-proxy/acme.sh
Replace simp_le with acme.sh
2020-11-30 15:10:07 +01:00
Nicolas Duchon
680b1a4575
[skip ci] Build & push to DockerHub w/ GH Actions 2020-11-30 12:28:59 +01:00
Nicolas Duchon
65520ff6fb
Small fixes on symlinks test 2020-11-29 02:55:13 +01:00
Nicolas Duchon
5cfe0cb2d3
Fix the container_restart test
This might be a race condition
2020-11-29 02:55:13 +01:00
Nicolas Duchon
f069d50d33
Fix tests self cleanup 2020-11-29 02:55:13 +01:00
Nicolas Duchon
2cece8658a
Fix docker_api test expecting wrong signal 2020-11-29 02:55:13 +01:00
Nicolas Duchon
d4c4c32a7b
Warn that v2.0.0 is not 100% backward compatible 2020-11-29 02:55:13 +01:00
Nicolas Duchon
7a787c7700
Add ACMESH_VERSION build arg to the Dockerfile 2020-11-29 02:55:12 +01:00
Nicolas Duchon
10e866210d
Silence detached had warning on docker-gen build 2020-11-29 02:55:12 +01:00
Nicolas Duchon
c2ca8ef944
Cleaner use of apk in install_acme.sh 2020-11-29 02:55:12 +01:00
Nicolas Duchon
26f61150a1
Add Zero SSL documentation 2020-11-29 02:55:12 +01:00
Nicolas Duchon
8a57f95994
Comment clarification in single domain test 2020-11-29 02:55:12 +01:00
Nicolas Duchon
ce78b18088
User correct DEBUG level in tests 2020-11-29 02:55:12 +01:00
Nicolas Duchon
1966e52a61
Support for preferred chain 2020-11-29 02:55:11 +01:00
Nicolas Duchon
d119a7e5f4
Go template formatting adjustments 2020-11-27 14:47:28 +01:00
Nicolas Duchon
e9aa88f091
Add support for Zero SSL API key 2020-11-27 14:47:28 +01:00
Nicolas Duchon
3db820a339
Test for private keys types 2020-11-27 14:47:28 +01:00
Nicolas Duchon
caf79a0071
Shell linting 2020-11-27 14:47:28 +01:00
Nicolas Duchon
a5b3b0a8b6
Do not attempt to read inexistant files in tests 2020-11-27 14:47:28 +01:00
Nicolas Duchon
b2e85e1c6c
Update Boulder to 2020-10-19 release 2020-11-27 14:47:27 +01:00
Nicolas Duchon
92cf9ff147
Test suite refactoring
As much as possible, output to stdout on error condition only
in order to reduce the need for expected-std-out.txt
2020-11-27 14:47:25 +01:00
Nicolas Duchon
f8a24a6dff
Use acme.sh maximum debug level 2020-11-27 14:46:48 +01:00
Nicolas Duchon
d36dea9dda
More robust ACME account registration 2020-11-27 14:46:48 +01:00
Nicolas Duchon
5778216fad
Rename LETSENCRYPT_ACME_CA_URI > ACME_CA_URI 2020-11-27 14:46:48 +01:00
Nicolas Duchon
83aa3c9fda
Support for Zero SSL 2020-11-27 14:46:47 +01:00
Nicolas Duchon
a90c82a1a7
Add support for elliptic curve 512 bits keys 2020-11-27 14:46:47 +01:00
Nicolas Duchon
90c3ec6aa6
Update doc on failing authorizations 2020-11-27 14:46:47 +01:00
Nicolas Duchon
8052f04b07
Store and use image version based on git describe 2020-11-27 14:46:47 +01:00
Nicolas Duchon
9e6655c665
Update maintainer label 2020-11-27 14:46:47 +01:00
Henrique Bastos
d3dcce19ba
Reduce intermediary image size 2020-11-27 14:46:47 +01:00
Nicolas Duchon
fc6677404c
netcat is no longer required by acme.sh 2020-11-27 14:46:46 +01:00
Nicolas Duchon
3f2c852998
Upgrade image to alpine 3.12 2020-11-27 14:46:46 +01:00
Nicolas Duchon
54e73a994b
Backward compatibility with REUSE_PRIVATE_KEYS 2020-11-27 14:46:46 +01:00
Nicolas Duchon
14a019fedb
Fix private key reuse 2020-11-27 14:46:46 +01:00
Nicolas Duchon
e2d05afa25
Better debugging of acme.sh call parameters 2020-11-27 14:46:46 +01:00
Nicolas Duchon
cfc274642b
Reload nginx after creating the default cert 2020-11-27 14:46:46 +01:00
Nicolas Duchon
3e4b0a43dd
Linefeed typo 2020-11-27 14:46:45 +01:00
Nicolas Duchon
fde553364c
Use acme.sh --register-account in the service loop 2020-11-27 14:46:45 +01:00
Gilles Filippini
195b19d221
Use email-less staging conf for test certificates 2020-11-27 14:46:45 +01:00
Nicolas Duchon
aa62af0052
Code styling 2020-11-27 14:46:45 +01:00
Gilles Filippini
e5825d6fc4
Add doc on ACME accounts handling 2020-11-27 14:46:45 +01:00
Nicolas Duchon
0c165134b9
Update docs and comments 2020-11-27 14:46:45 +01:00
Nicolas Duchon
a3f32e11b4
Update acme_accounts test for LETSENCRYPT_EMAIL 2020-11-27 14:46:44 +01:00
Gilles Filippini
3b1e1ba851
Enable proxied containers ACME email override 2020-11-27 14:46:44 +01:00
Nicolas Duchon
562d7f1154
Add test for ACME accounts handling 2020-11-27 14:46:44 +01:00
Gilles Filippini
3b6d87e8c2
Use default config for empty DEFAULT_EMAIL only 2020-11-27 14:46:44 +01:00
Nicolas Duchon
7cf0a52bce
Refactor update_certs() into two functions 2020-11-27 14:46:44 +01:00
Gilles Filippini
4504e3b4ca
Remove unused function from entrypoint 2020-11-27 14:46:44 +01:00
Gilles Filippini
fe01d76866
Fix typo in Dockerfile comment 2020-11-27 14:46:43 +01:00
Gilles Filippini
f66c1d55bf
Links should be created on RENEW_SKIP as well 2020-11-27 14:46:43 +01:00
Nicolas Duchon
b07dbc5468
Test if symlinks get created on RENEW_SKIP 2020-11-27 14:46:43 +01:00
Nicolas Duchon
e63b22722e
Fix wrong container names on Travis 2020-11-27 14:46:43 +01:00
Nicolas Duchon
984235f36f
Shell linting 2020-11-27 14:46:40 +01:00
Nicolas Duchon
c27e66ef80
Update doc 2020-11-27 14:45:41 +01:00
Nicolas Duchon
52b7a51ca2
Extend timeout for restart test 2020-11-27 14:45:37 +01:00
Nicolas Duchon
c821d809f6
Change ACME client to acme.sh 2020-11-27 14:44:01 +01:00
Nicolas Duchon
96c9cbf821
Revert "Merge pull request #485 from Greek64/PR"
This feature is not supported in this form by acme.sh

This reverts commit 7dd2cd67e5, reversing
changes made to 6a90d53c97.
2020-11-27 14:44:01 +01:00
Nicolas Duchon
315e63201c
Merge pull request #717 from buchdag/ci-speedup
Speed up tests execution.
2020-11-23 10:11:56 +01:00
Nicolas Duchon
5c55c71720
Speed up tests 2020-11-23 04:25:30 +01:00
Nicolas Duchon
fa6745c01b
Make docker-gen wait configurable, lower default
This is supposed to just be a debounce
15s by default was way too long
2020-11-23 03:13:15 +01:00
Nicolas Duchon
1738ec7576
Cut Travis CI build time (#716)
Travis CI changed its pricing model, OSS builds are no longer free:
https://blog.travis-ci.com/2020-11-02-travis-ci-new-billing
this commit greatly reduce credit consumption before another solution can be found
2020-11-21 20:06:37 +01:00
Nicolas Duchon
a549a129de
Merge pull request #711 from remogloor/standalone-config
Search in all config files for standalone config
2020-11-13 15:06:17 +01:00
Remo Gloor
6ede7a7663
Search in all config files for standalone config
Search in all .conf files for the server configuration when a standalone configuration exists.
2020-11-12 18:04:53 +01:00
Nicolas Duchon
711c56acc1
Update build stage to golang 1.15 (#700) 2020-10-13 00:11:43 +02:00
Nicolas Duchon
931d22b222
[skip ci] Update shields on README.md 2020-10-12 14:48:55 +02:00
Nicolas Duchon
a14759502d
Merge pull request #697 from arteeh/patch-1
Fix typo
2020-09-26 09:06:58 +02:00
Maarten de Jong
04d2c32b91
Fix typo
People (like me) will think they should add a dot to the end of their label.
2020-09-25 20:42:33 +02:00
Nicolas Duchon
88949415bd
Merge pull request #683 from buchdag/fix-674
Don't incorrectly grep existing subdomain with add_standalone_configuration()
2020-07-17 15:13:12 +02:00
Nicolas Duchon
09518fec98
Grep more than just the domain for standalone conf 2020-07-17 14:24:39 +02:00
Nicolas Duchon
b822743871
Test for incorrect greping 2020-07-17 14:23:50 +02:00
Nicolas Duchon
20c0f01dee
Merge pull request #677 from buchdag/fix-676
Trim trailing dot from domains
2020-07-04 18:57:13 +02:00
Nicolas Duchon
3052654431
Fix issue with trailing dot on domains list 2020-07-04 15:29:37 +02:00
Nicolas Duchon
5a5f4d8886
Test for trailing dots on domains list 2020-07-04 15:07:08 +02:00
Nicolas Duchon
c79c8c556f
[skip ci] Merge pull request #657 from noahbliss/patch-1
s/proxyed/proxied/g
2020-05-17 14:23:30 +02:00
noahbliss
4d8eabb915
s/proxyed/proxied/g
;)
2020-05-15 19:06:51 -04:00
Nicolas Duchon
e8facb6bdd
Merge pull request #654 from nginx-proxy/dev
Merge standalone certificate feature to master
2020-05-11 16:43:45 +02:00
Nicolas Duchon
478f3a13de
Remove beta status of standalone cert feature 2020-05-11 15:48:12 +02:00
Nicolas Duchon
728a893505
Set auth_request off
#570
2020-05-11 15:47:00 +02:00
Nicolas Duchon
45df81962e
Shell linting 2020-05-11 15:43:07 +02:00
Nicolas Duchon
6dd7bc45f5
Test unit for standalone certificates 2020-05-10 19:37:05 +02:00
Nicolas Duchon
8a936cc4fe
Add standalone certificate feature
Standalone certificates are generated from a static user provided
configuration file rather than from the dynamicaly generated (from
running containers environment variables) letsencrypt_service_data file.
2020-05-10 19:35:59 +02:00
Nicolas Duchon
7e3d341bf4
Merge pull request #647 from hiqdev/feature/single_domain_cert
Implemented LETSENCRYPT_SINGLE_DOMAIN_CERTS environment variable
2020-05-10 19:17:55 +02:00
Dmytro Naumenko
177d60bc11
Clarify the LETSENCRYPT_SINGLE_DOMAIN_CERTS value 2020-05-07 17:16:27 +03:00
SilverFire - Dmitry Naumenko
5592543838 Implemented LETSENCRYPT_SINGLE_DOMAIN_CERTS environment variable 2020-04-21 13:29:41 +03:00
Nicolas Duchon
e49c2d5213
Shellcheck linting (#641) 2020-03-23 08:20:46 +01:00
Nicolas Duchon
8640038d45
[skip ci] Multi-domains certificates examples (#640)
fix #613
2020-03-21 16:41:38 +01:00
Nicolas Duchon
004e98b3bf
Merge pull request #637 from OscarKolsrud/patch-1
Update simp_le to 0.18.0
2020-03-17 13:12:34 +01:00
Oscar S Kolsrud
b334366e29
Update simp_le to 0.18.0
Updated simp_le tagged version to include fix for Buypass CA. Fixes issue https://github.com/nginx-proxy/docker-letsencrypt-nginx-proxy-companion/issues/634
2020-03-17 12:07:51 +01:00
Nicolas Duchon
92a5f0a86b
Merge pull request #628 from buchdag/chore
Upgrade golang build stage and alpine image
2020-03-09 15:15:58 +01:00
Nicolas Duchon
70a9c8a8eb
Remove deprecated / aliases keys from .travis.yml (#632) 2020-03-09 15:14:59 +01:00
Nicolas Duchon
32b4c05ba7
Update Travis CI badge 2020-03-09 12:50:29 +01:00
Nicolas Duchon
7d8bab1ae5
Upgrade image to alpine 3.11 2020-03-05 16:31:19 +01:00
Nicolas Duchon
36efdd8ec4
Update build stage to golang 1.14 2020-03-05 16:30:56 +01:00
Nicolas Duchon
f5e6cfc688
Merge pull request #626 from imaemo/master
fix typo
2020-03-04 12:29:32 +01:00
maemo
2dcb2a3c76
fix typo 2020-03-02 00:36:40 +08:00
Nicolas Duchon
82b088353d
Fix and improve the /app/cert_status utility 2019-12-09 23:01:29 +01:00
Nicolas Duchon
9806ba2587
[skip-ci] Update doc to reflect ACME v2 changes (#594) 2019-10-18 00:02:55 +02:00
Nicolas Duchon
cec2482b27
Verify account files existence before perm check (#592)
fix #591
2019-10-17 13:57:48 +02:00
Nicolas Duchon
bcaefd1ac5
Merge pull request #590 from buchdag/acmev2
Switch to ACME v2
2019-10-16 14:27:16 +02:00
Nicolas Duchon
2d418615c7
Warn about ACME v1 incompatibility in README 2019-10-16 13:46:28 +02:00
Nicolas Duchon
3764b1b4ea
Update simp_le to 0.16.0 2019-10-16 13:13:51 +02:00
Nicolas Duchon
c5b3715b12
Merge pull request #589 from cherouvim/patch-1
docs: typo
2019-10-14 11:21:42 +02:00
Ioannis Cherouvim
38a689aad4
docs: typo
"must answers" → "must answer"
2019-10-14 11:22:13 +03:00
Nicolas Duchon
b546bbaa17
Update Boulder to 2019-10-07 release 2019-10-10 11:50:19 +02:00
Nicolas Duchon
30c51154e4
Clearer debug and comments on set_ownership_and_permissions() 2019-10-10 11:50:18 +02:00
Nicolas Duchon
5e818db602
Update the test suite for ACME v2 2019-10-10 11:50:18 +02:00
Nicolas Duchon
57fae83e70
ACME v2 compatibility
Use simp_le master branch for now
2019-10-10 11:50:14 +02:00
Nicolas Duchon
d26118f615
Merge pull request #588 from buchdag/fix-561
Enable use of wildcard location configurations
2019-10-10 11:30:58 +02:00
Nicolas Duchon
dc37364c5d
Update build stage to golang 1.13 (#587) 2019-10-10 11:20:32 +02:00
Nicolas Duchon
099cc93c1a
Upgrade image to alpine 3.10 (#586) 2019-10-10 11:20:14 +02:00
Nicolas Duchon
7cc7f10f43
Merge pull request #570 from dsullivan/master
Set auth_request off for acme challenge location
2019-08-20 22:30:32 +02:00
Dan Sullivan
9071075d32 Set auth_request off for acme challenge location 2019-08-18 21:19:36 -04:00
Nicolas Duchon
308bd8f81c
Add tests for wildcard locations 2019-08-02 20:01:36 +02:00
Nicolas Duchon
90e6fbcb49
Use wildcard location configurations
fix #561
2019-08-02 19:55:12 +02:00
Nicolas Duchon
d5f48cf8ec
Merge pull request #555 from buchdag/ci-update
Test suite update / upgrade
2019-06-24 16:46:45 +02:00
Nicolas Duchon
6e4a6197c8
Limit container logs to failed tests 2019-06-24 15:42:33 +02:00
Nicolas Duchon
154c3e1f70
Fix Travis's Docker containers logs 2019-06-24 15:42:19 +02:00
Nicolas Duchon
337ca32a20
Move Travis docker-compose update to a script
and udate docker-compose to 1.24.0
2019-06-24 15:40:56 +02:00
Nicolas Duchon
05b3433c37
Allow 18.04 builds to fail
this dist is not yet officially supported by Travis CI
2019-06-24 15:40:56 +02:00
Nicolas Duchon
ed19587e24
Add 18.04 Bionic to the test matrix 2019-06-24 11:01:06 +02:00
Nicolas Duchon
64019c9bfc
Add 16.04 Xenial to the test matrix 2019-06-24 10:53:52 +02:00
Nicolas Duchon
f03efbb51b
[skip ci] Merge pull request #552 from AsterYujano/patch-1
[doc] Fix: missing backslash in 2 commands
2019-06-23 18:48:03 +02:00
Nicolas Duchon
33d1514a62
Fix CI breakage due to Boulder (#554)
Boulder master is currently broken on Docker:
https://github.com/letsencrypt/boulder/issues/4284
2019-06-23 18:42:59 +02:00
François
33a18c012f
[doc] Fix: missing backslash in 2 commands 2019-06-20 14:16:17 +02:00
Nicolas Duchon
33b7d93196
Merge pull request #550 from buchdag/default-email
Add environment variable for global default email.
2019-06-11 10:42:48 +02:00
Nicolas Duchon
ba8ae0fb76
[skip-ci] recommend using DEFAULT_EMAIL
instead or in addition to LETSENCRYPT_EMAIL
2019-06-10 17:13:32 +02:00
Nicolas Duchon
a0b59c9688
[skip-ci] warn about LETSENCRYPT_EMAIL issue 2019-06-10 17:13:11 +02:00
Nicolas Duchon
4febd32f93
Merge pull request #548 from buchdag/cow
Use cp+rm / sed+cp+rm instead of mv / sed -i on location configuration
2019-06-09 14:44:00 +02:00
Nicolas Duchon
36ef610f0e
Use cp/rm instead of mv on location configuration
mv does not work with file bind mounted
inside a container with --volume as their inode
can't be changed from within the container
2019-06-09 14:18:05 +02:00
Nicolas Duchon
bd3e3e6156
Add location configuration test unit 2019-06-09 14:05:10 +02:00
Nicolas Duchon
b66d21b60e
Add DEFAULT_EMAIL global env variable 2019-05-30 11:17:17 +02:00
Nicolas Duchon
6b07d6bd29
[skip ci] Remove nginx-proxy DH parameters issue's warning
This reverts commit 425ef0eee7.
nginx-proxy issue has been fixed.
2019-05-28 23:37:47 +02:00
Nicolas Duchon
425ef0eee7
Warn about nginx-proxy DH parameters issue 2019-05-21 00:54:17 +02:00
Nicolas Duchon
48b4b450ce
Fix DH parameters overwrite issue (#545)
fix #543
2019-05-20 17:16:37 +02:00
Nicolas Duchon
522d396b0d
Fix unbound variable issue in get_self_cid
fix #542
2019-05-17 18:01:59 +02:00
Nicolas Duchon
b89be7eae4
Switch to Python 3 (#540)
Python 2.7 will reach its end of life on 1st Jan 2020
2019-05-16 10:42:32 +02:00
Nicolas Duchon
6910b72d41
Upgrade image to alpine 3.9 (#538)
Base image upgrade.
2019-05-15 12:40:46 +02:00
Nicolas Duchon
dbb3e6fb35
Update build stage to golang 1.12 (#539)
golang version bump
2019-05-15 12:40:32 +02:00
Nicolas Duchon
8f694ca63e
Update simp_le to 0.14.0 (#537) 2019-05-15 11:29:13 +02:00
Nicolas Duchon
ca8befd5d8
[skip ci] Merge pull request #519 from MalteT/patch-1
Add  a minor help on how to create certs for multiple hosts
2019-03-12 15:46:47 +01:00
MalteT
46065a1cd2
Make usage with multiple hosts explicit 2019-03-11 15:12:06 +01:00
Nicolas Duchon
4b4fd76b45
Merge pull request #516 from xiamaz/fixcid
Correctly retry if format is not expected
2019-03-08 00:13:31 +01:00
Max Zhao
af4784d444 Fixing get cid to retry alternative sources. Also use regex to exactly match 64 char CID 2019-02-28 10:47:14 +01:00
Nicolas Duchon
04fbee42cf
Update simp_le to 0.13.0 (#515) 2019-02-25 16:06:12 +01:00
Nicolas Duchon
16e50ac24e
[skip ci] Merge pull request #507 from TeddyBear06/master
Update README.md
2019-02-11 18:48:27 +01:00
T3d
20ae17f063
Update README.md
Missing backslash.
2019-02-10 12:15:50 +01:00
Nicolas Duchon
85a6a5d225
[skip ci] Merge pull request #502 from mujahed1987/patch-1
fix syntax in step 3
2019-01-23 20:45:09 +01:00
mujahed1987
1637f7df92
fix syntax in step 3
adding missed " \"  ### Step 3 - proxyed container(s) after ```---name your-proxyed-app```
2019-01-22 18:40:00 +02:00
Nicolas Duchon
3f796ded2b
Merge pull request #500 from MatthijsKok/patch-1
Fix typo
2019-01-17 13:05:07 +01:00
Matthijs Kok
c4153c5836
Fix typo 2019-01-17 09:24:52 +01:00
Nicolas Duchon
fb2d0b2371
Use multiple methods to obtain self cid (#499)
fix #498
2019-01-16 11:29:24 +01:00
Nicolas Duchon
804344b4e4
[skip ci] Update test certificate doc (#497)
fix #496

Co-authored-by: @VincentSaelzler
2019-01-15 14:01:47 +01:00
Nicolas Duchon
d0e2cd4155
[skip ci] Merge pull request #495 from chrisarusso/patch-1
Update Invalid-authorizations.md
2019-01-14 11:17:24 +01:00
Chris Russo
4dfa6ea7ea
Update Invalid-authorizations.md
fix typo
2019-01-14 01:00:16 -05:00
Nicolas Duchon
cff8e96da6
[skip ci] Documentation rework (#493) 2019-01-11 18:58:49 +01:00
Nicolas Duchon
407c7c4467
Merge pull request #491 from buchdag/self-cid
Simplify the self cid discovery mechanism
2019-01-10 12:27:54 +01:00
Nicolas Duchon
70405ffde9
Simplify the self cid discovery mechanism
Also remove the hard requirement that the container get its
self cid during entrypoint as it is only used to produce a warning
and to get the nginx-proxy container id through --volumes-from,
which is only one of the three methods.

The fact that the LE container could get an nginx-proxy container id
is independently verified by the entrypoint script anyway and this
hard requirement on getting its self cid has been known to cause
troubles with containers running under orchestrators such as Amazon ECS
or Kubernetes.
2019-01-09 20:49:11 +01:00
Nicolas Duchon
7dd2cd67e5
Merge pull request #485 from Greek64/PR
Add LETSENCRYPT_MIN_VALIDITY variable
2019-01-08 19:22:49 +01:00
Grieche
a0e54d86f4 Add LETSENCRYPT_MIN_VALIDITY info to the README
Add MIN_VALIDITY_CAP info to the README
2019-01-08 18:37:02 +01:00
Grieche
9f32ebcd83 Added test unit for LETSENCRYPT_MIN_VALIDITY environment variable
Modified Boulder to issue certificates with 88 day lifetime
2019-01-08 18:11:48 +01:00
Grieche
a911b12661 Added LETSENCRYPT_MIN_VALIDITY environment variable.
Allows to specify the minimum validity of certificates
  per container.
2019-01-08 18:11:21 +01:00
Nicolas Duchon
6a90d53c97
Update build stage to golang 1.11 (#487) 2018-12-26 14:51:31 +01:00
Nicolas Duchon
f3809e1e12
Update docker-compose in test suite (#486)
Test suite docker-compose version bump to `1.23.2`
2018-12-25 21:03:07 +01:00
Nicolas Duchon
6c73fc2675
Merge pull request #442 from Greek64/PR
New environmental variable LETSENCRYPT_RESTART_CONTAINER
2018-12-21 22:32:53 +01:00
Grieche
f79496af98 * Add Feature Info in the README 2018-12-21 18:42:54 +01:00
Grieche
31daa643e5 * Fix whitespace typos 2018-12-21 18:19:05 +01:00
Grieche
f5070d49bf * Added new test unit to test LETSENCRYPT_RESTART_CONTAINER variable functionality 2018-12-21 13:05:20 +01:00
Nicolas Duchon
e933996e61
Typo in default_cert_subject function
missing break spotted by @Greek64
2018-12-20 13:52:10 +01:00
Nicolas Duchon
43ead553e7
Update simp_le to 0.12.0 (#484) 2018-12-20 13:47:36 +01:00
Grieche
fbab2c2913 * New environmental variable LETSENCRYPT_RESTART_CONTAINER
Containers with this variable set to true will be restarted
      	when their respective certificates are updated/modified.
2018-12-19 20:29:49 +01:00
Nicolas Duchon
9b69fb50c7
Fix local test suite (#483)
credits to @Greek64
2018-12-19 16:49:32 +01:00
Nicolas Duchon
4f0e2140a4
Merge pull request #481 from buchdag/own-symlinks
Change ownership of symlinks in addition to regular files
2018-12-15 00:59:59 +01:00
Nicolas Duchon
af82747162
Update permissions test units for symlinks 2018-12-15 00:14:42 +01:00
Nicolas Duchon
feefd2d646
Manage ownership of symlinks too
Fix #471
2018-12-15 00:14:42 +01:00
Nicolas Duchon
5ac2ba963e
Make the DEBUG env var case insensitive (#480)
bug spotted by @desimaniac
2018-12-14 09:49:51 +01:00
85 changed files with 4133 additions and 1157 deletions

View File

@ -1,6 +1,6 @@
+.*
+docs
+go
+test
+LICENSE
+README.md
+schema.png

62
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,62 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
If your are using the latest image tag and recently updated your image: please make sure you've checked the required read on the project's README.
HTTPS does not work / certificate aren't created : please check in your acme-companion container logs if an authorization or verify error is mentioned, if it is please do the following before opening an issue:
- check and follow the troubleshooting instructions in the docs.
- search the existing similar issues, both opened and closed.
Bug description
-----------------
A clear and concise description of what the bug is.
acme-companion image version
-----------------
Please provide the container version that should be printed to the first line of log at container startup:
```
Info: running acme-companion version v2.0.0
```
If this first log line isn't present you are using a v1 image: please provide the tagged version you are using. If you are not using a tagged version latest, please try again with a tagged release before opening an issue (the last v1 tagged release is v1.13.1).
nginx-proxy's Docker configuration
-----------------
Please provide the configuration (either command line, compose file, or other) of your nginx-proxy stack and your proxied container(s).
You can obfuscate information you want to keep private (and should obfuscate configuration secrets) such as domain(s) and/or email adress(es), but other than that please provide the full configurations and not the just snippets of the parts that seem relevants to you.
rendered nginx configuration
-----------------
Please provide the rendered nginx configuration:
```console
docker exec name-of-the-nginx-container nginx -T
```
Containers logs
-----------------
Please provide the logs of:
- your acme-companion container
- your nginx-proxy container (or nginx and docker-gen container in a three containers setup)
```console
docker logs name-of-the-companion-container
```
Docker host
-----------------
- OS: [e.g. Ubuntu 20.04]
- Docker version: output of `docker version`

21
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,21 @@
version: 2
updates:
# Maintain dependencies for Docker
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "daily"
commit-message:
prefix: "build"
labels:
- "type/build"
# Maintain GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
commit-message:
prefix: "ci"
labels:
- "type/ci"

76
.github/workflows/build-publish.yml vendored Normal file
View File

@ -0,0 +1,76 @@
name: Build and publish Docker image
on:
workflow_dispatch:
push:
tags:
- "v*"
paths:
- ".dockerignore"
- ".github/workflows/build-publish.yml"
- "app/*"
- "Dockerfile"
- "install_acme.sh"
jobs:
multiarch-build:
name: Build and publish Docker image
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Retrieve version
id: acme-companion_version
run: echo "VERSION=$(git describe --tags)" >> "$GITHUB_OUTPUT"
- name: Get Docker tags
id: docker_meta
uses: docker/metadata-action@v5
with:
images: |
btcpayserver/letsencrypt-nginx-proxy-companion
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest,enable={{is_default_branch}}
labels: |
org.opencontainers.image.authors=Nicolas Duchon <nicolas.duchon@gmail.com> (@buchdag), Yves Blusseau
org.opencontainers.image.version=${{ steps.acme-companion_version.outputs.VERSION }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
id: docker_build
uses: docker/build-push-action@v5
with:
context: .
build-args: GIT_DESCRIBE=${{ steps.acme-companion_version.outputs.VERSION }}
platforms: linux/amd64,linux/arm64,linux/arm/v7
push: true
tags: ${{ steps.docker_meta.outputs.tags }}
labels: ${{ steps.docker_meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}

173
.github/workflows/test.yml vendored Normal file
View File

@ -0,0 +1,173 @@
name: Tests
on:
workflow_dispatch:
push:
branches:
- main
paths-ignore:
- "docs/**"
- "*.md"
pull_request:
paths-ignore:
- "docs/**"
- "*.md"
env:
DOCKER_GEN_CONTAINER_NAME: nginx-proxy-gen
IMAGE: nginxproxy/acme-companion
NGINX_CONTAINER_NAME: nginx-proxy
TEST_DOMAINS: le1.wtf,le2.wtf,le3.wtf
jobs:
companion-build:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Build Image
run: docker build -t "$IMAGE" .
- name: Inspect Image
run: docker inspect "$IMAGE"
- name: Get acme.sh Version
run: docker run --rm "$IMAGE" acme.sh --version
- name: List Docker Images
run: docker images
- name: Export Image Artifact
run: docker save $IMAGE > companion.tar
- name: Upload Image Artifact
uses: actions/upload-artifact@v3
with:
name: companion.tar
path: companion.tar
docker-specs-tests:
needs: companion-build
runs-on: ubuntu-latest
steps:
- name: Checkout Docker official images tests
uses: actions/checkout@v4
with:
repository: docker-library/official-images
path: official-images
- name: Download Builded Image
uses: actions/download-artifact@v3
with:
name: companion.tar
- name: Import Builded Image
run: docker load < companion.tar
- name: Docker Specifications Testing
run: official-images/test/run.sh "$IMAGE"
- name: Display containers logs
if: ${{ failure() }}
run: test/github_actions/containers-logs.sh
integration-tests:
needs:
- companion-build
strategy:
fail-fast: false
matrix:
test-name:
[
docker_api,
docker_api_legacy,
location_config,
certs_single,
certs_san,
certs_single_domain,
certs_standalone,
force_renew,
acme_accounts,
private_keys,
container_restart,
permissions_default,
permissions_custom,
symlinks,
acme_hooks,
]
setup: [2containers, 3containers]
acme-ca: [pebble]
pebble-config: [pebble-config.json]
include:
- test-name: acme_eab
setup: 2containers
acme-ca: pebble
pebble-config: pebble-config-eab.json
- test-name: acme_eab
setup: 3containers
acme-ca: pebble
pebble-config: pebble-config-eab.json
- test-name: ocsp_must_staple
setup: 2containers
acme-ca: boulder
- test-name: ocsp_must_staple
setup: 3containers
acme-ca: boulder
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
# PREPARE RUNNER ENV
- name: Add Test Domains in /etc/hosts
run: |
IFS=',' read -r -a test_domains <<< "$TEST_DOMAINS"
test_domains+=(pebble pebble-challtestsrv)
for domain in "${test_domains[@]}"
do
echo "127.0.0.1 $domain" | sudo tee -a /etc/hosts
done
- name: Setup Pebble
if: ${{ matrix.acme-ca == 'pebble' }}
env:
PEBBLE_CONFIG: ${{ matrix.pebble-config }}
run: test/setup/pebble/setup-pebble.sh
- name: Setup Boulder
if: ${{ matrix.acme-ca == 'boulder' }}
run: test/setup/setup-boulder.sh
- name: Setup nginx-proxy
env:
SETUP: ${{ matrix.setup }}
ACME_CA: ${{ matrix.acme-ca }}
run: test/setup/setup-nginx-proxy.sh
# ADD BUILT IMAGE
- name: Download Built Image
uses: actions/download-artifact@v3
with:
name: companion.tar
- name: Import Built Image
run: docker load < companion.tar
# TEST
- name: Integration Testing
env:
SETUP: ${{ matrix.setup }}
ACME_CA: ${{ matrix.acme-ca }}
PEBBLE_CONFIG: ${{ matrix.pebble-config }}
run: test/run.sh -t ${{ matrix.test-name }} "$IMAGE"
- name: Display containers logs
if: ${{ failure() }}
env:
SETUP: ${{ matrix.setup }}
ACME_CA: ${{ matrix.acme-ca }}
run: test/github_actions/containers-logs.sh

6
.gitignore vendored
View File

@ -1,3 +1,5 @@
.docker
.vscode
certs/
conf.d/
data/
@ -6,5 +8,9 @@ vhost.d/
# tests
go/
nginx.tmpl
pebble.minica.pem
test/local_test_env.sh
test/tests/docker_api/expected-std-out.txt
test/tests/container_restart/docker_event_out.txt
test/tests/certs_standalone/letsencrypt_user_data
test/tests/location_config/le2.wtf

View File

@ -1,55 +0,0 @@
dist: trusty
sudo: required
language: bash
addons:
hosts:
- le1.wtf
- le2.wtf
- le3.wtf
env:
global:
- IMAGE=jrcs/letsencrypt-nginx-proxy-companion
- NGINX_CONTAINER_NAME=nginx-proxy
- DOCKER_GEN_CONTAINER_NAME=nginx-proxy-gen
- TEST_DOMAINS=le1.wtf,le2.wtf,le3.wtf
matrix:
- SETUP=2containers
- SETUP=3containers
before_install:
- sudo rm /usr/local/bin/docker-compose
- curl -L https://github.com/docker/compose/releases/download/1.21.1/docker-compose-`uname -s`-`uname -m` > docker-compose.temp
- chmod +x docker-compose.temp
- sudo mv docker-compose.temp /usr/local/bin/docker-compose
install:
- docker build -t "$IMAGE" .
- docker inspect "$IMAGE"
- docker run --rm "$IMAGE" simp_le --version
- docker run --rm "$IMAGE" simp_le -v --test
- docker images
before_script:
- git clone https://github.com/docker-library/official-images.git official-images
- test/setup/setup-boulder.sh
- test/setup/setup-nginx-proxy.sh
- docker pull nginx:alpine
script:
- official-images/test/run.sh "$IMAGE"
- test/run.sh "$IMAGE"
after_failure:
- docker logs $NGINX_CONTAINER_NAME
- docker logs default_cert
- docker logs certs_single
- docker logs certs_san
- docker logs force_renew
- docker logs permissions_default
- docker logs permissions_custom
- docker logs symlinks
- docker logs boulder
- if [[ $SETUP = "3containers" ]]; then docker logs $DOCKER_GEN_CONTAINER_NAME; fi

View File

@ -1,49 +1,34 @@
FROM golang:1.10-alpine AS go-builder
FROM nginxproxy/docker-gen:0.10.7 AS docker-gen
ENV DOCKER_GEN_VERSION=0.7.4
FROM alpine:3.19.0
# Install build dependencies for docker-gen
RUN apk add --update \
curl \
gcc \
git \
make \
musl-dev
ARG GIT_DESCRIBE
ARG ACMESH_VERSION=2.9.0
# Build docker-gen
RUN go get github.com/jwilder/docker-gen \
&& cd /go/src/github.com/jwilder/docker-gen \
&& git checkout $DOCKER_GEN_VERSION \
&& make get-deps \
&& make all
FROM alpine:3.8
LABEL maintainer="Yves Blusseau <90z7oey02@sneakemail.com> (@blusseau)"
ENV DEBUG=false \
DOCKER_HOST=unix:///var/run/docker.sock
ENV COMPANION_VERSION=$GIT_DESCRIBE \
DOCKER_HOST=unix:///var/run/docker.sock \
PATH=$PATH:/app
# Install packages required by the image
RUN apk add --update \
bash \
ca-certificates \
curl \
jq \
openssl \
&& rm /var/cache/apk/*
RUN apk add --no-cache --virtual .bin-deps \
bash \
coreutils \
curl \
jq \
openssl \
socat
# Install docker-gen from build stage
COPY --from=go-builder /go/src/github.com/jwilder/docker-gen/docker-gen /usr/local/bin/
# Install docker-gen from the nginxproxy/docker-gen image
COPY --from=docker-gen /usr/local/bin/docker-gen /usr/local/bin/
# Install simp_le
COPY /install_simp_le.sh /app/install_simp_le.sh
RUN chmod +rx /app/install_simp_le.sh \
# Install acme.sh
COPY /install_acme.sh /app/install_acme.sh
RUN chmod +rx /app/install_acme.sh \
&& sync \
&& /app/install_simp_le.sh \
&& rm -f /app/install_simp_le.sh
&& /app/install_acme.sh \
&& rm -f /app/install_acme.sh
COPY /app/ /app/
COPY app LICENSE /app/
WORKDIR /app

View File

@ -1,6 +1,7 @@
The MIT License (MIT)
Copyright (c) 2015-2016 Yves Blusseau
Copyright (c) 2015-2017 Yves Blusseau
Copyright (c) 2017-2022 Nicolas Duchon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

357
README.md
View File

@ -1,240 +1,117 @@
[![Build Status](https://travis-ci.org/JrCs/docker-letsencrypt-nginx-proxy-companion.svg?branch=master)](https://travis-ci.org/JrCs/docker-letsencrypt-nginx-proxy-companion)
[![GitHub release](https://img.shields.io/github/release/jrcs/docker-letsencrypt-nginx-proxy-companion.svg)](https://github.com/JrCs/docker-letsencrypt-nginx-proxy-companion/releases)
[![Image info](https://images.microbadger.com/badges/image/jrcs/letsencrypt-nginx-proxy-companion.svg)](https://hub.docker.com/r/jrcs/letsencrypt-nginx-proxy-companion "Click to view the image on Docker Hub")
[![Docker stars](https://img.shields.io/docker/stars/jrcs/letsencrypt-nginx-proxy-companion.svg)](https://hub.docker.com/r/jrcs/letsencrypt-nginx-proxy-companion "Click to view the image on Docker Hub")
[![Docker pulls](https://img.shields.io/docker/pulls/jrcs/letsencrypt-nginx-proxy-companion.svg)](https://hub.docker.com/r/jrcs/letsencrypt-nginx-proxy-companion "Click to view the image on Docker Hub")
letsencrypt-nginx-proxy-companion is a lightweight companion container for the [nginx-proxy](https://github.com/jwilder/nginx-proxy). It allows the creation/renewal of Let's Encrypt certificates automatically. See [Let's Encrypt section](#lets-encrypt) for configuration details.
Please note that [letsencrypt-nginx-proxy-companion does not work with ACME v2 endpoints yet](https://github.com/JrCs/docker-letsencrypt-nginx-proxy-companion/issues/319).
### Features:
* Automatic creation/renewal of Let's Encrypt certificates using original nginx-proxy container.
* Support creation of Multi-Domain ([SAN](https://www.digicert.com/subject-alternative-name.htm)) Certificates.
* Automatic creation of a Strong Diffie-Hellman Group (for having an A+ Rate on the [Qualsys SSL Server Test](https://www.ssllabs.com/ssltest/)).
* Automatic creation of a self-signed [default certificate](https://github.com/jwilder/nginx-proxy#how-ssl-support-works) if a user-provided one can't be found.
* Work with all versions of docker.
![schema](./schema.png)
#### Usage
To use it with original [nginx-proxy](https://github.com/jwilder/nginx-proxy) container you must declare 3 writable volumes from the [nginx-proxy](https://github.com/jwilder/nginx-proxy) container:
* `/etc/nginx/certs` to create/renew Let's Encrypt certificates
* `/etc/nginx/vhost.d` to change the configuration of vhosts (needed by Let's Encrypt)
* `/usr/share/nginx/html` to write challenge files.
Example of use:
* First start nginx with the 3 volumes declared:
```bash
$ docker run -d -p 80:80 -p 443:443 \
--name nginx-proxy \
-v /path/to/certs:/etc/nginx/certs:ro \
-v /etc/nginx/vhost.d \
-v /usr/share/nginx/html \
-v /var/run/docker.sock:/tmp/docker.sock:ro \
--label com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy \
jwilder/nginx-proxy
```
The "com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy" label is needed so that the letsencrypt container knows which nginx proxy container to use.
* Second start this container:
```bash
$ docker run -d \
-v /path/to/certs:/etc/nginx/certs:rw \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
--volumes-from nginx-proxy \
jrcs/letsencrypt-nginx-proxy-companion
```
Then start any containers you want proxied with a env var `VIRTUAL_HOST=subdomain.youdomain.com`
$ docker run -e "VIRTUAL_HOST=foo.bar.com" ...
The containers being proxied must [expose](https://docs.docker.com/reference/run/#expose-incoming-ports) the port to be proxied, either by using the `EXPOSE` directive in their `Dockerfile` or by using the `--expose` flag to `docker run` or `docker create`. See [nginx-proxy](https://github.com/jwilder/nginx-proxy) for more informations. To generate automatically Let's Encrypt certificates see next section.
#### Separate Containers
nginx proxy can also be run as two separate containers using the [jwilder/docker-gen](https://github.com/jwilder/docker-gen)
image and the official [nginx](https://hub.docker.com/_/nginx/) image.
You may want to do this to prevent having the docker socket bound to a publicly exposed container service (avoid to mount the docker socket in the nginx exposed container). It's better in a security point of view.
To run nginx proxy as a separate container you'll need:
1) To mount the template file [nginx.tmpl](https://github.com/jwilder/nginx-proxy/blob/master/nginx.tmpl) into the docker-gen container. You can get the latest official [nginx.tmpl](https://github.com/jwilder/nginx-proxy/blob/master/nginx.tmpl) with a command like:
```bash
curl https://raw.githubusercontent.com/jwilder/nginx-proxy/master/nginx.tmpl > /path/to/nginx.tmpl
```
2) Use the `com.github.jrcs.letsencrypt_nginx_proxy_companion.docker_gen` label on the docker-gen container, or explicitly set the `NGINX_DOCKER_GEN_CONTAINER` environment variable to the name or id of that container.
Examples:
* First start nginx (official image) with volumes:
```bash
$ docker run -d -p 80:80 -p 443:443 \
--name nginx \
-v /etc/nginx/conf.d \
-v /etc/nginx/vhost.d \
-v /usr/share/nginx/html \
-v /path/to/certs:/etc/nginx/certs:ro \
--label com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy \
nginx
```
* Second start the docker-gen container with the shared volumes and the template file:
```bash
$ docker run -d \
--name nginx-gen \
--volumes-from nginx \
-v /path/to/nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl:ro \
-v /var/run/docker.sock:/tmp/docker.sock:ro \
--label com.github.jrcs.letsencrypt_nginx_proxy_companion.docker_gen \
jwilder/docker-gen \
-notify-sighup nginx -watch -wait 5s:30s /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf
```
* Then start this container:
```bash
$ docker run -d \
--name nginx-letsencrypt \
--volumes-from nginx \
-v /path/to/certs:/etc/nginx/certs:rw \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
jrcs/letsencrypt-nginx-proxy-companion
```
* Then start any containers to be proxied as described previously.
Note:
If the 3 containers are using static names, both labels `com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy` on nginx container and `com.github.jrcs.letsencrypt_nginx_proxy_companion.docker_gen` on the docker-gen container can be removed.
The docker environment variables to be set on the letsencrypt container are:
* `NGINX_PROXY_CONTAINER` set to the name of the nginx container (here `nginx`)
* `NGINX_DOCKER_GEN_CONTAINER` set to the name of the docker-gen container (here `nginx-gen`)
Example:
```bash
$ docker run -d \
--name nginx-letsencrypt \
--volumes-from nginx \
-v /path/to/certs:/etc/nginx/certs:rw \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
-e NGINX_DOCKER_GEN_CONTAINER=nginx-gen \
-e NGINX_PROXY_CONTAINER=nginx \
jrcs/letsencrypt-nginx-proxy-companion
```
#### Let's Encrypt
To use the Let's Encrypt service to automatically create a valid certificate for virtual host(s), declare the `LETSENCRYPT_HOST` environment variable in each to-be-proxied application containers.
The `LETSENCRYPT_HOST` variable most likely needs to be set to the same value as the `VIRTUAL_HOST` variable and must be publicly reachable domains. Specify multiple hosts with a comma delimiter.
##### Example:
```bash
$ docker run -d \
--name example-app \
-e "VIRTUAL_HOST=example.com,www.example.com,mail.example.com" \
-e "LETSENCRYPT_HOST=example.com,www.example.com,mail.example.com" \
-e "LETSENCRYPT_EMAIL=foo@bar.com" \
tutum/apache-php
```
**Note:** the `VIRTUAL_HOST` (and `LETSENCRYPT_HOST`) must be (a) reachable domain(s) for LetEncrypt to be able to validate the challenge and provide the certificate.
**Note on CAA**: Please ensure that your DNS provider answers correctly to CAA record requests. [If your DNS provider answer with an error, Let's Encrypt won't issue a certificate for your domain](https://letsencrypt.org/docs/caa/). Let's Encrypt do not require that you set a CAA record on your domain, just that your DNS provider answers correctly.
**Note on IPv6**: If the domain or subdomain you want to issue certificate for has an AAAA record set, Let's Encrypt will favor challenge validation over IPv6. [There is an IPv6 to IPv4 fallback in place but Let's Encrypt cannot guarantee it'll work in every possible case](https://github.com/letsencrypt/boulder/issues/2770#issuecomment-340489871), so bottom line is **if you are not sure of both your host and your host's Docker reachability over IPv6, do not advertise an AAAA record** or LE challenge validation might fail.
The following environment variables are optional and parameterize the way the Let's Encrypt client works.
##### Contact address
The `LETSENCRYPT_EMAIL` variable must be a valid email and will be used by Let's Encrypt to warn you of impeding certificate expiration (should the automated renewal fail) or for account recovery. It is strongly advised to provide a valid contact address using this variable.
##### Private key size
The `LETSENCRYPT_KEYSIZE` variable determines the size of the requested key (in bit, defaults to 4096).
##### Multi-domain ([SAN](https://www.digicert.com/subject-alternative-name.htm)) certificates
If you want to create multi-domain ([SAN](https://www.digicert.com/subject-alternative-name.htm)) certificates add the base domain as the first domain of the `LETSENCRYPT_HOST` environment variable (see [the example](#example) above).
##### Test certificates
If you want to create test certificates that don't have the 5 certs/week/domain limits define the `LETSENCRYPT_TEST` environment variable with a value of `true` (in the containers where you request certificates with `LETSENCRYPT_HOST`). If you want to do this globally for all containers, set `ACME_CA_URI` as described below.
##### Automatic certificate renewal
Every hour (3600 seconds) the certificates are checked and every certificate that will expire in the next [30 days](https://github.com/kuba/simp_le/blob/ecf4290c4f7863bb5427b50cdd78bc3a5df79176/simp_le.py#L72) (90 days / 3) are renewed.
##### Force certificates renewal
If needed, you can force a running letsencrypt-nginx-proxy-companion container to renew all certificates that are currently in use. Replace `nginx-letsencrypt` with the name of your letsencrypt-nginx-proxy-companion container in the following command:
```bash
$ docker exec nginx-letsencrypt /app/force_renew
```
##### Show certificates informations
To display informations about your existing certificates, use the following command:
```bash
$ docker exec nginx-letsencrypt /app/cert_status
```
As for the forced renewal command, replace `nginx-letsencrypt` with the name of your letsencrypt-nginx-proxy-companion container.
##### ACME account keys
By default the container will save the first ACME account key created for each ACME API endpoint used, and will reuse it for all subsequent authorizations and issuances requests made to this endpoint. This behavior is enabled by default to avoid running into Let's Encrypt account [rate limits](https://letsencrypt.org/docs/rate-limits/).
For instance, when using the default Let's Encrypt production endpoint, the container will save the first account key created for this endpoint as `/etc/nginx/certs/accounts/acme-v01.api.letsencrypt.org/directory/default.json` and will reuse it for future requests made to this endpoint.
If required, you can use multiple accounts for the same ACME API endpoint by using the `LETSENCRYPT_ACCOUNT_ALIAS` environment variable on your proxyed container. This instruct the letsencrypt_nginx_proxy_companion container to look for an account key named after the provided alias instead of `default.json`. For example, `LETSENCRYPT_ACCOUNT_ALIAS=client1` will use the key named `client1.json` in the corresponding ACME API endpoint folder for this proxyed container (or will create it if it does not exists yet).
Please see the **One Account or Many?** paragraph on [Let's Encrypt Integration Guide](https://letsencrypt.org/docs/integration-guide/) for additional informations.
If you want to disable the account key reutilization entirely, you can set the environment variable `REUSE_ACCOUNT_KEYS` to `false` on the letsencrypt_nginx_proxy_companion container. This creates a new ACME registration with a corresponding account key for each new certificate issuance. Note that this won't create new account keys for certs already issued before `REUSE_ACCOUNT_KEYS` is set to `false`. This is not recommended unless you have specific reasons to do so.
#### Optional container environment variables
Optional letsencrypt-nginx-proxy-companion container environment variables for custom configuration.
* `ACME_CA_URI` - Directory URI for the CA ACME API endpoint (default: ``https://acme-v01.api.letsencrypt.org/directory``). If you set it's value to `https://acme-staging.api.letsencrypt.org/directory` letsencrypt will use test servers that don't have the 5 certs/week/domain limits. You can also create test certificates per container (see [let's encrypt test certificates](#test-certificates))
For example
```bash
$ docker run -d \
-e "ACME_CA_URI=https://acme-staging.api.letsencrypt.org/directory" \
-v /path/to/certs:/etc/nginx/certs:rw \
--volumes-from nginx-proxy \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
jrcs/letsencrypt-nginx-proxy-companion
```
* `DEBUG` - Set it to `true` to enable debugging of the entrypoint script and generation of LetsEncrypt certificates, which could help you pin point any configuration issues.
* `REUSE_ACCOUNT_KEYS` - Set it to `false` to disable the account keys reutilization (see [ACME account keys](#acme-account-keys)).
* `REUSE_PRIVATE_KEYS` - Set it to `true` to make simp_le reuse previously generated private key for each certificate instead of creating a new one on certificate renewal. Recommended if you intend to use HPKP.
* The `com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy` label - set this label on the nginx-proxy container to tell the docker-letsencrypt-nginx-proxy-companion container to use it as the proxy.
* The `com.github.jrcs.letsencrypt_nginx_proxy_companion.docker_gen` label - set this label on the docker-gen container to tell the docker-letsencrypt-nginx-proxy-companion container to use it as the docker-gen when it's split from nginx (separate containers).
* `DOCKER_PROVIDER` - Set this to change behavior on container ID retrieval. Optional. Current supported values:
* No value (empty, not set): no change in behavior.
* `ecs` [Amazon ECS using ECS_CONTAINER_METADATA_FILE environment variable](http://docs.aws.amazon.com/AmazonECS/latest/developerguide/container-metadata.html)
* `DHPARAM_BITS` - Change the size of the Diffie-Hellman key generated by the container from the default value of 2048 bits. For example `-e DHPARAM_BITS=1024` to support some older clients like Java 6 and 7.
#### Examples:
If you want other examples how to use this container, look at:
* [Evert Ramos's Examples](https://github.com/evertramos/docker-compose-letsencrypt-nginx-proxy-companion) - using docker-compose version '3'
* [Karl Fathi's Examples](https://github.com/fatk/docker-letsencrypt-nginx-proxy-companion-examples)
* [More examples from Karl](https://github.com/pixelfordinner/pixelcloud-docker-apps/tree/master/nginx-proxy)
* [George Ilyes' Examples](https://github.com/gilyes/docker-nginx-letsencrypt-sample)
* [Dmitry's simple docker-compose example](https://github.com/dmitrym0/simple-lets-encrypt-docker-compose-sample)
* [Radek's docker-compose jenkins example](https://github.com/dataminelab/docker-jenkins-nginx-letsencrypt)
[![Tests](https://github.com/nginx-proxy/acme-companion/actions/workflows/test.yml/badge.svg)](https://github.com/nginx-proxy/acme-companion/actions/workflows/test.yml)
[![GitHub release](https://img.shields.io/github/release/nginx-proxy/acme-companion.svg)](https://github.com/nginx-proxy/acme-companion/releases)
[![Docker Image Size](https://img.shields.io/docker/image-size/nginxproxy/acme-companion?sort=semver)](https://hub.docker.com/r/nginxproxy/acme-companion "Click to view the image on Docker Hub")
[![Docker stars](https://img.shields.io/docker/stars/nginxproxy/acme-companion.svg)](https://hub.docker.com/r/nginxproxy/acme-companion "Click to view the image on Docker Hub")
[![Docker pulls](https://img.shields.io/docker/pulls/nginxproxy/acme-companion.svg)](https://hub.docker.com/r/nginxproxy/acme-companion "Click to view the image on Docker Hub")
**acme-companion** is a lightweight companion container for [**nginx-proxy**](https://github.com/nginx-proxy/nginx-proxy).
It handles the automated creation, renewal and use of SSL certificates for proxied Docker containers through the ACME protocol.
**Required read if you use the `latest` version** : the `v2.0.0` release of this project mark the switch of the ACME client used by the Docker image from [**simp.le**](https://github.com/zenhack/simp_le) to [**acme.sh**](https://github.com/acmesh-official/acme.sh). This switch result in some backward incompatible changes, so please read [this issue](https://github.com/nginx-proxy/acme-companion/issues/510) and the updated docs for more details before updating your image. The single most important change is that the container now requires a volume mounted to `/etc/acme.sh` in order to persist ACME account keys and SSL certificates. The last tagged version that uses **simp_le** is `v1.13.1`.
### Features:
* Automated creation/renewal of Let's Encrypt (or other ACME CAs) certificates using [**acme.sh**](https://github.com/acmesh-official/acme.sh).
* Let's Encrypt / ACME domain validation through `http-01` challenge only.
* Automated update and reload of nginx config on certificate creation/renewal.
* Support creation of [Multi-Domain (SAN) Certificates](https://github.com/nginx-proxy/acme-companion/blob/main/docs/Let's-Encrypt-and-ACME.md#multi-domains-certificates).
* Creation of a strong [RFC7919 Diffie-Hellman Group](https://datatracker.ietf.org/doc/html/rfc7919#appendix-A) at startup.
* Work with all versions of docker.
### Requirements:
* Your host **must** be publicly reachable on **both** port [`80`](https://letsencrypt.org/docs/allow-port-80/) and [`443`](https://github.com/nginx-proxy/acme-companion/discussions/873#discussioncomment-1410225).
* Check your firewall rules and [**do not attempt to block port `80`**](https://letsencrypt.org/docs/allow-port-80/) as that will prevent `http-01` challenges from completing.
* For the same reason, you can't use nginx-proxy's [`HTTPS_METHOD=nohttp`](https://github.com/nginx-proxy/nginx-proxy#how-ssl-support-works).
* The (sub)domains you want to issue certificates for must correctly resolve to the host.
* Your DNS provider must [answer correctly to CAA record requests](https://letsencrypt.org/docs/caa/).
* If your (sub)domains have AAAA records set, the host must be publicly reachable over IPv6 on port `80` and `443`.
![schema](https://github.com/nginx-proxy/acme-companion/blob/main/schema.png)
## Basic usage (with the nginx-proxy container)
Three writable volumes must be declared on the **nginx-proxy** container so that they can be shared with the **acme-companion** container:
* `/etc/nginx/certs` to store certificates and private keys (readonly for the **nginx-proxy** container).
* `/etc/nginx/vhost.d` to change the configuration of vhosts (required so the CA may access `http-01` challenge files).
* `/usr/share/nginx/html` to write `http-01` challenge files.
Additionally, a fourth volume must be declared on the **acme-companion** container to store `acme.sh` configuration and state: `/etc/acme.sh`.
Please also read the doc about [data persistence](./docs/Persistent-data.md).
Example of use:
### Step 1 - nginx-proxy
Start **nginx-proxy** with the three additional volumes declared:
```shell
$ docker run --detach \
--name nginx-proxy \
--publish 80:80 \
--publish 443:443 \
--volume certs:/etc/nginx/certs \
--volume vhost:/etc/nginx/vhost.d \
--volume html:/usr/share/nginx/html \
--volume /var/run/docker.sock:/tmp/docker.sock:ro \
nginxproxy/nginx-proxy
```
Binding the host docker socket (`/var/run/docker.sock`) inside the container to `/tmp/docker.sock` is a requirement of **nginx-proxy**.
### Step 2 - acme-companion
Start the **acme-companion** container, getting the volumes from **nginx-proxy** with `--volumes-from`:
```shell
$ docker run --detach \
--name nginx-proxy-acme \
--volumes-from nginx-proxy \
--volume /var/run/docker.sock:/var/run/docker.sock:ro \
--volume acme:/etc/acme.sh \
--env "DEFAULT_EMAIL=mail@yourdomain.tld" \
nginxproxy/acme-companion
```
The host docker socket has to be bound inside this container too, this time to `/var/run/docker.sock`.
Albeit **optional**, it is **recommended** to provide a valid default email address through the `DEFAULT_EMAIL` environment variable, so that Let's Encrypt can warn you about expiring certificates and allow you to recover your account.
### Step 3 - proxied container(s)
Once both **nginx-proxy** and **acme-companion** containers are up and running, start any container you want proxied with environment variables `VIRTUAL_HOST` and `LETSENCRYPT_HOST` both set to the domain(s) your proxied container is going to use.
[`VIRTUAL_HOST`](https://github.com/nginx-proxy/nginx-proxy#usage) control proxying by **nginx-proxy** and `LETSENCRYPT_HOST` control certificate creation and SSL enabling by **acme-companion**.
Certificates will only be issued for containers that have both `VIRTUAL_HOST` and `LETSENCRYPT_HOST` variables set to domain(s) that correctly resolve to the host, provided the host is publicly reachable.
```shell
$ docker run --detach \
--name your-proxied-app \
--env "VIRTUAL_HOST=subdomain.yourdomain.tld" \
--env "LETSENCRYPT_HOST=subdomain.yourdomain.tld" \
nginx
```
The containers being proxied must expose the port to be proxied, either by using the `EXPOSE` directive in their Dockerfile or by using the `--expose` flag to `docker run` or `docker create`.
If the proxied container listen on and expose another port than the default `80`, you can force **nginx-proxy** to use this port with the [`VIRTUAL_PORT`](https://github.com/nginx-proxy/nginx-proxy#multiple-ports) environment variable.
Example using [Grafana](https://hub.docker.com/r/grafana/grafana/) (expose and listen on port 3000):
```shell
$ docker run --detach \
--name grafana \
--env "VIRTUAL_HOST=othersubdomain.yourdomain.tld" \
--env "VIRTUAL_PORT=3000" \
--env "LETSENCRYPT_HOST=othersubdomain.yourdomain.tld" \
--env "LETSENCRYPT_EMAIL=mail@yourdomain.tld" \
grafana/grafana
```
Repeat [Step 3](#step-3---proxied-containers) for any other container you want to proxy.
## Additional documentation
Please check the [docs section](https://github.com/nginx-proxy/acme-companion/tree/main/docs).

View File

@ -5,13 +5,17 @@ function print_cert_info {
local san_str
# Get the wanted informations with OpenSSL.
issuer="$(openssl x509 -noout -issuer -in "$1" | sed -n 's/.*CN=\(.*\)/\1/p')"
issuer="$(openssl x509 -noout -issuer -in "$1" | sed -n 's/.*CN = \(.*\)/\1/p')"
enddate="$(openssl x509 -noout -enddate -in "$1" | sed -n 's/notAfter=\(.*$\)/\1/p')"
subject="$(openssl x509 -noout -subject -in "$1" | sed -n 's/.*CN=\([a-z0-9.-]*\)/- \1/p')"
subject="$(openssl x509 -noout -subject -in "$1" | sed -n 's/.*CN = \([a-z0-9.-]*\)/- \1/p')"
san_str="$(openssl x509 -text -in "$1" | grep 'DNS:')"
echo "Certificate was issued by $issuer"
echo "Certificate is valid until $enddate"
if [[ "$2" == "expired" ]]; then
echo "Certificate was valid until $enddate"
else
echo "Certificate is valid until $enddate"
fi
echo "Subject Name:"
echo "$subject"
@ -29,11 +33,22 @@ function print_cert_info {
echo '##### Certificate status #####'
for cert in /etc/nginx/certs/*/fullchain.pem; do
[[ -e "$cert" ]] || continue
# Verify the certificate with OpenSSL.
openssl verify -CAfile "${cert%fullchain.pem}chain.pem" "$cert"
# Print certificate info.
print_cert_info "$cert"
if [[ -e "${cert%fullchain.pem}chain.pem" ]]; then
# Verify the certificate with OpenSSL.
if verify=$(openssl verify -CAfile "${cert%fullchain.pem}chain.pem" "$cert" 2>&1); then
echo "$verify"
# Print certificate info.
print_cert_info "$cert"
else
echo "${cert}: EXPIRED"
# Print certificate info.
print_cert_info "$cert" "expired"
fi
else
echo "${cert}: no corresponding chain.pem file, unable to verify certificate"
# Print certificate info.
print_cert_info "$cert"
fi
# Find the .crt files in /etc/nginx/certs which are
# symlinks pointing to the current certificate.
@ -41,7 +56,8 @@ for cert in /etc/nginx/certs/*/fullchain.pem; do
for symlink in /etc/nginx/certs/*.crt; do
[[ -e "$symlink" ]] || continue
if [[ "$(readlink -f "$symlink")" == "$cert" ]]; then
domain="$(echo "${symlink%.crt}" | sed 's#/etc/nginx/certs/##g')"
domain="${symlink%.crt}"
domain="${domain//\/etc\/nginx\/certs\//}"
symlinked_domains+=("$domain")
fi
done

46
app/cleanup_test_artifacts Executable file
View File

@ -0,0 +1,46 @@
#!/bin/bash
# This script should not be run outside of a test container
[[ "$TEST_MODE" == 'true' ]] || exit 1
while [[ $# -gt 0 ]]; do
flag="$1"
case $flag in
--default-cert)
for filename in default.crt default.key; do
filepath="/etc/nginx/certs/$filename"
[[ -f "$filepath" ]] && rm -rf "$filepath"
done
shift
;;
--location-config)
for domain in 'le1.wtf' '*.example.com' 'test.*' 'le3.pizza' 'subdomain.example.com' 'test.domain.tld'; do
[[ -f "/etc/nginx/vhost.d/$domain" ]] && rm -f "/etc/nginx/vhost.d/$domain"
done
shift
;;
*) #Unknown option
shift
;;
esac
done
for domain in le1.wtf le2.wtf le3.wtf le4.wtf lim.it; do
folder="/etc/nginx/certs/$domain"
[[ -d "$folder" ]] && rm -rf "$folder"
folder="/etc/acme.sh/default/$domain"
[[ -d "$folder" ]] && rm -rf "$folder"
folder="/etc/acme.sh/default/${domain}_ecc"
[[ -d "$folder" ]] && rm -rf "$folder"
location_file="/etc/nginx/vhost.d/$domain"
[[ -f "$location_file" ]] && rm -rf "$location_file" 2> /dev/null
for extension in key crt chain.pem dhparam.pem; do
symlink="/etc/nginx/certs/${domain}.${extension}"
[[ -L "$symlink" ]] && rm -rf "$symlink"
done
done
exit 0

View File

@ -0,0 +1,8 @@
-----BEGIN DH PARAMETERS-----
MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
-----END DH PARAMETERS-----

11
app/dhparam/ffdhe3072.pem Normal file
View File

@ -0,0 +1,11 @@
-----BEGIN DH PARAMETERS-----
MIIBiAKCAYEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
ssbzSibBsu/6iGtCOGEfz9zeNVs7ZRkDW7w09N75nAI4YbRvydbmyQd62R0mkff3
7lmMsPrBhtkcrv4TCYUTknC0EwyTvEN5RPT9RFLi103TZPLiHnH1S/9croKrnJ32
nuhtK8UiNjoNq8Uhl5sN6todv5pC1cRITgq80Gv6U93vPBsg7j/VnXwl5B0rZsYu
N///////////AgEC
-----END DH PARAMETERS-----

13
app/dhparam/ffdhe4096.pem Normal file
View File

@ -0,0 +1,13 @@
-----BEGIN DH PARAMETERS-----
MIICCAKCAgEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
ssbzSibBsu/6iGtCOGEfz9zeNVs7ZRkDW7w09N75nAI4YbRvydbmyQd62R0mkff3
7lmMsPrBhtkcrv4TCYUTknC0EwyTvEN5RPT9RFLi103TZPLiHnH1S/9croKrnJ32
nuhtK8UiNjoNq8Uhl5sN6todv5pC1cRITgq80Gv6U93vPBsg7j/VnXwl5B0rZp4e
8W5vUsMWTfT7eTDp5OWIV7asfV9C1p9tGHdjzx1VA0AEh/VbpX4xzHpxNciG77Qx
iu1qHgEtnmgyqQdgCpGBMMRtx3j5ca0AOAkpmaMzy4t6Gh25PXFAADwqTs6p+Y0K
zAqCkc3OyX3Pjsm1Wn+IpGtNtahR9EGC4caKAH5eZV9q//////////8CAQI=
-----END DH PARAMETERS-----

View File

@ -1,12 +1,16 @@
#!/bin/bash
# shellcheck disable=SC2155
set -u
function check_deprecated_env_var {
if [[ -n "${ACME_TOS_HASH:-}" ]]; then
echo "Info: the ACME_TOS_HASH environment variable is no longer used by simp_le and has been deprecated."
echo "simp_le now implicitly agree to the ACME CA ToS."
# shellcheck source=functions.sh
source /app/functions.sh
load_acme_ca_uri
function print_version {
if [[ -n "${COMPANION_VERSION:-}" ]]; then
echo "Info: running acme-companion version ${COMPANION_VERSION}"
fi
}
@ -23,75 +27,84 @@ function check_docker_socket {
function check_writable_directory {
local dir="$1"
docker_api "/containers/${SELF_CID:-$(get_self_cid)}/json" | jq ".Mounts[].Destination" | grep -q "^\"$dir\"$"
if [[ $? -ne 0 ]]; then
echo "Warning: '$dir' does not appear to be a mounted volume."
if [[ $(get_self_cid) ]]; then
if ! docker_api "/containers/$(get_self_cid)/json" | jq ".Mounts[].Destination" | grep -q "^\"$dir\"$"; then
echo "Warning: '$dir' does not appear to be a mounted volume."
fi
else
echo "Warning: can't check if '$dir' is a mounted volume without self container ID."
fi
if [[ ! -d "$dir" ]]; then
echo "Error: can't access to '$dir' directory !" >&2
echo "Check that '$dir' directory is declared as a writable volume." >&2
exit 1
fi
touch $dir/.check_writable 2>/dev/null
if [[ $? -ne 0 ]]; then
if ! touch "$dir/.check_writable" 2>/dev/null ; then
echo "Error: can't write to the '$dir' directory !" >&2
echo "Check that '$dir' directory is export as a writable volume." >&2
exit 1
fi
rm -f $dir/.check_writable
rm -f "$dir/.check_writable"
}
function check_dh_group {
# Credits to Steve Kamerman for the background Diffie-Hellman creation logic.
# https://github.com/jwilder/nginx-proxy/pull/589
local DHPARAM_BITS="${DHPARAM_BITS:-2048}"
re='^[0-9]*$'
if ! [[ "$DHPARAM_BITS" =~ $re ]] ; then
echo "Error: invalid Diffie-Hellman size of $DHPARAM_BITS !" >&2
exit 1
# DH params will be supplied for acme-companion here:
local DHPARAM_FILE='/etc/nginx/certs/dhparam.pem'
# Should be 2048, 3072, or 4096 (default):
local DHPARAM_BITS="${DHPARAM_BITS:=4096}"
# Skip generation if DHPARAM_SKIP is set to true
if parse_true "${DHPARAM_SKIP:=false}"; then
echo "Info: Skipping Diffie-Hellman group setup."
return 0
fi
# If a dhparam file is not available, use the pre-generated one and generate a new one in the background.
local PREGEN_DHPARAM_FILE="/app/dhparam.pem.default"
local DHPARAM_FILE="/etc/nginx/certs/dhparam.pem"
local GEN_LOCKFILE="/tmp/le_companion_dhparam_generating.lock"
# Let's check DHPARAM_BITS is set to a supported value
if [[ ! "$DHPARAM_BITS" =~ ^(2048|3072|4096)$ ]]; then
echo "Error: Unsupported DHPARAM_BITS size: ${DHPARAM_BITS}. Supported values are 2048, 3072, or 4096 (default)." >&2
exit 1
fi
# The hash of the pregenerated dhparam file is used to check if the pregen dhparam is already in use
local PREGEN_HASH=$(sha256sum "$PREGEN_DHPARAM_FILE" | cut -d ' ' -f1)
if [[ -f "$DHPARAM_FILE" ]]; then
local CURRENT_HASH=$(sha256sum "$DHPARAM_FILE" | cut -d ' ' -f1)
if [[ "$PREGEN_HASH" != "$CURRENT_HASH" ]]; then
# There is already a dhparam, and it's not the default
# Use an existing pre-generated DH group from RFC7919 (https://datatracker.ietf.org/doc/html/rfc7919#appendix-A):
local RFC7919_DHPARAM_FILE="/app/dhparam/ffdhe${DHPARAM_BITS}.pem"
local EXPECTED_DHPARAM_HASH; EXPECTED_DHPARAM_HASH=$(sha256sum "$RFC7919_DHPARAM_FILE" | cut -d ' ' -f1)
# DH params may be provided by the user (rarely necessary)
if [[ -f "$DHPARAM_FILE" ]]; then
local USER_PROVIDED_DH
# Check if the DH params file is user provided or comes from acme-companion
local DHPARAM_HASH; DHPARAM_HASH=$(sha256sum "$DHPARAM_FILE" | cut -d ' ' -f1)
for f in /app/dhparam/ffdhe*.pem; do
local FFDHE_HASH; FFDHE_HASH=$(sha256sum "$f" | cut -d ' ' -f1)
if [[ "$DHPARAM_HASH" == "$FFDHE_HASH" ]]; then
# This is an acme-companion created DH params file
USER_PROVIDED_DH='false'
# Check if /etc/nginx/certs/dhparam.pem matches the expected pre-generated DH group
if [[ "$DHPARAM_HASH" == "$EXPECTED_DHPARAM_HASH" ]]; then
set_ownership_and_permissions "$DHPARAM_FILE"
echo "Info: ${DHPARAM_BITS} bits RFC7919 Diffie-Hellman group found, generation skipped."
return 0
fi
fi
done
if parse_true "${USER_PROVIDED_DH:=true}"; then
# This is a user provided DH params file
set_ownership_and_permissions "$DHPARAM_FILE"
echo "Info: Custom Diffie-Hellman group found, generation skipped."
return 0
fi
if [[ -f "$GEN_LOCKFILE" ]]; then
# Generation is already in progress
echo "Info: A custom dhparam.pem file was provided. Best practice is to use standardized RFC7919 Diffie-Hellman groups instead."
return 0
fi
fi
fi
echo "Info: Creating Diffie-Hellman group in the background."
echo "A pre-generated Diffie-Hellman group will be used for now while the new one
is being created."
# Put the default dhparam file in place so we can start immediately
cp "$PREGEN_DHPARAM_FILE" "$DHPARAM_FILE"
# The RFC7919 DH params file either need to be created or replaced
echo "Info: Setting up ${DHPARAM_BITS} bits RFC7919 Diffie-Hellman group..."
cp "$RFC7919_DHPARAM_FILE" "${DHPARAM_FILE}.tmp"
mv "${DHPARAM_FILE}.tmp" "$DHPARAM_FILE"
set_ownership_and_permissions "$DHPARAM_FILE"
touch "$GEN_LOCKFILE"
# Generate a new dhparam in the background in a low priority and reload nginx when finished (grep removes the progress indicator).
(
(
nice -n +5 openssl dhparam -out "$DHPARAM_FILE" "$DHPARAM_BITS" 2>&1 \
&& echo "Info: Diffie-Hellman group creation complete, reloading nginx." \
&& set_ownership_and_permissions "$DHPARAM_FILE" \
&& reload_nginx
) | grep -vE '^[\.+]+'
rm "$GEN_LOCKFILE"
) &disown
}
function check_default_cert_key {
@ -103,7 +116,7 @@ function check_default_cert_key {
# than 3 months / 7776000 seconds (60 x 60 x 24 x 30 x 3).
check_cert_min_validity /etc/nginx/certs/default.crt 7776000
cert_validity=$?
[[ $DEBUG == true ]] && echo "Debug: a default certificate with $default_cert_cn is present."
[[ "$DEBUG" == 1 ]] && echo "Debug: a default certificate with $default_cert_cn is present."
fi
# Create a default cert and private key if:
@ -118,54 +131,53 @@ function check_default_cert_key {
-keyout /etc/nginx/certs/default.key.new \
-out /etc/nginx/certs/default.crt.new \
&& mv /etc/nginx/certs/default.key.new /etc/nginx/certs/default.key \
&& mv /etc/nginx/certs/default.crt.new /etc/nginx/certs/default.crt
&& mv /etc/nginx/certs/default.crt.new /etc/nginx/certs/default.crt \
&& reload_nginx
echo "Info: a default key and certificate have been created at /etc/nginx/certs/default.key and /etc/nginx/certs/default.crt."
elif [[ $DEBUG == true && "${default_cert_cn:-}" =~ $cn ]]; then
elif [[ "$DEBUG" == 1 && "${default_cert_cn:-}" =~ $cn ]]; then
echo "Debug: the self generated default certificate is still valid for more than three months. Skipping default certificate creation."
elif [[ $DEBUG == true ]]; then
elif [[ "$DEBUG" == 1 ]]; then
echo "Debug: the default certificate is user provided. Skipping default certificate creation."
fi
set_ownership_and_permissions "/etc/nginx/certs/default.key"
set_ownership_and_permissions "/etc/nginx/certs/default.crt"
}
source /app/functions.sh
function check_default_account {
# The default account is now for empty account email
if [[ -f /etc/acme.sh/default/account.conf ]]; then
if grep -q ACCOUNT_EMAIL /etc/acme.sh/default/account.conf; then
sed -i '/ACCOUNT_EMAIL/d' /etc/acme.sh/default/account.conf
fi
fi
}
if [[ "$*" == "/bin/bash /app/start.sh" ]]; then
acmev2_re='https://acme-.*v02\.api\.letsencrypt\.org/directory'
if [[ "${ACME_CA_URI:-}" =~ $acmev2_re ]]; then
echo "Error: ACME v2 API is not yet supported by simp_le."
echo "See https://github.com/zenhack/simp_le/issues/101"
exit 1
fi
print_version
check_docker_socket
if [[ -z "$(get_self_cid)" ]]; then
echo "Error: can't get my container ID !" >&2
exit 1
else
export SELF_CID="$(get_self_cid)"
fi
if [[ -z "$(get_nginx_proxy_container)" ]]; then
echo "Error: can't get nginx-proxy container ID !" >&2
echo "Check that you are doing one of the following :" >&2
echo -e "\t- Use the --volumes-from option to mount volumes from the nginx-proxy container." >&2
echo -e "\t- Set the NGINX_PROXY_CONTAINER env var on the letsencrypt-companion container to the name of the nginx-proxy container." >&2
echo -e "\t- Label the nginx-proxy container to use with 'com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy'." >&2
echo -e "\t- Label the nginx-proxy container to use with 'com.github.nginx-proxy.nginx'." >&2
exit 1
elif [[ -z "$(get_docker_gen_container)" ]] && ! is_docker_gen_container "$(get_nginx_proxy_container)"; then
echo "Error: can't get docker-gen container id !" >&2
echo "If you are running a three containers setup, check that you are doing one of the following :" >&2
echo -e "\t- Set the NGINX_DOCKER_GEN_CONTAINER env var on the letsencrypt-companion container to the name of the docker-gen container." >&2
echo -e "\t- Label the docker-gen container to use with 'com.github.jrcs.letsencrypt_nginx_proxy_companion.docker_gen.'" >&2
echo -e "\t- Label the docker-gen container to use with 'com.github.nginx-proxy.docker-gen'." >&2
exit 1
fi
check_writable_directory '/etc/nginx/certs'
check_writable_directory '/etc/nginx/vhost.d'
check_writable_directory '/etc/acme.sh'
check_writable_directory '/usr/share/nginx/html'
check_deprecated_env_var
[[ -f /app/letsencrypt_user_data ]] && check_writable_directory '/etc/nginx/conf.d'
check_default_cert_key
check_dh_group
reload_nginx
check_default_account
fi
exec "$@"

View File

@ -1,5 +1,6 @@
#!/bin/bash
# shellcheck source=letsencrypt_service
source /app/letsencrypt_service --source-only
update_certs --force-renew

View File

@ -1,5 +1,37 @@
#!/bin/bash
# shellcheck disable=SC2155
# Convert argument to lowercase (bash 4 only)
function lc {
echo "${@,,}"
}
DEBUG="$(lc "${DEBUG:-}")"
if [[ "$DEBUG" == true ]]; then
DEBUG=1 && export DEBUG
fi
function parse_true() {
case "$1" in
true | True | TRUE | 1)
return 0
;;
*)
return 1
;;
esac
}
function in_array() {
local needle="$1" item
local -n arrref="$2"
for item in "${arrref[@]}"; do
[[ "$item" == "$needle" ]] && return 0
done
return 1
}
[[ -z "${VHOST_DIR:-}" ]] && \
declare -r VHOST_DIR=/etc/nginx/vhost.d
@ -9,7 +41,7 @@
declare -r END_HEADER='## End of configuration add by letsencrypt container'
function check_nginx_proxy_container_run {
local _nginx_proxy_container=$(get_nginx_proxy_container)
local _nginx_proxy_container; _nginx_proxy_container=$(get_nginx_proxy_container)
if [[ -n "$_nginx_proxy_container" ]]; then
if [[ $(docker_api "/containers/${_nginx_proxy_container}/json" | jq -r '.State.Status') = "running" ]];then
return 0
@ -23,27 +55,130 @@ function check_nginx_proxy_container_run {
fi
}
function ascending_wildcard_locations {
# Given foo.bar.baz.example.com as argument, will output:
# - *.bar.baz.example.com
# - *.baz.example.com
# - *.example.com
local domain="${1:?}"
local first_label
tld_regex="^[[:alpha:]]+$"
regex="^[^.]+\..+$"
while [[ "$domain" =~ $regex ]]; do
first_label="${domain%%.*}"
domain="${domain/#"${first_label}."/}"
if [[ "$domain" == "*" || "$domain" =~ $tld_regex ]]; then
return
else
echo "*.${domain}"
fi
done
}
function descending_wildcard_locations {
# Given foo.bar.baz.example.com as argument, will output:
# - foo.bar.baz.example.*
# - foo.bar.baz.*
# - foo.bar.*
# - foo.*
local domain="${1:?}"
local last_label
regex="^.+\.[^.]+$"
while [[ "$domain" =~ $regex ]]; do
last_label="${domain##*.}"
domain="${domain/%".${last_label}"/}"
if [[ "$domain" == "*" ]]; then
return
else
echo "${domain}.*"
fi
done
}
function enumerate_wildcard_locations {
# Goes through ascending then descending wildcard locations for a given FQDN
local domain="${1:?}"
ascending_wildcard_locations "$domain"
descending_wildcard_locations "$domain"
}
function add_location_configuration {
local domain="${1:-}"
[[ -z "$domain" || ! -f "${VHOST_DIR}/${domain}" ]] && domain=default
[[ -f "${VHOST_DIR}/${domain}" && \
-n $(sed -n "/$START_HEADER/,/$END_HEADER/p" "${VHOST_DIR}/${domain}") ]] && return 0
echo "$START_HEADER" > "${VHOST_DIR}/${domain}".new
cat /app/nginx_location.conf >> "${VHOST_DIR}/${domain}".new
echo "$END_HEADER" >> "${VHOST_DIR}/${domain}".new
[[ -f "${VHOST_DIR}/${domain}" ]] && cat "${VHOST_DIR}/${domain}" >> "${VHOST_DIR}/${domain}".new
mv -f "${VHOST_DIR}/${domain}".new "${VHOST_DIR}/${domain}"
return 1
local wildcard_domain
# If no domain was passed use default instead
[[ -z "$domain" ]] && domain='default'
# If the domain does not have an exact matching location file, test the possible
# wildcard locations files. Use default is no location file is present at all.
if [[ ! -f "${VHOST_DIR}/${domain}" ]]; then
while read -r wildcard_domain; do
if [[ -f "${VHOST_DIR}/${wildcard_domain}" ]]; then
domain="$wildcard_domain"
break
fi
domain='default'
done <<< "$(enumerate_wildcard_locations "$domain")"
fi
if [[ -f "${VHOST_DIR}/${domain}" && -n $(sed -n "/$START_HEADER/,/$END_HEADER/p" "${VHOST_DIR}/${domain}") ]]; then
# If the config file exist and already have the location configuration, end with exit code 0
return 0
else
# Else write the location configuration to a temp file ...
echo "$START_HEADER" > "${VHOST_DIR}/${domain}".new
cat /app/nginx_location.conf >> "${VHOST_DIR}/${domain}".new
echo "$END_HEADER" >> "${VHOST_DIR}/${domain}".new
# ... append the existing file content to the temp one ...
[[ -f "${VHOST_DIR}/${domain}" ]] && cat "${VHOST_DIR}/${domain}" >> "${VHOST_DIR}/${domain}".new
# ... and copy the temp file to the old one (if the destination file is bind mounted, you can't change
# its inode from within the container, so mv won't work and cp has to be used), then remove the temp file.
cp -f "${VHOST_DIR}/${domain}".new "${VHOST_DIR}/${domain}" && rm -f "${VHOST_DIR}/${domain}".new
return 1
fi
}
function add_standalone_configuration {
local domain="${1:?}"
if grep -q "server_name ${domain};" /etc/nginx/conf.d/*.conf; then
# If the domain is already present in nginx's conf, use the location configuration.
add_location_configuration "$domain"
else
# Else use the standalone configuration.
cat > "/etc/nginx/conf.d/standalone-cert-$domain.conf" << EOF
server {
server_name $domain;
listen 80;
access_log /var/log/nginx/access.log vhost;
location ^~ /.well-known/acme-challenge/ {
auth_basic off;
auth_request off;
allow all;
root /usr/share/nginx/html;
try_files \$uri =404;
break;
}
}
EOF
fi
}
function remove_all_standalone_configurations {
local old_shopt_options; old_shopt_options=$(shopt -p) # Backup shopt options
shopt -s nullglob
for file in "/etc/nginx/conf.d/standalone-cert-"*".conf"; do
rm -f "$file"
done
eval "$old_shopt_options" # Restore shopt options
}
function remove_all_location_configurations {
local old_shopt_options=$(shopt -p) # Backup shopt options
shopt -s nullglob
for file in "${VHOST_DIR}"/*; do
[[ -n $(sed -n "/$START_HEADER/,/$END_HEADER/p" "$file") ]] && \
sed -i "/$START_HEADER/,/$END_HEADER/d" "$file"
[[ -e "$file" ]] || continue
if [[ -n $(sed -n "/$START_HEADER/,/$END_HEADER/p" "$file") ]]; then
sed "/$START_HEADER/,/$END_HEADER/d" "$file" > "$file".new
cp -f "$file".new "$file" && rm -f "$file".new
fi
done
eval "$old_shopt_options" # Restore shopt options
}
function check_cert_min_validity {
@ -60,22 +195,26 @@ function check_cert_min_validity {
}
function get_self_cid {
DOCKER_PROVIDER=${DOCKER_PROVIDER:-docker}
local self_cid=""
case "${DOCKER_PROVIDER}" in
ecs|ECS)
# AWS ECS. Enabled in /etc/ecs/ecs.config (http://docs.aws.amazon.com/AmazonECS/latest/developerguide/container-metadata.html)
if [[ -n "${ECS_CONTAINER_METADATA_FILE:-}" ]]; then
grep ContainerID "${ECS_CONTAINER_METADATA_FILE}" | sed 's/.*: "\(.*\)",/\1/g'
else
echo "${DOCKER_PROVIDER} specified as 'ecs' but not available. See: http://docs.aws.amazon.com/AmazonECS/latest/developerguide/container-metadata.html" >&2
exit 1
fi
;;
*)
sed -nE 's/^.+docker[\/-]([a-f0-9]{64}).*/\1/p' /proc/self/cgroup | head -n 1
;;
esac
# Try the /proc files methods first then resort to the Docker API.
if [[ -f /proc/1/cpuset ]]; then
self_cid="$(grep -Eo '[[:alnum:]]{64}' /proc/1/cpuset)"
fi
if [[ ( ${#self_cid} != 64 ) && ( -f /proc/self/cgroup ) ]]; then
self_cid="$(grep -Eo -m 1 '[[:alnum:]]{64}' /proc/self/cgroup)"
fi
if [[ ( ${#self_cid} != 64 ) ]]; then
self_cid="$(docker_api "/containers/$(hostname)/json" | jq -r '.Id')"
fi
# If it's not 64 characters long, then it's probably not a container ID.
if [[ ${#self_cid} == 64 ]]; then
echo "$self_cid"
else
echo "$(date "+%Y/%m/%d %T"), Error: can't get my container ID !" >&2
return 1
fi
}
## Docker API
@ -92,28 +231,33 @@ function docker_api {
return 1
fi
if [[ $DOCKER_HOST == unix://* ]]; then
curl_opts+=(--unix-socket ${DOCKER_HOST#unix://})
curl_opts+=(--unix-socket "${DOCKER_HOST#unix://}")
scheme='http://localhost'
else
scheme="http://${DOCKER_HOST#*://}"
fi
[[ $method = "POST" ]] && curl_opts+=(-H 'Content-Type: application/json')
curl "${curl_opts[@]}" -X${method} ${scheme}$1
curl "${curl_opts[@]}" -X "${method}" "${scheme}$1"
}
function docker_exec {
local id="${1?missing id}"
local cmd="${2?missing command}"
local data=$(printf '{ "AttachStdin": false, "AttachStdout": true, "AttachStderr": true, "Tty":false,"Cmd": %s }' "$cmd")
local data; data=$(printf '{ "AttachStdin": false, "AttachStdout": true, "AttachStderr": true, "Tty":false,"Cmd": %s }' "$cmd")
exec_id=$(docker_api "/containers/$id/exec" "POST" "$data" | jq -r .Id)
if [[ -n "$exec_id" && "$exec_id" != "null" ]]; then
docker_api /exec/$exec_id/start "POST" '{"Detach": false, "Tty":false}'
docker_api "/exec/${exec_id}/start" "POST" '{"Detach": false, "Tty":false}'
else
echo "$(date "+%Y/%m/%d %T"), Error: can't exec command ${cmd} in container ${id}. Check if the container is running." >&2
return 1
fi
}
function docker_restart {
local id="${1?missing id}"
docker_api "/containers/$id/restart" "POST"
}
function docker_kill {
local id="${1?missing id}"
local signal="${2?missing signal}"
@ -121,12 +265,12 @@ function docker_kill {
}
function labeled_cid {
docker_api "/containers/json" | jq -r '.[] | select(.Labels["'$1'"])|.Id'
docker_api "/containers/json" | jq -r '.[] | select(.Labels["'"$1"'"])|.Id'
}
function is_docker_gen_container {
local id="${1?missing id}"
if [[ $(docker_api "/containers/$id/json" | jq -r '.Config.Env[]' | egrep -c '^DOCKER_GEN_VERSION=') = "1" ]]; then
if [[ $(docker_api "/containers/$id/json" | jq -r '.Config.Env[]' | grep -c -E '^DOCKER_GEN_VERSION=') = "1" ]]; then
return 0
else
return 1
@ -135,7 +279,9 @@ function is_docker_gen_container {
function get_docker_gen_container {
# First try to get the docker-gen container ID from the container label.
local docker_gen_cid="$(labeled_cid com.github.jrcs.letsencrypt_nginx_proxy_companion.docker_gen)"
local legacy_docker_gen_cid; legacy_docker_gen_cid="$(labeled_cid com.github.jrcs.letsencrypt_nginx_proxy_companion.docker_gen)"
local new_docker_gen_cid; new_docker_gen_cid="$(labeled_cid com.github.nginx-proxy.docker-gen)"
local docker_gen_cid; docker_gen_cid="${new_docker_gen_cid:-$legacy_docker_gen_cid}"
# If the labeled_cid function dit not return anything and the env var is set, use it.
if [[ -z "$docker_gen_cid" ]] && [[ -n "${NGINX_DOCKER_GEN_CONTAINER:-}" ]]; then
@ -149,7 +295,9 @@ function get_docker_gen_container {
function get_nginx_proxy_container {
local volumes_from
# First try to get the nginx container ID from the container label.
local nginx_cid="$(labeled_cid com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy)"
local legacy_nginx_cid; legacy_nginx_cid="$(labeled_cid com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy)"
local new_nginx_cid; new_nginx_cid="$(labeled_cid com.github.nginx-proxy.nginx)"
local nginx_cid; nginx_cid="${new_nginx_cid:-$legacy_nginx_cid}"
# If the labeled_cid function dit not return anything ...
if [[ -z "${nginx_cid}" ]]; then
@ -157,11 +305,11 @@ function get_nginx_proxy_container {
if [[ -n "${NGINX_PROXY_CONTAINER:-}" ]]; then
nginx_cid="$NGINX_PROXY_CONTAINER"
# ... else try to get the container ID with the volumes_from method.
else
volumes_from=$(docker_api "/containers/${SELF_CID:-$(get_self_cid)}/json" | jq -r '.HostConfig.VolumesFrom[]' 2>/dev/null)
elif [[ $(get_self_cid) ]]; then
volumes_from=$(docker_api "/containers/$(get_self_cid)/json" | jq -r '.HostConfig.VolumesFrom[]' 2>/dev/null)
for cid in $volumes_from; do
cid="${cid%:*}" # Remove leading :ro or :rw set by remote docker-compose (thx anoopr)
if [[ $(docker_api "/containers/$cid/json" | jq -r '.Config.Env[]' | egrep -c '^NGINX_VERSION=') = "1" ]];then
if [[ $(docker_api "/containers/$cid/json" | jq -r '.Config.Env[]' | grep -c -E '^NGINX_VERSION=') = "1" ]];then
nginx_cid="$cid"
break
fi
@ -175,8 +323,8 @@ function get_nginx_proxy_container {
## Nginx
function reload_nginx {
local _docker_gen_container=$(get_docker_gen_container)
local _nginx_proxy_container=$(get_nginx_proxy_container)
local _docker_gen_container; _docker_gen_container=$(get_docker_gen_container)
local _nginx_proxy_container; _nginx_proxy_container=$(get_nginx_proxy_container)
if [[ -n "${_docker_gen_container:-}" ]]; then
# Using docker-gen and nginx in separate container
@ -201,10 +349,10 @@ function reload_nginx {
function set_ownership_and_permissions {
local path="${1:?}"
# The default ownership is root:root, with 755 permissions for folders and 644 for files.
# The default ownership is root:root, with 755 permissions for folders and 600 for private files.
local user="${FILES_UID:-root}"
local group="${FILES_GID:-$user}"
local f_perms="${FILES_PERMS:-644}"
local f_perms="${FILES_PERMS:-600}"
local d_perms="${FOLDERS_PERMS:-755}"
if [[ ! "$f_perms" =~ ^[0-7]{3,4}$ ]]; then
@ -216,14 +364,16 @@ function set_ownership_and_permissions {
return 1
fi
[[ "$DEBUG" == 1 ]] && echo "Debug: checking $path ownership and permissions."
# Find the user numeric ID if the FILES_UID environment variable isn't numeric.
if [[ "$user" =~ ^[0-9]+$ ]]; then
user_num="$user"
# Check if this user exist inside the container
elif id -u "$user" > /dev/null 2>&1; then
# Convert the user name to numeric ID
local user_num="$(id -u "$user")"
[[ $DEBUG == true ]] && echo "Debug: numeric ID of user $user is $user_num."
local user_num; user_num="$(id -u "$user")"
[[ "$DEBUG" == 1 ]] && echo "Debug: numeric ID of user $user is $user_num."
else
echo "Warning: user $user not found in the container, please use a numeric user ID instead of a user name. Skipping ownership and permissions check."
return 1
@ -235,8 +385,8 @@ function set_ownership_and_permissions {
# Check if this group exist inside the container
elif getent group "$group" > /dev/null 2>&1; then
# Convert the group name to numeric ID
local group_num="$(getent group "$group" | awk -F ':' '{print $3}')"
[[ $DEBUG == true ]] && echo "Debug: numeric ID of group $group is $group_num."
local group_num; group_num="$(getent group "$group" | awk -F ':' '{print $3}')"
[[ "$DEBUG" == 1 ]] && echo "Debug: numeric ID of group $group is $group_num."
else
echo "Warning: group $group not found in the container, please use a numeric group ID instead of a group name. Skipping ownership and permissions check."
return 1
@ -245,39 +395,55 @@ function set_ownership_and_permissions {
# Check and modify ownership if required.
if [[ -e "$path" ]]; then
if [[ "$(stat -c %u:%g "$path" )" != "$user_num:$group_num" ]]; then
[[ $DEBUG == true ]] && echo "Debug: setting $path ownership to $user:$group."
chown "$user_num:$group_num" "$path"
[[ "$DEBUG" == 1 ]] && echo "Debug: setting $path ownership to $user:$group."
if [[ -L "$path" ]]; then
chown -h "$user_num:$group_num" "$path"
else
chown "$user_num:$group_num" "$path"
fi
fi
# If the path is a folder, check and modify permissions if required.
if [[ -d "$path" ]]; then
if [[ "$(stat -c %a "$path")" != "$d_perms" ]]; then
[[ "$DEBUG" == 1 ]] && echo "Debug: setting $path permissions to $d_perms."
chmod "$d_perms" "$path"
fi
# If the path is a file, check and modify permissions if required.
elif [[ -f "$path" ]]; then
# Use different permissions for private files (private keys and ACME account files) ...
if [[ "$path" =~ ^.*(key\.pem|\.key)$ ]]; then
if [[ "$(stat -c %a "$path")" != "$f_perms" ]]; then
[[ "$DEBUG" == 1 ]] && echo "Debug: setting $path permissions to $f_perms."
chmod "$f_perms" "$path"
fi
# ... and for public files (certificates, chains, fullchains, DH parameters).
else
if [[ "$(stat -c %a "$path")" != "644" ]]; then
[[ "$DEBUG" == 1 ]] && echo "Debug: setting $path permissions to 644."
chmod "644" "$path"
fi
fi
fi
else
echo "Warning: $path does not exist. Skipping ownership and permissions check."
return 1
fi
}
# If the path is a folder, check and modify permissions if required.
if [[ -d "$path" ]]; then
if [[ "$(stat -c %a "$path")" != "$d_perms" ]]; then
[[ $DEBUG == true ]] && echo "Debug: setting $path permissions to $d_perms."
chmod "$d_perms" "$path"
fi
# If the path is a file, check and modify permissions if required.
elif [[ -f "$path" ]]; then
# Use different permissions for private files (private keys and ACME account keys) ...
if [[ "$path" =~ ^.*(default\.key|key\.pem|\.json)$ ]]; then
if [[ "$(stat -c %a "$path")" != "$f_perms" ]]; then
[[ $DEBUG == true ]] && echo "Debug: setting $path permissions to $f_perms."
chmod "$f_perms" "$path"
fi
# ... and for public files (certificates, chains, fullchains, DH parameters).
else
if [[ "$(stat -c %a "$path")" != "644" ]]; then
[[ $DEBUG == true ]] && echo "Debug: setting $path permissions to 644."
chmod "$f_perms" "$path"
fi
fi
function load_acme_ca_uri {
if [[ "$ACME_CA_URI" == "https://acme-v01.api.letsencrypt.org/directory" ]] || \
[[ "$ACME_CA_URI" == "production" ]] || \
[[ "$ACME_CA_URI" == "prod" ]]; then
original_acme="$ACME_CA_URI"
export ACME_CA_URI="https://acme-v02.api.letsencrypt.org/directory"
echo "Info: Rewriting ACME_CA_URI from $original_acme to $ACME_CA_URI"
fi
if [[ "$ACME_CA_URI" == "https://acme-staging.api.letsencrypt.org/directory" ]] || \
[[ "$ACME_CA_URI" == "staging" ]] || \
[[ "$ACME_CA_URI" == "test" ]]; then
original_acme="$ACME_CA_URI"
export ACME_CA_URI="https://acme-staging-v02.api.letsencrypt.org/directory"
echo "Info: Rewriting ACME_CA_URI from $original_acme to $ACME_CA_URI"
fi
}
# Convert argument to lowercase (bash 4 only)
function lc {
echo "${@,,}"
}

View File

@ -1,22 +1,29 @@
#!/bin/bash
# shellcheck disable=SC2120
# shellcheck source=functions.sh
source /app/functions.sh
seconds_to_wait=3600
ACME_CA_URI="${ACME_CA_URI:-https://acme-v01.api.letsencrypt.org/directory}"
DEFAULT_KEY_SIZE=4096
REUSE_ACCOUNT_KEYS="$(lc ${REUSE_ACCOUNT_KEYS:-true})"
REUSE_PRIVATE_KEYS="$(lc ${REUSE_PRIVATE_KEYS:-false})"
load_acme_ca_uri
CERTS_UPDATE_INTERVAL="${CERTS_UPDATE_INTERVAL:-3600}"
ACME_CA_URI="${ACME_CA_URI:-"https://acme-v02.api.letsencrypt.org/directory"}"
ACME_CA_TEST_URI="https://acme-staging-v02.api.letsencrypt.org/directory"
DEFAULT_KEY_SIZE="${DEFAULT_KEY_SIZE:-4096}"
RENEW_PRIVATE_KEYS="$(lc "${RENEW_PRIVATE_KEYS:-true}")"
# Backward compatibility environment variable
REUSE_PRIVATE_KEYS="$(lc "${REUSE_PRIVATE_KEYS:-false}")"
function create_link {
local -r source=${1?missing source argument}
local -r target=${2?missing target argument}
if [[ -f "$target" ]] && [[ "$(readlink "$target")" == "$source" ]]; then
[[ $DEBUG == true ]] && echo "$target already linked to $source"
set_ownership_and_permissions "$target"
[[ "$DEBUG" == 1 ]] && echo "$target already linked to $source"
return 1
else
ln -sf "$source" "$target"
ln -sf "$source" "$target" \
&& set_ownership_and_permissions "$target"
fi
}
@ -30,21 +37,23 @@ function create_links {
fi
local return_code=1
create_link "./$base_domain/fullchain.pem" "/etc/nginx/certs/$domain.crt"
return_code=$(( $return_code & $? ))
return_code=$(( return_code & $? ))
create_link "./$base_domain/key.pem" "/etc/nginx/certs/$domain.key"
return_code=$(( $return_code & $? ))
return_code=$(( return_code & $? ))
if [[ -f "/etc/nginx/certs/dhparam.pem" ]]; then
create_link ./dhparam.pem "/etc/nginx/certs/$domain.dhparam.pem"
return_code=$(( $return_code & $? ))
return_code=$(( return_code & $? ))
fi
if [[ -f "/etc/nginx/certs/$base_domain/chain.pem" ]]; then
create_link "./$base_domain/chain.pem" "/etc/nginx/certs/$domain.chain.pem"
return_code=$(( $return_code & $? ))
return_code=$(( return_code & $? ))
fi
return $return_code
}
function cleanup_links {
local -a LETSENCRYPT_CONTAINERS
local -a LETSENCRYPT_STANDALONE_CERTS
local -a ENABLED_DOMAINS
local -a SYMLINKED_DOMAINS
local -a DISABLED_DOMAINS
@ -57,53 +66,53 @@ function cleanup_links {
symlinked_domain="${symlinked_domain%*.crt}"
SYMLINKED_DOMAINS+=("$symlinked_domain")
done
[[ $DEBUG == true ]] && echo "Symlinked domains: ${SYMLINKED_DOMAINS[*]}"
[[ "$DEBUG" == 1 ]] && echo "Symlinked domains: ${SYMLINKED_DOMAINS[*]}"
# Create an array containing domains that are considered
# enabled (ie present on /app/letsencrypt_service_data).
# shellcheck source=/dev/null
source /app/letsencrypt_service_data
# enabled (ie present on /app/letsencrypt_service_data or /app/letsencrypt_user_data).
[[ -f /app/letsencrypt_service_data ]] && source /app/letsencrypt_service_data
[[ -f /app/letsencrypt_user_data ]] && source /app/letsencrypt_user_data
LETSENCRYPT_CONTAINERS+=( "${LETSENCRYPT_STANDALONE_CERTS[@]}" )
for cid in "${LETSENCRYPT_CONTAINERS[@]}"; do
host_varname="LETSENCRYPT_${cid}_HOST"
hosts_array="${host_varname}[@]"
for domain in "${!hosts_array}"; do
local -n hosts_array="LETSENCRYPT_${cid}_HOST"
for domain in "${hosts_array[@]}"; do
# Add domain to the array storing currently enabled domains.
ENABLED_DOMAINS+=("$domain")
done
done
[[ $DEBUG == true ]] && echo "Enabled domains: ${ENABLED_DOMAINS[*]}"
[[ "$DEBUG" == 1 ]] && echo "Enabled domains: ${ENABLED_DOMAINS[*]}"
# Create an array containing only domains for which a symlinked private key exists
# in /etc/nginx/certs but that no longer have a corresponding LETSENCRYPT_HOST set
# on an active container.
# on an active container or on /app/letsencrypt_user_data
if [[ ${#SYMLINKED_DOMAINS[@]} -gt 0 ]]; then
mapfile -t DISABLED_DOMAINS < <(echo "${SYMLINKED_DOMAINS[@]}" \
"${ENABLED_DOMAINS[@]}" \
"${ENABLED_DOMAINS[@]}" \
| tr ' ' '\n' | sort | uniq -u)
fi
[[ $DEBUG == true ]] && echo "Disabled domains: ${DISABLED_DOMAINS[*]}"
[[ "$DEBUG" == 1 ]] && echo "Disabled domains: ${DISABLED_DOMAINS[*]}"
# Remove disabled domains symlinks if present.
# Return 1 if nothing was removed and 0 otherwise.
if [[ ${#DISABLED_DOMAINS[@]} -gt 0 ]]; then
[[ $DEBUG == true ]] && echo "Some domains are disabled :"
[[ "$DEBUG" == 1 ]] && echo "Some domains are disabled :"
for disabled_domain in "${DISABLED_DOMAINS[@]}"; do
[[ $DEBUG == true ]] && echo "Checking domain ${disabled_domain}"
cert_folder="$(readlink -f /etc/nginx/certs/${disabled_domain}.crt)"
[[ "$DEBUG" == 1 ]] && echo "Checking domain ${disabled_domain}"
cert_folder="$(readlink -f "/etc/nginx/certs/${disabled_domain}.crt")"
# If the dotfile is absent, skip domain.
if [[ ! -e "${cert_folder%/*}/.companion" ]]; then
[[ $DEBUG == true ]] && echo "No .companion file found in ${cert_folder}. ${disabled_domain} is not managed by letsencrypt-nginx-proxy-companion. Skipping domain."
[[ "$DEBUG" == 1 ]] && echo "No .companion file found in ${cert_folder}. ${disabled_domain} is not managed by acme-companion. Skipping domain."
continue
else
[[ $DEBUG == true ]] && echo "${disabled_domain} is managed by letsencrypt-nginx-proxy-companion. Removing unused symlinks."
[[ "$DEBUG" == 1 ]] && echo "${disabled_domain} is managed by acme-companion. Removing unused symlinks."
fi
for extension in .crt .key .dhparam.pem .chain.pem; do
file="${disabled_domain}${extension}"
if [[ -n "${file// }" ]] && [[ -L "/etc/nginx/certs/${file}" ]]; then
[[ $DEBUG == true ]] && echo "Removing /etc/nginx/certs/${file}"
[[ "$DEBUG" == 1 ]] && echo "Removing /etc/nginx/certs/${file}"
rm -f "/etc/nginx/certs/${file}"
fi
done
@ -114,146 +123,343 @@ function cleanup_links {
fi
}
function update_certs {
function update_cert {
local cid="${1:?}"
local -n hosts_array="LETSENCRYPT_${cid}_HOST"
# First domain will be our base domain
local base_domain="${hosts_array[0]}"
local should_restart_container='false'
# Base CLI parameters array, used for both --register-account and --issue
local -a params_base_arr
params_base_arr+=(--log /dev/null)
[[ "$DEBUG" == 1 ]] && params_base_arr+=(--debug 2)
# Alternative trusted root CA path, used for test with Pebble
if [[ -n "${CA_BUNDLE// }" ]]; then
if [[ -f "$CA_BUNDLE" ]]; then
params_base_arr+=(--ca-bundle "$CA_BUNDLE")
[[ "$DEBUG" == 1 ]] && echo "Debug: acme.sh will use $CA_BUNDLE as trusted root CA."
else
echo "Warning: the path to the alternate CA bundle ($CA_BUNDLE) is not valid, using default Alpine trust store."
fi
fi
# CLI parameters array used for --register-account
local -a params_register_arr
# CLI parameters array used for --issue
local -a params_issue_arr
params_issue_arr+=(--webroot /usr/share/nginx/html)
local -n cert_keysize="LETSENCRYPT_${cid}_KEYSIZE"
if [[ -z "$cert_keysize" ]] || \
[[ ! "$cert_keysize" =~ ^(2048|3072|4096|ec-256|ec-384)$ ]]; then
cert_keysize=$DEFAULT_KEY_SIZE
fi
params_issue_arr+=(--keylength "$cert_keysize")
# OCSP-Must-Staple extension
local -n ocsp="ACME_${cid}_OCSP"
if [[ $(lc "$ocsp") == true ]]; then
params_issue_arr+=(--ocsp-must-staple)
fi
local -n accountemail="LETSENCRYPT_${cid}_EMAIL"
local config_home
# If we don't have a LETSENCRYPT_EMAIL from the proxied container
# and DEFAULT_EMAIL is set to a non empty value, use the latter.
if [[ -z "$accountemail" ]]; then
if [[ -n "${DEFAULT_EMAIL// }" ]]; then
accountemail="$DEFAULT_EMAIL"
else
unset accountemail
fi
fi
if ! [[ -z "$accountemail" ]] && [[ "$accountemail" == "<no value>" ]]; then
unset accountemail
fi
if [[ -n "${accountemail// }" ]]; then
# If we got an email, use it with the corresponding config home
config_home="/etc/acme.sh/$accountemail"
else
# If we did not get any email at all, use the default (empty mail) config
config_home="/etc/acme.sh/default"
fi
local -n acme_ca_uri="ACME_${cid}_CA_URI"
if [[ -z "$acme_ca_uri" ]]; then
# Use default or user provided ACME end point
acme_ca_uri="$ACME_CA_URI"
fi
# LETSENCRYPT_TEST overrides LETSENCRYPT_ACME_CA_URI
local -n test_certificate="LETSENCRYPT_${cid}_TEST"
if [[ $(lc "$test_certificate") == true ]]; then
# Use Let's Encrypt ACME V2 staging end point
acme_ca_uri="$ACME_CA_TEST_URI"
fi
# Set relevant --server parameter and ca folder name
params_base_arr+=(--server "$acme_ca_uri")
local ca_dir="${acme_ca_uri##*://}" \
&& ca_dir="${ca_dir%%/*}" \
&& ca_dir="${ca_dir%%:*}"
local certificate_dir
# If we're going to use one of LE stating endpoints ...
if [[ "$acme_ca_uri" =~ ^https://acme-staging.* ]]; then
# Unset accountemail
# force config dir to 'staging'
unset accountemail
config_home="/etc/acme.sh/staging"
# Prefix test certificate directory with _test_
certificate_dir="/etc/nginx/certs/_test_$base_domain"
else
certificate_dir="/etc/nginx/certs/$base_domain"
fi
params_issue_arr+=( \
--cert-file "${certificate_dir}/cert.pem" \
--key-file "${certificate_dir}/key.pem" \
--ca-file "${certificate_dir}/chain.pem" \
--fullchain-file "${certificate_dir}/fullchain.pem" \
)
[[ ! -d "$config_home" ]] && mkdir -p "$config_home"
params_base_arr+=(--config-home "$config_home")
local account_file="${config_home}/ca/${ca_dir}/account.json"
# External Account Binding (EAB)
local -n eab_kid="ACME_${cid}_EAB_KID"
local -n eab_hmac_key="ACME_${cid}_EAB_HMAC_KEY"
if [[ ! -f "$account_file" ]]; then
if [[ -n "${eab_kid}" && -n "${eab_hmac_key}" ]]; then
# Register the ACME account with the per container EAB credentials.
params_register_arr+=(--eab-kid "$eab_kid" --eab-hmac-key "$eab_hmac_key")
elif [[ -n "${ACME_EAB_KID// }" && -n "${ACME_EAB_HMAC_KEY// }" ]]; then
# We don't have per-container EAB kid and hmac key or Zero SSL API key.
# Register the ACME account with the default EAB credentials.
params_register_arr+=(--eab-kid "$ACME_EAB_KID" --eab-hmac-key "$ACME_EAB_HMAC_KEY")
elif [[ -n "${accountemail// }" ]]; then
# We don't have per container nor default EAB credentials, register a new account.
params_register_arr+=(--accountemail "$accountemail")
fi
fi
# Zero SSL
if [[ "$acme_ca_uri" == "https://acme.zerossl.com/v2/DV90" ]]; then
# Test if we already have:
# - an account file
# - the --accountemail account registration parameter
# - the --eab-kid and --eab-hmac-key account registration parameters
local account_ok='false'
if [[ -f "$account_file" ]]; then
account_ok='true'
elif in_array '--accountemail' 'params_register_arr'; then
account_ok='true'
elif in_array '--eab-kid' 'params_register_arr' && in_array '--eab-hmac-key' 'params_register_arr'; then
account_ok='true'
fi
if [[ $account_ok == 'false' ]]; then
local -n zerossl_api_key="ZEROSSL_${cid}_API_KEY"
if [[ -z "$zerossl_api_key" ]]; then
# Try using the default API key
zerossl_api_key="${ZEROSSL_API_KEY:-}"
fi
if [[ -n "${zerossl_api_key// }" ]]; then
# Generate a set of ACME EAB credentials using the ZeroSSL API.
local zerossl_api_response
if zerossl_api_response="$(curl -s -X POST "https://api.zerossl.com/acme/eab-credentials?access_key=${zerossl_api_key}")"; then
if [[ "$(jq -r .success <<< "$zerossl_api_response")" == 'true' ]]; then
eab_kid="$(jq -r .eab_kid <<< "$zerossl_api_response")"
eab_hmac_key="$(jq -r .eab_hmac_key <<< "$zerossl_api_response")"
params_register_arr+=(--eab-kid "$eab_kid" --eab-hmac-key "$eab_hmac_key")
[[ "$DEBUG" == 1 ]] && echo "Successfull EAB credentials request against the ZeroSSL API, got the following EAB kid : ${eab_kid}"
else
# The JSON response body indicated an unsuccesfull API call.
echo "Warning: the EAB credentials request against the ZeroSSL API was not successfull."
fi
else
# curl failed.
echo "Warning: curl failed to make an HTTP POST request to https://api.zerossl.com/acme/eab-credentials."
fi
else
# We don't have a Zero SSL ACME account, EAB credentials, a ZeroSSL API key or an account email :
# skip certificate account registration and certificate issuance.
echo "Error: usage of ZeroSSL require an email bound account. No EAB credentials, ZeroSSL API key or email were provided for this certificate, creation aborted."
return 1
fi
fi
fi
# Account registration and update if required
if [[ ! -f "$account_file" ]]; then
params_register_arr=("${params_base_arr[@]}" "${params_register_arr[@]}")
[[ "$DEBUG" == 1 ]] && echo "Calling acme.sh --register-account with the following parameters : ${params_register_arr[*]}"
acme.sh --register-account "${params_register_arr[@]}"
fi
if [[ -n "${accountemail// }" ]] && ! grep -q "mailto:$accountemail" "$account_file"; then
local -a params_update_arr=("${params_base_arr[@]}" --accountemail "$accountemail")
[[ "$DEBUG" == 1 ]] && echo "Calling acme.sh --update-account with the following parameters : ${params_update_arr[*]}"
acme.sh --update-account "${params_update_arr[@]}"
fi
# If we still don't have an account.json file by this point, we've got an issue
if [[ ! -f "$account_file" ]]; then
echo "Error: no ACME account was found or registered for $accountemail and $acme_ca_uri, certificate creation aborted."
return 1
fi
# acme.sh pre and post hooks
local -n acme_pre_hook="ACME_${cid}_PRE_HOOK"
if [[ -n "${acme_pre_hook}" ]]; then
# Use per-container pre hook
params_issue_arr+=(--pre-hook "$acme_pre_hook")
elif [[ -n ${ACME_PRE_HOOK// } ]]; then
# Use default pre hook
params_issue_arr+=(--pre-hook "$ACME_PRE_HOOK")
fi
local -n acme_post_hook="ACME_${cid}_POST_HOOK"
if [[ -n "${acme_post_hook}" ]]; then
# Use per-container post hook
params_issue_arr+=(--post-hook "$acme_post_hook")
elif [[ -n ${ACME_POST_HOOK// } ]]; then
# Use default post hook
params_issue_arr+=(--post-hook "$ACME_POST_HOOK")
fi
local -n acme_preferred_chain="ACME_${cid}_PREFERRED_CHAIN"
if [[ -n "${acme_preferred_chain}" ]]; then
# Using amce.sh --preferred-chain to select alternate chain.
params_issue_arr+=(--preferred-chain "$acme_preferred_chain")
fi
if [[ "$RENEW_PRIVATE_KEYS" != 'false' && "$REUSE_PRIVATE_KEYS" != 'true' ]]; then
params_issue_arr+=(--always-force-new-domain-key)
fi
[[ "${2:-}" == "--force-renew" ]] && params_issue_arr+=(--force)
# Create directory for the first domain
mkdir -p "$certificate_dir"
set_ownership_and_permissions "$certificate_dir"
local temp_hosts_array=()
for domain in "${hosts_array[@]}"; do
if [[ $domain = *.local ]] || [[ $domain = *.onion ]] || [[ $domain = *.lan ]]; then
echo "Skipping update_cert for: ${domain}"
else
echo "Running update_cert for: ${domain}"
temp_hosts_array+=("$domain")
fi
done
hosts_array=("${temp_hosts_array[@]}")
if [ ${#hosts_array[@]} -eq 0 ]; then
echo "No domain elligible for base domain $base_domain"
return
fi
for domain in "${hosts_array[@]}"; do
# Add all the domains to certificate
params_issue_arr+=(--domain "$domain")
# Add location configuration for the domain
add_location_configuration "$domain" || reload_nginx
done
params_issue_arr=("${params_base_arr[@]}" "${params_issue_arr[@]}")
[[ "$DEBUG" == 1 ]] && echo "Calling acme.sh --issue with the following parameters : ${params_issue_arr[*]}"
echo "Creating/renewal $base_domain certificates... (${hosts_array[*]})"
acme.sh --issue "${params_issue_arr[@]}"
local acmesh_return=$?
# 0 = success, 2 = RENEW_SKIP
if [[ $acmesh_return == 0 || $acmesh_return == 2 ]]; then
for domain in "${hosts_array[@]}"; do
if [[ $acme_ca_uri =~ ^https://acme-staging.* ]]; then
create_links "_test_$base_domain" "$domain" \
&& should_reload_nginx='true' \
&& should_restart_container='true'
else
create_links "$base_domain" "$domain" \
&& should_reload_nginx='true' \
&& should_restart_container='true'
fi
done
echo "${COMPANION_VERSION:-}" > "${certificate_dir}/.companion"
set_ownership_and_permissions "${certificate_dir}/.companion"
# Make private key root readable only
for file in cert.pem key.pem chain.pem fullchain.pem; do
local file_path="${certificate_dir}/${file}"
[[ -e "$file_path" ]] && set_ownership_and_permissions "$file_path"
done
local acme_private_key="$(find /etc/acme.sh -name "*.key" -path "*${hosts_array[0]}*")"
[[ -e "$acme_private_key" ]] && set_ownership_and_permissions "$acme_private_key"
# Queue nginx reload if a certificate was issued or renewed
[[ $acmesh_return -eq 0 ]] \
&& should_reload_nginx='true' \
&& should_restart_container='true'
fi
# Restart container if certs are updated and the respective environmental variable is set
local -n restart_container="LETSENCRYPT_${cid}_RESTART_CONTAINER"
if [[ $(lc "$restart_container") == true ]] && [[ "$should_restart_container" == 'true' ]]; then
echo "Restarting container (${cid})..."
docker_restart "${cid}"
fi
for domain in "${hosts_array[@]}"; do
if [[ -f "/etc/nginx/conf.d/standalone-cert-$domain.conf" ]]; then
[[ "$DEBUG" == 1 ]] && echo "Debug: removing standalone configuration file /etc/nginx/conf.d/standalone-cert-$domain.conf"
rm -f "/etc/nginx/conf.d/standalone-cert-$domain.conf" && should_reload_nginx='true'
fi
done
}
function update_certs {
local -a LETSENCRYPT_CONTAINERS
local -a LETSENCRYPT_STANDALONE_CERTS
pushd /etc/nginx/certs > /dev/null || return
check_nginx_proxy_container_run || return
[[ -f /app/letsencrypt_service_data ]] || return
# Load relevant container settings
unset LETSENCRYPT_CONTAINERS
# shellcheck source=/dev/null
source /app/letsencrypt_service_data
if [[ -f /app/letsencrypt_service_data ]]; then
source /app/letsencrypt_service_data
else
echo "Warning: /app/letsencrypt_service_data not found, skipping data from containers."
fi
# Load settings for standalone certs
if [[ -f /app/letsencrypt_user_data ]]; then
if source /app/letsencrypt_user_data; then
for cid in "${LETSENCRYPT_STANDALONE_CERTS[@]}"; do
local -n hosts_array="LETSENCRYPT_${cid}_HOST"
for domain in "${hosts_array[@]}"; do
add_standalone_configuration "$domain"
done
done
reload_nginx
LETSENCRYPT_CONTAINERS+=( "${LETSENCRYPT_STANDALONE_CERTS[@]}" )
else
echo "Warning: could not source /app/letsencrypt_user_data, skipping user data"
fi
fi
should_reload_nginx='false'
for cid in "${LETSENCRYPT_CONTAINERS[@]}"; do
# Derive host and email variable names
host_varname="LETSENCRYPT_${cid}_HOST"
# Array variable indirection hack: http://stackoverflow.com/a/25880676/350221
hosts_array="${host_varname}[@]"
hosts_array_expanded=("${!hosts_array}")
# First domain will be our base domain
base_domain="${hosts_array_expanded[0]}"
params_d_str=""
email_varname="LETSENCRYPT_${cid}_EMAIL"
email_address="${!email_varname}"
if [[ "$email_address" != "<no value>" ]]; then
params_d_str+=" --email $email_address"
fi
keysize_varname="LETSENCRYPT_${cid}_KEYSIZE"
cert_keysize="${!keysize_varname}"
if [[ "$cert_keysize" == "<no value>" ]]; then
cert_keysize=$DEFAULT_KEY_SIZE
fi
test_certificate_varname="LETSENCRYPT_${cid}_TEST"
le_staging_uri="https://acme-staging.api.letsencrypt.org/directory"
if [[ $(lc "${!test_certificate_varname:-}") == true ]] || \
[[ "$ACME_CA_URI" == "$le_staging_uri" ]]; then
# Use staging Let's Encrypt ACME end point
acme_ca_uri="$le_staging_uri"
# Prefix test certificate directory with _test_
certificate_dir="/etc/nginx/certs/_test_$base_domain"
else
# Use default or user provided ACME end point
acme_ca_uri="$ACME_CA_URI"
certificate_dir="/etc/nginx/certs/$base_domain"
fi
account_varname="LETSENCRYPT_${cid}_ACCOUNT_ALIAS"
account_alias="${!account_varname}"
if [[ "$account_alias" == "<no value>" ]]; then
account_alias=default
fi
[[ $DEBUG == true ]] && params_d_str+=" -v"
[[ $REUSE_PRIVATE_KEYS == true ]] && params_d_str+=" --reuse_key"
[[ "${1}" == "--force-renew" ]] && params_d_str+=" --valid_min 7776000"
# Create directory for the first domain,
# make it root readable only and make it the cwd
mkdir -p "$certificate_dir"
set_ownership_and_permissions "$certificate_dir"
pushd "$certificate_dir" || return
for domain in "${!hosts_array}"; do
# Add all the domains to certificate
params_d_str+=" -d $domain"
# Add location configuration for the domain
add_location_configuration "$domain" || reload_nginx
done
# The ACME account key full path is derived from the endpoint URI
# + the account alias (set to 'default' if no alias is provided)
account_key_dir="../accounts/${acme_ca_uri#*://}"
account_key_full_path="${account_key_dir}/${account_alias}.json"
if [[ $REUSE_ACCOUNT_KEYS == true ]]; then
if [[ -f "$account_key_full_path" ]]; then
# If there is no symlink to the account key, create it
if [[ ! -L ./account_key.json ]]; then
ln -sf "$account_key_full_path" ./account_key.json
# If the symlink target the wrong account key, replace it
elif [[ "$(readlink -f ./account_key.json)" != "$account_key_full_path" ]]; then
ln -sf "$account_key_full_path" ./account_key.json
fi
fi
fi
echo "Creating/renewal $base_domain certificates... (${hosts_array_expanded[*]})"
/usr/bin/simp_le \
-f account_key.json -f key.pem -f chain.pem -f fullchain.pem -f cert.pem \
$params_d_str \
--cert_key_size=$cert_keysize \
--server=$acme_ca_uri \
--default_root /usr/share/nginx/html/
simp_le_return=$?
if [[ $REUSE_ACCOUNT_KEYS == true ]]; then
# If the account key to be reused does not exist yet, copy it
# from the CWD and replace the file in CWD with a symlink
if [[ ! -f "$account_key_full_path" && -f ./account_key.json ]]; then
mkdir -p "$account_key_dir"
cp ./account_key.json "$account_key_full_path"
ln -sf "$account_key_full_path" ./account_key.json
fi
fi
popd || return
if [[ $simp_le_return -ne 2 ]]; then
for domain in "${!hosts_array}"; do
if [[ "$acme_ca_uri" == "$le_staging_uri" ]]; then
create_links "_test_$base_domain" "$domain" && should_reload_nginx='true'
else
create_links "$base_domain" "$domain" && should_reload_nginx='true'
fi
touch "${certificate_dir}/.companion"
set_ownership_and_permissions "${certificate_dir}/.companion"
done
# Make private key root readable only
for filename in cert key chain fullchain; do
set_ownership_and_permissions "${certificate_dir}/${filename}.pem"
done
# Make the account key and its parent folders (up to
# /etc/nginx/certs/accounts included) root readable only
account_key_perm_path="/etc/nginx/certs/accounts/${acme_ca_uri#*://}/${account_alias}.json"
until [[ "$account_key_perm_path" == /etc/nginx/certs ]]; do
set_ownership_and_permissions "$account_key_perm_path"
account_key_perm_path="$(dirname "$account_key_perm_path")"
done
# Queue nginx reload if a certificate was issued or renewed
[[ $simp_le_return -eq 0 ]] && should_reload_nginx='true'
fi
# Pass the eventual --force-renew arg to update_cert() as second arg
update_cert "$cid" "${1:-}"
done
cleanup_links && should_reload_nginx='true'
[[ "$should_reload_nginx" == 'true' ]] && reload_nginx
popd > /dev/null || return
}
# Allow the script functions to be sourced without starting the Service Loop.
@ -266,10 +472,10 @@ pid=
trap '[[ $pid ]] && kill $pid; exec $0' EXIT
trap 'trap - EXIT' INT TERM
update_certs
update_certs "$@"
# Wait some amount of time
echo "Sleep for ${seconds_to_wait}s"
sleep $seconds_to_wait & pid=$!
echo "Sleep for ${CERTS_UPDATE_INTERVAL}s"
sleep $CERTS_UPDATE_INTERVAL & pid=$!
wait
pid=

View File

@ -1,15 +1,80 @@
LETSENCRYPT_CONTAINERS=({{ range $hosts, $containers := groupBy $ "Env.LETSENCRYPT_HOST" }}{{ if trim $hosts }}{{ range $container := $containers }} '{{ printf "%.12s" $container.ID }}' {{ end }}{{ end }}{{ end }})
LETSENCRYPT_CONTAINERS=(
{{ range $hosts, $containers := groupBy $ "Env.LETSENCRYPT_HOST" }}
{{ if trim $hosts }}
{{ range $container := $containers }}
{{ if parseBool (coalesce $container.Env.LETSENCRYPT_SINGLE_DOMAIN_CERTS "false") }}
{{/* Explicit per-domain splitting of the certificate */}}
{{ range $host := split $hosts "," }}
{{ $host := trim $host }}
{{- "\t"}}'{{ printf "%.12s" $container.ID }}_{{ sha1 $host }}'
{{ end }}
{{ else }}
{{/* Default: multi-domain (SAN) certificate */}}
{{- "\t"}}'{{ printf "%.12s" $container.ID }}'
{{ end }}
{{ end }}
{{ end }}
{{ end }}
)
{{ range $hosts, $containers := groupBy $ "Env.LETSENCRYPT_HOST" }}
{{ $hosts := trimSuffix "," $hosts }}
{{ range $container := $containers }}{{ $cid := printf "%.12s" $container.ID }}
LETSENCRYPT_{{ $cid }}_HOST=( {{ range $host := split $hosts "," }}{{ $host := trim $host }}'{{ $host }}' {{ end }})
LETSENCRYPT_{{ $cid }}_EMAIL="{{ $container.Env.LETSENCRYPT_EMAIL }}"
LETSENCRYPT_{{ $cid }}_KEYSIZE="{{ $container.Env.LETSENCRYPT_KEYSIZE }}"
LETSENCRYPT_{{ $cid }}_TEST="{{ $container.Env.LETSENCRYPT_TEST }}"
LETSENCRYPT_{{ $cid }}_ACCOUNT_ALIAS="{{ $container.Env.LETSENCRYPT_ACCOUNT_ALIAS }}"
{{ end }}
{{ $hosts := trimSuffix "," $hosts }}
{{ range $container := $containers }}
{{/* Trim spaces and set empty values on per-container environment variables */}}
{{ $KEYSIZE := trim (coalesce $container.Env.LETSENCRYPT_KEYSIZE "") }}
{{ $STAGING := trim (coalesce $container.Env.LETSENCRYPT_TEST "") }}
{{ $EMAIL := trim (coalesce $container.Env.LETSENCRYPT_EMAIL "") }}
{{ $CA_URI := trim (coalesce $container.Env.ACME_CA_URI "") }}
{{ $PREFERRED_CHAIN := trim (coalesce $container.Env.ACME_PREFERRED_CHAIN "") }}
{{ $OCSP := trim (coalesce $container.Env.ACME_OCSP "") }}
{{ $EAB_KID := trim (coalesce $container.Env.ACME_EAB_KID "") }}
{{ $EAB_HMAC_KEY := trim (coalesce $container.Env.ACME_EAB_HMAC_KEY "") }}
{{ $ZEROSSL_API_KEY := trim (coalesce $container.Env.ZEROSSL_API_KEY "") }}
{{ $RESTART_CONTAINER := trim (coalesce $container.Env.LETSENCRYPT_RESTART_CONTAINER "") }}
{{ $PRE_HOOK := trim (coalesce $container.Env.ACME_PRE_HOOK "") }}
{{ $POST_HOOK := trim (coalesce $container.Env.ACME_POST_HOOK "") }}
{{ $cid := printf "%.12s" $container.ID }}
{{ if parseBool (coalesce $container.Env.LETSENCRYPT_SINGLE_DOMAIN_CERTS "false") }}
{{/* Explicit per-domain splitting of the certificate */}}
{{ range $host := split $hosts "," }}
{{ $host := trim $host }}
{{ $host := trimSuffix "." $host }}
{{ $hostHash := sha1 $host }}
{{- "\n" }}LETSENCRYPT_{{ $cid }}_{{ $hostHash }}_HOST=('{{ $host }}')
{{- "\n" }}LETSENCRYPT_{{ $cid }}_{{ $hostHash }}_KEYSIZE="{{ $KEYSIZE }}"
{{- "\n" }}LETSENCRYPT_{{ $cid }}_{{ $hostHash }}_TEST="{{ $STAGING }}"
{{- "\n" }}LETSENCRYPT_{{ $cid }}_{{ $hostHash }}_EMAIL="{{ $EMAIL }}"
{{- "\n" }}ACME_{{ $cid }}_{{ $hostHash }}_CA_URI="{{ $CA_URI }}"
{{- "\n" }}ACME_{{ $cid }}_{{ $hostHash }}_PREFERRED_CHAIN="{{ $PREFERRED_CHAIN }}"
{{- "\n" }}ACME_{{ $cid }}_{{ $hostHash }}_OCSP="{{ $OCSP }}"
{{- "\n" }}ACME_{{ $cid }}_{{ $hostHash }}_EAB_KID="{{ $EAB_KID }}"
{{- "\n" }}ACME_{{ $cid }}_{{ $hostHash }}_EAB_HMAC_KEY="{{ $EAB_HMAC_KEY }}"
{{- "\n" }}ZEROSSL_{{ $cid }}_{{ $hostHash }}_API_KEY="{{ $ZEROSSL_API_KEY }}"
{{- "\n" }}LETSENCRYPT_{{ $cid }}_{{ $hostHash }}_RESTART_CONTAINER="{{ $RESTART_CONTAINER }}"
{{- "\n" }}ACME_{{ $cid }}_{{ $hostHash }}_PRE_HOOK="{{ $PRE_HOOK }}"
{{- "\n" }}ACME_{{ $cid }}_{{ $hostHash }}_POST_HOOK="{{ $POST_HOOK }}"
{{ end }}
{{ else }}
{{/* Default: multi-domain (SAN) certificate */}}
{{- "\n" }}LETSENCRYPT_{{ $cid }}_HOST=(
{{- range $host := split $hosts "," }}
{{- $host := trim $host }}
{{- $host := trimSuffix "." $host -}}
'{{ $host }}'{{ " " }}
{{- end -}}
)
{{- "\n" }}LETSENCRYPT_{{ $cid }}_KEYSIZE="{{ $KEYSIZE }}"
{{- "\n" }}LETSENCRYPT_{{ $cid }}_TEST="{{ $STAGING }}"
{{- "\n" }}LETSENCRYPT_{{ $cid }}_EMAIL="{{ $EMAIL }}"
{{- "\n" }}ACME_{{ $cid }}_CA_URI="{{ $CA_URI }}"
{{- "\n" }}ACME_{{ $cid }}_PREFERRED_CHAIN="{{ $PREFERRED_CHAIN }}"
{{- "\n" }}ACME_{{ $cid }}_OCSP="{{ $OCSP }}"
{{- "\n" }}ACME_{{ $cid }}_EAB_KID="{{ $EAB_KID }}"
{{- "\n" }}ACME_{{ $cid }}_EAB_HMAC_KEY="{{ $EAB_HMAC_KEY }}"
{{- "\n" }}ZEROSSL_{{ $cid }}_API_KEY="{{ $ZEROSSL_API_KEY }}"
{{- "\n" }}LETSENCRYPT_{{ $cid }}_RESTART_CONTAINER="{{ $RESTART_CONTAINER }}"
{{- "\n" }}ACME_{{ $cid }}_PRE_HOOK="{{ $PRE_HOOK }}"
{{- "\n" }}ACME_{{ $cid }}_POST_HOOK="{{ $POST_HOOK }}"
{{ end }}
{{ end }}
{{ end }}

View File

@ -1,5 +1,6 @@
location ^~ /.well-known/acme-challenge/ {
auth_basic off;
auth_request off;
allow all;
root /usr/share/nginx/html;
try_files $uri =404;

View File

@ -2,11 +2,13 @@
# SIGTERM-handler
term_handler() {
[[ -n "$docker_gen_pid" ]] && kill $docker_gen_pid
[[ -n "$letsencrypt_service_pid" ]] && kill $letsencrypt_service_pid
[[ -n "$docker_gen_pid" ]] && kill "$docker_gen_pid"
[[ -n "$letsencrypt_service_pid" ]] && kill "$letsencrypt_service_pid"
# shellcheck source=functions.sh
source /app/functions.sh
remove_all_location_configurations
remove_all_standalone_configurations
exit 0
}
@ -16,7 +18,9 @@ trap 'term_handler' INT QUIT TERM
/app/letsencrypt_service &
letsencrypt_service_pid=$!
docker-gen -watch -notify '/app/signal_le_service' -wait 15s:60s /app/letsencrypt_service_data.tmpl /app/letsencrypt_service_data &
wait_default="5s:20s"
DOCKER_GEN_WAIT="${DOCKER_GEN_WAIT:-$wait_default}"
docker-gen -watch -notify '/app/signal_le_service' -wait "$DOCKER_GEN_WAIT" /app/letsencrypt_service_data.tmpl /app/letsencrypt_service_data &
docker_gen_pid=$!
# wait "indefinitely"

79
docs/Advanced-usage.md Normal file
View File

@ -0,0 +1,79 @@
## Advanced usage (with the nginx and docker-gen containers)
**nginx-proxy** can also be run as two separate containers using the [nginx-proxy/**docker-gen**](https://github.com/nginx-proxy/docker-gen) image and the official [**nginx**](https://hub.docker.com/_/nginx/) image. You may want to do this to prevent having the docker socket bound to a publicly exposed container service (ie avoid mounting the docker socket in the nginx exposed container).
Please read and try [basic usage](./Basic-usage.md), and **validate that you have a working two containers setup** before using the three containers setup. In addition to the steps described there, running **nginx-proxy** as two separate containers with **acme-companion** requires the following:
1) Download and mount the template file [nginx.tmpl](https://github.com/nginx-proxy/nginx-proxy/blob/main/nginx.tmpl) into the **docker-gen** container. You can get the nginx.tmpl file with a command like:
```
curl https://raw.githubusercontent.com/nginx-proxy/nginx-proxy/main/nginx.tmpl > /path/to/nginx.tmpl
```
2) Use the `com.github.nginx-proxy.docker-gen` label on the **docker-gen** container, or explicitly set the `NGINX_DOCKER_GEN_CONTAINER` environment variable on the **acme-companion** container to the name or id of the **docker-gen** container (we'll use the later method in the example).
3) Declare `/etc/nginx/conf.d` as a volume on the nginx container so that it can be shared with the **docker-gen** container.
Example:
### Step 1 - nginx
* Start nginx [(official image)](https://hub.docker.com/_/nginx/) with the required volumes:
```shell
$ docker run --detach \
--name nginx-proxy \
--publish 80:80 \
--publish 443:443 \
--volume conf:/etc/nginx/conf.d \
--volume vhost:/etc/nginx/vhost.d \
--volume html:/usr/share/nginx/html \
--volume certs:/etc/nginx/certs \
nginx
```
### Step 2 - docker-gen
* Start the **docker-gen** container with the shared volumes (with `--volume-from`), the template file and the docker socket:
```shell
$ docker run --detach \
--name nginx-proxy-gen \
--volumes-from nginx-proxy \
--volume /path/to/nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl:ro \
--volume /var/run/docker.sock:/tmp/docker.sock:ro \
nginxproxy/docker-gen \
-notify-sighup nginx-proxy -watch -wait 5s:30s /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf
```
Note that you must pass the exact name of the **nginx** container to **docker-gen** `-notify-sighup` argument (here `nginx-proxy`).
### Step 3 - acme-companion
* Start the **acme-companion** container with the `NGINX_DOCKER_GEN_CONTAINER` environment variable correctly set:
```shell
$ docker run --detach \
--name nginx-proxy-acme \
--volumes-from nginx-proxy \
--volume /var/run/docker.sock:/var/run/docker.sock:ro \
--volume acme:/etc/acme.sh \
--env "NGINX_DOCKER_GEN_CONTAINER=nginx-proxy-gen" \
--env "DEFAULT_EMAIL=mail@yourdomain.tld" \
nginxproxy/acme-companion
```
### Step 4 - proxyed container(s)
* Once the three containers are up, start any containers to be proxied as described in [basic usage](./Basic-usage.md).
```shell
$ docker run --detach \
--name your-proxyed-app \
--env "VIRTUAL_HOST=subdomain.yourdomain.tld" \
--env "LETSENCRYPT_HOST=subdomain.yourdomain.tld" \
nginx
```
If you are experiencing issues with this setup, fall back to the [basic setup](./Basic-usage.md). The advanced setup is not meant to be obligatory.

83
docs/Basic-usage.md Normal file
View File

@ -0,0 +1,83 @@
## Basic usage (with the nginx-proxy container)
Three writable volumes must be declared on the **nginx-proxy** container so that they can be shared with the **acme-companion** container:
* `/etc/nginx/certs` to store certificates and private keys (readonly for the **nginx-proxy** container).
* `/etc/nginx/vhost.d` to change the configuration of vhosts (required so the CA may access `http-01` challenge files).
* `/usr/share/nginx/html` to write `http-01` challenge files.
Additionally, a fourth volume must be declared on the **acme-companion** container to store `acme.sh` configuration and state: `/etc/acme.sh`.
Please also read the doc about [data persistence](./Persistent-data.md).
Example of use:
### Step 1 - nginx-proxy
Start **nginx-proxy** with the three additional volumes declared:
```shell
$ docker run --detach \
--name nginx-proxy \
--publish 80:80 \
--publish 443:443 \
--volume certs:/etc/nginx/certs \
--volume vhost:/etc/nginx/vhost.d \
--volume html:/usr/share/nginx/html \
--volume /var/run/docker.sock:/tmp/docker.sock:ro \
nginxproxy/nginx-proxy
```
Binding the host docker socket (`/var/run/docker.sock`) inside the container to `/tmp/docker.sock` is a requirement of **nginx-proxy**.
### Step 2 - acme-companion
Start the **acme-companion** container, getting the volumes from **nginx-proxy** with `--volumes-from`:
```shell
$ docker run --detach \
--name nginx-proxy-acme \
--volumes-from nginx-proxy \
--volume /var/run/docker.sock:/var/run/docker.sock:ro \
--volume acme:/etc/acme.sh \
--env "DEFAULT_EMAIL=mail@yourdomain.tld" \
nginxproxy/acme-companion
```
The host docker socket has to be bound inside this container too, this time to `/var/run/docker.sock`.
Albeit **optional**, it is **recommended** to provide a valid default email address through the `DEFAULT_EMAIL` environment variable, so that Let's Encrypt can warn you about expiring certificates and allow you to recover your account.
### Step 3 - proxyed container(s)
Once both **nginx-proxy** and **acme-companion** containers are up and running, start any container you want proxyed with environment variables `VIRTUAL_HOST` and `LETSENCRYPT_HOST` both set to the domain(s) your proxyed container is going to use. Multiple hosts can be separated using commas.
[`VIRTUAL_HOST`](https://github.com/nginx-proxy/nginx-proxy#usage) control proxying by **nginx-proxy** and `LETSENCRYPT_HOST` control certificate creation and SSL enabling by **acme-companion**.
Certificates will only be issued for containers that have both `VIRTUAL_HOST` and `LETSENCRYPT_HOST` variables set to domain(s) that correctly resolve to the host, provided the host is publicly reachable.
```shell
$ docker run --detach \
--name your-proxyed-app \
--env "VIRTUAL_HOST=subdomain.yourdomain.tld" \
--env "LETSENCRYPT_HOST=subdomain.yourdomain.tld" \
nginx
```
The containers being proxied must expose the port to be proxied, either by using the `EXPOSE` directive in their Dockerfile or by using the `--expose` flag to `docker run` or `docker create`.
If the proxyed container listen on and expose another port than the default `80`, you can force **nginx-proxy** to use this port with the [`VIRTUAL_PORT`](https://github.com/nginx-proxy/nginx-proxy#multiple-ports) environment variable.
Example using [Grafana](https://hub.docker.com/r/grafana/grafana/) (expose and listen on port 3000):
```shell
$ docker run --detach \
--name grafana \
--env "VIRTUAL_HOST=othersubdomain.yourdomain.tld" \
--env "VIRTUAL_PORT=3000" \
--env "LETSENCRYPT_HOST=othersubdomain.yourdomain.tld" \
--env "LETSENCRYPT_EMAIL=mail@yourdomain.tld" \
grafana/grafana
```
Repeat [Step 3](#step-3---proxyed-containers) for any other container you want to proxy.

View File

@ -0,0 +1,35 @@
## Optional container environment variables for custom configuration.
* `ACME_CA_URI` - Directory URI for the CA ACME API endpoint (defaults to ``https://acme-v02.api.letsencrypt.org/directory``).
If you set this environment variable value to `https://acme-staging-v02.api.letsencrypt.org/directory` the container will obtain its certificates from Let's Encrypt test API endpoint that don't have the [5 certs/week/domain limit](https://letsencrypt.org/docs/rate-limits/) (but are not trusted by browsers).
For example
```bash
$ docker run --detach \
--name nginx-proxy-acme \
--volumes-from nginx-proxy \
--volume /var/run/docker.sock:/var/run/docker.sock:ro \
--volume certs:/etc/nginx/certs:rw \
--volume acme:/etc/acme.sh \
--env "ACME_CA_URI=https://acme-staging-v02.api.letsencrypt.org/directory" \
nginxproxy/acme-companion
```
You can also create test certificates per container (see [Test certificates](./Let's-Encrypt-and-ACME.md#test-certificates))
* `DEBUG` - Set it to `1` to enable debugging of the entrypoint script and generation of LetsEncrypt certificates, which could help you pin point any configuration issues.
* `RENEW_PRIVATE_KEYS` - Set it to `false` to make `acme.sh` reuse previously generated private key for each certificate instead of creating a new one on certificate renewal. Reusing private keys can help if you intend to use [HPKP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Public_Key_Pinning), but please note that HPKP has been deprecated by Google's Chrome and that it is therefore strongly discouraged to use it at all.
* `DHPARAM_BITS` - Change the key size of the RFC7919 Diffie-Hellman group used by the container from the default value of 4096 bits. Supported values are `2048`, `3072` and `4096`. The DH group file will be located in the container at `/etc/nginx/certs/dhparam.pem`. Mounting a different `dhparam.pem` file at that location will override the RFC7919 group creation by the acme-companion container. **COMPATIBILITY WARNING**: some older clients (like Java 6 and 7) do not support DH keys with over 1024 bits. In order to support these clients, you must provide your own `dhparam.pem`.
* `DHPARAM_SKIP` - Set it to `true` to disable the Diffie-Hellman group creation by the container entirely.
* `CA_BUNDLE` - This is a test only variable [for use with Pebble](https://github.com/letsencrypt/pebble#avoiding-client-https-errors). It changes the trusted root CA used by `acme.sh`, from the default Alpine trust store to the CA bundle file located at the provided path (inside the container). Do **not** use it in production unless you are running your own ACME CA.
* `CERTS_UPDATE_INTERVAL` - 3600 seconds by default, this defines how often the container will check if the certificates require update.
* `ACME_PRE_HOOK` - The provided command will be run before every certificate issuance. The action is limited to the commands available inside the **acme-companion** container. For example `--env "ACME_PRE_HOOK=echo 'start'"`. For more information see [Pre- and Post-Hook](./Hooks.md)
* `ACME_POST_HOOK` - The provided command will be run after every certificate issuance. The action is limited to the commands available inside the **acme-companion** container. For example `--env "ACME_POST_HOOK=echo 'end'"`. For more information see [Pre- and Post-Hook](./Hooks.md)

View File

@ -0,0 +1,23 @@
The container provide the following utilities (replace `nginx-proxy-acme` with the name or ID of your **acme-companion** container when executing the commands):
### Force certificates renewal
If needed, you can force a running **acme-companion** container to renew all certificates that are currently in use with the following command:
```bash
$ docker exec nginx-proxy-acme /app/force_renew
```
### Manually trigger the service loop
You can trigger the execution of the service loop before the hourly execution with:
```bash
$ docker exec nginx-proxy-acme /app/signal_le_service
```
Unlike the previous command, this won't force renewal of certificates that don't need to be renewed.
### Show certificates informations
To display informations about your existing certificates, use the following command:
```bash
$ docker exec nginx-proxy-acme /app/cert_status
```

123
docs/Docker-Compose.md Normal file
View File

@ -0,0 +1,123 @@
## Usage with Docker Compose
As stated by its repository, [Docker Compose](https://github.com/docker/compose) is a tool for defining and running multi-container Docker applications using a single _Compose file_. This Wiki page is not meant to be a definitive reference on how to run **nginx-proxy** and **acme-companion** with Docker Compose, as the number of possible setups is quite extensive and they can't be all covered.
### Before your start
Be sure to be familiar with both the [basic](./Basic-usage.md) and [advanced](./Advanced-usage.md) non compose setups, and Docker Compose usage.
Please read [getting container IDs](./Getting-containers-IDs.md) and be aware that the `--volumes-from method` is **only** available on compose file version 2.
The following examples are minimal, clean starting points using compose file version 2. Again they are not intended as a definitive reference.
The use of named containers and volume is not required but helps keeping everything clear and organized.
### Two containers example
```yaml
version: '2'
services:
nginx-proxy:
image: nginxproxy/nginx-proxy
container_name: nginx-proxy
ports:
- "80:80"
- "443:443"
volumes:
- conf:/etc/nginx/conf.d
- vhost:/etc/nginx/vhost.d
- html:/usr/share/nginx/html
- certs:/etc/nginx/certs:ro
- /var/run/docker.sock:/tmp/docker.sock:ro
network_mode: bridge
acme-companion:
image: nginxproxy/acme-companion
container_name: nginx-proxy-acme
environment:
- DEFAULT_EMAIL=mail@yourdomain.tld
volumes_from:
- nginx-proxy
volumes:
- certs:/etc/nginx/certs:rw
- acme:/etc/acme.sh
- /var/run/docker.sock:/var/run/docker.sock:ro
network_mode: bridge
volumes:
conf:
vhost:
html:
certs:
acme:
```
### Three containers example
```yaml
version: '2'
services:
nginx-proxy:
image: nginx:alpine
container_name: nginx-proxy
ports:
- "80:80"
- "443:443"
volumes:
- conf:/etc/nginx/conf.d
- vhost:/etc/nginx/vhost.d
- html:/usr/share/nginx/html
- certs:/etc/nginx/certs:ro
network_mode: bridge
docker-gen:
image: nginxproxy/docker-gen
container_name: nginx-proxy-gen
command: -notify-sighup nginx-proxy -watch 5s:30s /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf
volumes_from:
- nginx-proxy
volumes:
- /path/to/nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl:ro
- /var/run/docker.sock:/tmp/docker.sock:ro
labels:
- "com.github.nginx-proxy.docker-gen"
network_mode: bridge
acme-companion:
image: nginxproxy/acme-companion
container_name: nginx-proxy-acme
environment:
- DEFAULT_EMAIL=mail@yourdomain.tld
volumes_from:
- nginx-proxy
volumes:
- certs:/etc/nginx/certs:rw
- acme:/etc/acme.sh
- /var/run/docker.sock:/var/run/docker.sock:ro
network_mode: bridge
volumes:
conf:
vhost:
html:
certs:
acme:
```
**Note:** don't forget to replace `/path/to/nginx.tmpl` with the actual path to the [`nginx.tmpl`](https://raw.githubusercontent.com/nginx-proxy/nginx-proxy/main/nginx.tmpl) file you downloaded.
### Other (external) examples
**Warning:** some of those examples might be outdated and not working properly with version >= `2.0` of this project.
If you want other examples how to use this container with Docker Compose, look at:
* [Nicolas Duchon's Examples](https://github.com/buchdag/letsencrypt-nginx-proxy-companion-compose) - with automated testing
* [Evert Ramos's Examples](https://github.com/evertramos/docker-compose-letsencrypt-nginx-proxy-companion) - using docker-compose version '3'
* [Karl Fathi's Examples](https://github.com/fatk/docker-letsencrypt-nginx-proxy-companion-examples)
* [More examples from Karl](https://github.com/pixelfordinner/pixelcloud-docker-apps/tree/master/nginx-proxy)
* [George Ilyes' Examples](https://github.com/gilyes/docker-nginx-letsencrypt-sample)
* [Dmitry's simple docker-compose example](https://github.com/dmitrym0/simple-lets-encrypt-docker-compose-sample)
* [Radek's docker-compose jenkins example](https://github.com/dataminelab/docker-jenkins-nginx-letsencrypt)

View File

@ -0,0 +1,96 @@
## Getting nginx-proxy/nginx/docker-gen containers IDs
For **acme-companion** to work properly, it needs to know the ID of the **nginx**/**nginx-proxy** container (in both [two](./Basic-usage.md) and [three](./Advanced-usage.md) containers setups), plus the ID of the **docker-gen** container in a [three container setup](./Advanced-usage.md).
There are three methods to inform the **acme-companion** container of the **nginx**/**nginx-proxy** container ID:
* `label` method: add the label `com.github.nginx-proxy.nginx` to the **nginx**/**nginx-proxy** container.
* `environment variable` method: assign a fixed name to the **nginx**/**nginx-proxy** container with `container_name:` and set the environment variable `NGINX_PROXY_CONTAINER` to this name on the **acme-companion** container.
* `volumes_from` method. Using this method, the **acme-companion** container will get the **nginx**/**nginx-proxy** container ID from the volumes it got using the `volumes_from` option.
And two methods to inform the **acme-companion** container of the **docker-gen** container ID:
* `label` method: add the label `com.github.nginx-proxy.docker-gen` to the **docker-gen** container.
* `environment variable` method: assign a fixed name to the **docker-gen** container with `container_name:` and set the environment variable `NGINX_DOCKER_GEN_CONTAINER` to this name on the **acme-companion** container.
The methods for each container are sorted by order of precedence, meaning that if you use both the label and the volumes_from method, the ID of the **nginx**/**nginx-proxy** container that will be used will be the one found using the label. **There is no point in using more than one method at a time for either the nginx/nginx-proxy or docker-gen container beside potentially confusing yourself**.
The advantage the `label` methods have over the `environment variable` (and `volumes_from`) methods is enabling the use of the **acme-companion** in environments where containers names are dynamic, like in Swarm Mode or in Docker Cloud. Howhever if you intend to do so, as upstream **docker-gen** lacks the ability to identify containers from labels, you'll need both to either use the two containers setup or to replace nginx-proxy/docker-gen with a fork that has this ability like [herlderco/docker-gen](https://github.com/helderco/docker-gen). Be advised that for now, this works to a very limited extent [(everything has to be on the same node)](https://github.com/nginx-proxy/acme-companion/pull/231#issuecomment-330624331).
#### Examples with three containers setups:
`label` method.
```
$ docker run --detach \
[...]
--label com.github.nginx-proxy.nginx \
nginx
$ docker run --detach \
[...]
--label com.github.nginx-proxy.docker-gen \
nginxproxy/docker-gen
$ docker run --detach \
[...]
nginxproxy/acme-companion
```
`environment variable` method
```
$ docker run --detach \
[...]
--name unique-container-name \
nginx
$ docker run --detach \
[...]
--name another-unique-container-name \
nginxproxy/docker-gen
$ docker run --detach \
[...]
--env NGINX_PROXY_CONTAINER=unique-container-name \
--env NGINX_DOCKER_GEN_CONTAINER=another-unique-container-name \
nginxproxy/acme-companion
```
`volumes_from` (**nginx**) + `label` (**docker-gen**) method
```
$ docker run --detach \
[...]
--name unique-container-name \
nginx
$ docker run --detach \
[...]
--label com.github.nginx-proxy.docker-gen \
nginxproxy/docker-gen
$ docker run --detach \
[...]
--volumes-from unique-container-name \
nginxproxy/acme-companion
```
`volumes_from` (**nginx**) + `environment variable` (**docker-gen**) method
```
$ docker run --detach \
[...]
--name unique-container-name \
nginx
$ docker run --detach \
[...]
--name another-unique-container-name \
nginxproxy/docker-gen
$ docker run --detach \
[...]
--volumes-from unique-container-name \
--env NGINX_DOCKER_GEN_CONTAINER=another-unique-container-name \
nginxproxy/acme-companion
```

70
docs/Hooks.md Normal file
View File

@ -0,0 +1,70 @@
## Pre-Hooks and Post-Hooks
The Pre- and Post-Hooks of [acme.sh](https://github.com/acmesh-official/acme.sh/) are available through the corresponding environment variables. This allows to trigger actions just before and after certificates are issued (see [acme.sh documentation](https://github.com/acmesh-official/acme.sh/wiki/Using-pre-hook-post-hook-renew-hook-reloadcmd)).
If you set `ACME_PRE_HOOK` and/or `ACME_POST_HOOK` on the **acme-companion** container, **the actions for all certificates will be the same**. If you want specific actions to be run for specific certificates, set the `ACME_PRE_HOOK` / `ACME_POST_HOOK` environment variable(s) on the proxied container(s) instead. Default (on the **acme-companion** container) and per-container `ACME_PRE_HOOK` / `ACME_POST_HOOK` environment variables aren't combined : if both default and per-container variables are set for a given proxied container, the per-container variables will take precedence over the default.
If you want to run the same default hooks for most containers but not for some of them, you can set the `ACME_PRE_HOOK` / `ACME_POST_HOOK` environment variables to the Bash noop operator (ie, `ACME_PRE_HOOK=:`) on those containers.
#### Pre-Hook: `ACME_PRE_HOOK`
This command will be run before certificates are issued.
For example `echo 'start'` on the **acme-companion** container (setting a default Pre-Hook):
```shell
$ docker run --detach \
--name nginx-proxy-acme \
--volumes-from nginx-proxy \
--volume /var/run/docker.sock:/var/run/docker.sock:ro \
--volume acme:/etc/acme.sh \
--env "DEFAULT_EMAIL=mail@yourdomain.tld" \
--env "ACME_PRE_HOOK=echo 'start'" \
nginxproxy/acme-companion
```
And on a proxied container (setting a per-container Pre-Hook):
```shell
$ docker run --detach \
--name your-proxyed-app \
--env "VIRTUAL_HOST=yourdomain.tld" \
--env "LETSENCRYPT_HOST=yourdomain.tld" \
--env "ACME_PRE_HOOK=echo 'start'" \
nginx
```
#### Post-Hook: `ACME_POST_HOOK`
This command will be run after certificates are issued.
For example `echo 'end'` on the **acme-companion** container (setting a default Post-Hook):
```shell
$ docker run --detach \
--name nginx-proxy-acme \
--volumes-from nginx-proxy \
--volume /var/run/docker.sock:/var/run/docker.sock:ro \
--volume acme:/etc/acme.sh \
--env "DEFAULT_EMAIL=mail@yourdomain.tld" \
--env "ACME_POST_HOOK=echo 'end'" \
nginxproxy/acme-companion
```
And on a proxied container (setting a per-container Post-Hook):
```shell
$ docker run --detach \
--name your-proxyed-app \
--env "VIRTUAL_HOST=yourdomain.tld" \
--env "LETSENCRYPT_HOST=yourdomain.tld" \
--env "ACME_POST_HOOK=echo 'start'" \
nginx
```
#### Verification:
If you want to check wether the hook-command is delivered properly to [acme.sh](https://github.com/acmesh-official/acme.sh/), you should check `/etc/acme.sh/[EMAILADDRESS]/[DOMAIN]/[DOMAIN].conf`.
The variable `Le_PreHook` contains the Pre-Hook-Command base64 encoded.
The variable `Le_PostHook` contains the Pre-Hook-Command base64 encoded.
#### Limitations
* The commands that can be used in the hooks are limited to the commands available inside the **acme-companion** container. `curl` and `wget` are available, therefore it is possible to communicate with tools outside the container via HTTP, allowing for complex actions to be implemented outside or in other containers.
#### Use-cases
* Changing some firewall rules just for the ACME authorization, so the ports 80 and/or 443 don't have to be publicly reachable at all time.
* Certificate "post processing" / conversion to another format.
* Monitoring.

View File

@ -0,0 +1,74 @@
## Troubleshooting failing authorizations
The first two things to do in case of failing authorization are to run the **acme-companion** container with the environment variable `DEBUG=1` to enable the more detailed error messages, and to [request test certificates](./Let's-Encrypt-and-ACME.md#test-certificates) while troubleshooting the issue.
Common causes of of failing authorizations:
#### port `80` or `443` on your host are closed / filtered from the outside, possibly because of a misconfigured firewall.
Check your host `80` and `443` ports **from the outside** (as in from a host having a different public IP) with `nmap` or a similar tool.
#### your domain name does not resolve to your host IPv4 and/or IPv6.
Check that your domain name A (and AAAA, if present) records points to the correct adresses using `drill`, `dig` or `nslookup`.
#### your domain name advertise an AAAA (IPv6) record, but your host or your host's docker isn't actually reachable over IPv6.
Create a test nginx container on your host and try to reach it over both IPv4 and IPv6.
```bash
you@remotedockerhost$ docker run -d -p 80:80 nginx:alpine
you@localcomputer$ curl http://your.domain.tld
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
[...]
</html>
you@localcomputer$ curl -6 http://your.domain.tld
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
[...]
</html>
```
If you are unsure of your host/hosts's docker IPv6 connectivity, drop the AAAA record from your domain name and wait for the modification to propagate.
#### your domain name DNS provider answers incorrectly to CAA record requests.
Read https://letsencrypt.org/docs/caa/ and test with https://unboundtest.com/
#### the **nginx-proxy**/**nginx**/**docker-gen**/**acme-companion** containers were misconfigured.
Review [basic usage](./Basic-usage.md) or [advanced usage](./Advanced-usage.md), plus the [nginx-proxy documentation](https://github.com/nginx-proxy/nginx-proxy).
Pay special attention to the fact that the volumes **MUST** be shared between the different containers.
#### you forgot to set both `VIRTUAL_HOST` and `LETSENCRYPT_HOST` on the proxyed container.
Both are required. Every domain on `LETSENCRYPT_HOST`**must** be on `VIRTUAL_HOST`too.
#### you are using an outdated version of either **acme-companion** or the nginx.tmpl file (if running a 3 containers setup)
Pull `nginxproxy/acme-companion:latest` again and get the latest [latest nginx.tmpl](https://raw.githubusercontent.com/nginx-proxy/nginx-proxy/main/nginx.tmpl).
***
When not in debug mode, the challenge files are automatically cleaned up **after** the authorization process, wether it succeeded or failed, so trying to `curl` them from the outside if you didn't enable debug mode won't yeld any result. If don't want to enable debug mode, you can however create a test file inside the same folder and use it to test the challenge files reachability from the outside (over both IPv4 and IPv6 if you want to use the latter):
```
you@remotedockerhost$ docker exec your-le-container bash -c 'echo "Hello world!" > /usr/share/nginx/html/.well-known/acme-challenge/hello-world'
you@localcomputer$ curl http://yourdomain.tld/.well-known/acme-challenge/hello-world
Hello world!
you@localcomputer$ curl -6 http://yourdomain.tld/.well-known/acme-challenge/hello-world
Hello world!
```
If you have issues with the [advanced setup](./Advanced-usage.md), fall back to the [basic setup](./Basic-usage.md). The advanced setup is not meant to be obligatory.

View File

@ -0,0 +1,103 @@
## Let's Encrypt / ACME
**NOTE on CAA**: Please ensure that your DNS provider answers correctly to CAA record requests. [If your DNS provider answer with an error, Let's Encrypt won't issue a certificate for your domain](https://letsencrypt.org/docs/caa/). Let's Encrypt do not require that you set a CAA record on your domain, just that your DNS provider answers correctly.
**NOTE on IPv6**: If the domain or sub domain you want to issue certificate for has an AAAA record set, Let's Encrypt will favor challenge validation over IPv6. [There is an IPv6 to IPv4 fallback in place but Let's Encrypt can't guarantee it'll work in every possible case](https://github.com/letsencrypt/boulder/issues/2770#issuecomment-340489871), so bottom line is **if you are not sure of both your host and your host's Docker reachability over IPv6, do not advertise an AAAA record** or LE challenge validation might fail.
As described on [basic usage](./Basic-usage.md), the `LETSENCRYPT_HOST` environment variables needs to be declared in each to-be-proxied application containers for which you want to enable SSL and create certificate. It most likely needs to be the same as the `VIRTUAL_HOST` variable and must resolve to your host (which has to be publicly reachable).
The following environment variables are optional and parametrize the way the Let's Encrypt client works.
### per proxyed container
#### Multi-domains certificates
Specify multiple hosts with a comma delimiter to create multi-domains ([SAN](https://www.digicert.com/subject-alternative-name.htm)) certificates (the first domain in the list will be the base domain).
Example:
```shell
$ docker run --detach \
--name your-proxyed-app \
--env "VIRTUAL_HOST=yourdomain.tld,www.yourdomain.tld,anotherdomain.tld" \
--env "LETSENCRYPT_HOST=yourdomain.tld,www.yourdomain.tld,anotherdomain.tld" \
nginx
```
Let's Encrypt has a limit of [100 domains per certificate](https://letsencrypt.org/fr/docs/rate-limits/), while Buypass limit is [15 domains per certificate](https://www.buypass.com/ssl/products/go-ssl-campaign).
#### Separate certificate for each domain
The example above will issue a single domain certificate for all the domains listed in the `LETSENCRYPT_HOST` environment variable. If you need to have a separate certificate for each of the domains, you can add set the `LETSENCRYPT_SINGLE_DOMAIN_CERTS` environment variable to `true`.
Example:
```shell
$ docker run --detach \
--name your-proxyed-app \
--env "VIRTUAL_HOST=yourdomain.tld,www.yourdomain.tld,anotherdomain.tld" \
--env "LETSENCRYPT_HOST=yourdomain.tld,www.yourdomain.tld,anotherdomain.tld" \
--env "LETSENCRYPT_SINGLE_DOMAIN_CERTS=true" \
nginx
```
#### Automatic certificate renewal
Every hour (3600 seconds) the certificates are checked and per default every certificate that have been issued at least [60 days](https://github.com/acmesh-official/acme.sh/blob/f2d350002e7c387fad9777a42cf9befe34996c35/acme.sh#L61) ago is renewed. For Let's Encrypt certificates, that mean they will be renewed 30 days before expiration.
#### Contact address
The `LETSENCRYPT_EMAIL` environment variable must be a valid email and will be used by Let's Encrypt to warn you of impeding certificate expiration (should the automated renewal fail) and to recover an account.
#### Private key size
The `LETSENCRYPT_KEYSIZE` environment variable determines the type and size of the requested key. Supported values are `2048`, `3072` and `4096` for RSA keys, and `ec-256` or `ec-384` for elliptic curve keys. The default is RSA 4096.
To change the global default set the `DEFAULT_KEY_SIZE` environment variable on the **acme-companion** container to one of the supported values specified above.
#### OCSP stapling
The `ACME_OCSP` environment variable, when set to `true` on a proxied application container, will add the [OCSP Must-Staple extension](https://blog.apnic.net/2019/01/15/is-the-web-ready-for-ocsp-must-staple/) to the issued certificate. Please read about OCSP Must-Staple support in Nginx if you intend to use this feature (https://trac.nginx.org/nginx/ticket/812 and https://trac.nginx.org/nginx/ticket/1830)
#### Test certificates
The `LETSENCRYPT_TEST` environment variable, when set to `true` on a proxied application container, will create a test certificates that don't have the [5 certs/week/domain limits](https://letsencrypt.org/docs/rate-limits/) and are signed by an untrusted intermediate (they won't be trusted by browsers).
If you want to do this globally for all containers, set `ACME_CA_URI` on the **acme-companion** container as described in [Container configuration](./Container-configuration.md).
#### ACME CA URI
The `ACME_CA_URI` environment variable is used to set the ACME API endpoint from which the container's certificate(s) will be requested (defaults to ``https://acme-v02.api.letsencrypt.org/directory``).
#### Preferred chain
If the ACME CA provides multiple cert chain, you can use the `ACME_PREFERRED_CHAIN` environment variable to select one. See [`acme.sh --preferred-chain` documentation](https://github.com/acmesh-official/acme.sh/wiki/Preferred-Chain) for more info.
#### Container restart on cert renewal
The `LETSENCRYPT_RESTART_CONTAINER` environment variable, when set to `true` on an application container, will restart this container whenever the corresponding cert (`LETSENCRYPT_HOST`) is renewed. This is useful when certificates are directly used inside a container for other purposes than HTTPS (e.g. an FTPS server), to make sure those containers always use an up to date certificate.
#### Pre-Hook and Post-Hook
The `ACME_PRE_HOOK` and `ACME_POST_HOOK` let you use the [`acme.sh` Pre- and Post-Hooks feature](https://github.com/acmesh-official/acme.sh/wiki/Using-pre-hook-post-hook-renew-hook-reloadcmd) to run commands respectively before and after the container's certificate has been issued. For more information see [Pre- and Post-Hook](./Hooks.md)
### global (set on acme-companion container)
#### Default contact address
The `DEFAULT_EMAIL` variable must be a valid email and, when set on the **acme-companion** container, will be used as a fallback when no email address is provided using proxyed container's `LETSENCRYPT_EMAIL` environment variables. It is highly recommended to set this variable to a valid email address that you own.
#### Private key re-utilization
The `RENEW_PRIVATE_KEYS` environment variable, when set to `false` on the **acme-companion** container, will set `acme.sh` to reuse previously generated private key instead of generating a new one at renewal for all domains.
Reusing private keys can help if you intend to use [HPKP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Public_Key_Pinning), but please note that HPKP has been deprecated by Google's Chrome and that it is therefore strongly discouraged to use it at all.
#### ACME accounts handling
- Use one `acme.sh` configuration directory (`--config-home`) per account email address.
- Each `acme.sh` configuration directory can hold several accounts on different ACME service providers. But only one per service provider.
- The `default` configuration directory holds the configuration for empty account email address.
- When in testing mode (`LETSENCRYPT_TEST=true`):
1. The container will use the special purpose `staging` configuration directory.
1. The directory URI is forced to The Let's Encrypt v2 staging one (`ACME_CA_URI` is ignored)
2. The account email address is forced empty (`DEFAULT_EMAIL` and `LETSENCRYPT_EMAIL` are ignored)

108
docs/Persistent-data.md Normal file
View File

@ -0,0 +1,108 @@
## Persistent data
### Named volumes (recommended)
When you follow instructions from Basic usage or Advanced usage, Docker will automatically create **named volumes** for every `--volume` / `-v` argument passed. Named volume will make it easy for you to mount the same persisted data even if you delete then re-create the container:
```shell
$ docker run -d \
--name nginx-proxy \
-p 80:80 \
-p 443:443 \
-v certs:/etc/nginx/certs \
-v vhost:/etc/nginx/vhost.d \
-v html:/usr/share/nginx/html \
-v /var/run/docker.sock:/tmp/docker.sock:ro \
nginxproxy/nginx-proxy
$ docker volume ls
DRIVER VOLUME NAME
local certs
local vhost
local html
```
### Anonymous volumes (not recommended)
If you don't prefix your volumes with a name, Docker will instead create **anonymous volumes** (volumes with a random name). Those volume persist after the container is deleted but aren't automatically re-mounted when you re-create the container. Their usage is **not recommended** as they don't provide any advantages over named volumes and make keeping tracks of what volume store which data way harder.
```shell
$ docker run -d \
--name nginx-proxy \
-p 80:80 \
-p 443:443 \
-v /etc/nginx/certs \
-v /etc/nginx/vhost.d \
-v /usr/share/nginx/html \
-v /var/run/docker.sock:/tmp/docker.sock:ro \
nginxproxy/nginx-proxy
$ docker volume ls
DRIVER VOLUME NAME
local 287be3abd610e5566500d719ceb8b952952f12c9324ef02d05785d4ee9737ae9
local 6530b1b40cf89efb71aa7fd19bddec927fa2bcae59b04b9c1c850af72ffe0123
local f260f71fefadcdfc311d285d69151f2312915174d3fb1fab89949ec5ec871a54
```
### Host volumes
Alternatively, you might want to store the certificates on a local folder rather than letting Docker create and manage a volume for them. This is easily achieved by using a **host volume** (binding an absolute path on your host to the `/ect/nginx/certs` folder on your containers):
`-v /path/to/certificates:/etc/nginx/certs`
No matter the type of volume you choose, if you set them on the nginx-proxy or nginx container and use `--volumes_from` on the others containers, they will automatically be mounted inside the container to the path your first defined.
### Restraining other containers write permission
If you want to restrain the **nginx** and **docker-gen** processes to read only access on the certificates, you'll have to use different volume flags depending on the container.
Example with named volumes:
`-v certs:/etc/nginx/certs:ro` on the **nginx-proxy** or **nginx** + **docker-gen** container(s).
`-v certs:/etc/nginx/certs:rw` on the **acme-companion** container.
## Ownership & permissions of private and ACME account keys
By default, the **acme-companion** container will enforce the following ownership and permissions scheme on the files it creates and manage:
```
[drwxr-xr-x] /etc/nginx/certs
├── [-rw-r--r-- root root] dhparam.pem
├── [-rw-r--r-- root root] default.crt
├── [-rw------- root root] default.key
├── [drwxr-xr-x root root] domain.tld
│ ├── [-rw-r--r-- root root] cert.pem
│ ├── [-rw-r--r-- root root] chain.pem
│ ├── [-rw-r--r-- root root] fullchain.pem
│ └── [-rw------- root root] key.pem
├── [lrwxrwxrwx root root] domain.tld.chain.pem -> ./domain.tld/chain.pem
├── [lrwxrwxrwx root root] domain.tld.crt -> ./domain.tld/fullchain.pem
├── [lrwxrwxrwx root root] domain.tld.dhparam.pem -> ./dhparam.pem
└── [lrwxrwxrwx root root] domain.tld.key -> ./domain.tld/key.pem
```
This behavior can be customized using the following environment variable on the **acme-companion** container:
* `FILES_UID` - Set the user owning the files and folders managed by the container. The variable can be either a user name if this user exists inside the container or a user numeric ID. Default to `root` (user ID `0`).
* `FILES_GID` - Set the group owning the files and folders managed by the container. The variable can be either a group name if this group exists inside the container or a group numeric ID. Default to the same value as `FILES_UID`.
* `FILES_PERMS` - Set the permissions of the private keys. The variable must be a valid octal permission setting and defaults to `600`.
* `FOLDERS_PERMS` - Set the permissions of the folders managed by the container. The variable must be a valid octal permission setting and defaults to `755`.
For example, `FILES_UID=1000`, `FILES_PERMS=644` and `FOLDERS_PERMS=700` will result in the following:
```
[drwxr-xr-x] /etc/nginx/certs
├── [-rw-r--r-- 1000 1000] dhparam.pem
├── [-rw-r--r-- 1000 1000] default.crt
├── [-rw-r--r-- 1000 1000] default.key
├── [drwx------ 1000 1000] domain.tld
│ ├── [-rw-r--r-- 1000 1000] cert.pem
│ ├── [-rw-r--r-- 1000 1000] chain.pem
│ ├── [-rw-r--r-- 1000 1000] fullchain.pem
│ └── [-rw-r--r-- 1000 1000] key.pem
├── [lrwxrwxrwx 1000 1000] domain.tld.chain.pem -> ./domain.tld/chain.pem
├── [lrwxrwxrwx 1000 1000] domain.tld.crt -> ./domain.tld/fullchain.pem
├── [lrwxrwxrwx 1000 1000] domain.tld.dhparam.pem -> ./dhparam.pem
└── [lrwxrwxrwx 1000 1000] domain.tld.key -> ./domain.tld/key.pem
```

29
docs/README.md Normal file
View File

@ -0,0 +1,29 @@
#### Usage:
[Basic (two containers).](./Basic-usage.md)
[Advanced (three containers).](./Advanced-usage.md)
[Getting containers IDs](./Getting-containers-IDs.md)
[with Docker Compose](./Docker-Compose.md)
[Container utilities](./Container-utilities.md)
#### Additional configuration:
[Let's Encrypt / ACME](./Let's-Encrypt-and-ACME.md)
[Container configuration](./Container-configuration.md)
[Persistent data](./Persistent-data.md)
[Standalone certificates](./Standalone-certificates.md)
[Zero SSL](./Zero-SSL.md)
[Pre-Hooks and Post-Hooks](./Hooks.md)
#### Troubleshooting:
[Invalid / failing authorizations](./Invalid-authorizations.md)

View File

@ -0,0 +1,73 @@
## Standalone certificates
You can generate certificate that are not tied to containers environment variable by mounting a user configuration file inside the container at `/app/letsencrypt_user_data`. This feature also require sharing the `/etc/nginx/conf.d` folder between the **nginx-proxy** and **acme-companion** container (and the **docker-gen** container if you are running a [three container setup](./Advanced-usage.md)):
```bash
$ docker run --detach \
--name nginx-proxy \
--publish 80:80 \
--publish 443:443 \
--volume certs:/etc/nginx/certs \
--volume vhost:/etc/nginx/vhost.d \
--volume conf:/etc/nginx/conf.d \
--volume html:/usr/share/nginx/html \
--volume /var/run/docker.sock:/tmp/docker.sock:ro \
nginxproxy/nginx-proxy
```
```bash
$ docker run --detach \
--name nginx-proxy-acme \
--volumes-from nginx-proxy \
--volume /var/run/docker.sock:/var/run/docker.sock:ro \
--volume acme:/etc/acme.sh \
--volume /path/to/your/config_file:/app/letsencrypt_user_data:ro \
nginxproxy/acme-companion
```
The user configuration file is a collection of bash variables and array, and follows the syntax of the `/app/letsencrypt_service_data` file that get created by **docker-gen**.
### Required configuration parameters:
`LETSENCRYPT_STANDALONE_CERTS` : a bash array containing identifier(s) for you standalone certificate(s). Each element in the array has to be unique. Those identifiers are internal to the container process and won't ever be visible to the outside world or appear on your certificate.
`LETSENCRYPT_uniqueidentifier_HOST` : a bash array containing domain(s) that will be covered by the certificate corresponding to `uniqueidentifier`.
Each identifier in `LETSENCRYPT_STANDALONE_CERTS` must have its own corresponding `LETSENCRYPT_uniqueidentifier_HOST` array, where the string `uniqueidentifier` has to be identical to that identifier.
**Minimal example generating a single certificate for a single domain:**
```bash
LETSENCRYPT_STANDALONE_CERTS=('uniqueidentifier')
LETSENCRYPT_uniqueidentifier_HOST=('yourdomain.tld')
```
**Example with multiple certificates and domains:**
```bash
LETSENCRYPT_STANDALONE_CERTS=('web' 'app' 'othersite')
LETSENCRYPT_web_HOST=('yourdomain.tld' 'www.yourdomain.tld')
LETSENCRYPT_app_HOST=('myapp.yourdomain.tld' 'myapp.yourotherdomain.tld' 'service.yourotherdomain.tld')
LETSENCRYPT_othersite_HOST=('yetanotherdomain.tld')
```
### Optional configuration parameters:
Those are all single bash variables.
`LETSENCRYPT_uniqueidentifier_EMAIL` : must be a valid email and will be used by Let's Encrypt to warn you of impeding certificate expiration (should the automated renewal fail).
`LETSENCRYPT_uniqueidentifier_KEYSIZE` : determines the size of the requested private key. See [private key size](./Let's-Encrypt-and-ACME.md#private-key-size) for accepted values.
`LETSENCRYPT_uniqueidentifier_TEST` : if set to true, the corresponding certificate will be a test certificates: it won't have the 5 certs/week/domain limits and will be signed by an untrusted intermediate (ie it won't be trusted by browsers).
### Picking up changes to letsencrypt_user_data
The container does not actively watch the `/app/letsencrypt_user_data` file for changes.
Changes will either be picked up every hour when the service loop execute again, or by using `docker exec your-le-container-name-or-id /app/signal_le_service` to manually trigger the service loop execution.
### Proxying to something else than a Docker container
Please see the [**nginx-proxy** documentation](https://github.com/nginx-proxy/nginx-proxy#proxy-wide).
No support will be provided on the **acme-companion** repo for proxying related issues or questions.

22
docs/Zero-SSL.md Normal file
View File

@ -0,0 +1,22 @@
## Zero SSL
[Zero SSL](https://zerossl.com/) is an ACME CA that offer some advantages over Let's Encrypt:
- no staging endpoint and [no rate limiting on the production endpoint](https://zerossl.com/features/acme/).
- web based [management console](https://zerossl.com/features/console/) to keep track of your SSL certificates.
Using Zero SSL through an ACME client, like in this container, allows for unlimited 90 days and multi-domains (SAN) certificates.
### Activation
The Zero SSL support is activated when the `ACME_CA_URI` environment variable is set to the Zero SSL ACME endpoint (`https://acme.zerossl.com/v2/DV90`).
### Account
Unlike Let's Encrypt, Zero SSL requires the use of an email bound account. If you already created a Zero SSL account, you can either:
- provide pre-generated [EAB credentials](https://tools.ietf.org/html/rfc8555#section-7.3.4) using the `ACME_EAB_KID` and `ACME_EAB_HMAC_KEY` environment variables.
- provide your ZeroSSL API key using the `ZEROSSL_API_KEY` environment variable.
These variables can be set on the proxied containers or directly on the **acme-companion** container.
If you don't have a ZeroSSL account, you can let **acme-companion** create a Zero SSL account with the adress provided in the `ACME_EMAIL` or `DEFAULT_EMAIL` environment variable. Note that the adresse that will be used must be a valid email adress that you actually own.

26
install_acme.sh Executable file
View File

@ -0,0 +1,26 @@
#!/bin/bash
set -e
# Install git (required to fetch acme.sh)
apk --no-cache --virtual .acmesh-deps add git
# Get acme.sh ACME client source
mkdir /src
git -C /src clone https://github.com/Neilpang/acme.sh.git
cd /src/acme.sh
if [[ "$ACMESH_VERSION" != "master" ]]; then
git -c advice.detachedHead=false checkout "$ACMESH_VERSION"
fi
# Install acme.sh in /app
./acme.sh --install \
--nocron \
--auto-upgrade 0 \
--home /app \
--config-home /etc/acme.sh/default
# Make house cleaning
cd /
rm -rf /src
apk del .acmesh-deps

View File

@ -1,26 +0,0 @@
#!/bin/bash
set -e
# Install python packages needed to build simp_le
apk --update add python py-setuptools git gcc py-pip musl-dev libffi-dev python-dev openssl-dev
# Get Let's Encrypt simp_le client source
branch="0.11.0"
mkdir -p /src
git -C /src clone --depth=1 --branch $branch https://github.com/zenhack/simp_le.git
# Install simp_le in /usr/bin
cd /src/simp_le
#pip install wheel requests
for pkg in pip distribute setuptools wheel
do
pip install -U "${pkg?}"
done
pip install .
# Make house cleaning
cd /
rm -rf /src
apk del git gcc py-pip musl-dev libffi-dev python-dev openssl-dev
rm -rf /var/cache/apk/*

View File

@ -1,32 +1,32 @@
### letsencrypt-nginx-proxy-companion test suite
### acme-companion test suite
The test suite can be run locally on a Linux or macOS host.
To prepare the test setup:
```bash
git clone https://github.com/JrCs/docker-letsencrypt-nginx-proxy-companion.git
cd docker-letsencrypt-nginx-proxy-companion
git clone https://github.com/nginx-proxy/acme-companion.git
cd acme-companion
test/setup/setup-local.sh --setup
```
Then build the docker image and run the tests:
```bash
docker build -t jrcs/letsencrypt-nginx-proxy-companion .
test/run.sh jrcs/letsencrypt-nginx-proxy-companion
docker build -t nginxproxy/acme-companion .
test/run.sh nginxproxy/acme-companion
```
You can limit the test run to specific test(s) with the `-t` flag:
```bash
test/run.sh -t docker_api jrcs/letsencrypt-nginx-proxy-companion
test/run.sh -t docker_api nginxproxy/acme-companion
```
When running the test suite, the standard output of each individual test is captured and compared to its expected-std-out.txt file. When developing or modifying a test, you can use the `-d` flag to disable the standard output capture by the test suite.
```bash
test/run.sh -d jrcs/letsencrypt-nginx-proxy-companion
test/run.sh -d nginxproxy/acme-companion
```
To remove the test setup:

View File

@ -1,19 +1,35 @@
#!/bin/bash
set -e
testAlias+=(
[jrcs/letsencrypt-nginx-proxy-companion]='le-companion'
)
imageTests+=(
[le-companion]='
globalTests+=(
docker_api
docker_api_legacy
location_config
default_cert
certs_single
certs_san
certs_single_domain
certs_standalone
force_renew
acme_accounts
private_keys
container_restart
permissions_default
permissions_custom
symlinks
'
acme_hooks
)
# The acme_eab test requires Pebble with a specific configuration
if [[ "$ACME_CA" == 'pebble' && "$PEBBLE_CONFIG" == 'pebble-config-eab.json' ]]; then
globalTests+=(
acme_eab
)
fi
# The ocsp_must_staple test does not work with Pebble
if [[ "$ACME_CA" == 'boulder' ]]; then
globalTests+=(
ocsp_must_staple
)
fi

View File

@ -0,0 +1,25 @@
#!/bin/bash
bold_echo() {
echo -e "\033[33;1m$1\033[0m"
}
if [[ -f "$GITHUB_WORKSPACE/test/github_actions/failed_tests.txt" ]]; then
mapfile -t containers < "$GITHUB_WORKSPACE/test/github_actions/failed_tests.txt"
fi
containers+=("$NGINX_CONTAINER_NAME")
[[ $SETUP = "3containers" ]] && containers+=("$DOCKER_GEN_CONTAINER_NAME")
[[ $ACME_CA = "boulder" ]] && containers+=(boulder)
[[ $ACME_CA = "pebble" ]] && containers+=(pebble challtestserv)
for container in "${containers[@]}"; do
bold_echo "Docker container output for $container"
docker logs "$container"
docker inspect "$container"
if [[ "$container" == "acme_accounts" ]]; then
bold_echo "Docker container output for ${container}_default"
docker logs "${container}_default"
docker inspect "${container}_default"
fi
done

View File

@ -198,11 +198,12 @@
set -e
## Next eleven lines were added by jrcs/docker-letsencrypt-nginx-proxy-companion
## Next twelve lines were added by nginxproxy/acme-companion
dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
self="$(basename "$0")"
failed_tests=()
if [[ -z $TRAVIS_CI ]] && [[ -f "$dir/local_test_env.sh" ]]; then
if [[ -z $GITHUB_ACTIONS ]] && [[ -f "$dir/local_test_env.sh" ]]; then
# shellcheck source=/dev/null
source "$dir/local_test_env.sh"
fi
@ -225,7 +226,7 @@ EOUSAGE
}
# arg handling
## Next nine lines were added or modified by jrcs/docker-letsencrypt-nginx-proxy-companion
## Next nine lines were added or modified by nginxproxy/acme-companion
case "$(uname)" in
Linux)
opts="$(getopt -o 'hdkt:c:?' --long 'dry-run,help,test:,config:,keep-namespace' -- "$@" || { usage >&2 && false; })"
@ -246,12 +247,12 @@ while true; do
flag=$1
shift
case "$flag" in
## Next line was modified by jrcs/docker-letsencrypt-nginx-proxy-companion
--dry-run|-d) dryRun=1 ;;
## Next line was modified by nginxproxy/acme-companion
--dry-run|-d) dryRun=1 && export DRY_RUN=1 ;;
--help|-h|'-?') usage && exit 0 ;;
--test|-t) argTests["$1"]=1 && shift ;;
--config|-c) configs+=("$(readlink -f "$1")") && shift ;;
## Next line was modified by jrcs/docker-letsencrypt-nginx-proxy-companion
## Next line was modified by nginxproxy/acme-companion
--keep-namespace|-k) keepNamespace=1 ;;
--) break ;;
*)
@ -284,7 +285,7 @@ fi
# load the configs
declare -A testPaths=()
for conf in "${configs[@]}"; do
## Next two line were modified by jrcs/docker-letsencrypt-nginx-proxy-companion
## Next two line were modified by nginxproxy/acme-companion
# shellcheck source=./config.sh
source "$conf"
## End of modifications
@ -320,7 +321,7 @@ for dockerImage in "$@"; do
#version="${tagVar%-*}"
variant="${tagVar##*-}"
## Irrelevant code was removed here by jrcs/docker-letsencrypt-nginx-proxy-companion
## Irrelevant code was removed here by nginxproxy/acme-companion
testRepo="$repo"
if [ -z "$keepNamespace" ]; then
@ -329,7 +330,7 @@ for dockerImage in "$@"; do
[ -z "${testAlias[$repo]}" ] || testRepo="${testAlias[$repo]}"
explicitVariant=
## Next four lines were modified by jrcs/docker-letsencrypt-nginx-proxy-companion
## Next four lines were modified by nginxproxy/acme-companion
if [ "${explicitTests[:$variant]}" ] \
|| [ "${explicitTests[$repo:$variant]}" ] \
|| [ "${explicitTests[$testRepo:$variant]}" ]
@ -366,14 +367,14 @@ for dockerImage in "$@"; do
tests=()
for t in "${testCandidates[@]}"; do
## Next line was modified by jrcs/docker-letsencrypt-nginx-proxy-companion
## Next line was modified by nginxproxy/acme-companion
if [ ${#argTests[@]} -gt 0 ] && [ -z "${argTests[$t]}" ]; then
## End of modified code
# skipping due to -t
continue
fi
## Next seven lines were modified by jrcs/docker-letsencrypt-nginx-proxy-companion
## Next seven lines were modified by nginxproxy/acme-companion
if [ -n "${globalExcludeTests[${testRepo}_$t]}" ] \
|| [ -n "${globalExcludeTests[${testRepo}:${variant}_$t]}" ] \
|| [ -n "${globalExcludeTests[:${variant}_$t]}" ] \
@ -400,10 +401,10 @@ for dockerImage in "$@"; do
scriptDir="${testPaths[$t]}"
if [ -d "$scriptDir" ]; then
script="$scriptDir/run.sh"
## Next nine lines were modified or added by jrcs/docker-letsencrypt-nginx-proxy-companion
## Next nine lines were modified or added by nginxproxy/acme-companion
if [ -x "$script" ] && [ ! -d "$script" ]; then
if [ $dryRun ]; then
if "$script" $dockerImage; then
if "$script" "$dockerImage"; then
echo 'passed'
else
echo 'failed'
@ -411,16 +412,22 @@ for dockerImage in "$@"; do
fi
else
## End of modified / additional code
if output="$("$script" $dockerImage)"; then
if output="$("$script" "$dockerImage")"; then
if [ -f "$scriptDir/expected-std-out.txt" ] && ! d="$(echo "$output" | diff -u "$scriptDir/expected-std-out.txt" - 2>/dev/null)"; then
echo 'failed; unexpected output:'
echo "$d"
## Next line was added by nginxproxy/acme-companion
failed_tests+=("$(basename "$scriptDir")")
## End of additional code
didFail=1
else
echo 'passed'
fi
else
echo 'failed'
## Next line was added by nginxproxy/acme-companion
failed_tests+=("$(basename "$scriptDir")")
## End of additional code
didFail=1
fi
fi
@ -440,5 +447,12 @@ for dockerImage in "$@"; do
done
if [ "$didFail" ]; then
## Next five lines were added by nginxproxy/acme-companion
if [[ $GITHUB_ACTIONS == 'true' ]]; then
for test in "${failed_tests[@]}"; do
echo "$test" >> "$dir/github_actions/failed_tests.txt"
done
fi
## End of additional code
exit 1
fi

2
test/setup/pebble/.env Normal file
View File

@ -0,0 +1,2 @@
PEBBLE_VERSION='v2.3.1'
PEBBLE_CONFIG='pebble-config.json'

View File

@ -0,0 +1,36 @@
version: '3'
services:
pebble:
image: "letsencrypt/pebble:${PEBBLE_VERSION}"
container_name: pebble
volumes:
- "./${PEBBLE_CONFIG}:/test/config/pebble-config.json"
environment:
- PEBBLE_VA_NOSLEEP=1
command: pebble -config /test/config/pebble-config.json -dnsserver 10.30.50.3:8053
ports:
- 14000:14000 # HTTPS ACME API
- 15000:15000 # HTTPS Management API
networks:
acme_net:
ipv4_address: 10.30.50.2
challtestsrv:
image: "letsencrypt/pebble-challtestsrv:${PEBBLE_VERSION}"
container_name: challtestserv
command: pebble-challtestsrv -tlsalpn01 ""
ports:
- 8055:8055 # HTTP Management API
networks:
acme_net:
ipv4_address: 10.30.50.3
networks:
acme_net:
name: acme_net
driver: bridge
ipam:
driver: default
config:
- subnet: 10.30.50.0/24

View File

@ -0,0 +1,16 @@
{
"pebble": {
"listenAddress": "0.0.0.0:14000",
"managementListenAddress": "0.0.0.0:15000",
"certificate": "test/certs/localhost/cert.pem",
"privateKey": "test/certs/localhost/key.pem",
"httpPort": 80,
"tlsPort": 443,
"ocspResponderURL": "",
"externalAccountBindingRequired": true,
"externalAccountMACKeys": {
"kid-1": "zWNDZM6eQGHWpSRTPal5eIUYFTu7EajVIoguysqZ9wG44nMEtx3MUAsUDkMTQ12W",
"kid-2": "b10lLJs8l1GPIzsLP0s6pMt8O0XVGnfTaCeROxQM0BIt2XrJMDHJZBM5NuQmQJQH"
}
}
}

View File

@ -0,0 +1,12 @@
{
"pebble": {
"listenAddress": "0.0.0.0:14000",
"managementListenAddress": "0.0.0.0:15000",
"certificate": "test/certs/localhost/cert.pem",
"privateKey": "test/certs/localhost/key.pem",
"httpPort": 80,
"tlsPort": 443,
"ocspResponderURL": "",
"externalAccountBindingRequired": false
}
}

View File

@ -0,0 +1,33 @@
#!/bin/bash
set -e
setup_pebble() {
curl --silent --show-error https://raw.githubusercontent.com/letsencrypt/pebble/master/test/certs/pebble.minica.pem > "${GITHUB_WORKSPACE}/pebble.minica.pem"
cat "${GITHUB_WORKSPACE}/pebble.minica.pem"
docker-compose --file "${GITHUB_WORKSPACE}/test/setup/pebble/docker-compose.yml" up --detach
}
wait_for_pebble() {
for endpoint in 'https://pebble:14000/dir' 'http://pebble-challtestsrv:8055'; do
while ! curl --cacert "${GITHUB_WORKSPACE}/pebble.minica.pem" "$endpoint" >/dev/null 2>&1; do
if [ $((i * 5)) -gt $((5 * 60)) ]; then
echo "$endpoint was not available under 5 minutes, timing out."
exit 1
fi
i=$((i + 1))
sleep 5
done
done
}
setup_pebble_challtestserv() {
curl --silent --show-error --data '{"ip":"10.30.50.1"}' http://pebble-challtestsrv:8055/set-default-ipv4
curl --silent --show-error --data '{"ip":""}' http://pebble-challtestsrv:8055/set-default-ipv6
curl --silent --show-error --data '{"host":"lim.it", "addresses":["10.0.0.0"]}' http://pebble-challtestsrv:8055/add-a
}
setup_pebble
wait_for_pebble
setup_pebble_challtestserv
docker-compose --file "${GITHUB_WORKSPACE}/test/setup/pebble/docker-compose.yml" logs

View File

@ -2,21 +2,30 @@
set -e
acme_endpoint='http://boulder:4000/directory'
acme_endpoint='http://boulder:4001/directory'
setup_boulder() {
export GOPATH=${TRAVIS_BUILD_DIR}/go
export GOPATH=${GITHUB_WORKSPACE}/go
[[ ! -d $GOPATH/src/github.com/letsencrypt/boulder ]] \
&& git clone --depth=1 https://github.com/letsencrypt/boulder \
$GOPATH/src/github.com/letsencrypt/boulder
pushd $GOPATH/src/github.com/letsencrypt/boulder
&& git clone https://github.com/letsencrypt/boulder \
"$GOPATH/src/github.com/letsencrypt/boulder"
pushd "$GOPATH/src/github.com/letsencrypt/boulder"
git checkout release-2023-12-04
if [[ "$(uname)" == 'Darwin' ]]; then
sed -i '' 's/ 5002/ 80/g' test/config/va.json
sed -i '' 's/ 5001/ 443/g' test/config/va.json
# Set Standard Ports
for file in test/config/va.json test/config/va-remote-a.json test/config/va-remote-b.json; do
sed -i '' 's/ 5002/ 80/g' "$file"
sed -i '' 's/ 5001/ 443/g' "$file"
done
# Modify custom rate limit
sed -i '' 's/le.wtf,le1.wtf/le1.wtf,le2.wtf,le3.wtf/g' test/rate-limit-policies.yml
else
sed --in-place 's/ 5002/ 80/g' test/config/va.json
sed --in-place 's/ 5001/ 443/g' test/config/va.json
# Set Standard Ports
for file in test/config/va.json test/config/va-remote-a.json test/config/va-remote-b.json; do
sed --in-place 's/ 5002/ 80/g' "$file"
sed --in-place 's/ 5001/ 443/g' "$file"
done
# Modify custom rate limit
sed --in-place 's/le.wtf,le1.wtf/le1.wtf,le2.wtf,le3.wtf/g' test/rate-limit-policies.yml
fi
docker-compose build --pull

View File

@ -1,25 +1,28 @@
#!/bin/bash
set -e
function get_environment {
dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
LOCAL_BUILD_DIR="$(cd "$dir/../.." && pwd)"
export TRAVIS_BUILD_DIR="$LOCAL_BUILD_DIR"
export GITHUB_WORKSPACE="$LOCAL_BUILD_DIR"
# shellcheck source=/dev/null
[[ -f "${TRAVIS_BUILD_DIR}/test/local_test_env.sh" ]] && \
source "${TRAVIS_BUILD_DIR}/test/local_test_env.sh"
[[ -f "${GITHUB_WORKSPACE}/test/local_test_env.sh" ]] && \
source "${GITHUB_WORKSPACE}/test/local_test_env.sh"
# Get the environment variables from the .travis.yml file with sed
declare -a travis_yml
travis_yml[0]="$(sed -n 's/.*- NGINX_CONTAINER_NAME=//p' "$LOCAL_BUILD_DIR/.travis.yml")"
travis_yml[1]="$(sed -n 's/.*- DOCKER_GEN_CONTAINER_NAME=//p' "$LOCAL_BUILD_DIR/.travis.yml")"
travis_yml[2]="$(sed -n 's/.*- TEST_DOMAINS=//p' "$LOCAL_BUILD_DIR/.travis.yml")"
# Get the environment variables from the .github/workflows/test.yml file with sed
declare -a ci_test_yml
ci_test_yml[0]="$(sed -n 's/.* NGINX_CONTAINER_NAME: //p' "$LOCAL_BUILD_DIR/.github/workflows/test.yml")"
ci_test_yml[1]="$(sed -n 's/.* DOCKER_GEN_CONTAINER_NAME: //p' "$LOCAL_BUILD_DIR/.github/workflows/test.yml")"
ci_test_yml[2]="$(sed -n 's/.* TEST_DOMAINS: //p' "$LOCAL_BUILD_DIR/.github/workflows/test.yml")"
# If environment variable where sourced or manually set use them, else use those from .travis.yml
export NGINX_CONTAINER_NAME="${NGINX_CONTAINER_NAME:-${travis_yml[0]}}"
export DOCKER_GEN_CONTAINER_NAME="${DOCKER_GEN_CONTAINER_NAME:-${travis_yml[1]}}"
export TEST_DOMAINS="${TEST_DOMAINS:-${travis_yml[2]}}"
# If environment variable where sourced or manually set use them, else use those from
# .github/workflows/test.yml
export NGINX_CONTAINER_NAME="${NGINX_CONTAINER_NAME:-${ci_test_yml[0]}}"
export DOCKER_GEN_CONTAINER_NAME="${DOCKER_GEN_CONTAINER_NAME:-${ci_test_yml[1]}}"
export TEST_DOMAINS="${TEST_DOMAINS:-${ci_test_yml[2]}}"
# Build the array containing domains to add to /etc/hosts
IFS=',' read -r -a domains <<< "$TEST_DOMAINS"
@ -41,13 +44,44 @@ function get_environment {
break
;;
*)
:
exit 1
;;
esac
done
fi
export SETUP="${SETUP:-$setup}"
if [[ -z $ACME_CA ]]; then
while true; do
echo "Which ACME CA do you want to use or remove ?"
echo ""
echo " 1) Boulder https://github.com/letsencrypt/boulder"
echo " 2) Pebble with base configuration https://github.com/letsencrypt/pebble"
echo " 3) Pebble with EAB configuration https://github.com/letsencrypt/pebble"
read -re -p "Select an option [1-3]: " option
case $option in
1)
acme_ca="boulder"
break
;;
2)
acme_ca="pebble"
pebble_config="pebble-config.json"
break
;;
3)
acme_ca="pebble"
pebble_config="pebble-config-eab.json"
break
;;
*)
exit 1
;;
esac
done
fi
export ACME_CA="${ACME_CA:-$acme_ca}"
export PEBBLE_CONFIG="${PEBBLE_CONFIG:-$pebble_config}"
}
case $1 in
@ -55,19 +89,25 @@ case $1 in
get_environment
# Prepare the env file that run.sh will source
cat > "${TRAVIS_BUILD_DIR}/test/local_test_env.sh" <<EOF
export TRAVIS_BUILD_DIR="$LOCAL_BUILD_DIR"
cat > "${GITHUB_WORKSPACE}/test/local_test_env.sh" <<EOF
export GITHUB_WORKSPACE="$LOCAL_BUILD_DIR"
export NGINX_CONTAINER_NAME="$NGINX_CONTAINER_NAME"
export DOCKER_GEN_CONTAINER_NAME="$DOCKER_GEN_CONTAINER_NAME"
export TEST_DOMAINS="$TEST_DOMAINS"
export SETUP="$SETUP"
export ACME_CA="$ACME_CA"
export PEBBLE_CONFIG="$PEBBLE_CONFIG"
EOF
# Add the required custom entries to /etc/hosts
echo "Adding custom entries to /etc/hosts (requires sudo)."
for domain in "${domains[@]}"; do
grep -q "127.0.0.1 $domain # le-companion test suite" /etc/hosts \
|| echo "127.0.0.1 $domain # le-companion test suite" \
declare -a hosts=("${domains[@]}")
if [[ "$ACME_CA" == 'pebble' ]]; then
hosts+=(pebble pebble-challtestsrv)
fi
for host in "${hosts[@]}"; do
grep -q "127.0.0.1 $host # le-companion test suite" /etc/hosts \
|| echo "127.0.0.1 $host # le-companion test suite" \
| sudo tee -a /etc/hosts
done
@ -75,39 +115,58 @@ EOF
docker pull nginx:alpine
# Prepare the test setup using the setup scripts
"${TRAVIS_BUILD_DIR}/test/setup/setup-nginx-proxy.sh"
"${TRAVIS_BUILD_DIR}/test/setup/setup-boulder.sh"
if [[ "$ACME_CA" == 'boulder' ]]; then
"${GITHUB_WORKSPACE}/test/setup/setup-boulder.sh"
elif [[ "$ACME_CA" == 'pebble' ]]; then
"${GITHUB_WORKSPACE}/test/setup/pebble/setup-pebble.sh"
else
echo "ACME_CA is not set, aborting."
exit 1
fi
"${GITHUB_WORKSPACE}/test/setup/setup-nginx-proxy.sh"
;;
--teardown)
get_environment
# Stop and remove boulder
docker-compose --project-name 'boulder' \
--file "${TRAVIS_BUILD_DIR}/go/src/github.com/letsencrypt/boulder/docker-compose.yml" \
down --volumes
# Stop and remove nginx-proxy and (if required) docker-gen
for cid in $(docker ps -a --filter "label=com.github.jrcs.letsencrypt_nginx_proxy_companion.test_suite" --format "{{.ID}}"); do
for cid in $(docker ps -a --filter "label=com.github.nginx-proxy.acme-companion.test-suite" --format "{{.ID}}"); do
docker stop "$cid"
docker rm --volumes "$cid"
done
if [[ "$ACME_CA" == 'boulder' ]]; then
# Stop and remove Boulder
docker stop boulder
docker-compose --project-name 'boulder' \
--file "${GITHUB_WORKSPACE}/go/src/github.com/letsencrypt/boulder/docker-compose.yml" \
down --volumes
docker rm boulder
elif [[ "$ACME_CA" == 'pebble' ]]; then
# Stop and remove Pebble
docker-compose --file "${GITHUB_WORKSPACE}/test/setup/pebble/docker-compose.yml" down
[[ -f "${GITHUB_WORKSPACE}/pebble.minica.pem" ]] && rm "${GITHUB_WORKSPACE}/pebble.minica.pem"
fi
# Cleanup files created by the setup
if [[ -n "${TRAVIS_BUILD_DIR// }" ]]; then
[[ -f "${TRAVIS_BUILD_DIR}/nginx.tmpl" ]]&& rm "${TRAVIS_BUILD_DIR}/nginx.tmpl"
rm "${TRAVIS_BUILD_DIR}/test/local_test_env.sh"
echo "The ${TRAVIS_BUILD_DIR}/go folder require superuser permission to fully remove."
if [[ -n "${GITHUB_WORKSPACE// }" ]]; then
[[ -f "${GITHUB_WORKSPACE}/nginx.tmpl" ]] && rm "${GITHUB_WORKSPACE}/nginx.tmpl"
rm "${GITHUB_WORKSPACE}/test/local_test_env.sh"
echo "The ${GITHUB_WORKSPACE}/go folder require superuser permission to fully remove."
echo "Doing sudo rm -rf in scripts is dangerous, so the folder won't be automatically removed."
fi
# Remove custom entries to /etc/hosts
echo "Removing custom entries from /etc/hosts (requires sudo)."
for domain in "${domains[@]}"; do
declare -a hosts=("${domains[@]}")
if [[ "$ACME_CA" == 'pebble' ]]; then
hosts+=(pebble pebble-challtestsrv)
fi
for host in "${hosts[@]}"; do
if [[ "$(uname)" == 'Darwin' ]]; then
sudo sed -i '' "/127\.0\.0\.1 $domain # le-companion test suite/d" /etc/hosts
sudo sed -i '' "/127\.0\.0\.1 $host # le-companion test suite/d" /etc/hosts
else
sudo sed --in-place "/127\.0\.0\.1 $domain # le-companion test suite/d" /etc/hosts
sudo sed --in-place "/127\.0\.0\.1 $host # le-companion test suite/d" /etc/hosts
fi
done
;;

View File

@ -2,42 +2,59 @@
set -e
case $ACME_CA in
pebble)
test_net='acme_net'
;;
boulder)
test_net='boulder_bluenet'
;;
*)
echo "$0 $ACME_CA: invalid option."
exit 1
esac
case $SETUP in
2containers)
docker run -d -p 80:80 -p 443:443 \
--name $NGINX_CONTAINER_NAME \
--env "DHPARAM_BITS=256" \
--name "$NGINX_CONTAINER_NAME" \
-v /etc/nginx/certs \
-v /etc/nginx/conf.d \
-v /etc/nginx/vhost.d \
-v /usr/share/nginx/html \
-v /var/run/docker.sock:/tmp/docker.sock:ro \
--label com.github.jrcs.letsencrypt_nginx_proxy_companion.test_suite \
--network boulder_bluenet \
jwilder/nginx-proxy
--label com.github.nginx-proxy.acme-companion.test-suite \
--network "$test_net" \
nginxproxy/nginx-proxy
;;
3containers)
curl https://raw.githubusercontent.com/jwilder/nginx-proxy/master/nginx.tmpl > ${TRAVIS_BUILD_DIR}/nginx.tmpl
curl https://raw.githubusercontent.com/nginx-proxy/nginx-proxy/main/nginx.tmpl > "${GITHUB_WORKSPACE}/nginx.tmpl"
docker run -d -p 80:80 -p 443:443 \
--name $NGINX_CONTAINER_NAME \
-v /etc/nginx/conf.d \
--name "$NGINX_CONTAINER_NAME" \
-v /etc/nginx/certs \
-v /etc/nginx/conf.d \
-v /etc/nginx/vhost.d \
-v /usr/share/nginx/html \
--label com.github.jrcs.letsencrypt_nginx_proxy_companion.test_suite \
--network boulder_bluenet \
--label com.github.nginx-proxy.acme-companion.test-suite \
--network "$test_net" \
nginx:alpine
docker run -d \
--name $DOCKER_GEN_CONTAINER_NAME \
--volumes-from $NGINX_CONTAINER_NAME \
-v ${TRAVIS_BUILD_DIR}/nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl:ro \
--name "$DOCKER_GEN_CONTAINER_NAME" \
--volumes-from "$NGINX_CONTAINER_NAME" \
-v "${GITHUB_WORKSPACE}/nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl:ro" \
-v /var/run/docker.sock:/tmp/docker.sock:ro \
--label com.github.jrcs.letsencrypt_nginx_proxy_companion.test_suite \
--network boulder_bluenet \
jwilder/docker-gen \
-notify-sighup $NGINX_CONTAINER_NAME -watch -wait 5s:30s /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf
--label com.github.nginx-proxy.acme-companion.test-suite \
--network "$test_net" \
nginxproxy/docker-gen \
-notify-sighup "$NGINX_CONTAINER_NAME" -watch /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf
;;
*)

View File

@ -0,0 +1 @@

97
test/tests/acme_accounts/run.sh Executable file
View File

@ -0,0 +1,97 @@
#!/bin/bash
## Test for ACME accounts handling.
if [[ -z $GITHUB_ACTIONS ]]; then
le_container_name="$(basename "${0%/*}")_$(date "+%Y-%m-%d_%H.%M.%S")"
else
le_container_name="$(basename "${0%/*}")"
fi
run_le_container "${1:?}" "$le_container_name"
# Create the $domains array from comma separated domains in TEST_DOMAINS.
IFS=',' read -r -a domains <<< "$TEST_DOMAINS"
# Cleanup function with EXIT trap
function cleanup {
# Remove any remaining Nginx container(s) silently.
for domain in "${domains[@]}"; do
docker rm --force "$domain" &> /dev/null
done
# Cleanup the files created by this run of the test to avoid foiling following test(s).
docker exec "$le_container_name" /app/cleanup_test_artifacts
# Stop the LE container
docker stop "$le_container_name" > /dev/null
}
trap cleanup EXIT
# Run an nginx container for ${domains[0]}.
run_nginx_container --hosts "${domains[0]}"
# Wait for a symlink at /etc/nginx/certs/${domains[0]}.crt
wait_for_symlink "${domains[0]}" "$le_container_name"
# Test if the expected folder / file / content are there.
json_file="/etc/acme.sh/default/ca/$ACME_CA/account.json"
if [[ "$ACME_CA" == 'boulder' ]]; then
no_mail_str='[]'
elif [[ "$ACME_CA" == 'pebble' ]]; then
no_mail_str='null'
fi
if docker exec "$le_container_name" [[ ! -d "/etc/acme.sh/default" ]]; then
echo "The /etc/acme.sh/default folder does not exist."
elif docker exec "$le_container_name" [[ ! -f "$json_file" ]]; then
echo "The $json_file file does not exist."
elif [[ "$(docker exec "$le_container_name" jq .contact "$json_file")" != "$no_mail_str" ]]; then
echo "There is an address set on ${json_file}."
docker exec "$le_container_name" jq . "$json_file"
docker exec "$le_container_name" jq .contact "$json_file"
fi
# Stop the nginx and companion containers silently.
docker stop "${domains[0]}" &> /dev/null
docker stop "$le_container_name" &> /dev/null
# Run the companion container with the DEFAULT_EMAIL env var set.
default_email="contact@${domains[1]}"
le_container_name="${le_container_name}_default"
run_le_container "${1:?}" "$le_container_name" "--env DEFAULT_EMAIL=${default_email}"
# Run an nginx container for ${domains[1]} without LETSENCRYPT_EMAIL set.
run_nginx_container --hosts "${domains[1]}"
# Wait for a symlink at /etc/nginx/certs/${domains[1]}.crt
wait_for_symlink "${domains[1]}" "$le_container_name"
# Test if the expected folder / file / content are there.
json_file="/etc/acme.sh/${default_email}/ca/$ACME_CA/account.json"
if docker exec "$le_container_name" [[ ! -d "/etc/acme.sh/$default_email" ]]; then
echo "The /etc/acme.sh/$default_email folder does not exist."
elif docker exec "$le_container_name" [[ ! -f "$json_file" ]]; then
echo "The $json_file file does not exist."
elif [[ "$(docker exec "$le_container_name" jq -r '.contact|.[0]' "$json_file")" != "mailto:${default_email}" ]]; then
echo "$default_email is not set on ${json_file}."
docker exec "$le_container_name" jq . "$json_file"
fi
# Run an nginx container for ${domains[2]} with LETSENCRYPT_EMAIL set.
container_email="contact@${domains[2]}"
run_nginx_container --hosts "${domains[2]}" --cli-args "--env LETSENCRYPT_EMAIL=${container_email}"
# Wait for a symlink at /etc/nginx/certs/${domains[2]}.crt
wait_for_symlink "${domains[2]}" "$le_container_name"
# Test if the expected folder / file / content are there.
json_file="/etc/acme.sh/${container_email}/ca/$ACME_CA/account.json"
if docker exec "$le_container_name" [[ ! -d "/etc/acme.sh/$container_email" ]]; then
echo "The /etc/acme.sh/$container_email folder does not exist."
elif docker exec "$le_container_name" [[ ! -f "$json_file" ]]; then
echo "The $json_file file does not exist."
elif [[ "$(docker exec "$le_container_name" jq -r '.contact|.[0]' "$json_file")" != "mailto:${container_email}" ]]; then
echo "$default_email is not set on ${json_file}."
docker exec "$le_container_name" jq . "$json_file"
fi
# Stop the nginx containers silently.
docker stop "${domains[1]}" &> /dev/null
docker stop "${domains[2]}" &> /dev/null

View File

@ -0,0 +1 @@

73
test/tests/acme_eab/run.sh Executable file
View File

@ -0,0 +1,73 @@
#!/bin/bash
## Test for ACME External Account Binding (EAB).
declare -A eab=( \
[kid-1]=zWNDZM6eQGHWpSRTPal5eIUYFTu7EajVIoguysqZ9wG44nMEtx3MUAsUDkMTQ12W \
[kid-2]=b10lLJs8l1GPIzsLP0s6pMt8O0XVGnfTaCeROxQM0BIt2XrJMDHJZBM5NuQmQJQH \
)
if [[ -z $GITHUB_ACTIONS ]]; then
le_container_name="$(basename "${0%/*}")_$(date "+%Y-%m-%d_%H.%M.%S")"
else
le_container_name="$(basename "${0%/*}")"
fi
run_le_container "${1:?}" "$le_container_name" \
--cli-args "--env ACME_EAB_KID=kid-1" \
--cli-args "--env ACME_EAB_HMAC_KEY=${eab[kid-1]}"
# Create the $domains array from comma separated domains in TEST_DOMAINS.
IFS=',' read -r -a domains <<< "$TEST_DOMAINS"
# Cleanup function with EXIT trap
function cleanup {
# Remove any remaining Nginx container(s) silently.
for domain in "${domains[@]}"; do
docker rm --force "$domain" &> /dev/null
done
# Cleanup the files created by this run of the test to avoid foiling following test(s).
docker exec "$le_container_name" /app/cleanup_test_artifacts
# Stop the LE container
docker stop "$le_container_name" > /dev/null
}
trap cleanup EXIT
# Run an nginx container for ${domains[0]}.
run_nginx_container --hosts "${domains[0]}"
# Run an nginx container for ${domains[1]} with LETSENCRYPT_EMAIL and ACME_EAB_* set.
container_email="contact@${domains[1]}"
run_nginx_container --hosts "${domains[1]}" \
--cli-args "--env LETSENCRYPT_EMAIL=${container_email}" \
--cli-args "--env ACME_EAB_KID=kid-2" \
--cli-args "--env ACME_EAB_HMAC_KEY=${eab[kid-2]}"
# Wait for a symlink at /etc/nginx/certs/${domains[0]}.crt
wait_for_symlink "${domains[0]}" "$le_container_name"
# Test if the expected file is there.
config_path="/etc/acme.sh/default/ca/$ACME_CA"
json_file="${config_path}/account.json"
conf_file="${config_path}/ca.conf"
if docker exec "$le_container_name" [[ ! -f "$json_file" ]]; then
echo "The $json_file file does not exist."
elif ! docker exec "$le_container_name" grep -q "${eab[kid-1]}" "$conf_file"; then
echo "There correct EAB HMAC key isn't on ${conf_file}."
fi
# Wait for a symlink at /etc/nginx/certs/${domains[1]}.crt
wait_for_symlink "${domains[1]}" "$le_container_name"
# Test if the expected file is there.
config_path="/etc/acme.sh/${container_email}/ca/$ACME_CA"
json_file="${config_path}/account.json"
conf_file="${config_path}/ca.conf"
if docker exec "$le_container_name" [[ ! -f "$json_file" ]]; then
echo "The $json_file file does not exist."
elif ! docker exec "$le_container_name" grep -q "${eab[kid-2]}" "$conf_file"; then
echo "There correct EAB HMAC key isn't on ${conf_file}."
fi
# Stop the nginx containers silently.
docker stop "${domains[0]}" &> /dev/null
docker stop "${domains[1]}" &> /dev/null

View File

@ -0,0 +1 @@

119
test/tests/acme_hooks/run.sh Executable file
View File

@ -0,0 +1,119 @@
#!/bin/bash
## Test for the hooks of acme.sh
default_pre_hook_file="/tmp/default_prehook"
default_pre_hook_command="touch $default_pre_hook_file"
default_post_hook_file="/tmp/default_posthook"
default_post_hook_command="touch $default_post_hook_file"
percontainer_pre_hook_file="/tmp/percontainer_prehook"
percontainer_pre_hook_command="touch $percontainer_pre_hook_file"
percontainer_post_hook_file="/tmp/percontainer_posthook"
percontainer_post_hook_command="touch $percontainer_post_hook_file"
if [[ -z $GITHUB_ACTIONS ]]; then
le_container_name="$(basename "${0%/*}")_$(date "+%Y-%m-%d_%H.%M.%S")"
else
le_container_name="$(basename "${0%/*}")"
fi
run_le_container "${1:?}" "$le_container_name" \
--cli-args "--env ACME_PRE_HOOK=$default_pre_hook_command" \
--cli-args "--env ACME_POST_HOOK=$default_post_hook_command"
# Create the $domains array from comma separated domains in TEST_DOMAINS.
IFS=',' read -r -a domains <<< "$TEST_DOMAINS"
# Cleanup function with EXIT trap
function cleanup {
# Remove the Nginx container silently.
docker rm --force "${domains[0]}" &> /dev/null
# Cleanup the files created by this run of the test to avoid foiling following test(s).
docker exec "$le_container_name" /app/cleanup_test_artifacts
# Stop the LE container
docker stop "$le_container_name" > /dev/null
}
trap cleanup EXIT
container_email="contact@${domains[0]}"
# Run an nginx container for ${domains[0]} with LETSENCRYPT_EMAIL set.
run_nginx_container --hosts "${domains[0]}" \
--cli-args "--env LETSENCRYPT_EMAIL=${container_email}"
# Run an nginx container for ${domains[1]} with LETSENCRYPT_EMAIL, ACME_PRE_HOOK and ACME_POST_HOOK set.
run_nginx_container --hosts "${domains[1]}" \
--cli-args "--env LETSENCRYPT_EMAIL=${container_email}" \
--cli-args "--env ACME_PRE_HOOK=$percontainer_pre_hook_command" \
--cli-args "--env ACME_POST_HOOK=$percontainer_post_hook_command"
# Wait for a symlink at /etc/nginx/certs/${domains[0]}.crt
wait_for_symlink "${domains[0]}" "$le_container_name"
acme_pre_hook_key="Le_PreHook="
acme_post_hook_key="Le_PostHook="
acme_base64_start="'__ACME_BASE64__START_"
acme_base64_end="__ACME_BASE64__END_'"
# Check if the default command is deliverd properly in /etc/acme.sh
if docker exec "$le_container_name" [[ ! -d "/etc/acme.sh/$container_email" ]]; then
echo "The /etc/acme.sh/$container_email folder does not exist."
elif docker exec "$le_container_name" [[ ! -d "/etc/acme.sh/$container_email/${domains[0]}" ]]; then
echo "The /etc/acme.sh/$container_email/${domains[0]} folder does not exist."
elif docker exec "$le_container_name" [[ ! -f "/etc/acme.sh/$container_email/${domains[0]}/${domains[0]}.conf" ]]; then
echo "The /etc/acme.sh/$container_email/${domains[0]}/${domains[0]}.conf file does not exist."
fi
default_pre_hook_command_base64="${acme_pre_hook_key}${acme_base64_start}$(echo -n "$default_pre_hook_command" | base64)${acme_base64_end}"
default_post_hook_command_base64="${acme_post_hook_key}${acme_base64_start}$(echo -n "$default_post_hook_command" | base64)${acme_base64_end}"
default_acme_pre_hook="$(docker exec "$le_container_name" grep "$acme_pre_hook_key" "/etc/acme.sh/$container_email/${domains[0]}/${domains[0]}.conf")"
default_acme_post_hook="$(docker exec "$le_container_name" grep "$acme_post_hook_key" "/etc/acme.sh/$container_email/${domains[0]}/${domains[0]}.conf")"
if [[ "$default_pre_hook_command_base64" != "$default_acme_pre_hook" ]]; then
echo "Default prehook command not saved properly"
fi
if [[ "$default_post_hook_command_base64" != "$default_acme_post_hook" ]]; then
echo "Default posthook command not saved properly"
fi
# Check if the default action is performed
if docker exec "$le_container_name" [[ ! -f "$default_pre_hook_file" ]]; then
echo "Default prehook action failed"
fi
if docker exec "$le_container_name" [[ ! -f "$default_post_hook_file" ]]; then
echo "Default posthook action failed"
fi
# Wait for a symlink at /etc/nginx/certs/${domains[1]}.crt
wait_for_symlink "${domains[1]}" "$le_container_name"
# Check if the per-container command is deliverd properly in /etc/acme.sh
if docker exec "$le_container_name" [[ ! -d "/etc/acme.sh/$container_email/${domains[1]}" ]]; then
echo "The /etc/acme.sh/$container_email/${domains[1]} folder does not exist."
elif docker exec "$le_container_name" [[ ! -f "/etc/acme.sh/$container_email/${domains[1]}/${domains[1]}.conf" ]]; then
echo "The /etc/acme.sh/$container_email/${domains[1]}/${domains[1]}.conf file does not exist."
fi
percontainer_pre_hook_command_base64="${acme_pre_hook_key}${acme_base64_start}$(echo -n "$percontainer_pre_hook_command" | base64)${acme_base64_end}"
percontainer_post_hook_command_base64="${acme_post_hook_key}${acme_base64_start}$(echo -n "$percontainer_post_hook_command" | base64)${acme_base64_end}"
percontainer_acme_pre_hook="$(docker exec "$le_container_name" grep "$acme_pre_hook_key" "/etc/acme.sh/$container_email/${domains[1]}/${domains[1]}.conf")"
percontainer_acme_post_hook="$(docker exec "$le_container_name" grep "$acme_post_hook_key" "/etc/acme.sh/$container_email/${domains[1]}/${domains[1]}.conf")"
if [[ "$percontainer_pre_hook_command_base64" != "$percontainer_acme_pre_hook" ]]; then
echo "Per-container prehook command not saved properly"
fi
if [[ "$percontainer_post_hook_command_base64" != "$percontainer_acme_post_hook" ]]; then
echo "Per-container posthook command not saved properly"
fi
# Check if the percontainer action is performed
if docker exec "$le_container_name" [[ ! -f "$percontainer_pre_hook_file" ]]; then
echo "Per-container prehook action failed"
fi
if docker exec "$le_container_name" [[ ! -f "$percontainer_post_hook_file" ]]; then
echo "Per-container posthook action failed"
fi

View File

@ -1,37 +1 @@
Started letsencrypt container for test certs_san
Started test web server for le1.wtf,le2.wtf,le3.wtf
Symlink to le1.wtf certificate has been generated.
The link is pointing to the file ./le1.wtf/fullchain.pem
le1.wtf is on certificate.
Connection to le1.wtf using https was successful.
The correct certificate for le1.wtf was served by Nginx.
le2.wtf is on certificate.
Connection to le2.wtf using https was successful.
The correct certificate for le2.wtf was served by Nginx.
le3.wtf is on certificate.
Connection to le3.wtf using https was successful.
The correct certificate for le3.wtf was served by Nginx.
Started test web server for le2.wtf, le3.wtf, le1.wtf
Symlink to le2.wtf certificate has been generated.
The link is pointing to the file ./le2.wtf/fullchain.pem
le1.wtf is on certificate.
Connection to le1.wtf using https was successful.
The correct certificate for le1.wtf was served by Nginx.
le2.wtf is on certificate.
Connection to le2.wtf using https was successful.
The correct certificate for le2.wtf was served by Nginx.
le3.wtf is on certificate.
Connection to le3.wtf using https was successful.
The correct certificate for le3.wtf was served by Nginx.
Started test web server for le3.wtf, le1.wtf, le2.wtf,
Symlink to le3.wtf certificate has been generated.
The link is pointing to the file ./le3.wtf/fullchain.pem
le1.wtf is on certificate.
Connection to le1.wtf using https was successful.
The correct certificate for le1.wtf was served by Nginx.
le2.wtf is on certificate.
Connection to le2.wtf using https was successful.
The correct certificate for le2.wtf was served by Nginx.
le3.wtf is on certificate.
Connection to le3.wtf using https was successful.
The correct certificate for le3.wtf was served by Nginx.

View File

@ -2,12 +2,12 @@
## Test for SAN (Subject Alternative Names) certificates.
if [[ -z $TRAVIS_CI ]]; then
le_container_name="$(basename ${0%/*})_$(date "+%Y-%m-%d_%H.%M.%S")"
if [[ -z $GITHUB_ACTIONS ]]; then
le_container_name="$(basename "${0%/*}")_$(date "+%Y-%m-%d_%H.%M.%S")"
else
le_container_name="$(basename ${0%/*})"
le_container_name="$(basename "${0%/*}")"
fi
run_le_container ${1:?} "$le_container_name"
run_le_container "${1:?}" "$le_container_name"
# Create the $domains array from comma separated domains in TEST_DOMAINS.
IFS=',' read -r -a domains <<< "$TEST_DOMAINS"
@ -17,23 +17,25 @@ function cleanup {
# Remove any remaining Nginx container(s) silently.
i=1
for hosts in "${letsencrypt_hosts[@]}"; do
docker rm --force "test$i" > /dev/null 2>&1
i=$(( $i + 1 ))
docker rm --force "test$i" &> /dev/null
i=$(( i + 1 ))
done
# Cleanup the files created by this run of the test to avoid foiling following test(s).
docker exec "$le_container_name" bash -c 'rm -rf /etc/nginx/certs/le?.wtf*'
docker exec "$le_container_name" /app/cleanup_test_artifacts
# Stop the LE container
docker stop "$le_container_name" > /dev/null
}
trap cleanup EXIT
# Create three different comma separated list from the first three domains in $domains.
# testing for regression on spaced lists https://github.com/JrCs/docker-letsencrypt-nginx-proxy-companion/issues/288
# and with trailing comma https://github.com/JrCs/docker-letsencrypt-nginx-proxy-companion/issues/254
# testing for regression on spaced lists https://github.com/nginx-proxy/acme-companion/issues/288
# with trailing comma https://github.com/nginx-proxy/acme-companion/issues/254
# and with trailing dot https://github.com/nginx-proxy/acme-companion/issues/676
letsencrypt_hosts=( \
[0]="${domains[0]},${domains[1]},${domains[2]}" \ #straight comma separated list
[1]="${domains[1]}, ${domains[2]}, ${domains[0]}" \ #comma separated list with spaces
[2]="${domains[2]}, ${domains[0]}, ${domains[1]}," ) #comma separated list with spaces and a trailing comma
[2]="${domains[2]}, ${domains[0]}, ${domains[1]}," \ #comma separated list with spaces and a trailing comma
[3]="${domains[0]}.,${domains[2]}.,${domains[1]}" ) #trailing dots
i=1
@ -44,36 +46,32 @@ for hosts in "${letsencrypt_hosts[@]}"; do
container="test$i"
# Run an Nginx container passing one of the comma separated list as LETSENCRYPT_HOST env var.
docker run --rm -d \
--name "$container" \
-e "VIRTUAL_HOST=${TEST_DOMAINS}" \
-e "LETSENCRYPT_HOST=${hosts}" \
--network boulder_bluenet \
nginx:alpine > /dev/null && echo "Started test web server for $hosts"
run_nginx_container --hosts "$hosts" --name "$container"
# Wait for a symlink at /etc/nginx/certs/$base_domain.crt
# then grab the certificate in text form ...
wait_for_symlink "$base_domain" "$le_container_name"
created_cert="$(docker exec "$le_container_name" \
openssl x509 -in /etc/nginx/certs/${base_domain}/cert.pem -text -noout)"
# ... as well as the certificate fingerprint.
created_cert_fingerprint="$(docker exec "$le_container_name" \
sh -c "openssl x509 -in "/etc/nginx/certs/${base_domain}/cert.pem" -fingerprint -noout")"
if wait_for_symlink "$base_domain" "$le_container_name" "./${base_domain}/fullchain.pem"; then
# then grab the certificate in text form ...
created_cert="$(docker exec "$le_container_name" \
openssl x509 -in "/etc/nginx/certs/${base_domain}/cert.pem" -text -noout)"
# ... as well as the certificate fingerprint.
created_cert_fingerprint="$(docker exec "$le_container_name" \
openssl x509 -in "/etc/nginx/certs/${base_domain}/cert.pem" -fingerprint -noout)"
fi
for domain in "${domains[@]}"; do
## For all the domains in the $domains array ...
# Check if the domain is on the certificate.
if grep -q "$domain" <<< "$created_cert"; then
echo "$domain is on certificate."
else
if ! grep -q "$domain" <<< "$created_cert"; then
echo "$domain did not appear on certificate."
elif [[ "${DRY_RUN:-}" == 1 ]]; then
echo "$domain is on certificate."
fi
# Wait for a connection to https://domain then grab the served certificate in text form.
wait_for_conn --domain "$domain"
served_cert_fingerprint="$(echo \
| openssl s_client -showcerts -servername $domain -connect $domain:443 2>/dev/null \
| openssl s_client -showcerts -servername "$domain" -connect "$domain:443" 2>/dev/null \
| openssl x509 -fingerprint -noout)"
@ -85,14 +83,14 @@ for hosts in "${letsencrypt_hosts[@]}"; do
| openssl s_client -showcerts -servername "$domain" -connect "$domain:443" 2>/dev/null \
| openssl x509 -text -noout \
| sed 's/ = /=/g' )"
diff -u <(echo "$created_cert" | sed 's/ = /=/g') <(echo "$served_cert")
else
echo "The correct certificate for $domain was served by Nginx."
diff -u <(echo "${created_cert// = /=}") <(echo "$served_cert")
elif [[ "${DRY_RUN:-}" == 1 ]]; then
echo "The correct certificate for $domain was served by Nginx."
fi
done
docker stop "$container" > /dev/null 2>&1
docker exec "$le_container_name" bash -c 'rm -rf /etc/nginx/certs/le?.wtf*'
i=$(( $i + 1 ))
docker stop "$container" &> /dev/null
docker exec "$le_container_name" /app/cleanup_test_artifacts
i=$(( i + 1 ))
done

View File

@ -1,19 +1 @@
Started letsencrypt container for test certs_single
Started test web server for le1.wtf
Started test web server for le2.wtf
Started test web server for le3.wtf
Symlink to le1.wtf certificate has been generated.
The link is pointing to the file ./le1.wtf/fullchain.pem
Domain le1.wtf is on certificate.
Connection to le1.wtf using https was successful.
The correct certificate for le1.wtf was served by Nginx.
Symlink to le2.wtf certificate has been generated.
The link is pointing to the file ./le2.wtf/fullchain.pem
Domain le2.wtf is on certificate.
Connection to le2.wtf using https was successful.
The correct certificate for le2.wtf was served by Nginx.
Symlink to le3.wtf certificate has been generated.
The link is pointing to the file ./le3.wtf/fullchain.pem
Domain le3.wtf is on certificate.
Connection to le3.wtf using https was successful.
The correct certificate for le3.wtf was served by Nginx.

View File

@ -2,12 +2,12 @@
## Test for single domain certificates.
if [[ -z $TRAVIS_CI ]]; then
le_container_name="$(basename ${0%/*})_$(date "+%Y-%m-%d_%H.%M.%S")"
if [[ -z $GITHUB_ACTIONS ]]; then
le_container_name="$(basename "${0%/*}")_$(date "+%Y-%m-%d_%H.%M.%S")"
else
le_container_name="$(basename ${0%/*})"
le_container_name="$(basename "${0%/*}")"
fi
run_le_container ${1:?} "$le_container_name"
run_le_container "${1:?}" "$le_container_name"
# Create the $domains array from comma separated domains in TEST_DOMAINS.
IFS=',' read -r -a domains <<< "$TEST_DOMAINS"
@ -16,10 +16,10 @@ IFS=',' read -r -a domains <<< "$TEST_DOMAINS"
function cleanup {
# Remove any remaining Nginx container(s) silently.
for domain in "${domains[@]}"; do
docker rm --force "$domain" > /dev/null 2>&1
docker rm --force "$domain" &> /dev/null
done
# Cleanup the files created by this run of the test to avoid foiling following test(s).
docker exec "$le_container_name" bash -c 'rm -rf /etc/nginx/certs/le?.wtf*'
docker exec "$le_container_name" /app/cleanup_test_artifacts
# Stop the LE container
docker stop "$le_container_name" > /dev/null
}
@ -28,30 +28,27 @@ trap cleanup EXIT
# Run a separate nginx container for each domain in the $domains array.
# Start all the containers in a row so that docker-gen debounce timers fire only once.
for domain in "${domains[@]}"; do
docker run --rm -d \
--name "$domain" \
-e "VIRTUAL_HOST=${domain}" \
-e "LETSENCRYPT_HOST=${domain}" \
--network boulder_bluenet \
nginx:alpine > /dev/null && echo "Started test web server for $domain"
run_nginx_container --hosts "$domain"
done
for domain in "${domains[@]}"; do
# Wait for a symlink at /etc/nginx/certs/$domain.crt
# then grab the certificate in text form from the file ...
wait_for_symlink "$domain" "$le_container_name"
created_cert="$(docker exec "$le_container_name" \
sh -c "openssl x509 -in "/etc/nginx/certs/${domain}/cert.pem" -text -noout")"
# ... as well as the certificate fingerprint.
created_cert_fingerprint="$(docker exec "$le_container_name" \
sh -c "openssl x509 -in "/etc/nginx/certs/${domain}/cert.pem" -fingerprint -noout")"
if wait_for_symlink "$domain" "$le_container_name" "./${domain}/fullchain.pem" ; then
# then grab the certificate in text form from the file ...
created_cert="$(docker exec "$le_container_name" \
openssl x509 -in "/etc/nginx/certs/${domain}/cert.pem" -text -noout)"
# ... as well as the certificate fingerprint.
created_cert_fingerprint="$(docker exec "$le_container_name" \
openssl x509 -in "/etc/nginx/certs/${domain}/cert.pem" -fingerprint -noout)"
fi
# Check if the domain is on the certificate.
if grep -q "$domain" <<< "$created_cert"; then
echo "Domain $domain is on certificate."
else
if ! grep -q "$domain" <<< "$created_cert"; then
echo "Domain $domain isn't on certificate."
elif [[ "${DRY_RUN:-}" == 1 ]]; then
echo "Domain $domain is on certificate."
fi
# Wait for a connection to https://domain then grab the served certificate fingerprint.
@ -68,8 +65,8 @@ for domain in "${domains[@]}"; do
| openssl s_client -showcerts -servername "$domain" -connect "$domain:443" 2>/dev/null \
| openssl x509 -text -noout \
| sed 's/ = /=/g' )"
diff -u <(echo "$created_cert" | sed 's/ = /=/g') <(echo "$served_cert")
else
diff -u <(echo "${created_cert// = /=}") <(echo "$served_cert")
elif [[ "${DRY_RUN:-}" == 1 ]]; then
echo "The correct certificate for $domain was served by Nginx."
fi

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,102 @@
#!/bin/bash
## Test for spliting SAN certificates into single domain certificates by NGINX container env variables
if [[ -z $GITHUB_ACTIONS ]]; then
le_container_name="$(basename "${0%/*}")_$(date "+%Y-%m-%d_%H.%M.%S")"
else
le_container_name="$(basename "${0%/*}")"
fi
run_le_container "${1:?}" "$le_container_name"
# Create the $domains array from comma separated domains in TEST_DOMAINS.
IFS=',' read -r -a domains <<< "$TEST_DOMAINS"
# Cleanup function with EXIT trap
function cleanup {
# Remove any remaining Nginx container(s) silently.
i=1
for hosts in "${letsencrypt_hosts[@]}"; do
docker rm --force "test$i" &> /dev/null
i=$(( i + 1 ))
done
# Cleanup the files created by this run of the test to avoid foiling following test(s).
docker exec "$le_container_name" /app/cleanup_test_artifacts
# Stop the LE container
docker stop "$le_container_name" > /dev/null
}
trap cleanup EXIT
# Create three different comma separated list from the first three domains in $domains.
# testing for regression on spaced lists https://github.com/nginx-proxy/acme-companion/issues/288
# and with trailing comma https://github.com/nginx-proxy/acme-companion/issues/254
letsencrypt_hosts=( \
[0]="${domains[0]},${domains[1]},${domains[2]}" \ #straight comma separated list
[1]="${domains[1]}, ${domains[2]}, ${domains[0]}" \ #comma separated list with spaces
[2]="${domains[2]}, ${domains[0]}, ${domains[1]}," ) #comma separated list with spaces and a trailing comma
i=1
for hosts in "${letsencrypt_hosts[@]}"; do
container="test$i"
# Run an Nginx container passing one of the comma separated list as LETSENCRYPT_HOST env var.
run_nginx_container --hosts "${hosts}" --name "$container" --cli-args "--env LETSENCRYPT_SINGLE_DOMAIN_CERTS=true"
for domain in "${domains[@]}"; do
## For all the domains in the $domains array ...
# Wait for a symlink at /etc/nginx/certs/$domain.crt
if wait_for_symlink "${domain}" "$le_container_name" "./${domain}/fullchain.pem"; then
# then grab the certificate in text form from the file ...
created_cert="$(docker exec "$le_container_name" \
openssl x509 -in "/etc/nginx/certs/${domain}/cert.pem" -text -noout)"
# ... as well as the certificate fingerprint.
created_cert_fingerprint="$(docker exec "$le_container_name" \
openssl x509 -in "/etc/nginx/certs/${domain}/cert.pem" -fingerprint -noout)"
fi
# Check if the domain is on the certificate.
if grep -q "$domain" <<< "$created_cert"; then
if [[ "${DRY_RUN:-}" == 1 ]]; then
echo "$domain is on certificate."
fi
for otherdomain in "${domains[@]}"; do
if [ "$domain" != "$otherdomain" ]; then
if grep -q "$otherdomain" <<< "$created_cert"; then
echo "$otherdomain is on certificate for $domain, but it must not!"
elif [[ "${DRY_RUN:-}" == 1 ]]; then
echo "$otherdomain did not appear on certificate for $domain."
fi
fi
done
else
echo "$domain did not appear on certificate."
fi
# Wait for a connection to https://domain then grab the served certificate in text form.
wait_for_conn --domain "$domain"
served_cert_fingerprint="$(echo \
| openssl s_client -showcerts -servername "$domain" -connect "$domain:443" 2>/dev/null \
| openssl x509 -fingerprint -noout)"
# Compare the cert on file and what we got from the https connection.
# If not identical, display a full diff.
if [ "$created_cert_fingerprint" != "$served_cert_fingerprint" ]; then
echo "Nginx served an incorrect certificate for $domain."
served_cert="$(echo \
| openssl s_client -showcerts -servername "$domain" -connect "$domain:443" 2>/dev/null \
| openssl x509 -text -noout \
| sed 's/ = /=/g' )"
diff -u <(echo "${created_cert// = /=}") <(echo "$served_cert")
elif [[ "${DRY_RUN:-}" == 1 ]]; then
echo "The correct certificate for $domain was served by Nginx."
fi
done
docker stop "$container" &> /dev/null
docker exec "$le_container_name" /app/cleanup_test_artifacts --default-cert
i=$(( i + 1 ))
done

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,112 @@
#!/bin/bash
## Test for standalone certificates.
case $ACME_CA in
pebble)
test_net='acme_net'
;;
boulder)
test_net='boulder_bluenet'
;;
*)
echo "$0 $ACME_CA: invalid option."
exit 1
esac
if [[ -z $GITHUB_ACTIONS ]]; then
le_container_name="$(basename "${0%/*}")_$(date "+%Y-%m-%d_%H.%M.%S")"
else
le_container_name="$(basename "${0%/*}")"
fi
# Create the $domains array from comma separated domains in TEST_DOMAINS.
IFS=',' read -r -a domains <<< "$TEST_DOMAINS"
subdomain="sub.${domains[0]}"
# Cleanup function with EXIT trap
function cleanup {
# Remove the Nginx container silently.
docker rm --force "$subdomain" &> /dev/null
# Cleanup the files created by this run of the test to avoid foiling following test(s).
docker exec "$le_container_name" /app/cleanup_test_artifacts
# Stop the LE container
docker stop "$le_container_name" > /dev/null
}
trap cleanup EXIT
# Create letsencrypt_user_data with a single domain cert
cat > "${GITHUB_WORKSPACE}/test/tests/certs_standalone/letsencrypt_user_data" <<EOF
LETSENCRYPT_STANDALONE_CERTS=('single')
LETSENCRYPT_single_HOST=('${domains[0]}')
EOF
# Run an nginx container with a VIRTUAL_HOST set to a subdomain of ${domains[0]} in order to check for
# this regression : https://github.com/nginx-proxy/acme-companion/issues/674
if ! docker run --rm -d \
--name "$subdomain" \
-e "VIRTUAL_HOST=$subdomain" \
--network "$test_net" \
nginx:alpine > /dev/null;
then
echo "Could not start test web server for $subdomain"
elif [[ "${DRY_RUN:-}" == 1 ]]; then
echo "Started test web server for $subdomain"
fi
run_le_container "${1:?}" "$le_container_name" \
"--volume ${GITHUB_WORKSPACE}/test/tests/certs_standalone/letsencrypt_user_data:/app/letsencrypt_user_data"
# Wait for a file at /etc/nginx/conf.d/standalone-cert-${domains[0]}.conf
wait_for_standalone_conf "${domains[0]}" "$le_container_name"
# Wait for a symlink at /etc/nginx/certs/${domains[0]}.crt
if wait_for_symlink "${domains[0]}" "$le_container_name"; then
# then grab the certificate in text form ...
created_cert="$(docker exec "$le_container_name" \
openssl x509 -in "/etc/nginx/certs/${domains[0]}/cert.pem" -text -noout)"
fi
# Check if the domain is on the certificate.
if ! grep -q "${domains[0]}" <<< "$created_cert"; then
echo "Domain ${domains[0]} did not appear on certificate."
elif [[ "${DRY_RUN:-}" == 1 ]]; then
echo "Domain ${domains[0]} is on certificate."
fi
docker exec "$le_container_name" bash -c "[[ -f /etc/nginx/conf.d/standalone-cert-${domains[0]}.conf ]]" \
&& echo "Standalone configuration for ${domains[0]} wasn't correctly removed."
# Add another (SAN) certificate to letsencrypt_user_data
cat > "${GITHUB_WORKSPACE}/test/tests/certs_standalone/letsencrypt_user_data" <<EOF
LETSENCRYPT_STANDALONE_CERTS=('single' 'san')
LETSENCRYPT_single_HOST=('${domains[0]}')
LETSENCRYPT_san_HOST=('${domains[1]}' '${domains[2]}')
EOF
# Manually trigger the service loop
docker exec "$le_container_name" /app/signal_le_service > /dev/null
for domain in "${domains[1]}" "${domains[2]}"; do
# Wait for a file at /etc/nginx/conf.d/standalone-cert-$domain.conf
wait_for_standalone_conf "$domain" "$le_container_name"
done
# Wait for a symlink at /etc/nginx/certs/${domains[1]}.crt
if wait_for_symlink "${domains[1]}" "$le_container_name"; then
# then grab the certificate in text form ...
created_cert="$(docker exec "$le_container_name" \
openssl x509 -in "/etc/nginx/certs/${domains[1]}/cert.pem" -text -noout)"
fi
for domain in "${domains[1]}" "${domains[2]}"; do
# Check if the domain is on the certificate.
if ! grep -q "$domain" <<< "$created_cert"; then
echo "Domain $domain did not appear on certificate."
elif [[ "${DRY_RUN:-}" == 1 ]]; then
echo "Domain $domain is on certificate."
fi
done
docker exec "$le_container_name" bash -c "[[ ! -f /etc/nginx/conf.d/standalone-cert-${domains[1]}.conf ]]" \
|| echo "Standalone configuration for ${domains[1]} wasn't correctly removed."

View File

@ -0,0 +1,3 @@
Container le1.wtf restarted
Container le2.wtf restarted
Container le3.wtf restarted

View File

@ -0,0 +1,52 @@
#!/bin/bash
## Test for LETSENCRYPT_RESTART_CONTAINER variable.
if [[ -z $GITHUB_ACTIONS ]]; then
le_container_name="$(basename "${0%/*}")_$(date "+%Y-%m-%d_%H.%M.%S")"
else
le_container_name="$(basename "${0%/*}")"
fi
run_le_container "${1:?}" "$le_container_name"
# Create the $domains array from comma separated domains in TEST_DOMAINS.
IFS=',' read -r -a domains <<< "$TEST_DOMAINS"
# Listen for Docker restart events
docker events \
--filter event=restart \
--format 'Container {{.Actor.Attributes.name}} restarted' > "${GITHUB_WORKSPACE}/test/tests/container_restart/docker_event_out.txt" &
docker_events_pid=$!
# Cleanup function with EXIT trap
function cleanup {
# Kill the Docker events listener
kill $docker_events_pid && wait $docker_events_pid 2>/dev/null
# Remove temporary files
rm -f "${GITHUB_WORKSPACE}/test/tests/container_restart/docker_event_out.txt"
# Remove any remaining Nginx container(s) silently.
for domain in "${domains[@]}"; do
docker rm --force "$domain" &> /dev/null
done
# Cleanup the files created by this run of the test to avoid foiling following test(s).
docker exec "$le_container_name" /app/cleanup_test_artifacts
# Stop the LE container
docker stop "$le_container_name" > /dev/null
}
trap cleanup EXIT
# Run a separate nginx container for each domain in the $domains array.
for domain in "${domains[@]}"; do
run_nginx_container --hosts "$domain" --cli-args "--env LETSENCRYPT_RESTART_CONTAINER=true"
# Check if container restarted
timeout="$(date +%s)"
timeout="$((timeout + 120))"
until grep "$domain" "${GITHUB_WORKSPACE}"/test/tests/container_restart/docker_event_out.txt; do
if [[ "$(date +%s)" -gt "$timeout" ]]; then
echo "Container $domain didn't restart in under one minute."
break
fi
sleep 0.1
done
done

View File

@ -1,7 +1 @@
Started letsencrypt container for test default_cert
Connection to le1.wtf using https was successful.
Connection to le2.wtf using https was successful.
Connection to le3.wtf using https was successful.
Connection to le1.wtf using https was successful.
Connection to le2.wtf using https was successful.
Connection to le3.wtf using https was successful.

View File

@ -2,12 +2,12 @@
## Test for default certificate creation.
if [[ -z $TRAVIS_CI ]]; then
le_container_name="$(basename ${0%/*})_$(date "+%Y-%m-%d_%H.%M.%S")"
if [[ -z $GITHUB_ACTIONS ]]; then
le_container_name="$(basename "${0%/*}")_$(date "+%Y-%m-%d_%H.%M.%S")"
else
le_container_name="$(basename ${0%/*})"
le_container_name="$(basename "${0%/*}")"
fi
run_le_container ${1:?} "$le_container_name"
run_le_container "${1:?}" "$le_container_name"
# Create the $domains array from comma separated domains in TEST_DOMAINS.
IFS=',' read -r -a domains <<< "$TEST_DOMAINS"
@ -15,28 +15,37 @@ IFS=',' read -r -a domains <<< "$TEST_DOMAINS"
# Cleanup function with EXIT trap
function cleanup {
# Cleanup the files created by this run of the test to avoid foiling following test(s).
docker exec "$le_container_name" bash -c 'rm -f /etc/nginx/certs/default.*'
docker exec "$le_container_name" /app/cleanup_test_artifacts --default-cert
docker stop "$le_container_name" > /dev/null
}
trap cleanup EXIT
function check_default_cert_existence {
docker exec "$le_container_name" [[ -f "/etc/nginx/certs/default.crt" ]]
}
function default_cert_fingerprint {
docker exec "$le_container_name" openssl x509 -in "/etc/nginx/certs/default.crt" -fingerprint -noout
if check_default_cert_existence; then
docker exec "$le_container_name" openssl x509 -in "/etc/nginx/certs/default.crt" -fingerprint -noout
fi
}
function default_cert_subject {
docker exec "$le_container_name" openssl x509 -in "/etc/nginx/certs/default.crt" -subject -noout
if check_default_cert_existence; then
docker exec "$le_container_name" openssl x509 -in "/etc/nginx/certs/default.crt" -subject -noout
fi
}
user_cn="user-provided"
i=0
timeout="$(date +%s)"
timeout="$((timeout + 120))"
until docker exec "$le_container_name" [[ -f /etc/nginx/certs/default.crt ]]; do
if [ $i -gt 60 ]; then
if [[ "$(date +%s)" -gt "$timeout" ]]; then
echo "Default cert wasn't created under one minute at container first launch."
break
fi
i=$((i + 2))
sleep 2
sleep 0.1
done
# Connection test to unconfigured domains
@ -48,16 +57,16 @@ done
# the certificate or private key file are deleted
for file in 'default.key' 'default.crt'; do
old_default_cert_fingerprint="$(default_cert_fingerprint)"
docker exec "$le_container_name" rm -f /etc/nginx/certs/$file
docker restart "$le_container_name" > /dev/null && sleep 5
i=0
docker exec "$le_container_name" /app/cleanup_test_artifacts --default-cert
docker restart "$le_container_name" > /dev/null
timeout="$(date +%s)"
timeout="$((timeout + 120))"
while [[ "$(default_cert_fingerprint)" == "$old_default_cert_fingerprint" ]]; do
if [ $i -gt 55 ]; then
if [[ "$(date +%s)" -gt "$timeout" ]]; then
echo "Default cert wasn't re-created under one minute after $file deletion."
break
fi
i=$((i + 2))
sleep 2
sleep 0.1
done
done
@ -68,17 +77,17 @@ docker exec "$le_container_name" openssl req -x509 \
-newkey rsa:4096 -sha256 -nodes -days 60 \
-subj "/CN=letsencrypt-nginx-proxy-companion" \
-keyout /etc/nginx/certs/default.key \
-out /etc/nginx/certs/default.crt > /dev/null 2>&1
-out /etc/nginx/certs/default.crt &> /dev/null
old_default_cert_fingerprint="$(default_cert_fingerprint)"
docker restart "$le_container_name" > /dev/null && sleep 5
i=0
docker restart "$le_container_name" > /dev/null && sleep 10
timeout="$(date +%s)"
timeout="$((timeout + 110))"
while [[ "$(default_cert_fingerprint)" == "$old_default_cert_fingerprint" ]]; do
if [ $i -gt 55 ]; then
if [[ "$(date +%s)" -gt "$timeout" ]]; then
echo "Default cert wasn't re-created under one minute when the certificate expire in less than three months."
break
fi
i=$((i + 2))
sleep 2
sleep 0.1
done
# Test that a user provided default certificate isn't overwrited
@ -87,7 +96,7 @@ docker exec "$le_container_name" openssl req -x509 \
-newkey rsa:4096 -sha256 -nodes -days 60 \
-subj "/CN=$user_cn" \
-keyout /etc/nginx/certs/default.key \
-out /etc/nginx/certs/default.crt > /dev/null 2>&1
-out /etc/nginx/certs/default.crt &> /dev/null
docker restart "$le_container_name" > /dev/null
# Connection test to unconfigured domains

View File

@ -14,16 +14,19 @@ case $SETUP in
# Cleanup function with EXIT trap
function cleanup {
# Kill the Docker events listener
kill $docker_events_pid && wait $docker_events_pid 2>/dev/null
kill "$docker_events_pid" && wait "$docker_events_pid" 2>/dev/null
# Remove the remaining containers silently
docker rm --force \
"$nginx_vol" \
"$nginx_env" \
"$nginx_lbl" \
> /dev/null 2>&1
&> /dev/null
}
trap cleanup EXIT
# Set the commands to be passed to docker exec
commands='source /app/functions.sh; reload_nginx > /dev/null; check_nginx_proxy_container_run; get_nginx_proxy_container'
# Listen to Docker exec_start events
docker events \
--filter event=exec_start \
@ -34,13 +37,13 @@ case $SETUP in
docker run --rm -d \
--name "$nginx_vol" \
-v /var/run/docker.sock:/tmp/docker.sock:ro \
jwilder/nginx-proxy > /dev/null
nginxproxy/nginx-proxy > /dev/null
# Run a nginx-proxy container named nginx-env-var, without the nginx_proxy label
docker run --rm -d \
--name "$nginx_env" \
-v /var/run/docker.sock:/tmp/docker.sock:ro \
jwilder/nginx-proxy > /dev/null
nginxproxy/nginx-proxy > /dev/null
# This should target the nginx-proxy container obtained with
# the --volume-from argument (nginx-volumes-from)
@ -48,10 +51,7 @@ case $SETUP in
-v /var/run/docker.sock:/var/run/docker.sock:ro \
--volumes-from "$nginx_vol" \
"$1" \
bash -c 'source /app/functions.sh; \
reload_nginx > /dev/null; \
check_nginx_proxy_container_run; \
get_nginx_proxy_container' 2>&1
bash -c "$commands" 2>&1
# This should target the nginx-proxy container obtained with
# the NGINX_PROXY_CONTAINER environment variable (nginx-env-var)
@ -60,18 +60,15 @@ case $SETUP in
--volumes-from "$nginx_vol" \
-e "NGINX_PROXY_CONTAINER=$nginx_env" \
"$1" \
bash -c 'source /app/functions.sh; \
reload_nginx > /dev/null; \
check_nginx_proxy_container_run; \
get_nginx_proxy_container' 2>&1
bash -c "$commands" 2>&1
# Run a nginx-proxy container named nginx-label, with the nginx_proxy label.
# Store the container id in the labeled_nginx_cid variable.
labeled_nginx_cid="$(docker run --rm -d \
--name "$nginx_lbl" \
-v /var/run/docker.sock:/tmp/docker.sock:ro \
--label com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy \
jwilder/nginx-proxy)"
--label com.github.nginx-proxy.nginx \
nginxproxy/nginx-proxy)"
# This should target the nginx-proxy container with the label (nginx-label)
docker run --rm \
@ -79,12 +76,9 @@ case $SETUP in
--volumes-from "$nginx_vol" \
-e "NGINX_PROXY_CONTAINER=$nginx_env" \
"$1" \
bash -c 'source /app/functions.sh; \
reload_nginx > /dev/null; \
check_nginx_proxy_container_run; \
get_nginx_proxy_container' 2>&1
bash -c "$commands" 2>&1
cat > ${TRAVIS_BUILD_DIR}/test/tests/docker_api/expected-std-out.txt <<EOF
cat > "${GITHUB_WORKSPACE}/test/tests/docker_api/expected-std-out.txt" <<EOF
Container $nginx_vol received exec_start: sh -c /app/docker-entrypoint.sh /usr/local/bin/docker-gen /app/nginx.tmpl /etc/nginx/conf.d/default.conf; /usr/sbin/nginx -s reload
$nginx_vol
Container $nginx_env received exec_start: sh -c /app/docker-entrypoint.sh /usr/local/bin/docker-gen /app/nginx.tmpl /etc/nginx/conf.d/default.conf; /usr/sbin/nginx -s reload
@ -106,10 +100,13 @@ EOF
"$nginx_lbl" \
"$docker_gen" \
"$docker_gen_lbl" \
> /dev/null 2>&1
&> /dev/null
}
trap cleanup EXIT
# Set the commands to be passed to docker exec
commands='source /app/functions.sh; reload_nginx > /dev/null; check_nginx_proxy_container_run; get_docker_gen_container; get_nginx_proxy_container'
# Listen to Docker kill events
docker events \
--filter event=kill \
@ -142,11 +139,7 @@ EOF
--volumes-from "$nginx_vol" \
-e "NGINX_DOCKER_GEN_CONTAINER=$docker_gen" \
"$1" \
bash -c 'source /app/functions.sh; \
reload_nginx > /dev/null; \
check_nginx_proxy_container_run; \
get_docker_gen_container; \
get_nginx_proxy_container;' 2>&1
bash -c "$commands" 2>&1
# This should target the nginx container whose id or name was obtained with
# the NGINX_PROXY_CONTAINER environment variable (nginx-env-var)
@ -158,16 +151,12 @@ EOF
-e "NGINX_PROXY_CONTAINER=$nginx_env" \
-e "NGINX_DOCKER_GEN_CONTAINER=$docker_gen" \
"$1" \
bash -c 'source /app/functions.sh; \
reload_nginx > /dev/null; \
check_nginx_proxy_container_run; \
get_docker_gen_container; \
get_nginx_proxy_container;' 2>&1
bash -c "$commands" 2>&1
# Spawn a nginx container named nginx-label, with the nginx_proxy label.
labeled_nginx1_cid="$(docker run --rm -d \
--name "$nginx_lbl" \
--label com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy \
--label com.github.nginx-proxy.nginx \
nginx:alpine)"
# This should target the nginx container whose id or name was obtained with
@ -180,18 +169,14 @@ EOF
-e "NGINX_PROXY_CONTAINER=$nginx_env" \
-e "NGINX_DOCKER_GEN_CONTAINER=$docker_gen" \
"$1" \
bash -c 'source /app/functions.sh; \
reload_nginx > /dev/null; \
check_nginx_proxy_container_run; \
get_docker_gen_container; \
get_nginx_proxy_container;' 2>&1
bash -c "$commands" 2>&1
docker stop "$nginx_lbl" > /dev/null
# Spawn a "fake docker-gen" container named docker-gen-label, with the docker_gen label.
labeled_docker_gen_cid="$(docker run --rm -d \
--name "$docker_gen_lbl" \
--label com.github.jrcs.letsencrypt_nginx_proxy_companion.docker_gen \
--label com.github.nginx-proxy.docker-gen \
nginx:alpine)"
# This should target the nginx container whose id or name was obtained with
@ -203,11 +188,7 @@ EOF
--volumes-from "$nginx_vol" \
-e "NGINX_DOCKER_GEN_CONTAINER=$docker_gen" \
"$1" \
bash -c 'source /app/functions.sh; \
reload_nginx > /dev/null; \
check_nginx_proxy_container_run; \
get_docker_gen_container; \
get_nginx_proxy_container;' 2>&1
bash -c "$commands" 2>&1
# This should target the nginx container whose id or name was obtained with
# the NGINX_PROXY_CONTAINER environment variable (nginx-env-var)
@ -219,16 +200,12 @@ EOF
-e "NGINX_PROXY_CONTAINER=$nginx_env" \
-e "NGINX_DOCKER_GEN_CONTAINER=$docker_gen" \
"$1" \
bash -c 'source /app/functions.sh; \
reload_nginx > /dev/null; \
check_nginx_proxy_container_run; \
get_docker_gen_container; \
get_nginx_proxy_container;' 2>&1
bash -c "$commands" 2>&1
# Spawn a nginx container named nginx-label, with the nginx_proxy label.
labeled_nginx2_cid="$(docker run --rm -d \
--name "$nginx_lbl" \
--label com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy \
--label com.github.nginx-proxy.nginx \
nginx:alpine)"
# This should target the nginx container whose id or name was obtained with
@ -241,13 +218,9 @@ EOF
-e "NGINX_PROXY_CONTAINER=$nginx_env" \
-e "NGINX_DOCKER_GEN_CONTAINER=$docker_gen" \
"$1" \
bash -c 'source /app/functions.sh; \
reload_nginx > /dev/null; \
check_nginx_proxy_container_run; \
get_docker_gen_container; \
get_nginx_proxy_container;' 2>&1
bash -c "$commands" 2>&1
cat > ${TRAVIS_BUILD_DIR}/test/tests/docker_api/expected-std-out.txt <<EOF
cat > "${GITHUB_WORKSPACE}/test/tests/docker_api/expected-std-out.txt" <<EOF
Container $docker_gen received signal 1
Container $nginx_vol received signal 1
$docker_gen
@ -260,7 +233,7 @@ Container $docker_gen received signal 1
Container $nginx_lbl received signal 1
$docker_gen
$labeled_nginx1_cid
Container $nginx_lbl received signal 15
Container $nginx_lbl received signal 3
Container $docker_gen_lbl received signal 1
Container $nginx_vol received signal 1
$labeled_docker_gen_cid

View File

@ -0,0 +1,252 @@
#!/bin/bash
## Test for the Docker API with legacy labels.
nginx_vol='nginx-volumes-from-legacy'
nginx_env='nginx-env-var-legacy'
nginx_lbl='nginx-label-legacy'
docker_gen='docker-gen-no-label-legacy'
docker_gen_lbl='docker-gen-label-legacy'
case $SETUP in
2containers)
# Cleanup function with EXIT trap
function cleanup {
# Kill the Docker events listener
kill "$docker_events_pid" && wait "$docker_events_pid" 2>/dev/null
# Remove the remaining containers silently
docker rm --force \
"$nginx_vol" \
"$nginx_env" \
"$nginx_lbl" \
&> /dev/null
}
trap cleanup EXIT
# Set the commands to be passed to docker exec
commands='source /app/functions.sh; reload_nginx > /dev/null; check_nginx_proxy_container_run; get_nginx_proxy_container'
# Listen to Docker exec_start events
docker events \
--filter event=exec_start \
--format 'Container {{.Actor.Attributes.name}} received {{.Action}}' &
docker_events_pid=$!
# Run a nginx-proxy container named nginx-volumes-from, without the nginx_proxy label
docker run --rm -d \
--name "$nginx_vol" \
-v /var/run/docker.sock:/tmp/docker.sock:ro \
nginxproxy/nginx-proxy > /dev/null
# Run a nginx-proxy container named nginx-env-var, without the nginx_proxy label
docker run --rm -d \
--name "$nginx_env" \
-v /var/run/docker.sock:/tmp/docker.sock:ro \
nginxproxy/nginx-proxy > /dev/null
# This should target the nginx-proxy container obtained with
# the --volume-from argument (nginx-volumes-from)
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
--volumes-from "$nginx_vol" \
"$1" \
bash -c "$commands" 2>&1
# This should target the nginx-proxy container obtained with
# the NGINX_PROXY_CONTAINER environment variable (nginx-env-var)
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
--volumes-from "$nginx_vol" \
-e "NGINX_PROXY_CONTAINER=$nginx_env" \
"$1" \
bash -c "$commands" 2>&1
# Run a nginx-proxy container named nginx-label, with the nginx_proxy label.
# Store the container id in the labeled_nginx_cid variable.
labeled_nginx_cid="$(docker run --rm -d \
--name "$nginx_lbl" \
-v /var/run/docker.sock:/tmp/docker.sock:ro \
--label com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy \
nginxproxy/nginx-proxy)"
# This should target the nginx-proxy container with the label (nginx-label)
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
--volumes-from "$nginx_vol" \
-e "NGINX_PROXY_CONTAINER=$nginx_env" \
"$1" \
bash -c "$commands" 2>&1
cat > "${GITHUB_WORKSPACE}/test/tests/docker_api_legacy/expected-std-out.txt" <<EOF
Container $nginx_vol received exec_start: sh -c /app/docker-entrypoint.sh /usr/local/bin/docker-gen /app/nginx.tmpl /etc/nginx/conf.d/default.conf; /usr/sbin/nginx -s reload
$nginx_vol
Container $nginx_env received exec_start: sh -c /app/docker-entrypoint.sh /usr/local/bin/docker-gen /app/nginx.tmpl /etc/nginx/conf.d/default.conf; /usr/sbin/nginx -s reload
$nginx_env
Container $nginx_lbl received exec_start: sh -c /app/docker-entrypoint.sh /usr/local/bin/docker-gen /app/nginx.tmpl /etc/nginx/conf.d/default.conf; /usr/sbin/nginx -s reload
$labeled_nginx_cid
EOF
;;
3containers)
# Cleanup function with EXIT trap
function cleanup {
# Kill the Docker events listener
kill $docker_events_pid && wait $docker_events_pid 2>/dev/null
# Remove the remaining containers silently
docker stop \
"$nginx_vol" \
"$nginx_env" \
"$nginx_lbl" \
"$docker_gen" \
"$docker_gen_lbl" \
&> /dev/null
}
trap cleanup EXIT
# Set the commands to be passed to docker exec
commands='source /app/functions.sh; reload_nginx > /dev/null; check_nginx_proxy_container_run; get_docker_gen_container; get_nginx_proxy_container'
# Listen to Docker kill events
docker events \
--filter event=kill \
--format 'Container {{.Actor.Attributes.name}} received signal {{.Actor.Attributes.signal}}' &
docker_events_pid=$!
# Run a nginx container named nginx-volumes-from, without the nginx_proxy label.
docker run --rm -d \
--name "$nginx_vol" \
-v /var/run/docker.sock:/tmp/docker.sock:ro \
nginx:alpine > /dev/null
# Run a nginx container named nginx-env-var, without the nginx_proxy label.
docker run --rm -d \
--name "$nginx_env" \
-v /var/run/docker.sock:/tmp/docker.sock:ro \
nginx:alpine > /dev/null
# Spawn a "fake docker-gen" container named docker-gen-nolabel, without the docker_gen label.
docker run --rm -d \
--name "$docker_gen" \
nginx:alpine > /dev/null
# This should target the nginx container whose id or name was obtained with
# the --volumes-from argument (nginx-volumes-from)
# and the docker-gen container whose id or name was obtained with
# the NGINX_DOCKER_GEN_CONTAINER environment variable (docker-gen-nolabel).
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
--volumes-from "$nginx_vol" \
-e "NGINX_DOCKER_GEN_CONTAINER=$docker_gen" \
"$1" \
bash -c "$commands" 2>&1
# This should target the nginx container whose id or name was obtained with
# the NGINX_PROXY_CONTAINER environment variable (nginx-env-var)
# and the docker-gen container whose id or name was obtained with
# the NGINX_DOCKER_GEN_CONTAINER environment variable (docker-gen-nolabel)
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
--volumes-from "$nginx_vol" \
-e "NGINX_PROXY_CONTAINER=$nginx_env" \
-e "NGINX_DOCKER_GEN_CONTAINER=$docker_gen" \
"$1" \
bash -c "$commands" 2>&1
# Spawn a nginx container named nginx-label, with the nginx_proxy label.
labeled_nginx1_cid="$(docker run --rm -d \
--name "$nginx_lbl" \
--label com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy \
nginx:alpine)"
# This should target the nginx container whose id or name was obtained with
# the nginx_proxy label (nginx-label)
# and the docker-gen container whose id or name was obtained with
# the NGINX_DOCKER_GEN_CONTAINER environment variable (docker-gen-nolabel)
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
--volumes-from "$nginx_vol" \
-e "NGINX_PROXY_CONTAINER=$nginx_env" \
-e "NGINX_DOCKER_GEN_CONTAINER=$docker_gen" \
"$1" \
bash -c "$commands" 2>&1
docker stop "$nginx_lbl" > /dev/null
# Spawn a "fake docker-gen" container named docker-gen-label, with the docker_gen label.
labeled_docker_gen_cid="$(docker run --rm -d \
--name "$docker_gen_lbl" \
--label com.github.jrcs.letsencrypt_nginx_proxy_companion.docker_gen \
nginx:alpine)"
# This should target the nginx container whose id or name was obtained with
# the --volumes-from argument (nginx-volumes-from)
# and the docker-gen container whose id or name was obtained with
# the docker_gen label (docker-gen-label)
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
--volumes-from "$nginx_vol" \
-e "NGINX_DOCKER_GEN_CONTAINER=$docker_gen" \
"$1" \
bash -c "$commands" 2>&1
# This should target the nginx container whose id or name was obtained with
# the NGINX_PROXY_CONTAINER environment variable (nginx-env-var)
# and the docker-gen container whose id or name was obtained with
# the docker_gen label (docker-gen-label)
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
--volumes-from "$nginx_vol" \
-e "NGINX_PROXY_CONTAINER=$nginx_env" \
-e "NGINX_DOCKER_GEN_CONTAINER=$docker_gen" \
"$1" \
bash -c "$commands" 2>&1
# Spawn a nginx container named nginx-label, with the nginx_proxy label.
labeled_nginx2_cid="$(docker run --rm -d \
--name "$nginx_lbl" \
--label com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy \
nginx:alpine)"
# This should target the nginx container whose id or name was obtained with
# the nginx_proxy label (nginx-label)
# and the docker-gen container whose id or name was obtained with
# the docker_gen label (docker-gen-label)
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
--volumes-from "$nginx_vol" \
-e "NGINX_PROXY_CONTAINER=$nginx_env" \
-e "NGINX_DOCKER_GEN_CONTAINER=$docker_gen" \
"$1" \
bash -c "$commands" 2>&1
cat > "${GITHUB_WORKSPACE}/test/tests/docker_api_legacy/expected-std-out.txt" <<EOF
Container $docker_gen received signal 1
Container $nginx_vol received signal 1
$docker_gen
$nginx_vol
Container $docker_gen received signal 1
Container $nginx_env received signal 1
$docker_gen
$nginx_env
Container $docker_gen received signal 1
Container $nginx_lbl received signal 1
$docker_gen
$labeled_nginx1_cid
Container $nginx_lbl received signal 3
Container $docker_gen_lbl received signal 1
Container $nginx_vol received signal 1
$labeled_docker_gen_cid
$nginx_vol
Container $docker_gen_lbl received signal 1
Container $nginx_env received signal 1
$labeled_docker_gen_cid
$nginx_env
Container $docker_gen_lbl received signal 1
Container $nginx_lbl received signal 1
$labeled_docker_gen_cid
$labeled_nginx2_cid
EOF
;;
esac

View File

@ -1,5 +1 @@
Started letsencrypt container for test force_renew
Started test web server for le1.wtf
Symlink to le1.wtf certificate has been generated.
The link is pointing to the file ./le1.wtf/fullchain.pem
Certificate for le1.wtf was correctly renewed.

View File

@ -2,12 +2,12 @@
## Test for the /app/force_renew script.
if [[ -z $TRAVIS_CI ]]; then
le_container_name="$(basename ${0%/*})_$(date "+%Y-%m-%d_%H.%M.%S")"
if [[ -z $GITHUB_ACTIONS ]]; then
le_container_name="$(basename "${0%/*}")_$(date "+%Y-%m-%d_%H.%M.%S")"
else
le_container_name="$(basename ${0%/*})"
le_container_name="$(basename "${0%/*}")"
fi
run_le_container ${1:?} "$le_container_name"
run_le_container "${1:?}" "$le_container_name"
# Create the $domains array from comma separated domains in TEST_DOMAINS.
IFS=',' read -r -a domains <<< "$TEST_DOMAINS"
@ -15,21 +15,16 @@ IFS=',' read -r -a domains <<< "$TEST_DOMAINS"
# Cleanup function with EXIT trap
function cleanup {
# Remove the Nginx container silently.
docker rm --force "${domains[0]}" > /dev/null 2>&1
docker rm --force "${domains[0]}" &> /dev/null
# Cleanup the files created by this run of the test to avoid foiling following test(s).
docker exec "$le_container_name" bash -c 'rm -rf /etc/nginx/certs/le?.wtf*'
docker exec "$le_container_name" /app/cleanup_test_artifacts
# Stop the LE container
docker stop "$le_container_name" > /dev/null
}
trap cleanup EXIT
# Run a nginx container for ${domains[0]}.
docker run --rm -d \
--name "${domains[0]}" \
-e "VIRTUAL_HOST=${domains[0]}" \
-e "LETSENCRYPT_HOST=${domains[0]}" \
--network boulder_bluenet \
nginx:alpine > /dev/null && echo "Started test web server for ${domains[0]}"
run_nginx_container --hosts "${domains[0]}"
# Wait for a symlink at /etc/nginx/certs/${domains[0]}.crt
# Grab the expiration time of the certificate
@ -41,13 +36,13 @@ sleep 5
# Issue a forced renewal
# Grab the expiration time of the renewed certificate
docker exec "$le_container_name" /app/force_renew > /dev/null 2>&1
docker exec "$le_container_name" /app/force_renew &> /dev/null
second_cert_expire="$(get_cert_expiration_epoch "${domains[0]}" "$le_container_name")"
if [[ $second_cert_expire -gt $first_cert_expire ]]; then
echo "Certificate for ${domains[0]} was correctly renewed."
else
if ! [[ $second_cert_expire -gt $first_cert_expire ]]; then
echo "Certificate for ${domains[0]} was not correctly renewed."
echo "First certificate expiration epoch : $first_cert_expire."
echo "Second certificate expiration epoch : $second_cert_expire."
elif [[ "${DRY_RUN:-}" == 1 ]]; then
echo "Certificate for ${domains[0]} was correctly renewed."
fi

View File

@ -0,0 +1,9 @@
*.bar.baz.com.example.com
*.baz.com.example.com
*.com.example.com
*.example.com
foo.bar.baz.com.example.*
foo.bar.baz.com.*
foo.bar.baz.*
foo.bar.*
foo.*

171
test/tests/location_config/run.sh Executable file
View File

@ -0,0 +1,171 @@
#!/bin/bash
## Test for automatic location configuration.
# Set variables
test_comment='### This is a test comment'
vhost_path='/etc/nginx/vhost.d'
# Create custom location configuration file to be bind mounted
location_file="${GITHUB_WORKSPACE}/test/tests/location_config/le2.wtf"
echo "$test_comment" > "$location_file"
# Create le1.wtf configuration file, *.le3.wtf and test.* from inside the nginx container
docker exec "$NGINX_CONTAINER_NAME" sh -c "echo '### This is a test comment' > /etc/nginx/vhost.d/le1.wtf"
docker exec "$NGINX_CONTAINER_NAME" sh -c "echo '### This is a test comment' > /etc/nginx/vhost.d/\*.example.com"
docker exec "$NGINX_CONTAINER_NAME" sh -c "echo '### This is a test comment' > /etc/nginx/vhost.d/test.\*"
# Zero the default configuration file.
docker exec "$NGINX_CONTAINER_NAME" sh -c "echo '' > /etc/nginx/vhost.d/default"
if [[ -z $GITHUB_ACTIONS ]]; then
le_container_name="$(basename "${0%/*}")_$(date "+%Y-%m-%d_%H.%M.%S")"
else
le_container_name="$(basename "${0%/*}")"
fi
run_le_container "${1:?}" "$le_container_name" "--volume $location_file:$vhost_path/le2.wtf"
# Create the $domains array from comma separated domains in TEST_DOMAINS.
IFS=',' read -r -a domains <<< "$TEST_DOMAINS"
# Cleanup function with EXIT trap
function cleanup {
# Cleanup the files created by this run of the test to avoid foiling following test(s).
docker exec "$le_container_name" /app/cleanup_test_artifacts --location-config
# Stop the LE container
docker stop "$le_container_name" > /dev/null
}
trap cleanup EXIT
# Check if the ACME location configuration was correctly applied (ie only once) to the target file
function check_location {
local container="${1:?}"
local path="${2:?}"
local start_comment='## Start of configuration add by letsencrypt container'
local end_comment='## End of configuration add by letsencrypt container'
if [[ "$(docker exec "$container" grep -c "$start_comment" "$path")" != 1 ]]; then
return 1
elif [[ "$(docker exec "$container" grep -c "$end_comment" "$path")" != 1 ]]; then
return 1
else
return 0
fi
}
# check the wildcard location enumeration function
docker exec "$le_container_name" bash -c 'source /app/functions.sh; enumerate_wildcard_locations foo.bar.baz.com.example.com'
# default configuration file should be empty
config_path="$vhost_path/default"
if docker exec "$le_container_name" [ ! -s "$config_path" ]; then
echo "$config_path should be empty at container startup:"
docker exec "$le_container_name" cat "$config_path"
fi
# custom configuration files should only contains the test comment
for domain in "${domains[@]:0:2}" '*.example.com' 'test.*'; do
config_path="$vhost_path/$domain"
if check_location "$le_container_name" "$config_path"; then
echo "Unexpected location configuration on $config_path at container startup:"
docker exec "$le_container_name" cat "$config_path"
elif ! docker exec "$le_container_name" grep -q "$test_comment" "$config_path"; then
echo "$config_path should have test comment at container startup:"
docker exec "$le_container_name" cat "$config_path"
fi
done
# le3.wtf configuration file should not exist
config_path="$vhost_path/${domains[2]}"
if docker exec "$le_container_name" [ -e "$config_path" ]; then
echo "$config_path should not exist at container startup :"
docker exec "$le_container_name" ls -lh "$config_path"
docker exec "$le_container_name" cat "$config_path"
fi
# Add default location configuration then check
config_path="$vhost_path/default"
docker exec "$le_container_name" bash -c 'source /app/functions.sh; add_location_configuration'
if ! check_location "$le_container_name" "$config_path" ; then
echo "Unexpected location configuration on $config_path after call to add_location_configuration:"
docker exec "$le_container_name" cat "$config_path"
fi
# Add le1.wtf and le2.wtf location configurations then check
for domain in "${domains[@]:0:2}"; do
config_path="$vhost_path/$domain"
docker exec "$le_container_name" bash -c "source /app/functions.sh; add_location_configuration $domain"
if ! check_location "$le_container_name" "$config_path" ; then
echo "Unexpected location configuration on $config_path after call to add_location_configuration $domain:"
docker exec "$le_container_name" cat "$config_path"
elif ! docker exec "$le_container_name" grep -q "$test_comment" "$config_path"; then
echo "$config_path should still have test comment after call to add_location_configuration $domain:"
docker exec "$le_container_name" cat "$config_path"
fi
done
# Adding subdomain.example.com location configurations should use the *.example.com file
domain="subdomain.example.com"
config_path="$vhost_path/*.example.com"
docker exec "$le_container_name" bash -c "source /app/functions.sh; add_location_configuration $domain"
if ! check_location "$le_container_name" "$config_path" ; then
echo "Unexpected location configuration on $config_path after call to add_location_configuration $domain:"
docker exec "$le_container_name" cat "$config_path"
elif ! docker exec "$le_container_name" grep -q "$test_comment" "$config_path"; then
echo "$config_path should still have test comment after call to add_location_configuration $domain:"
docker exec "$le_container_name" cat "$config_path"
fi
# Adding test.domain.tld location configurations should use the test.* file
domain="test.domain.tld"
config_path="$vhost_path/test.*"
docker exec "$le_container_name" bash -c "source /app/functions.sh; add_location_configuration $domain"
if ! check_location "$le_container_name" "$config_path" ; then
echo "Unexpected location configuration on $config_path after call to add_location_configuration $domain:"
docker exec "$le_container_name" cat "$config_path"
elif ! docker exec "$le_container_name" grep -q "$test_comment" "$config_path"; then
echo "$config_path should still have test comment after call to add_location_configuration $domain:"
docker exec "$le_container_name" cat "$config_path"
fi
# Remove all location configurations
docker exec "$le_container_name" bash -c "source /app/functions.sh; remove_all_location_configurations"
# default configuration file should be empty again
config_path="$vhost_path/default"
if docker exec "$le_container_name" [ ! -s "$config_path" ]; then
echo "$config_path should be empty after call to remove_all_location_configurations:"
docker exec "$le_container_name" cat "$config_path"
fi
# Custom configuration files should have reverted to only containing the test comment
for domain in "${domains[@]:0:2}" '*.example.com' 'test.*'; do
config_path="$vhost_path/$domain"
if check_location "$le_container_name" "$config_path"; then
echo "Unexpected location configuration on $config_path after call to remove_all_location_configurations:"
docker exec "$le_container_name" cat "$config_path"
elif ! docker exec "$le_container_name" grep -q "$test_comment" "$config_path"; then
echo "$config_path should still have test comment after call to remove_all_location_configurations:"
docker exec "$le_container_name" cat "$config_path"
fi
done
# Should not be used by anything, but potentially matches an enumerate_wildcard_locations file glob.
docker exec "$NGINX_CONTAINER_NAME" touch /etc/nginx/vhost.d/le3.pizza
docker exec "$le_container_name" touch le3.pizza
# Trying to add location configuration to non existing le3.wtf should only configure default
docker exec "$le_container_name" bash -c "source /app/functions.sh; add_location_configuration ${domains[2]}"
config_path="$vhost_path/${domains[2]}"
if docker exec "$le_container_name" [ -e "$config_path" ]; then
echo "$config_path should not exist after call to add_location_configuration ${domains[2]}:"
docker exec "$le_container_name" ls -lh "$config_path"
docker exec "$le_container_name" cat "$config_path"
fi
config_path="$vhost_path/default"
if ! check_location "$le_container_name" "$config_path" ; then
echo "Unexpected location configuration on $config_path after call to add_location_configuration ${domains[2]}:"
docker exec "$le_container_name" cat "$config_path"
fi

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,52 @@
#!/bin/bash
## Test for OCSP Must-Staple extension.
if [[ -z $GITHUB_ACTIONS ]]; then
le_container_name="$(basename "${0%/*}")_$(date "+%Y-%m-%d_%H.%M.%S")"
else
le_container_name="$(basename "${0%/*}")"
fi
run_le_container "${1:?}" "$le_container_name"
# Create the $domains array from comma separated domains in TEST_DOMAINS.
IFS=',' read -r -a domains <<< "$TEST_DOMAINS"
# Cleanup function with EXIT trap
function cleanup {
# Remove any remaining Nginx container(s) silently.
for domain in "${domains[0]}" "${domains[1]}"; do
docker rm --force "$domain" &> /dev/null
done
# Cleanup the files created by this run of the test to avoid foiling following test(s).
docker exec "$le_container_name" /app/cleanup_test_artifacts
# Stop the LE container
docker stop "$le_container_name" > /dev/null
}
trap cleanup EXIT
# Run an nginx container with ACME_OCSP=true
run_nginx_container --hosts "${domains[0]}" --cli-args "--env ACME_OCSP=true"
# Run an second nginx container without ACME_OCSP=true
run_nginx_container --hosts "${domains[1]}"
# Wait for the symlink to the ${domains[0]} certificate
wait_for_symlink "${domains[0]}" "$le_container_name"
# Check if the OCSP Must-Staple extension is present in the ${domains[0]} certificate
if docker exec "$le_container_name" openssl x509 -in "/etc/nginx/certs/${domains[0]}/cert.pem" -text -noout | grep -q -E '1\.3\.6\.1\.5\.5\.7\.1\.24|status_request'; then
[[ "${DRY_RUN:-}" == 1 ]] && echo "The OCSP Must-Staple extension is present on the ${domains[0]} certificate."
else
echo "The OCSP Must-Staple extension is absent from the ${domains[0]} certificate."
fi
# Wait for the symlink to the ${domains[1]} certificate
wait_for_symlink "${domains[1]}" "$le_container_name"
# Check if the OCSP Must-Staple extension is absent from the ${domains[1]} certificate
if docker exec "$le_container_name" openssl x509 -in "/etc/nginx/certs/${domains[1]}/cert.pem" -text -noout | grep -q -E '1\.3\.6\.1\.5\.5\.7\.1\.24|status_request'; then
echo "The OCSP Must-Staple extension is present on the ${domains[1]} certificate."
elif [[ "${DRY_RUN:-}" == 1 ]]; then
echo "The OCSP Must-Staple extension is absent from the ${domains[1]} certificate."
fi

View File

@ -1,4 +1 @@
Started letsencrypt container for test permissions_custom
Started test web server for le1.wtf
Symlink to le1.wtf certificate has been generated.
The link is pointing to the file ./le1.wtf/fullchain.pem

View File

@ -4,15 +4,15 @@
files_uid=1000
files_gid=1001
files_perms=640
files_perms=644
folders_perms=750
if [[ -z $TRAVIS_CI ]]; then
le_container_name="$(basename ${0%/*})_$(date "+%Y-%m-%d_%H.%M.%S")"
if [[ -z $GITHUB_ACTIONS ]]; then
le_container_name="$(basename "${0%/*}")_$(date "+%Y-%m-%d_%H.%M.%S")"
else
le_container_name="$(basename ${0%/*})"
le_container_name="$(basename "${0%/*}")"
fi
run_le_container ${1:?} "$le_container_name" \
run_le_container "${1:?}" "$le_container_name" \
"--env FILES_UID=$files_uid --env FILES_GID=$files_gid --env FILES_PERMS=$files_perms --env FOLDERS_PERMS=$folders_perms"
# Create the $domains array from comma separated domains in TEST_DOMAINS.
@ -21,30 +21,23 @@ IFS=',' read -r -a domains <<< "$TEST_DOMAINS"
# Cleanup function with EXIT trap
function cleanup {
# Remove the ${domains[0]} Nginx container silently.
docker rm --force "${domains[0]}" > /dev/null 2>&1
docker rm --force "${domains[0]}" &> /dev/null
# Cleanup the files created by this run of the test to avoid foiling following test(s).
docker exec "$le_container_name" bash -c 'rm -rf /etc/nginx/certs/le?.wtf*'
docker exec "$le_container_name" /app/cleanup_test_artifacts
# Stop the LE container
docker stop "$le_container_name" > /dev/null
}
trap cleanup EXIT
# Run an nginx container for ${domains[0]}.
docker run --rm -d \
--name "${domains[0]}" \
-e "VIRTUAL_HOST=${domains[0]}" \
-e "LETSENCRYPT_HOST=${domains[0]}" \
nginx:alpine > /dev/null && echo "Started test web server for ${domains[0]}"
run_nginx_container --hosts "${domains[0]}"
# Wait for the cert symlink.
wait_for_symlink "${domains[0]}" "$le_container_name"
# Array of folder paths to test
folders=( \
[0]="/etc/nginx/certs/accounts" \
[1]="/etc/nginx/certs/accounts/boulder:4000" \
[2]="/etc/nginx/certs/accounts/boulder:4000/directory" \
[3]="/etc/nginx/certs/${domains[0]}" \
[0]="/etc/nginx/certs/${domains[0]}" \
)
# Test folder paths
@ -55,11 +48,27 @@ for folder in "${folders[@]}"; do
fi
done
# Array of symlinks paths to test
symlinks=( \
[0]="/etc/nginx/certs/${domains[0]}.crt" \
[1]="/etc/nginx/certs/${domains[0]}.key" \
[2]="/etc/nginx/certs/${domains[0]}.chain.pem" \
[3]="/etc/nginx/certs/${domains[0]}.dhparam.pem" \
)
# Test symlinks paths
for symlink in "${symlinks[@]}"; do
ownership="$(docker exec "$le_container_name" stat -c %u:%g "$symlink")"
if [[ "$ownership" != ${files_uid}:${files_gid} ]]; then
echo "Expected ${files_uid}:${files_gid} on ${symlink}, found ${ownership}."
fi
done
# Array of private file paths to test
private_files=( \
[0]="/etc/nginx/certs/default.key" \
[1]="/etc/nginx/certs/accounts/boulder:4000/directory/default.json" \
[2]="/etc/nginx/certs/${domains[0]}/key.pem" \
[1]="/etc/nginx/certs/${domains[0]}/key.pem" \
[2]="/etc/acme.sh/default/${domains[0]}/${domains[0]}.key" \
)
# Test private file paths
@ -72,11 +81,12 @@ done
# Array of public files paths to test
public_files=( \
[0]="/etc/nginx/certs/${domains[0]}/cert.pem" \
[1]="/etc/nginx/certs/${domains[0]}/chain.pem" \
[2]="/etc/nginx/certs/${domains[0]}/fullchain.pem" \
[3]="/etc/nginx/certs/default.crt" \
[4]="/etc/nginx/certs/dhparam.pem" \
[0]="/etc/nginx/certs/${domains[0]}/.companion" \
[1]="/etc/nginx/certs/${domains[0]}/cert.pem" \
[2]="/etc/nginx/certs/${domains[0]}/chain.pem" \
[3]="/etc/nginx/certs/${domains[0]}/fullchain.pem" \
[4]="/etc/nginx/certs/default.crt" \
[5]="/etc/nginx/certs/dhparam.pem" \
)
# Test public file paths

View File

@ -1,4 +1 @@
Started letsencrypt container for test permissions_default
Started test web server for le1.wtf
Symlink to le1.wtf certificate has been generated.
The link is pointing to the file ./le1.wtf/fullchain.pem

View File

@ -2,12 +2,12 @@
## Test for sensitive files and folders permissions
if [[ -z $TRAVIS_CI ]]; then
le_container_name="$(basename ${0%/*})_$(date "+%Y-%m-%d_%H.%M.%S")"
if [[ -z $GITHUB_ACTIONS ]]; then
le_container_name="$(basename "${0%/*}")_$(date "+%Y-%m-%d_%H.%M.%S")"
else
le_container_name="$(basename ${0%/*})"
le_container_name="$(basename "${0%/*}")"
fi
run_le_container ${1:?} "$le_container_name"
run_le_container "${1:?}" "$le_container_name"
# Create the $domains array from comma separated domains in TEST_DOMAINS.
IFS=',' read -r -a domains <<< "$TEST_DOMAINS"
@ -15,30 +15,23 @@ IFS=',' read -r -a domains <<< "$TEST_DOMAINS"
# Cleanup function with EXIT trap
function cleanup {
# Remove the ${domains[0]} Nginx container silently.
docker rm --force "${domains[0]}" > /dev/null 2>&1
docker rm --force "${domains[0]}" &> /dev/null
# Cleanup the files created by this run of the test to avoid foiling following test(s).
docker exec "$le_container_name" bash -c 'rm -rf /etc/nginx/certs/le?.wtf*'
docker exec "$le_container_name" /app/cleanup_test_artifacts
# Stop the LE container
docker stop "$le_container_name" > /dev/null
}
trap cleanup EXIT
# Run an nginx container for ${domains[0]}.
docker run --rm -d \
--name "${domains[0]}" \
-e "VIRTUAL_HOST=${domains[0]}" \
-e "LETSENCRYPT_HOST=${domains[0]}" \
nginx:alpine > /dev/null && echo "Started test web server for ${domains[0]}"
run_nginx_container --hosts "${domains[0]}"
# Wait for the cert symlink.
wait_for_symlink "${domains[0]}" "$le_container_name"
# Array of folder paths to test
folders=( \
[0]="/etc/nginx/certs/accounts" \
[1]="/etc/nginx/certs/accounts/boulder:4000" \
[2]="/etc/nginx/certs/accounts/boulder:4000/directory" \
[3]="/etc/nginx/certs/${domains[0]}" \
[0]="/etc/nginx/certs/${domains[0]}" \
)
# Test folder paths
@ -49,28 +42,45 @@ for folder in "${folders[@]}"; do
fi
done
# Array of symlinks paths to test
symlinks=( \
[0]="/etc/nginx/certs/${domains[0]}.crt" \
[1]="/etc/nginx/certs/${domains[0]}.key" \
[2]="/etc/nginx/certs/${domains[0]}.chain.pem" \
[3]="/etc/nginx/certs/${domains[0]}.dhparam.pem" \
)
# Test symlinks paths
for symlink in "${symlinks[@]}"; do
ownership="$(docker exec "$le_container_name" stat -c %u:%g "$symlink")"
if [[ "$ownership" != 0:0 ]]; then
echo "Expected 0:0 on ${symlink}, found ${ownership}."
fi
done
# Array of private file paths to test
private_files=( \
[0]="/etc/nginx/certs/default.key" \
[1]="/etc/nginx/certs/accounts/boulder:4000/directory/default.json" \
[2]="/etc/nginx/certs/${domains[0]}/key.pem" \
[1]="/etc/nginx/certs/${domains[0]}/key.pem" \
[2]="/etc/acme.sh/default/${domains[0]}/${domains[0]}.key" \
)
# Test private file paths
for file in "${private_files[@]}"; do
ownership_and_permissions="$(docker exec "$le_container_name" stat -c %u:%g:%a "$file")"
if [[ "$ownership_and_permissions" != 0:0:644 ]]; then
echo "Expected 0:0:644 on ${file}, found ${ownership_and_permissions}."
if [[ "$ownership_and_permissions" != 0:0:600 ]]; then
echo "Expected 0:0:600 on ${file}, found ${ownership_and_permissions}."
fi
done
# Array of public files paths to test
public_files=( \
[0]="/etc/nginx/certs/${domains[0]}/cert.pem" \
[1]="/etc/nginx/certs/${domains[0]}/chain.pem" \
[2]="/etc/nginx/certs/${domains[0]}/fullchain.pem" \
[3]="/etc/nginx/certs/default.crt" \
[4]="/etc/nginx/certs/dhparam.pem" \
[0]="/etc/nginx/certs/${domains[0]}/.companion" \
[1]="/etc/nginx/certs/${domains[0]}/cert.pem" \
[2]="/etc/nginx/certs/${domains[0]}/chain.pem" \
[3]="/etc/nginx/certs/${domains[0]}/fullchain.pem" \
[4]="/etc/nginx/certs/default.crt" \
[5]="/etc/nginx/certs/dhparam.pem" \
)
# Test public file paths

View File

@ -0,0 +1 @@

56
test/tests/private_keys/run.sh Executable file
View File

@ -0,0 +1,56 @@
#!/usr/bin/env bash
## Test for private keys types
if [[ -z $GITHUB_ACTIONS ]]; then
le_container_name="$(basename "${0%/*}")_$(date "+%Y-%m-%d_%H.%M.%S")"
else
le_container_name="$(basename "${0%/*}")"
fi
run_le_container "${1:?}" "$le_container_name"
# Create the $domains array from comma separated domains in TEST_DOMAINS.
IFS=',' read -r -a domains <<< "$TEST_DOMAINS"
# Cleanup function with EXIT trap
function cleanup {
# Remove any remaining Nginx container(s) silently.
for key in "${!key_types[@]}"; do
docker rm --force "${key}" &> /dev/null
done
# Cleanup the files created by this run of the test to avoid foiling following test(s).
docker exec "$le_container_name" /app/cleanup_test_artifacts
# Stop the LE container
docker stop "$le_container_name" > /dev/null
}
trap cleanup EXIT
declare -A key_types
key_types=( \
['2048']='Public-Key: (2048 bit)' \
['3072']='Public-Key: (3072 bit)' \
['4096']='Public-Key: (4096 bit)' \
['ec-256']='prime256v1' \
['ec-384']='secp384r1' \
)
for key in "${!key_types[@]}"; do
# Run an Nginx container with the wanted key type.
run_nginx_container --hosts "${domains[0]}" --name "${key}" --cli-args "--env LETSENCRYPT_KEYSIZE=${key}"
# Grep the expected string from the public key in text form.
if wait_for_symlink "${domains[0]}" "$le_container_name"; then
public_key=$(docker exec "$le_container_name" openssl pkey -in "/etc/nginx/certs/${domains[0]}.key" -noout -text_pub)
if ! grep -q "${key_types[$key]}" <<< "$public_key"; then
echo "Keys for test $key were not of the correct type, expected ${key_types[$key]} and got the following:"
echo "$public_key"
fi
else
echo "${key_types[$key]} key test timed out"
fi
docker stop "${key}" &> /dev/null
docker exec "$le_container_name" /app/cleanup_test_artifacts
done

View File

@ -1,25 +1 @@
Started letsencrypt container for test symlinks
Started test web server for le1.wtf,le2.wtf
Started test web server for le3.wtf
Symlink to le1.wtf certificate has been generated.
The link is pointing to the file ./le1.wtf/fullchain.pem
Symlink to le2.wtf certificate has been generated.
The link is pointing to the file ./le1.wtf/fullchain.pem
Symlink to le3.wtf certificate has been generated.
The link is pointing to the file ./le3.wtf/fullchain.pem
Symlink to le1.wtf certificate has been removed.
Symlink to le2.wtf certificate has been removed.
Symlink to le3.wtf certificate has been removed.
Started test web server for le1.wtf,le2.wtf,le3.wtf
Symlink to le3.wtf certificate has been generated.
The link is pointing to the file ./le1.wtf/fullchain.pem
Symlink to le1.wtf certificate has been removed.
Symlink to le2.wtf certificate has been removed.
Symlink to le3.wtf certificate has been removed.
Started test web server for le2.wtf
Symlink to le2.wtf certificate has been generated.
The link is pointing to the file ./le2.wtf/fullchain.pem
Started test web server for lim.it,le2.wtf
Symlink for lim.it certificate was not generated under one minute, timing out.
Symlink to le2.wtf certificate has been generated.
The link is pointing to the file ./le2.wtf/fullchain.pem

View File

@ -2,12 +2,12 @@
## Test for symlink creation / removal.
if [[ -z $TRAVIS_CI ]]; then
le_container_name="$(basename ${0%/*})_$(date "+%Y-%m-%d_%H.%M.%S")"
if [[ -z $GITHUB_ACTIONS ]]; then
le_container_name="$(basename "${0%/*}")_$(date "+%Y-%m-%d_%H.%M.%S")"
else
le_container_name="$(basename ${0%/*})"
le_container_name="$(basename "${0%/*}")"
fi
run_le_container ${1:?} "$le_container_name"
run_le_container "${1:?}" "$le_container_name"
# Create the $domains array from comma separated domains in TEST_DOMAINS.
IFS=',' read -r -a domains <<< "$TEST_DOMAINS"
@ -21,42 +21,31 @@ function cleanup {
symlink-le2 \
symlink-le3 \
symlink-lim-le2 \
> /dev/null 2>&1
&> /dev/null
# Cleanup the files created by this run of the test to avoid foiling following test(s).
docker exec "$le_container_name" bash -c 'rm -rf /etc/nginx/certs/le?.wtf*'
docker exec "$le_container_name" bash -c 'rm -rf /etc/nginx/certs/lim.it*'
docker exec "$le_container_name" /app/cleanup_test_artifacts
# Stop the LE container
docker stop "$le_container_name" > /dev/null
}
trap cleanup EXIT
# Run a nginx container for the firs two domain in the $domains array ...
docker run --rm -d \
--name "symlink-le1-le2" \
-e "VIRTUAL_HOST=${domains[0]},${domains[1]}" \
-e "LETSENCRYPT_HOST=${domains[0]},${domains[1]}" \
--network boulder_bluenet \
nginx:alpine > /dev/null && echo "Started test web server for ${domains[0]},${domains[1]}"
run_nginx_container --hosts "${domains[0]},${domains[1]}" --name "symlink-le1-le2"
# ... plus another nginx container for the third domain.
docker run --rm -d \
--name "symlink-le3" \
-e "VIRTUAL_HOST=${domains[2]}" \
-e "LETSENCRYPT_HOST=${domains[2]}" \
--network boulder_bluenet \
nginx:alpine > /dev/null && echo "Started test web server for ${domains[2]}"
run_nginx_container --hosts "${domains[2]}" --name "symlink-le3"
# Wait for a file at /etc/nginx/certs/$domain/cert.pem
for domain in "${domains[@]}"; do
wait_for_symlink "$domain" "$le_container_name"
done
wait_for_symlink "${domains[0]}" "$le_container_name" "./${domains[0]}/fullchain.pem"
wait_for_symlink "${domains[1]}" "$le_container_name" "./${domains[0]}/fullchain.pem"
wait_for_symlink "${domains[2]}" "$le_container_name" "./${domains[2]}/fullchain.pem"
# Create a fake le4.wtf custom certificate and key
docker exec "$le_container_name" mkdir -p /etc/nginx/certs/le4.wtf
docker exec "$le_container_name" cp /etc/nginx/certs/le1.wtf/fullchain.pem /etc/nginx/certs/le4.wtf/
docker exec "$le_container_name" cp /etc/nginx/certs/le1.wtf/key.pem /etc/nginx/certs/le4.wtf/
docker exec "$le_container_name" ln -s /etc/nginx/certs/le4.wtf/fullchain.pem /etc/nginx/certs/le4.wtf.crt
docker exec "$le_container_name" ln -s /etc/nginx/certs/le4.wtf/key.pem /etc/nginx/certs/le4.wtf.key
docker exec "$le_container_name" bash -c 'cd /etc/nginx/certs; ln -s ./le4.wtf/fullchain.pem ./le4.wtf.crt'
docker exec "$le_container_name" bash -c 'cd /etc/nginx/certs; ln -s ./le4.wtf/key.pem ./le4.wtf.key'
# Stop the nginx containers for ${domains[0]} and ${domains[1]} silently,
# then check if the corresponding symlinks are removed.
@ -74,16 +63,20 @@ docker exec "$le_container_name" [ -L "/etc/nginx/certs/${domains[2]}.crt" ] \
docker stop "symlink-le3" > /dev/null
wait_for_symlink_rm "${domains[2]}" "$le_container_name"
# Start the nginx containers for ${domains[2]} again,
# and check if the corresponding symlink is re-created.
run_nginx_container --hosts "${domains[2]}" --name "symlink-le3"
wait_for_symlink "${domains[2]}" "$le_container_name" "./${domains[2]}/fullchain.pem"
# Stop the nginx containers for ${domains[2]} silently and wait for symlink removal.
docker stop "symlink-le3" > /dev/null
wait_for_symlink_rm "${domains[2]}" "$le_container_name"
# Move ${domains[2]} to a san certificate with ${domains[0]} and ${domains[1]}
docker run --rm -d \
--name "symlink-le1-le2-le3" \
-e "VIRTUAL_HOST=${domains[0]},${domains[1]},${domains[2]}" \
-e "LETSENCRYPT_HOST=${domains[0]},${domains[1]},${domains[2]}" \
--network boulder_bluenet \
nginx:alpine > /dev/null && echo "Started test web server for ${domains[0]},${domains[1]},${domains[2]}"
run_nginx_container --hosts "${domains[0]},${domains[1]},${domains[2]}" --name "symlink-le1-le2-le3"
# Check where the symlink points (should be ./le1.wtf/fullchain.pem)
wait_for_symlink "${domains[2]}" "$le_container_name"
wait_for_symlink "${domains[2]}" "$le_container_name" "./${domains[0]}/fullchain.pem"
# Stop the nginx container silently.
docker stop "symlink-le1-le2-le3" > /dev/null
@ -94,30 +87,20 @@ for domain in "${domains[@]}"; do
done
# Move ${domains[1]} to a new single domain certificate
docker run --rm -d \
--name "symlink-le2" \
-e "VIRTUAL_HOST=${domains[1]}" \
-e "LETSENCRYPT_HOST=${domains[1]}" \
--network boulder_bluenet \
nginx:alpine > /dev/null && echo "Started test web server for ${domains[1]}"
run_nginx_container --hosts "${domains[1]}" --name "symlink-le2"
# Check where the symlink points (should be ./le2.wtf/fullchain.pem)
wait_for_symlink "${domains[1]}" "$le_container_name"
wait_for_symlink "${domains[1]}" "$le_container_name" "./${domains[1]}/fullchain.pem"
# Stop the nginx container silently and try to put ${domains[1]} on a
# san certificate whose authorization will fail.
docker stop "symlink-le2" > /dev/null
docker run --rm -d \
--name "symlink-lim-le2" \
-e "VIRTUAL_HOST=lim.it,${domains[1]}" \
-e "LETSENCRYPT_HOST=lim.it,${domains[1]}" \
--network boulder_bluenet \
nginx:alpine > /dev/null && echo "Started test web server for lim.it,${domains[1]}"
run_nginx_container --hosts "lim.it,${domains[1]}" --name "symlink-lim-le2"
# The symlink creation for lim.it should time out, and the ${domains[1]}
# symlink should still point to ./le2.wtf/fullchain.pem
wait_for_symlink "lim.it" "$le_container_name"
wait_for_symlink "${domains[1]}" "$le_container_name"
wait_for_symlink "${domains[1]}" "$le_container_name" "./${domains[1]}/fullchain.pem"
# Aaaaaand stop the container.
docker stop "symlink-lim-le2" > /dev/null

View File

@ -4,51 +4,184 @@ set -e
# Get the first domain of a comma separated list.
function get_base_domain {
awk -F ',' '{print $1}' <(echo ${1:?}) | tr -d ' '
awk -F ',' '{print $1}' <<< "${1:?}" | tr -d ' ' | sed 's/\.$//'
}
export -f get_base_domain
# Run a letsencrypt-nginx-proxy-companion container
# Run a acme-companion container
function run_le_container {
local image="${1:?}"
local name="${2:?}"
local cli_args="${3:-}"
shift 2
local -a cli_args_arr
while [[ $# -gt 0 ]]; do
local flag="$1"
case $flag in
-c|--cli-args) #only one value per flag. Multiple args = use flag multiple times
local cli_args_arr_tmp
IFS=' ' read -r -a cli_args_arr_tmp <<< "${2:?}"
cli_args_arr+=("${cli_args_arr_tmp[0]}") #Head
cli_args_arr+=("${cli_args_arr_tmp[*]:1}") #Tail
shift 2
;;
*) #Legacy Option
local cli_args_str="${1:?}"
for arg in $cli_args_str; do
cli_args_arr+=("$arg")
done
shift
;;
esac
done
if [[ "$SETUP" == '3containers' ]]; then
cli_args+=" --env NGINX_DOCKER_GEN_CONTAINER=$DOCKER_GEN_CONTAINER_NAME"
cli_args_arr+=(--env "NGINX_DOCKER_GEN_CONTAINER=$DOCKER_GEN_CONTAINER_NAME")
fi
docker run -d \
if [[ "$ACME_CA" == 'boulder' ]]; then
cli_args_arr+=(--env "ACME_CA_URI=http://boulder:4001/directory")
cli_args_arr+=(--network boulder_bluenet)
elif [[ "$ACME_CA" == 'pebble' ]]; then
cli_args_arr+=(--env "ACME_CA_URI=https://pebble:14000/dir")
cli_args_arr+=(--env "CA_BUNDLE=/pebble.minica.pem")
cli_args_arr+=(--network acme_net)
cli_args_arr+=(--volume "${GITHUB_WORKSPACE}/pebble.minica.pem:/pebble.minica.pem")
else
return 1
fi
if docker run -d \
--name "$name" \
--volumes-from $NGINX_CONTAINER_NAME \
--volumes-from "$NGINX_CONTAINER_NAME" \
--volume /var/run/docker.sock:/var/run/docker.sock:ro \
$cli_args \
--env "DHPARAM_BITS=256" \
--env "DEBUG=true" \
--env "ACME_CA_URI=http://boulder:4000/directory" \
--label com.github.jrcs.letsencrypt_nginx_proxy_companion.test_suite \
--network boulder_bluenet \
"$image" > /dev/null && echo "Started letsencrypt container for test ${name%%_2*}"
"${cli_args_arr[@]}" \
--env "DOCKER_GEN_WAIT=500ms:2s" \
--env "TEST_MODE=true" \
--env "DEBUG=1" \
--label com.github.nginx-proxy.acme-companion.test-suite \
"$image" > /dev/null; \
then
[[ "${DRY_RUN:-}" == 1 ]] && echo "Started letsencrypt container for test ${name%%_2*}"
else
echo "Could not start letsencrypt container for test ${name%%_2*}"
return 1
fi
return 0
}
export -f run_le_container
# Run an nginx container
function run_nginx_container {
local -a cli_args_arr
while [[ $# -gt 0 ]]; do
local flag="$1"
case $flag in
-h|--hosts)
local le_host="${2:?}"
local virtual_host="${le_host// /}"; virtual_host="${virtual_host//.,/,}"; virtual_host="${virtual_host%,}"
shift 2
;;
-n|--name)
local container_name="${2:?}"
shift 2
;;
-c|--cli-args)
local cli_args_arr_tmp
IFS=' ' read -r -a cli_args_arr_tmp <<< "${2:?}"
cli_args_arr+=("${cli_args_arr_tmp[0]}") #Head
cli_args_arr+=("${cli_args_arr_tmp[*]:1}") #Tail
shift 2
;;
*) #Unknown option
shift
;;
esac
done
if [[ "$ACME_CA" == 'boulder' ]]; then
cli_args_arr+=(--network boulder_bluenet)
elif [[ "$ACME_CA" == 'pebble' ]]; then
cli_args_arr+=(--network acme_net)
else
return 1
fi
[[ "${DRY_RUN:-}" == 1 ]] && echo "Starting $container_name nginx container, with VIRTUAL_HOST=$virtual_host, LETSENCRYPT_HOST=$le_host and the following cli arguments : ${cli_args_arr[*]}."
if docker run --rm -d \
--name "${container_name:-$virtual_host}" \
-e "VIRTUAL_HOST=$virtual_host" \
-e "LETSENCRYPT_HOST=$le_host" \
--label com.github.nginx-proxy.acme-companion.test-suite \
"${cli_args_arr[@]}" \
nginx:alpine > /dev/null ; \
then
[[ "${DRY_RUN:-}" == 1 ]] && echo "Started $container_name nginx container."
else
echo "Failed to start $container_name nginx container, with VIRTUAL_HOST=$virtual_host, LETSENCRYPT_HOST=$le_host and the following cli arguments : ${cli_args_arr[*]}."
return 1
fi
return 0
}
export -f run_nginx_container
# Wait for the /etc/nginx/conf.d/standalone-cert-$1.conf file to exist inside container $2
function wait_for_standalone_conf {
local domain="${1:?}"
local name="${2:?}"
local timeout
timeout="$(date +%s)"
timeout="$((timeout + 120))"
local target
until docker exec "$name" [ -f "/etc/nginx/conf.d/standalone-cert-$domain.conf" ]; do
if [[ "$(date +%s)" -gt "$timeout" ]]; then
echo "Standalone configuration file for $domain was not generated under one minute, timing out."
return 1
fi
sleep 0.1
done
}
export -f wait_for_standalone_conf
# Wait for the /etc/nginx/certs/$1.crt symlink to exist inside container $2
function wait_for_symlink {
local domain="${1:?}"
local name="${2:?}"
local i=0
local expected_target="${3:-}"
local timeout
timeout="$(date +%s)"
timeout="$((timeout + 120))"
local target
until docker exec "$name" [ -L "/etc/nginx/certs/$domain.crt" ]; do
if [ $i -gt 60 ]; then
if [[ "$(date +%s)" -gt "$timeout" ]]; then
echo "Symlink for $domain certificate was not generated under one minute, timing out."
return 1
fi
i=$((i + 2))
sleep 2
sleep 0.1
done
target="$(docker exec "$name" readlink "/etc/nginx/certs/$domain.crt")"
echo "Symlink to $domain certificate has been generated."
echo "The link is pointing to the file $target"
[[ "${DRY_RUN:-}" == 1 ]] && echo "Symlink to $domain certificate has been generated."
if [[ -n "$expected_target" ]]; then
target="$(docker exec "$name" readlink "/etc/nginx/certs/$domain.crt")"
if [[ "$target" != "$expected_target" ]]; then
echo "The symlink to the $domain certificate is expected to point to $expected_target but point to $target instead."
return 1
elif [[ "${DRY_RUN:-}" == 1 ]]; then
echo "The symlink is pointing to the file $target"
fi
fi
return 0
}
export -f wait_for_symlink
@ -57,16 +190,18 @@ export -f wait_for_symlink
function wait_for_symlink_rm {
local domain="${1:?}"
local name="${2:?}"
local i=0
local timeout
timeout="$(date +%s)"
timeout="$((timeout + 120))"
until docker exec "$name" [ ! -L "/etc/nginx/certs/$domain.crt" ]; do
if [ $i -gt 60 ]; then
if [[ "$(date +%s)" -gt "$timeout" ]]; then
echo "Certificate symlink for $domain was not removed under one minute, timing out."
return 1
fi
i=$((i + 2))
sleep 2
sleep 0.1
done
echo "Symlink to $domain certificate has been removed."
[[ "${DRY_RUN:-}" == 1 ]] && echo "Symlink to $domain certificate has been removed."
return 0
}
export -f wait_for_symlink_rm
@ -108,7 +243,7 @@ function check_cert_subj {
esac
done
if curl -k https://"$domain" > /dev/null 2>&1; then
if curl -k https://"$domain" &> /dev/null; then
local cert_subject
cert_subject="$(echo \
| openssl s_client -showcerts -servername "$domain" -connect "$domain:443" 2>/dev/null \
@ -163,19 +298,21 @@ function wait_for_conn {
esac
done
local i=0
local timeout
timeout="$(date +%s)"
timeout="$((timeout + 120))"
action="${action:---no-match}"
string="${string:-letsencrypt-nginx-proxy-companion}"
until check_cert_subj --domain "$domain" "$action" "$string"; do
if [ $i -gt 120 ]; then
if [[ "$(date +%s)" -gt "$timeout" ]]; then
echo "Could not connect to $domain using https under two minutes, timing out."
return 1
fi
i=$((i + 2))
sleep 2
sleep 0.1
done
echo "Connection to $domain using https was successful."
[[ "${DRY_RUN:-}" == 1 ]] && echo "Connection to $domain using https was successful."
return 0
}
export -f wait_for_conn

View File

@ -0,0 +1 @@