embedded cert.sh
continuous-integration/drone/push Build was killed Details
continuous-integration/drone/tag Build was killed Details

This commit is contained in:
Paolo Asperti 2024-01-23 17:15:08 +01:00
parent 488acf16c1
commit 962624e294
Signed by: paspo
GPG Key ID: 06D46905D19D5182
13 changed files with 235 additions and 76 deletions

View File

@ -42,11 +42,11 @@ steps:
dockerfile: Dockerfile
force_tag: true
password:
from_secret: docker_gitea_password
from_secret: gitea_docker_password
registry: git.asperti.com
repo: git.asperti.com/paspo/docker-ftps
username:
from_secret: docker_gitea_username
from_secret: gitea_docker_username
tags:
- ${DRONE_TAG}-linux-amd64
- ${DRONE_SEMVER_MAJOR}.${DRONE_SEMVER_MINOR}-linux-amd64
@ -100,11 +100,11 @@ steps:
dockerfile: Dockerfile
force_tag: true
password:
from_secret: docker_gitea_password
from_secret: gitea_docker_password
registry: git.asperti.com
repo: git.asperti.com/paspo/docker-ftps
username:
from_secret: docker_gitea_username
from_secret: gitea_docker_username
tags:
- ${DRONE_TAG}-linux-arm64
- ${DRONE_SEMVER_MAJOR}.${DRONE_SEMVER_MINOR}-linux-arm64
@ -158,11 +158,11 @@ steps:
dockerfile: Dockerfile
force_tag: true
password:
from_secret: docker_gitea_password
from_secret: gitea_docker_password
registry: git.asperti.com
repo: git.asperti.com/paspo/docker-ftps
username:
from_secret: docker_gitea_username
from_secret: gitea_docker_username
tags:
- ${DRONE_TAG}-linux-arm
- ${DRONE_SEMVER_MAJOR}.${DRONE_SEMVER_MINOR}-linux-arm
@ -203,9 +203,9 @@ steps:
ignore_missing: true
spec: manifest2.tmpl
username:
from_secret: docker_gitea_username
from_secret: gitea_docker_username
password:
from_secret: docker_gitea_password
from_secret: gitea_docker_password
tags:
- latest
- ${DRONE_TAG}

View File

@ -1,17 +1,9 @@
FROM alpine:edge
FROM alpine:latest
RUN \
echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories && \
apk -U add proftpd proftpd-mod_tls proftpd-mod_ifsession proftpd-utils openssl perl && \
apk -U add proftpd proftpd-mod_tls proftpd-mod_ifsession proftpd-utils openssl perl acme.sh && \
mkdir -p /var/run/proftpd /etc/proftpd/custom.conf.d/
COPY custom.conf /etc/proftpd/conf.d/custom.conf
COPY run.sh /run.sh
COPY cron.sh /cron.sh
COPY rootfs /
RUN \
chmod +x /run.sh && \
chmod +x /cron.sh && \
ln -s /cron.sh /etc/periodic/15min/reconfigure_certs.sh
ENTRYPOINT ["/run.sh"]
ENTRYPOINT ["/app/entrypoint.sh"]

View File

@ -22,7 +22,7 @@ docker run -d --name my-ftps \
```
The *MASQUERADE* parameter is the only required one. You can use an IP address (which is discouraged) or a DNS name.
You must provide valid certificates for TLS; if you use Lets'Encrypt, you can mofify like this:
You must provide valid certificates for TLS; if you use Lets'Encrypt, you can modify like this:
```bash
docker run -d --name my-ftps \
@ -33,7 +33,7 @@ docker run -d --name my-ftps \
docker.asperti.com/paspo/ftps
```
## docker-compose
## docker-compose (external certificate)
```yaml
version: "3"
@ -56,12 +56,66 @@ services:
- PASSIVEPORTS_START=21210
- PASSIVEPORTS_END=21220
- MAXCLIENTS=500
- MAXCLIENTSPERHOST=100
- MAXCLIENTSPERHOST=100
- TLS_CERT=/certs/live/ftp.mydomain.com/cert.pem
- TLS_KEY=/certs/live/ftp.mydomain.com/privkey.pem
- TLS_CHAIN=/certs/live/ftp.mydomain.com/chain.pem
```
## docker-compose (using internal acme.sh)
```yaml
version: "3"
services:
ftps-server:
image: docker.asperti.com/paspo/ftps
restart: always
ports:
- "21:21"
- "20:20"
- "21210-21220:21210-21220"
volumes:
- "/srv/ftps/auth:/auth"
- "/srv/ftps/conf:/etc/proftpd/custom.conf.d:ro"
- "/srv/ftps/data:/home"
- "/srv/ftps/acme:/acme"
environment:
- MASQUERADE=ftp.mydomain.com
- PASSIVEPORTS_START=21210
- PASSIVEPORTS_END=21220
- MAXCLIENTS=500
- MAXCLIENTSPERHOST=100
- ENABLE_ACME=1 # "1" will enable, anything else means external cert
- ACME_SERVER=letsencrypt # optional
- ACME_EMAIL=myemail@gmail.com # used by letsencrypt
- ACME_DNS=dns_ovh # see below
- OVH_END_POINT=ovh-eu
- OVH_AK=abc123abc123abc1 # application key
- OVH_AS=abc123abc123abc1abc123abc123abc1 # application secret
- OVH_CK=abc123abc123abc1abc123abc123abc1 # consumer key
```
## The rationale behind the acme.sh alternative
You normally use an external letsencrypt client to obtain the certificate and then pass it to the docker container. In some cases, you can't use an external acme client and/or you can't do HTTP-01 auth.
The included `acme.sh` client will help you to setup DNS-01 auth and in keeping the cert updated.
Please check [here](https://github.com/acmesh-official/acme.sh/wiki/dnsapi) for supported dns providers.
Each provider will use different environment variables, you have to add these variables to the container's environment.
### OVH
A quick way to create required credentials for OVH:
- login to your [OVH accuont](https://www.ovh.com/manager/)
- paste in your browser an URL like the following one:
```txt
https://api.ovh.com/createToken/?GET=/domain/zone/mydomain.com/*&POST=/domain/zone/mydomain.com/*&PUT=/domain/zone/mydomain.com/*&GET=/domain/zone/mydomain.com&DELETE=/domain/zone/mydomain.com/record/*
```
This will create some credentials that'll allow management only for that domain (`mydomain.com`).
## passive ports
If you want to change the passive ports range (which by default is 50000-50050), you can do so via environment variables (PASSIVEPORTS_START and PASSIVEPORTS_END).

28
rootfs/app/acme-cert-init.sh Executable file
View File

@ -0,0 +1,28 @@
#!/bin/sh
MASQUERADE=${MASQUERADE:-127.0.0.1}
ACME_SERVER=${ACME_SERVER:-letsencrypt}
ACME_DNS=${ACME_DNS:-myapi}
if [ ! -d /acme/cert ] ; then
mkdir -p /acme/cert
fi
if [ -n "${ACME_EMAIL}" ] ; then
ACME_EMAIL="--accountemail ${ACME_EMAIL}"
fi
if [ ! -f "/acme/cert/cert.pem" ] ; then
echo "Initializing certificate with acme.sh"
# shellcheck disable=SC2086
acme.sh --issue -d "${MASQUERADE}" \
--home /acme \
--dns "${ACME_DNS}" \
--server "${ACME_SERVER}" \
--cert-file /acme/cert/cert.pem \
--key-file /acme/cert/privkey.pem \
--fullchain-file /acme/cert/chain.pem \
--reloadcmd /app/acme-refresh-cert.sh ${ACME_EMAIL}
else
echo "Certificate ready"
fi

5
rootfs/app/acme-cron.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/sh
if [ "$ENABLE_ACME" = "1" ] ; then
/usr/bin/acme.sh --cron --home /acme
fi

38
rootfs/app/acme-refresh-cert.sh Executable file
View File

@ -0,0 +1,38 @@
#!/bin/sh
############ FILES
TLS_CERT=/acme/cert/cert.pem
TLS_KEY=/acme/cert/privkey.pem
TLS_CHAIN=/acme/cert/chain.pem
[ ! -f "$TLS_CERT" ] && exit 1
[ ! -f "$TLS_KEY" ] && exit 1
[ ! -f "$TLS_CHAIN" ] && exit 1
############ CHECK CERT KEY ALGO
ALGO=$(openssl x509 -in "$TLS_CERT"-text | sed -n 's/\ *Public Key Algorithm: //p' | tr '\n')
############ UPDATE cert config if needed
if [ "$ALGO" = "id-ecPublicKey" ] ; then
cat > /etc/proftpd/conf.d/certificate.conf <<EOF
<IfModule mod_tls.c>
TLSECCertificateFile "$TLS_CERT"
TLSECCertificateKeyFile "$TLS_KEY"
TLSCertificateChainFile "$TLS_CHAIN"
</IfModule>
EOF
fi
if [ "$ALGO" = "rsaEncryption" ] ; then
cat > /etc/proftpd/conf.d/certificate.conf <<EOF
<IfModule mod_tls.c>
TLSRSACertificateFile "$TLS_CERT"
TLSRSACertificateKeyFile "$TLS_KEY"
TLSCertificateChainFile "$TLS_CHAIN"
</IfModule>
EOF
fi
############ RELOAD PROFTPD IF RUNNING
pidof proftpd >/dev/null && killall -HUP proftpd

20
cron.sh → rootfs/app/cert-init.sh Normal file → Executable file
View File

@ -1,23 +1,18 @@
#!/bin/sh
############ TLS
TLS_CERT=${TLS_CERT:-/certs/cert.pem}
TLS_KEY=${TLS_KEY:-/certs/privkey.pem}
TLS_CHAIN=${TLS_CHAIN:-/certs/chain.pem}
cat $TLS_CERT > /etc/proftpd/cert.pem
cat $TLS_KEY > /etc/proftpd/privkey.pem
cat $TLS_CHAIN > /etc/proftpd/chain.pem
############ IF CERT IS THE SAME, THEN EXIT
md5sum -c /sums 1&>2 2>/dev/null && exit
cat "$TLS_CERT" > /etc/proftpd/cert.pem
cat "$TLS_KEY" > /etc/proftpd/privkey.pem
cat "$TLS_CHAIN "> /etc/proftpd/chain.pem
############ CHECK CERT KEY ALGO
ALGO=$(openssl x509 -in /etc/proftpd/cert.pem -text | sed -n 's/\ *Public Key Algorithm: //p' | tr '\n')
############ UPDATE cert config if needed
if [ "$ALGO" = "id-ecPublicKey" ] ; then
cat > /etc/proftpd/conf.d/certificate.conf <<EOF
<IfModule mod_tls.c>
@ -38,8 +33,5 @@ cat > /etc/proftpd/conf.d/certificate.conf <<EOF
EOF
fi
md5sum "$TLS_CERT" > /sums
############ RELOAD
killall -HUP proftpd
md5sum "$TLS_CERT" > /app/sums
echo "Certificate ready"

52
run.sh → rootfs/app/cron.sh Normal file → Executable file
View File

@ -1,29 +1,23 @@
#!/bin/sh
############ MASQUERADE
MASQUERADE=${MASQUERADE:-127.0.0.1}
echo "MasqueradeAddress ${MASQUERADE}" > /etc/proftpd/conf.d/masquerade.conf
############ AUTH
[ ! -f /auth/passwd ] && touch /auth/passwd
chmod 0600 /auth/passwd
chmod 0700 /auth
############ IF ACME IS ENABLED, THIS IS THE WRONG SCRIPT
if [ ! "$ENABLE_ACME" = "1" ] ; then
exit
fi
############ TLS
TLS_CERT=${TLS_CERT:-/certs/cert.pem}
TLS_KEY=${TLS_KEY:-/certs/privkey.pem}
TLS_CHAIN=${TLS_CHAIN:-/certs/chain.pem}
cat $TLS_CERT > /etc/proftpd/cert.pem
cat $TLS_KEY > /etc/proftpd/privkey.pem
cat $TLS_CHAIN > /etc/proftpd/chain.pem
cat "$TLS_CERT" > /etc/proftpd/cert.pem
cat "$TLS_KEY" > /etc/proftpd/privkey.pem
cat "$TLS_CHAIN" > /etc/proftpd/chain.pem
############ IF CERT IS THE SAME, THEN EXIT
md5sum -c /app/sums >/dev/null 2>/dev/null && exit
############ CHECK CERT KEY ALGO
ALGO=$(openssl x509 -in /etc/proftpd/cert.pem -text | sed -n 's/\ *Public Key Algorithm: //p' | tr '\n')
if [ "$ALGO" = "id-ecPublicKey" ] ; then
@ -46,27 +40,7 @@ cat > /etc/proftpd/conf.d/certificate.conf <<EOF
EOF
fi
md5sum "$TLS_CERT" > /sums
md5sum "$TLS_CERT" > /app/sums
############ PASSIVE PORTS
PASSIVEPORTS_START=${PASSIVEPORTS_START:-50000}
PASSIVEPORTS_END=${PASSIVEPORTS_END:-50050}
echo "PassivePorts ${PASSIVEPORTS_START} ${PASSIVEPORTS_END}" > /etc/proftpd/conf.d/passive_ports.conf
############ MAX CLIENTS
MAXCLIENTS=${MAXCLIENTS:-30}
MAXCLIENTSPERHOST=${MAXCLIENTSPERHOST:-5}
echo "Maxclients ${MAXCLIENTS}" > /etc/proftpd/conf.d/maxclients.conf
echo "MaxClientsPerHost ${MAXCLIENTSPERHOST}" >> /etc/proftpd/conf.d/maxclients.conf
############ START CRON
crond -b
############ START
proftpd -n
############ RELOAD
killall -HUP proftpd

36
rootfs/app/entrypoint.sh Executable file
View File

@ -0,0 +1,36 @@
#!/bin/sh
############ MASQUERADE
MASQUERADE=${MASQUERADE:-127.0.0.1}
echo "MasqueradeAddress ${MASQUERADE}" > /etc/proftpd/conf.d/masquerade.conf
############ AUTH
[ ! -f /auth/passwd ] && touch /auth/passwd
chmod 0600 /auth/passwd
chmod 0700 /auth
############ PASSIVE PORTS
PASSIVEPORTS_START=${PASSIVEPORTS_START:-50000}
PASSIVEPORTS_END=${PASSIVEPORTS_END:-50050}
echo "PassivePorts ${PASSIVEPORTS_START} ${PASSIVEPORTS_END}" > /etc/proftpd/conf.d/passive_ports.conf
############ MAX CLIENTS
MAXCLIENTS=${MAXCLIENTS:-30}
MAXCLIENTSPERHOST=${MAXCLIENTSPERHOST:-5}
echo "Maxclients ${MAXCLIENTS}" > /etc/proftpd/conf.d/maxclients.conf
echo "MaxClientsPerHost ${MAXCLIENTSPERHOST}" >> /etc/proftpd/conf.d/maxclients.conf
############ CERT INIT
ENABLE_ACME=${ENABLE_ACME:-no}
if [ "$ENABLE_ACME" = "1" ] ; then
/app/acme-cert-init.sh
else
/app/cert-init.sh
fi
############ START CRON
crond -b
############ START
proftpd -n

38
rootfs/app/refresh-cert.sh Executable file
View File

@ -0,0 +1,38 @@
#!/bin/sh
############ FILES
TLS_CERT=/acme/cert/cert.pem
TLS_KEY=/acme/cert/privkey.pem
TLS_CHAIN=/acme/cert/chain.pem
[ ! -f "$TLS_CERT" ] && exit 1
[ ! -f "$TLS_KEY" ] && exit 1
[ ! -f "$TLS_CHAIN" ] && exit 1
############ CHECK CERT KEY ALGO
ALGO=$(openssl x509 -in "$TLS_CERT"-text | sed -n 's/\ *Public Key Algorithm: //p' | tr '\n')
############ UPDATE cert config if needed
if [ "$ALGO" = "id-ecPublicKey" ] ; then
cat > /etc/proftpd/conf.d/certificate.conf <<EOF
<IfModule mod_tls.c>
TLSECCertificateFile "$TLS_CERT"
TLSECCertificateKeyFile "$TLS_KEY"
TLSCertificateChainFile "$TLS_CHAIN"
</IfModule>
EOF
fi
if [ "$ALGO" = "rsaEncryption" ] ; then
cat > /etc/proftpd/conf.d/certificate.conf <<EOF
<IfModule mod_tls.c>
TLSRSACertificateFile "$TLS_CERT"
TLSRSACertificateKeyFile "$TLS_KEY"
TLSCertificateChainFile "$TLS_CHAIN"
</IfModule>
EOF
fi
############ RELOAD PROFTPD IF RUNNING
pidof proftpd >/dev/null && killall -HUP proftpd

View File

@ -0,0 +1 @@
../../../app/acme-cron.sh

View File

@ -0,0 +1 @@
../../../app/cron.sh