mirror of
https://github.com/balena-io/open-balena.git
synced 2025-02-06 19:20:02 +00:00
certs: Add support for an ACME certificate provider
Add a service which will acquire certificates from an ACME cert provider, such as LetsEncrypt (), to allow an openBalena instance to use a publicly trusted certificate instead of the self-signed one it wil generate on setup. Change-type: patch Signed-off-by: Rich Bayliss <rich@balena.io>
This commit is contained in:
parent
d3b021a1cb
commit
99dd615e55
20
cert-provider/Dockerfile
Normal file
20
cert-provider/Dockerfile
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
FROM alpine
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
VOLUME [ "/usr/src/app/certs" ]
|
||||||
|
|
||||||
|
RUN apk add --update bash curl git openssl ncurses socat
|
||||||
|
|
||||||
|
RUN git clone https://github.com/Neilpang/acme.sh.git && \
|
||||||
|
cd acme.sh && \
|
||||||
|
git checkout 08357e3cb0d80c84bdaf3e42ce0e439665387f57 . && \
|
||||||
|
./acme.sh --install \
|
||||||
|
--cert-home /usr/src/app/certs
|
||||||
|
|
||||||
|
COPY entry.sh /entry.sh
|
||||||
|
COPY cert-provider.sh ./cert-provider.sh
|
||||||
|
COPY fake-le-bundle.pem ./
|
||||||
|
|
||||||
|
ENTRYPOINT [ "/entry.sh" ]
|
||||||
|
CMD [ "/usr/src/app/cert-provider.sh" ]
|
181
cert-provider/cert-provider.sh
Executable file
181
cert-provider/cert-provider.sh
Executable file
@ -0,0 +1,181 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# the acme.sh client script, installed via Git in the Dockerfile...
|
||||||
|
ACME_BIN="$(realpath ~/.acme.sh/acme.sh)"
|
||||||
|
|
||||||
|
# the path to a bundle of certs to verify a LetsEncrypt staging certificate until Apr 2036...
|
||||||
|
ACME_STAGING_CA="/usr/src/app/fake-le-bundle.pem"
|
||||||
|
|
||||||
|
# the path to a file which stores the last successful mode of certificate we acquired...
|
||||||
|
ACME_MODE_FILE="/usr/src/app/certs/last_run_mode"
|
||||||
|
|
||||||
|
# colour output helpers...
|
||||||
|
reset=$(tput -T xterm sgr0)
|
||||||
|
red=$(tput -T xterm setaf 1)
|
||||||
|
green=$(tput -T xterm setaf 2)
|
||||||
|
yellow=$(tput -T xterm setaf 3)
|
||||||
|
blue=$(tput -T xterm setaf 4)
|
||||||
|
|
||||||
|
logError() {
|
||||||
|
echo "${red}[Error]${reset} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
logWarn() {
|
||||||
|
echo "${yellow}[Warn]${reset} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
logInfo() {
|
||||||
|
echo "${blue}[Info]${reset} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
logSuccess() {
|
||||||
|
echo "${green}[Success]${reset} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
logErrorAndStop() {
|
||||||
|
logError "$1 [Stopping]"
|
||||||
|
while true; do
|
||||||
|
# do nothing forever...
|
||||||
|
sleep 60
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
retryWithDelay() {
|
||||||
|
RETRIES=${2:-3}
|
||||||
|
DELAY=${3:-5}
|
||||||
|
|
||||||
|
local ATTEMPT=0
|
||||||
|
while [ $RETRIES -gt $ATTEMPT ]; do
|
||||||
|
let "ATTEMPT++"
|
||||||
|
if $1; then
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "($ATTEMPT/$RETRIES) Retrying in ${DELAY} seconds..."
|
||||||
|
sleep $DELAY
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
waitForOnline() {
|
||||||
|
ADDRESS="${1,,}"
|
||||||
|
|
||||||
|
logInfo "Waiting for ${ADDRESS} to be available via HTTP..."
|
||||||
|
retryWithDelay "curl --output /dev/null --silent --head --fail http://${ADDRESS}" 6 5
|
||||||
|
}
|
||||||
|
|
||||||
|
isUsingStagingCert() {
|
||||||
|
HOST="${1,,}"
|
||||||
|
echo "" | openssl s_client -host "$HOST" -port 443 -showcerts 2>/dev/null | awk '/BEGIN CERT/ {p=1} ; p==1; /END CERT/ {p=0}' | openssl verify -CAfile "$ACME_STAGING_CA" > /dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
pre-flight() {
|
||||||
|
case "$ACTIVE" in
|
||||||
|
"true"|"yes")
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
logError "ACTIVE variable is not enabled. Value should be \"true\" or \"yes\" to continue."
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -z "$DOMAINS" ]; then
|
||||||
|
logError "DOMAINS must be set. Value should be a comma-delimited string of domains."
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
IFS=, read -r -a ACME_DOMAINS <<< "$DOMAINS"
|
||||||
|
IFS=' ' read -r -a ACME_DOMAIN_ARGS <<< "${ACME_DOMAINS[@]/#/-d }"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$VALIDATION" ]; then
|
||||||
|
logInfo "VALIDATION not set. Using default: http-01"
|
||||||
|
VALIDATION="http-01"
|
||||||
|
else
|
||||||
|
case "$VALIDATION" in
|
||||||
|
"http-01")
|
||||||
|
logInfo "Using validation method: $VALIDATION"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
logError "VALIDATION is invalid. Use a valid value: http-01"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$OUTPUT_PEM" ]; then
|
||||||
|
logError "OUTPUT_PEM must be set. Value should be the path to install your certificate to."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
waitToSeeStagingCert() {
|
||||||
|
logInfo "Waiting for ${ACME_DOMAINS[0]} to use a staging certificate..."
|
||||||
|
retryWithDelay "isUsingStagingCert ${ACME_DOMAINS[0]}" 3 5
|
||||||
|
}
|
||||||
|
|
||||||
|
lastAcquiredCertFor() {
|
||||||
|
ACME_MODE="${1:-none}"
|
||||||
|
ACME_LAST_MODE="$(cat $ACME_MODE_FILE || echo '')"
|
||||||
|
logInfo "Last acquired certificate for ${ACME_LAST_MODE^^}"
|
||||||
|
[ "${ACME_LAST_MODE,,}" == "${ACME_MODE,,}" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
acquireCertificate() {
|
||||||
|
ACME_MODE="${1:-staging}"
|
||||||
|
ACME_FORCE="${2:-false}"
|
||||||
|
ACME_OPTS=()
|
||||||
|
|
||||||
|
if [ "${ACME_FORCE,,}" == "true" ];then ACME_OPTS+=("--force"); fi
|
||||||
|
case "$ACME_MODE" in
|
||||||
|
"production")
|
||||||
|
logInfo "Using PRODUCTION mode"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
logInfo "Using STAGING mode"
|
||||||
|
ACME_OPTS+=("--staging")
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
case "$VALIDATION" in
|
||||||
|
"http-01")
|
||||||
|
ACME_OPTS+=("--standalone")
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
logError "VALIDATION is invalid. Use a valid value: http-01"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if ! waitForOnline "${ACME_DOMAINS[0]}"; then
|
||||||
|
logError "Unable to access site over HTTP"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
logInfo "Issuing certificates..."
|
||||||
|
"$ACME_BIN" --issue "${ACME_OPTS[@]}" "${ACME_DOMAIN_ARGS[@]}"
|
||||||
|
|
||||||
|
logInfo "Installing certificates..." && \
|
||||||
|
"$ACME_BIN" --install-cert "${ACME_DOMAIN_ARGS[@]}" \
|
||||||
|
--cert-file /tmp/cert.pem \
|
||||||
|
--key-file /tmp/key.pem \
|
||||||
|
--fullchain-file /tmp/fullchain.pem \
|
||||||
|
--reloadcmd "cat /tmp/fullchain.pem /tmp/key.pem > $OUTPUT_PEM" && \
|
||||||
|
|
||||||
|
echo "${ACME_MODE}" > "${ACME_MODE_FILE}"
|
||||||
|
}
|
||||||
|
|
||||||
|
pre-flight || logErrorAndStop "Unable to continue due to misconfiguration. See errors above."
|
||||||
|
|
||||||
|
waitForOnline "${ACME_DOMAINS[0]}" || logErrorAndStop "Unable to access ${ACME_DOMAINS[0]} on port 80. This is needed for certificate validation."
|
||||||
|
|
||||||
|
if ! lastAcquiredCertFor "production"; then
|
||||||
|
acquireCertificate "staging" || logErrorAndStop "Unable to acquire a staging certificate."
|
||||||
|
waitToSeeStagingCert || logErrorAndStop "Unable to detect certificate change over. Cannot issue a production certificate."
|
||||||
|
acquireCertificate "production" "true" || logErrorAndStop "Unable to acquire a production certificate."
|
||||||
|
fi
|
||||||
|
|
||||||
|
logSuccess "Done!"
|
||||||
|
|
||||||
|
logInfo "Running cron..."
|
||||||
|
crond -f -d 7
|
3
cert-provider/entry.sh
Executable file
3
cert-provider/entry.sh
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
exec "$@"
|
56
cert-provider/fake-le-bundle.pem
Normal file
56
cert-provider/fake-le-bundle.pem
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
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
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEqzCCApOgAwIBAgIRAIvhKg5ZRO08VGQx8JdhT+UwDQYJKoZIhvcNAQELBQAw
|
||||||
|
GjEYMBYGA1UEAwwPRmFrZSBMRSBSb290IFgxMB4XDTE2MDUyMzIyMDc1OVoXDTM2
|
||||||
|
MDUyMzIyMDc1OVowIjEgMB4GA1UEAwwXRmFrZSBMRSBJbnRlcm1lZGlhdGUgWDEw
|
||||||
|
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDtWKySDn7rWZc5ggjz3ZB0
|
||||||
|
8jO4xti3uzINfD5sQ7Lj7hzetUT+wQob+iXSZkhnvx+IvdbXF5/yt8aWPpUKnPym
|
||||||
|
oLxsYiI5gQBLxNDzIec0OIaflWqAr29m7J8+NNtApEN8nZFnf3bhehZW7AxmS1m0
|
||||||
|
ZnSsdHw0Fw+bgixPg2MQ9k9oefFeqa+7Kqdlz5bbrUYV2volxhDFtnI4Mh8BiWCN
|
||||||
|
xDH1Hizq+GKCcHsinDZWurCqder/afJBnQs+SBSL6MVApHt+d35zjBD92fO2Je56
|
||||||
|
dhMfzCgOKXeJ340WhW3TjD1zqLZXeaCyUNRnfOmWZV8nEhtHOFbUCU7r/KkjMZO9
|
||||||
|
AgMBAAGjgeMwgeAwDgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAw
|
||||||
|
HQYDVR0OBBYEFMDMA0a5WCDMXHJw8+EuyyCm9Wg6MHoGCCsGAQUFBwEBBG4wbDA0
|
||||||
|
BggrBgEFBQcwAYYoaHR0cDovL29jc3Auc3RnLXJvb3QteDEubGV0c2VuY3J5cHQu
|
||||||
|
b3JnLzA0BggrBgEFBQcwAoYoaHR0cDovL2NlcnQuc3RnLXJvb3QteDEubGV0c2Vu
|
||||||
|
Y3J5cHQub3JnLzAfBgNVHSMEGDAWgBTBJnSkikSg5vogKNhcI5pFiBh54DANBgkq
|
||||||
|
hkiG9w0BAQsFAAOCAgEABYSu4Il+fI0MYU42OTmEj+1HqQ5DvyAeyCA6sGuZdwjF
|
||||||
|
UGeVOv3NnLyfofuUOjEbY5irFCDtnv+0ckukUZN9lz4Q2YjWGUpW4TTu3ieTsaC9
|
||||||
|
AFvCSgNHJyWSVtWvB5XDxsqawl1KzHzzwr132bF2rtGtazSqVqK9E07sGHMCf+zp
|
||||||
|
DQVDVVGtqZPHwX3KqUtefE621b8RI6VCl4oD30Olf8pjuzG4JKBFRFclzLRjo/h7
|
||||||
|
IkkfjZ8wDa7faOjVXx6n+eUQ29cIMCzr8/rNWHS9pYGGQKJiY2xmVC9h12H99Xyf
|
||||||
|
zWE9vb5zKP3MVG6neX1hSdo7PEAb9fqRhHkqVsqUvJlIRmvXvVKTwNCP3eCjRCCI
|
||||||
|
PTAvjV+4ni786iXwwFYNz8l3PmPLCyQXWGohnJ8iBm+5nk7O2ynaPVW0U2W+pt2w
|
||||||
|
SVuvdDM5zGv2f9ltNWUiYZHJ1mmO97jSY/6YfdOUH66iRtQtDkHBRdkNBsMbD+Em
|
||||||
|
2TgBldtHNSJBfB3pm9FblgOcJ0FSWcUDWJ7vO0+NTXlgrRofRT6pVywzxVo6dND0
|
||||||
|
WzYlTWeUVsO40xJqhgUQRER9YLOLxJ0O6C8i0xFxAMKOtSdodMB3RIwt7RFQ0uyt
|
||||||
|
n5Z5MqkYhlMI3J1tPRTp1nEt9fyGspBOO05gi148Qasp+3N+svqKomoQglNoAxU=
|
||||||
|
-----END CERTIFICATE-----
|
@ -1,10 +1,12 @@
|
|||||||
version: '2.1'
|
version: "2.1"
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
db:
|
certs: {}
|
||||||
registry:
|
cert-provider: {}
|
||||||
s3:
|
db: {}
|
||||||
redis:
|
redis: {}
|
||||||
|
registry: {}
|
||||||
|
s3: {}
|
||||||
|
|
||||||
services:
|
services:
|
||||||
api:
|
api:
|
||||||
@ -20,7 +22,7 @@ services:
|
|||||||
API_VPN_SERVICE_API_KEY: ${OPENBALENA_API_VPN_SERVICE_API_KEY}
|
API_VPN_SERVICE_API_KEY: ${OPENBALENA_API_VPN_SERVICE_API_KEY}
|
||||||
BALENA_ROOT_CA: ${OPENBALENA_ROOT_CA}
|
BALENA_ROOT_CA: ${OPENBALENA_ROOT_CA}
|
||||||
COOKIE_SESSION_SECRET: ${OPENBALENA_COOKIE_SESSION_SECRET}
|
COOKIE_SESSION_SECRET: ${OPENBALENA_COOKIE_SESSION_SECRET}
|
||||||
DB_HOST: db.${OPENBALENA_HOST_NAME}
|
DB_HOST: db
|
||||||
DB_PASSWORD: docker
|
DB_PASSWORD: docker
|
||||||
DB_PORT: 5432
|
DB_PORT: 5432
|
||||||
DB_USER: docker
|
DB_USER: docker
|
||||||
@ -36,10 +38,10 @@ services:
|
|||||||
JSON_WEB_TOKEN_EXPIRY_MINUTES: 10080
|
JSON_WEB_TOKEN_EXPIRY_MINUTES: 10080
|
||||||
JSON_WEB_TOKEN_SECRET: ${OPENBALENA_JWT_SECRET}
|
JSON_WEB_TOKEN_SECRET: ${OPENBALENA_JWT_SECRET}
|
||||||
MIXPANEL_TOKEN: __unused__
|
MIXPANEL_TOKEN: __unused__
|
||||||
PRODUCTION_MODE: '${OPENBALENA_PRODUCTION_MODE}'
|
PRODUCTION_MODE: "${OPENBALENA_PRODUCTION_MODE}"
|
||||||
PUBNUB_PUBLISH_KEY: __unused__
|
PUBNUB_PUBLISH_KEY: __unused__
|
||||||
PUBNUB_SUBSCRIBE_KEY: __unused__
|
PUBNUB_SUBSCRIBE_KEY: __unused__
|
||||||
REDIS_HOST: redis.${OPENBALENA_HOST_NAME}
|
REDIS_HOST: redis
|
||||||
REDIS_PORT: 6379
|
REDIS_PORT: 6379
|
||||||
REGISTRY2_HOST: registry.${OPENBALENA_HOST_NAME}
|
REGISTRY2_HOST: registry.${OPENBALENA_HOST_NAME}
|
||||||
REGISTRY_HOST: registry.${OPENBALENA_HOST_NAME}
|
REGISTRY_HOST: registry.${OPENBALENA_HOST_NAME}
|
||||||
@ -49,7 +51,7 @@ services:
|
|||||||
TOKEN_AUTH_CERT_KEY: ${OPENBALENA_TOKEN_AUTH_KEY}
|
TOKEN_AUTH_CERT_KEY: ${OPENBALENA_TOKEN_AUTH_KEY}
|
||||||
TOKEN_AUTH_CERT_KID: ${OPENBALENA_TOKEN_AUTH_KID}
|
TOKEN_AUTH_CERT_KID: ${OPENBALENA_TOKEN_AUTH_KID}
|
||||||
TOKEN_AUTH_CERT_PUB: ${OPENBALENA_TOKEN_AUTH_PUB}
|
TOKEN_AUTH_CERT_PUB: ${OPENBALENA_TOKEN_AUTH_PUB}
|
||||||
TOKEN_AUTH_JWT_ALGO: 'ES256'
|
TOKEN_AUTH_JWT_ALGO: "ES256"
|
||||||
VPN_HOST: vpn.${OPENBALENA_HOST_NAME}
|
VPN_HOST: vpn.${OPENBALENA_HOST_NAME}
|
||||||
VPN_PORT: 443
|
VPN_PORT: 443
|
||||||
VPN_SERVICE_API_KEY: ${OPENBALENA_VPN_SERVICE_API_KEY}
|
VPN_SERVICE_API_KEY: ${OPENBALENA_VPN_SERVICE_API_KEY}
|
||||||
@ -73,10 +75,10 @@ services:
|
|||||||
BALENA_ROOT_CA: ${OPENBALENA_ROOT_CA}
|
BALENA_ROOT_CA: ${OPENBALENA_ROOT_CA}
|
||||||
BALENA_TOKEN_AUTH_ISSUER: api.${OPENBALENA_HOST_NAME}
|
BALENA_TOKEN_AUTH_ISSUER: api.${OPENBALENA_HOST_NAME}
|
||||||
BALENA_TOKEN_AUTH_REALM: https://api.${OPENBALENA_HOST_NAME}/auth/v1/token
|
BALENA_TOKEN_AUTH_REALM: https://api.${OPENBALENA_HOST_NAME}/auth/v1/token
|
||||||
COMMON_REGION:
|
COMMON_REGION:
|
||||||
REGISTRY2_S3_BUCKET:
|
REGISTRY2_S3_BUCKET:
|
||||||
REGISTRY2_S3_KEY:
|
REGISTRY2_S3_KEY:
|
||||||
REGISTRY2_S3_SECRET:
|
REGISTRY2_S3_SECRET:
|
||||||
REGISTRY2_SECRETKEY: ${OPENBALENA_REGISTRY_SECRET_KEY}
|
REGISTRY2_SECRETKEY: ${OPENBALENA_REGISTRY_SECRET_KEY}
|
||||||
REGISTRY2_STORAGEPATH: /data
|
REGISTRY2_STORAGEPATH: /data
|
||||||
|
|
||||||
@ -94,10 +96,10 @@ services:
|
|||||||
BALENA_API_HOST: api.${OPENBALENA_HOST_NAME}
|
BALENA_API_HOST: api.${OPENBALENA_HOST_NAME}
|
||||||
BALENA_ROOT_CA: ${OPENBALENA_ROOT_CA}
|
BALENA_ROOT_CA: ${OPENBALENA_ROOT_CA}
|
||||||
BALENA_VPN_PORT: 443
|
BALENA_VPN_PORT: 443
|
||||||
PRODUCTION_MODE: '${OPENBALENA_PRODUCTION_MODE}'
|
PRODUCTION_MODE: "${OPENBALENA_PRODUCTION_MODE}"
|
||||||
RESIN_VPN_GATEWAY: 10.2.0.1
|
RESIN_VPN_GATEWAY: 10.2.0.1
|
||||||
SENTRY_DSN:
|
SENTRY_DSN:
|
||||||
VPN_HAPROXY_USEPROXYPROTOCOL: 'true'
|
VPN_HAPROXY_USEPROXYPROTOCOL: "true"
|
||||||
VPN_OPENVPN_CA_CRT: ${OPENBALENA_VPN_CA}
|
VPN_OPENVPN_CA_CRT: ${OPENBALENA_VPN_CA}
|
||||||
VPN_OPENVPN_SERVER_CRT: ${OPENBALENA_VPN_SERVER_CRT}
|
VPN_OPENVPN_SERVER_CRT: ${OPENBALENA_VPN_SERVER_CRT}
|
||||||
VPN_OPENVPN_SERVER_DH: ${OPENBALENA_VPN_SERVER_DH}
|
VPN_OPENVPN_SERVER_DH: ${OPENBALENA_VPN_SERVER_DH}
|
||||||
@ -135,11 +137,12 @@ services:
|
|||||||
build: ../haproxy
|
build: ../haproxy
|
||||||
depends_on:
|
depends_on:
|
||||||
- api
|
- api
|
||||||
- registry
|
- cert-provider
|
||||||
- vpn
|
|
||||||
- db
|
- db
|
||||||
- s3
|
- s3
|
||||||
- redis
|
- redis
|
||||||
|
- registry
|
||||||
|
- vpn
|
||||||
ports:
|
ports:
|
||||||
- "80:80"
|
- "80:80"
|
||||||
- "443:443"
|
- "443:443"
|
||||||
@ -162,3 +165,15 @@ services:
|
|||||||
BALENA_HAPROXY_KEY: ${OPENBALENA_ROOT_KEY}
|
BALENA_HAPROXY_KEY: ${OPENBALENA_ROOT_KEY}
|
||||||
BALENA_ROOT_CA: ${OPENBALENA_ROOT_CA}
|
BALENA_ROOT_CA: ${OPENBALENA_ROOT_CA}
|
||||||
HAPROXY_HOSTNAME: ${OPENBALENA_HOST_NAME}
|
HAPROXY_HOSTNAME: ${OPENBALENA_HOST_NAME}
|
||||||
|
volumes:
|
||||||
|
- certs:/certs:ro
|
||||||
|
|
||||||
|
cert-provider:
|
||||||
|
build: ../cert-provider
|
||||||
|
volumes:
|
||||||
|
- certs:/certs
|
||||||
|
- cert-provider:/usr/src/app/certs
|
||||||
|
environment:
|
||||||
|
ACTIVE: ${OPENBALENA_ACME_CERT_ENABLED}
|
||||||
|
DOMAINS: "api.${OPENBALENA_HOST_NAME},registry.${OPENBALENA_HOST_NAME},s3.${OPENBALENA_HOST_NAME},vpn.${OPENBALENA_HOST_NAME}"
|
||||||
|
OUTPUT_PEM: /certs/open-balena.pem
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
FROM haproxy:1.8-alpine
|
FROM haproxy:1.9-alpine
|
||||||
|
|
||||||
|
VOLUME [ "/certs" ]
|
||||||
|
|
||||||
|
RUN apk add --update inotify-tools
|
||||||
|
|
||||||
COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg
|
COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg
|
||||||
COPY entry.sh /open-balena-entry
|
COPY start-haproxy.sh /start-haproxy
|
||||||
|
|
||||||
CMD /open-balena-entry
|
CMD /start-haproxy
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
set -e
|
|
||||||
|
|
||||||
HAPROXY_CHAIN=/etc/ssl/private/open-balena.pem
|
|
||||||
mkdir -p "$(dirname "${HAPROXY_CHAIN}")"
|
|
||||||
(
|
|
||||||
echo "${BALENA_HAPROXY_CRT}" | base64 -d
|
|
||||||
echo "${BALENA_HAPROXY_KEY}" | base64 -d
|
|
||||||
echo "${BALENA_ROOT_CA}" | base64 -d
|
|
||||||
) > "${HAPROXY_CHAIN}"
|
|
||||||
exec haproxy -f /usr/local/etc/haproxy/haproxy.cfg
|
|
@ -12,6 +12,9 @@ frontend http-in
|
|||||||
bind *:80
|
bind *:80
|
||||||
reqadd X-Forwarded-Proto:\ http
|
reqadd X-Forwarded-Proto:\ http
|
||||||
|
|
||||||
|
acl is_cert_validation path -i -m beg "/.well-known/acme-challenge/"
|
||||||
|
use_backend cert-provider if is_cert_validation
|
||||||
|
|
||||||
acl host_api hdr_dom(host) -i "api.${HAPROXY_HOSTNAME}"
|
acl host_api hdr_dom(host) -i "api.${HAPROXY_HOSTNAME}"
|
||||||
use_backend backend_api if host_api
|
use_backend backend_api if host_api
|
||||||
|
|
||||||
@ -80,6 +83,12 @@ backend backend_s3
|
|||||||
option forwardfor
|
option forwardfor
|
||||||
balance roundrobin
|
balance roundrobin
|
||||||
|
|
||||||
|
backend cert-provider
|
||||||
|
mode http
|
||||||
|
option forwardfor
|
||||||
|
balance roundrobin
|
||||||
|
server resin_cert-provider_1 cert-provider:80 no-check
|
||||||
|
|
||||||
backend vpn-devices
|
backend vpn-devices
|
||||||
mode tcp
|
mode tcp
|
||||||
server resin_vpn_1 vpn:443 send-proxy-v2 check-send-proxy port 443
|
server resin_vpn_1 vpn:443 send-proxy-v2 check-send-proxy port 443
|
||||||
|
32
haproxy/start-haproxy.sh
Executable file
32
haproxy/start-haproxy.sh
Executable file
@ -0,0 +1,32 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
OPENBALENA_CERT=/etc/ssl/private/open-balena.pem
|
||||||
|
mkdir -p "$(dirname "${OPENBALENA_CERT}")"
|
||||||
|
|
||||||
|
if [ -f "/certs/open-balena.pem" ]; then
|
||||||
|
echo "Using certificate from cert-provider..."
|
||||||
|
cp /certs/open-balena.pem "${OPENBALENA_CERT}"
|
||||||
|
else
|
||||||
|
echo "Building certificate from environment variables..."
|
||||||
|
(
|
||||||
|
echo "${BALENA_HAPROXY_CRT}" | base64 -d
|
||||||
|
echo "${BALENA_HAPROXY_KEY}" | base64 -d
|
||||||
|
echo "${BALENA_ROOT_CA}" | base64 -d
|
||||||
|
) > "${OPENBALENA_CERT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
haproxy -f /usr/local/etc/haproxy/haproxy.cfg -W &
|
||||||
|
HAPROXY_PID=$!
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
inotifywait -r -e create -e modify -e delete /certs
|
||||||
|
|
||||||
|
if [ -f "/certs/open-balena.pem" ]; then
|
||||||
|
echo "Updating certificate from cert-provider..."
|
||||||
|
cp /certs/open-balena.pem "${OPENBALENA_CERT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Certificate change detected. Reloading..."
|
||||||
|
kill -SIGUSR2 $HAPROXY_PID
|
||||||
|
sleep 1;
|
||||||
|
done
|
@ -94,4 +94,5 @@ export OPENBALENA_REGISTRY_SECRET_KEY=$(randstr 32)
|
|||||||
export OPENBALENA_SSH_AUTHORIZED_KEYS=
|
export OPENBALENA_SSH_AUTHORIZED_KEYS=
|
||||||
export OPENBALENA_SUPERUSER_EMAIL=$SUPERUSER_EMAIL
|
export OPENBALENA_SUPERUSER_EMAIL=$SUPERUSER_EMAIL
|
||||||
export OPENBALENA_SUPERUSER_PASSWORD=$(printf "%q" "${SUPERUSER_PASSWORD}")
|
export OPENBALENA_SUPERUSER_PASSWORD=$(printf "%q" "${SUPERUSER_PASSWORD}")
|
||||||
|
export OPENBALENA_ACME_CERT_ENABLED=${ACME_CERT_ENABLED:-false}
|
||||||
STR
|
STR
|
||||||
|
@ -21,6 +21,10 @@ fi
|
|||||||
|
|
||||||
source "${BASH_SOURCE%/*}/_realpath"
|
source "${BASH_SOURCE%/*}/_realpath"
|
||||||
|
|
||||||
|
domainResolves() {
|
||||||
|
getent hosts "$1" > /dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
CMD="$(realpath "$0")"
|
CMD="$(realpath "$0")"
|
||||||
DIR="$(dirname "${CMD}")"
|
DIR="$(dirname "${CMD}")"
|
||||||
BASE_DIR="$(dirname "${DIR}")"
|
BASE_DIR="$(dirname "${DIR}")"
|
||||||
@ -30,8 +34,9 @@ CERTS_DIR="${CONFIG_DIR}/certs"
|
|||||||
DOMAIN=openbalena.local
|
DOMAIN=openbalena.local
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
echo "usage: $0 [-h] [-p] [-d DOMAIN] -U EMAIL -P PASSWORD"
|
echo "usage: $0 [-c] [-h] [-p] [-d DOMAIN] -U EMAIL -P PASSWORD"
|
||||||
echo
|
echo
|
||||||
|
echo " -c enable the ACME certificate service in staging or production mode."
|
||||||
echo " -p patch hosts - patch the host /etc/hosts file"
|
echo " -p patch hosts - patch the host /etc/hosts file"
|
||||||
echo " -d DOMAIN the domain name this deployment will run as, eg. example.com. Default is 'openbalena.local'"
|
echo " -d DOMAIN the domain name this deployment will run as, eg. example.com. Default is 'openbalena.local'"
|
||||||
echo " -U EMAIL the email address of the superuser account, used to login to your install from the Balena CLI"
|
echo " -U EMAIL the email address of the superuser account, used to login to your install from the Balena CLI"
|
||||||
@ -41,7 +46,7 @@ usage() {
|
|||||||
|
|
||||||
show_help=false
|
show_help=false
|
||||||
patch_hosts=false
|
patch_hosts=false
|
||||||
while getopts ":hpxd:U:P:" opt; do
|
while getopts ":chpxd:U:P:" opt; do
|
||||||
case "${opt}" in
|
case "${opt}" in
|
||||||
h) show_help=true;;
|
h) show_help=true;;
|
||||||
p) patch_hosts=true;;
|
p) patch_hosts=true;;
|
||||||
@ -49,6 +54,7 @@ while getopts ":hpxd:U:P:" opt; do
|
|||||||
d) DOMAIN="${OPTARG}";;
|
d) DOMAIN="${OPTARG}";;
|
||||||
U) SUPERUSER_EMAIL="${OPTARG}";;
|
U) SUPERUSER_EMAIL="${OPTARG}";;
|
||||||
P) SUPERUSER_PASSWORD="${OPTARG}";;
|
P) SUPERUSER_PASSWORD="${OPTARG}";;
|
||||||
|
c) ACME_CERT_ENABLED="true";;
|
||||||
*)
|
*)
|
||||||
echo "Invalid argument: -${OPTARG}"
|
echo "Invalid argument: -${OPTARG}"
|
||||||
usage
|
usage
|
||||||
@ -68,8 +74,17 @@ if [ "$show_help" = "true" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ ! -z "$ACME_CERT_ENABLED" ]; then
|
||||||
|
echo "${BLUE}[INFO]${RESET} ACME Certificate request is ${BOLD}ENABLED${RESET}."
|
||||||
|
|
||||||
|
if ! domainResolves "api.${DOMAIN}"; then
|
||||||
|
echo "${YELLOW}[WARN]${RESET} Unable to resolve \"api.${DOMAIN}\"!"
|
||||||
|
echo "${YELLOW}[WARN]${RESET} This might mean that you cannot use an ACME issued certificate."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
echo_bold() {
|
echo_bold() {
|
||||||
printf "\\033[1m%s\\033[0m\\n" "${@}"
|
echo "${BOLD}${@}${RESET}"
|
||||||
}
|
}
|
||||||
|
|
||||||
echo_bold "==> Creating new configuration at: $CONFIG_DIR"
|
echo_bold "==> Creating new configuration at: $CONFIG_DIR"
|
||||||
@ -110,5 +125,7 @@ fi
|
|||||||
echo_bold "==> Success!"
|
echo_bold "==> Success!"
|
||||||
echo ' - Start the instance with: ./scripts/compose up -d'
|
echo ' - Start the instance with: ./scripts/compose up -d'
|
||||||
echo ' - Stop the instance with: ./scripts/compose stop'
|
echo ' - Stop the instance with: ./scripts/compose stop'
|
||||||
echo ' - To create the superuser, see: ./scripts/create-superuser -h'
|
|
||||||
echo " - Use the following certificate with Balena CLI: ${CERTS_DIR}/root/ca.crt"
|
if [ -z "${ACME_CERT_ENABLED}" ]; then
|
||||||
|
echo " - Use the following certificate with Balena CLI: ${CERTS_DIR}/root/ca.crt"
|
||||||
|
fi
|
||||||
|
Loading…
x
Reference in New Issue
Block a user