Skip to content
This repository has been archived by the owner on Jul 20, 2022. It is now read-only.

Commit

Permalink
feat: First version (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
lholota authored May 27, 2020
1 parent 686c6e1 commit af09adc
Show file tree
Hide file tree
Showing 23 changed files with 311 additions and 34 deletions.
1 change: 1 addition & 0 deletions .dockeringore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
tests
3 changes: 2 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
* text=auto
*.sh eol=lf
**/run eol=lf
*/services.d/* eol=lf
*/services.d/* eol=lf
cron-tick eol=lf
2 changes: 1 addition & 1 deletion .github/settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ branches:
protection:
required_status_checks:
strict: true
contexts: [ ".github/workflows/ci.yml" ]
contexts: [ "build" ]
required_pull_request_reviews: null
enforce_admins: false
restrictions:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
pull_request:

env:
IMAGE_NAME: "homecentr/$$IMAGE_NAME$$"
IMAGE_NAME: "homecentr/certbot"

jobs:
build:
Expand All @@ -30,7 +30,7 @@ jobs:
run: docker build . -t ${{ env.IMAGE_NAME }}:${{ steps.vars.outputs.docker_tag }}

- name: Test Docker image
run: cd tests && sudo gradle test --info -Dimage_tag=${{ env.IMAGE_NAME }}:${{ steps.vars.outputs.docker_tag }}
run: cd tests && sudo gradle test --info -Ddocker_image_tag=${{ env.IMAGE_NAME }}:${{ steps.vars.outputs.docker_tag }} -Droot_domain=${{ secrets.ROOT_DOMAIN }} -Dacme_email=${{ secrets.ACME_EMAIL }} -Dcloudflare_token=${{ secrets.CLOUDFLARE_TOKEN }}

- name: Scan with Phonito Security
uses: phonito/phonito-scanner-action@master
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ci_cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
- master

env:
IMAGE_NAME: "homecentr/$$IMAGE_NAME$$"
IMAGE_NAME: "homecentr/certbot"

jobs:
build:
Expand Down Expand Up @@ -40,7 +40,7 @@ jobs:
- name: Test Docker image
if: env.RELEASE_VERSION != ''
run: cd tests && sudo gradle test -Dimage_tag=${{ env.IMAGE_NAME }}:${{ env.RELEASE_VERSION }}
run: cd tests && sudo gradle test -Ddocker_image_tag=${{ env.IMAGE_NAME }}:${{ env.RELEASE_VERSION }} -Droot_domain=${{ secrets.ROOT_DOMAIN }} -Dacme_email=${{ secrets.ACME_EMAIL }} -Dcloudflare_token=${{ secrets.CLOUDFLARE_TOKEN }}

- name: Scan with Phonito Security
if: env.RELEASE_VERSION != ''
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
*.tmp
*.class
*.jar
*.war
Expand Down
42 changes: 41 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1 +1,41 @@
FROM alpine
FROM certbot/certbot:v1.4.0 as certbot

FROM homecentr/cron-base:1.2.0

ARG CERTBOT_PIP_VERSION="1.4.0"

ENV CERTBOT_ARGS=""
ENV CRON_SCHEDULE="30 * * * *"
ENV S6_BEHAVIOUR_IF_STAGE2_FAILS=2

COPY --from=certbot /usr/local/bin/certbot /usr/local/bin/certbot

RUN apk add --no-cache python3=3.8.2-r0 && \
apk add --no-cache --virtual deps \
python3-dev=3.8.2-r0 \
gcc=9.2.0-r4 \
libffi-dev=3.2.1-r6 \
openssl-dev=1.1.1g-r0 \
musl-dev=1.1.24-r2 && \
pip3 install --upgrade pip==20.1.1 && \
pip3 install \
setuptools==46.4.0 \
acme==${CERTBOT_PIP_VERSION} \
certbot==${CERTBOT_PIP_VERSION} \
certbot-dns-cloudflare==${CERTBOT_PIP_VERSION} \
certbot-dns-cloudxns==${CERTBOT_PIP_VERSION} \
certbot-dns-digitalocean==${CERTBOT_PIP_VERSION} \
certbot-dns-dnsimple==${CERTBOT_PIP_VERSION} \
certbot-dns-dnsmadeeasy==${CERTBOT_PIP_VERSION} \
certbot-dns-google==${CERTBOT_PIP_VERSION} \
certbot-dns-linode==${CERTBOT_PIP_VERSION} \
certbot-dns-luadns==${CERTBOT_PIP_VERSION} \
certbot-dns-nsone==${CERTBOT_PIP_VERSION} \
certbot-dns-ovh==${CERTBOT_PIP_VERSION} \
certbot-dns-rfc2136==${CERTBOT_PIP_VERSION} \
certbot-dns-route53==${CERTBOT_PIP_VERSION} && \
apk del deps

COPY ./fs/ /

VOLUME "/etc/letsnecrypt"
41 changes: 25 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,56 @@
[![Project status](https://badgen.net/badge/project%20status/stable%20%26%20actively%20maintaned?color=green)](https://github.com/homecentr/docker-$$IMAGE_NAME$$/graphs/commit-activity) [![](https://badgen.net/github/label-issues/homecentr/docker-$$IMAGE_NAME$$/bug?label=open%20bugs&color=green)](https://github.com/homecentr/docker-$$IMAGE_NAME$$/labels/bug) [![](https://badgen.net/github/release/homecentr/docker-$$IMAGE_NAME$$)](https://hub.docker.com/repository/docker/homecentr/$$IMAGE_NAME$$)
[![](https://badgen.net/docker/pulls/homecentr/$$IMAGE_NAME$$)](https://hub.docker.com/repository/docker/homecentr/$$IMAGE_NAME$$)
[![](https://badgen.net/docker/size/homecentr/$$IMAGE_NAME$$)](https://hub.docker.com/repository/docker/homecentr/$$IMAGE_NAME$$)
[![Project status](https://badgen.net/badge/project%20status/stable%20%26%20actively%20maintaned?color=green)](https://github.com/homecentr/docker-certbot/graphs/commit-activity) [![](https://badgen.net/github/label-issues/homecentr/docker-certbot/bug?label=open%20bugs&color=green)](https://github.com/homecentr/docker-certbot/labels/bug) [![](https://badgen.net/github/release/homecentr/docker-certbot)](https://hub.docker.com/repository/docker/homecentr/certbot)
[![](https://badgen.net/docker/pulls/homecentr/certbots://hub.docker.com/repository/docker/homecentr/certbot
[![](https://badgen.net/docker/size/homecentr/certbots://hub.docker.com/repository/docker/homecentr/certbot

![CI/CD on master](https://github.com/homecentr/docker-$$IMAGE_NAME$$/workflows/CI/CD%20on%20master/badge.svg)
![Regular Docker image vulnerability scan](https://github.com/homecentr/docker-$$IMAGE_NAME$$/workflows/Regular%20Docker%20image%20vulnerability%20scan/badge.svg)
![CI/CD on master](https://github.com/homecentr/docker-certbot/CI/CD%20on%20master/badge.svg)
![Regular Docker image vulnerability scan](https://github.com/homecentr/docker-certbot/Regular%20Docker%20image%20vulnerability%20scan/badge.svg)


# HomeCentr - $$IMAGE_NAME$$
# HomeCentr - certbot

The image contains [Certbot](https://certbot.eff.org/) compliant with the HomeCenter docker images standard (S6 overlay, privilege drop etc.). All DNS plugins endorsed by Certbot are installed ([list](https://certbot.eff.org/docs/using.html#dns-plugins)).

This image is supposed to be used as a single purpose certificate manager. It does not include any reverse proxy. The proxy should be running in a separate container and read the certificates from a mounted volume.

## Usage

```yml
version: "3.7"
services:
$$IMAGE_NAME$$:
certbot
build: .
image: homecentr/$$IMAGE_NAME$$
image: homecentr/certbot
# Example uses Cloudflare dns verification, if you use a different provider, you need to adjust the arguments
environment:
CERTBOT_ARGS: "--email [email protected] --dns-cloudflare --dns-cloudflare-credentials /secrets/cloudflare.ini"
volumes:
- cloudflare.ini:/secrets/cloudflare.ini
```
> If you are just testing/are not 100% sure the arguments are correct, add the `--dry-run` which will not actually make the request to Let's encrypt or `--staging` argument which will use [Let's encrypts staging servers](https://letsencrypt.org/docs/staging-environment/) instead of the production ones. The production servers have low rate limits and running too many unsuccessful requests could **block you out for a week**.

## Environment variables

| Name | Default value | Description |
|------|---------------|-------------|
| PUID | 7077 | UID of the user $$IMAGE_NAME$$ should be running as. |
| PGID | 7077 | GID of the user $$IMAGE_NAME$$ should be running as. |
| PUID | 7077 | UID of the user certbot be running as. |
| PGID | 7077 | GID of the user certbot be running as. |
| CERTBOT_ARGS | | Additional arguments passed to certbot's `certonly` command. The argument `--agree-tos` is passed automatically, but you have to provide the `--email` argument. |

## Exposed ports

| Port | Protocol | Description |
|------|------|-------------|
| 80 | TCP | Some useful details |
This image does not expose any ports.

## Volumes

| Container path | Description |
|------------|---------------|
| /config | Some useful details |
| /etc/letsencrypt | Contains the provisioned certificates. Please note that the "files" in the `/etc/letsencrypt/*` are just symlinks and therefore when mounting, you need to mount either the whole `/etc/letsencrypt/` directory or mount both `/etc/letsencrypt/live` and `/etc/letsencrypt/archive` on same relative levels. |

## Security
The container is regularly scanned for vulnerabilities and updated. Further info can be found in the [Security tab](https://github.com/homecentr/docker-$$IMAGE_NAME$$/security).
The container is regularly scanned for vulnerabilities and updated. Further info can be found in the [Security tab](https://github.com/homecentr/docker-certbot).

### Container user
The container supports privilege drop. Even though the container starts as root, it will use the permissions only to perform the initial set up. The $$IMAGE_NAME$$ process runs as UID/GID provided in the PUID and PGID environment variables.
The container supports privilege drop. Even though the container starts as root, it will use the permissions only to perform the initial set up. The certbots runs as UID/GID provided in the PUID and PGID environment variables.

:warning: Do not change the container user directly using the `user` Docker compose property or using the `--user` argument. This would break the privilege drop logic.
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
version: "3.7"
services:
$$IMAGE_NAME$$:
certbot:
build: .
image: homecentr/$$IMAGE_NAME$$
image: homecentr/certbot
restart: unless-stopped
3 changes: 3 additions & 0 deletions fs/cron/cron-tick
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env ash

certbot certonly --agree-tos --non-interactive --no-permissions-check $CERTBOT_ARGS
5 changes: 5 additions & 0 deletions fs/etc/cont-init.d/98-verify-args.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/with-contenv ash

if [[ "$CERTBOT_ARGS" != *"--email"* ]]; then
echo "The CERTBOT_ARGS variable must contain '--email' as certbot is executed in a non-interactive way."
fi
4 changes: 4 additions & 0 deletions fs/etc/cont-init.d/99-first-execution.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/with-contenv ash

echo "Executing the certbot immediately to ensure the certificate exists..."
cron-tick-execute
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"name": "homecentr-$$IMAGE_NAME$$",
"name": "homecentr-certbot",
"version": "1.0.0",
"description": "",
"repository": {
"type": "git",
"url": "git+https://github.com/homecentr/docker-$$IMAGE_NAME$$.git"
"url": "git+https://github.com/homecentr/docker-certbot"
},
"author": "",
"license": "MIT",
"bugs": {
"url": "https://github.com/homecentr/docker-$$IMAGE_NAME$$/issues"
"url": "https://github.com/homecentr/docker-certbot"
},
"homepage": "https://github.com/homecentr/docker-$$IMAGE_NAME$$#readme"
"homepage": "https://github.com/homecentr/docker-certbot"
}
4 changes: 2 additions & 2 deletions tests/.idea/compiler.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions tests/.idea/dictionaries/lholota.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions tests/.idea/jarRepositories.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion tests/.project
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>docker-$$IMAGE_NAME$$-tests</name>
<name>docker-certbot-tests</name>
<comment>Project tests created by Buildship.</comment>
<projects>
</projects>
Expand Down
11 changes: 11 additions & 0 deletions tests/.settings/org.eclipse.buildship.core.prefs
Original file line number Diff line number Diff line change
@@ -1,2 +1,13 @@
arguments=
auto.sync=false
build.scans.enabled=false
connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
connection.project.dir=
eclipse.preferences.version=1
gradle.user.home=
java.home=C\:/Program Files/Java/jdk1.8.0_231
jvm.arguments=
offline.mode=false
override.workspace.settings=true
show.console.view=true
show.executions.view=true
12 changes: 10 additions & 2 deletions tests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,25 @@ sourceCompatibility = 1.8

repositories {
mavenCentral()
maven {
url "https://dl.bintray.com/homecentr/maven"
}
}

dependencies {
testImplementation group: 'junit', name: 'junit', version: '4.13'
testImplementation "org.testcontainers:testcontainers:1.14.2"
testImplementation 'org.testcontainers:testcontainers:1.14.2'
testImplementation 'io.homecentr:testcontainers-extensions:1.3.1'
testImplementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.30'
testImplementation group: 'org.slf4j', name: 'slf4j-simple', version: '1.7.30'
}

test {
systemProperty 'image_tag', System.getProperty('image_tag')
systemProperty 'docker_image_tag', System.getProperty('docker_image_tag')
systemProperty 'root_domain', System.getProperty('root_domain')
systemProperty 'acme_email', System.getProperty('acme_email')
systemProperty 'cloudflare_token', System.getProperty('cloudflare_token')

afterTest { desc, result ->
logger.quiet "Executing test ${desc.name} [${desc.className}] with result: ${result.resultType}"
}
Expand Down
70 changes: 70 additions & 0 deletions tests/src/test/java/CertbotContainerShould.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import helpers.CertbotDockerTagResolver;
import io.homecentr.testcontainers.containers.GenericContainerEx;
import io.homecentr.testcontainers.containers.wait.strategy.WaitEx;
import io.homecentr.testcontainers.images.PullPolicyEx;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testcontainers.containers.output.Slf4jLogConsumer;

import java.io.IOException;
import java.time.Duration;

import static io.homecentr.testcontainers.WaitLoop.waitFor;
import static org.junit.Assert.assertTrue;

public class CertbotContainerShould {
private static final Logger logger = LoggerFactory.getLogger(CertbotContainerShould.class);

private static GenericContainerEx _certbotContainer;
private static TestConfiguration _testConfig;

@BeforeClass
public static void before() throws Exception {
_testConfig = TestConfiguration.create();
_testConfig.createCredentialsSecretFile();

_certbotContainer = new GenericContainerEx<>(new CertbotDockerTagResolver())
.withEnv("CRON_SCHEDULE", "* * * * *")
.withEnv("CERTBOT_ARGS", _testConfig.getCertbotArgs())
.withFileSystemBind(TestConfiguration.cloudflareCredentialsHostPath, TestConfiguration.cloudflareCredentialsContainerPath)
.withImagePullPolicy(PullPolicyEx.never())
.waitingFor(WaitEx.forS6OverlayStart());

_certbotContainer.start();
_certbotContainer.followOutput(new Slf4jLogConsumer(logger));

waitFor(Duration.ofSeconds(80), () -> _certbotContainer.getLogsAnalyzer().contains("Execution finished"));
}

@AfterClass
public static void after() {
_certbotContainer.close();
}

@Test
public void createCertificateFullChainFile() throws IOException, InterruptedException {
assertTrue(fileExists(String.format("/etc/letsencrypt/live/%s/fullchain.pem", _testConfig.getDomain())));
}

@Test
public void createCertificateChainFile() throws IOException, InterruptedException {
assertTrue(fileExists(String.format("/etc/letsencrypt/live/%s/chain.pem", _testConfig.getDomain())));
}

@Test
public void createPrivateKeyFile() throws IOException, InterruptedException {
assertTrue(fileExists(String.format("/etc/letsencrypt/live/%s/privkey.pem", _testConfig.getDomain())));
}

@Test
public void createPublicKeyFile() throws IOException, InterruptedException {
assertTrue(fileExists(String.format("/etc/letsencrypt/live/%s/cert.pem", _testConfig.getDomain())));
}

private boolean fileExists(String fileNamePattern) throws IOException, InterruptedException {
return _certbotContainer.execInContainer("ls", fileNamePattern).getExitCode() == 0;
}
}
Loading

0 comments on commit af09adc

Please sign in to comment.