Merge pull request #37 from qdm12/docker

Docker
This commit is contained in:
Bubka 2021-08-09 21:41:43 +02:00 committed by GitHub
commit d1927fc41f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 628 additions and 0 deletions

16
.dockerignore Normal file
View File

@ -0,0 +1,16 @@
.git
.github
tests
.dockerignore
.editorconfig
.env.example
.env.testing
.gitattributes
.gitignore
.styleci.yml
.travis.yml
changelog.md
Dockerfile
LICENSE
README.md
webpack.mix.js

114
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,114 @@
name: CI
on:
push:
paths:
- .github/workflows/ci.yml
- app/**
- bootstrap/**
- config/**
- database/**
- docker/**
- public/**
- resources/**
- routes/**
- storage/**
- tests/**
- .dockerignore
- .env.travis
- artisan
- composer.json
- composer.lock
- Dockerfile
- phpunit.xml
- server.php
pull_request:
paths:
- .github/workflows/ci.yml
- app/**
- bootstrap/**
- config/**
- database/**
- docker/**
- public/**
- resources/**
- routes/**
- storage/**
- tests/**
- .dockerignore
- .env.travis
- artisan
- composer.json
- composer.lock
- Dockerfile
- phpunit.xml
- server.php
jobs:
verify:
runs-on: ubuntu-latest
env:
DOCKER_BUILDKIT: "1"
steps:
- uses: actions/checkout@v2.3.4
- name: Build test image
run: docker build --target test -t test-container .
- name: Run tests in test container
run: |
touch coverage.txt
docker run --rm \
test-container
- name: Build final image
run: docker build .
publish:
needs: [verify]
if: github.event_name == 'push' && github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.3.4
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- uses: docker/setup-qemu-action@v1
- uses: docker/setup-buildx-action@v1
- uses: docker/login-action@v1
with:
username: 2fauth
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Set variables
id: vars
env:
EVENT_NAME: ${{ github.event_name }}
run: |
BRANCH=${GITHUB_REF#refs/heads/}
TAG=${GITHUB_REF#refs/tags/}
echo ::set-output name=commit::$(git rev-parse --short HEAD)
echo ::set-output name=created::$(date -u +%Y-%m-%dT%H:%M:%SZ)
if [ "$TAG" != "$GITHUB_REF" ]; then
echo ::set-output name=version::$TAG
echo ::set-output name=platforms::linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7
elif [ "$BRANCH" = "master" ]; then
echo ::set-output name=version::latest
echo ::set-output name=platforms::linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7
else
echo ::set-output name=version::$BRANCH
echo ::set-output name=platforms::linux/amd64
fi
- name: Build and push final image
uses: docker/build-push-action@v2.6.1
with:
platforms: ${{ steps.vars.outputs.platforms }}
build-args: |
CREATED=${{ steps.vars.outputs.created }}
COMMIT=${{ steps.vars.outputs.commit }}
VERSION=${{ steps.vars.outputs.version }}
tags: |
2fauth/2fauth:${{ steps.vars.outputs.version }}
push: true

22
.github/workflows/dockerhub-readme.yml vendored Normal file
View File

@ -0,0 +1,22 @@
name: Docker Hub description
on:
push:
branches: [master]
paths:
- docker/README.md
- .github/workflows/dockerhub-readme.yml
jobs:
dockerHubDescription:
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2.3.4
- name: Docker Hub Description
uses: peter-evans/dockerhub-description@v2.4.3
with:
username: 2fauth
password: ${{ secrets.DOCKERHUB_PASSWORD }}
repository: 2fauth/2fauth
short-description: A web app to manage your Two-Factor Authentication (2FA) accounts and generate their security codes
readme-filepath: docker/README.md

185
Dockerfile Normal file
View File

@ -0,0 +1,185 @@
ARG BUILDPLATFORM=linux/amd64
ARG TARGETPLATFORM
ARG ALPINE_VERSION=3.14
ARG PHP_VERSION=7.3-alpine${ALPINE_VERSION}
ARG COMPOSER_VERSION=2.1
ARG SUPERVISORD_VERSION=v0.7.3
FROM --platform=${BUILDPLATFORM} composer:${COMPOSER_VERSION} AS build-composer
FROM composer:${COMPOSER_VERSION} AS composer
FROM qmcgaw/binpot:supervisord-${SUPERVISORD_VERSION} AS supervisord
FROM --platform=${BUILDPLATFORM} php:${PHP_VERSION} AS vendor
COPY --from=build-composer --chown=${UID}:${GID} /usr/bin/composer /usr/bin/composer
RUN apk add --no-cache unzip
WORKDIR /srv
COPY artisan composer.json composer.lock ./
COPY database ./database
RUN composer install --prefer-dist --no-scripts --no-dev --no-autoloader
RUN composer dump-autoload --no-scripts --no-dev --optimize
FROM --platform=${BUILDPLATFORM} vendor AS test
COPY . .
RUN mv .env.travis .env
RUN composer install
RUN php artisan key:generate
ENTRYPOINT [ "/srv/vendor/bin/phpunit" ]
FROM alpine:${ALPINE_VERSION}
ARG UID=1000
ARG GID=1000
# Composer 2
COPY --from=composer --chown=${UID}:${GID} /usr/bin/composer /usr/bin/composer
# Supervisord from https://github.com/ochinchina/supervisord
COPY --from=supervisord --chown=${UID}:${GID} /bin /usr/local/bin/supervisord
# Install PHP and PHP system dependencies
RUN apk add --update --no-cache \
# PHP
php7 \
# Composer dependencies
php7-phar \
# PHP SQLite driver
php7-pdo_sqlite php7-sqlite3 \
# PHP extensions
php7-xml php7-gd php7-mbstring \
# Runtime dependencies
php7-session php7-json php7-openssl \
# Nginx and PHP FPM to serve over HTTP
php7-fpm nginx \
&& \
# Clean up
rm /etc/nginx/nginx.conf && \
# Fix ownership to ${UID}:${GID}
chown -R ${UID}:${GID} /var/lib/nginx/
# PHP FPM configuration
# Change username and ownership in php-fpm pool config
RUN sed -i '/user = nobody/d' /etc/php7/php-fpm.d/www.conf && \
sed -i '/group = nobody/d' /etc/php7/php-fpm.d/www.conf && \
sed -i '/listen.owner/d' /etc/php7/php-fpm.d/www.conf && \
sed -i '/listen.group/d' /etc/php7/php-fpm.d/www.conf
# Pre-create files with the correct permissions
RUN mkdir /run/php && \
chown ${UID}:${GID} /run/php /var/log/php7 && \
chmod 700 /run/php /var/log/php7
# Nginx configuration
EXPOSE 8000/tcp
RUN touch /run/nginx/nginx.pid /var/lib/nginx/logs/error.log && \
chown ${UID}:${GID} /run/nginx/nginx.pid /var/lib/nginx/logs/error.log
COPY --chown=${UID}:${GID} docker/nginx.conf /etc/nginx/nginx.conf
RUN nginx -t
# Supervisord configuration
COPY --chown=${UID}:${GID} docker/supervisord.conf /etc/supervisor/supervisord.conf
# Create end user directory
RUN mkdir -p /2fauth && \
chown -R ${UID}:${GID} /2fauth && \
chmod 700 /2fauth
# Create /srv internal directory
WORKDIR /srv
RUN chown -R ${UID}:${GID} /srv && \
chmod 700 /srv
# Run without root
USER ${UID}:${GID}
# Dependencies
COPY --from=vendor --chown=${UID}:${GID} /srv/vendor /srv/vendor
# Copy the rest of the code
COPY --chown=${UID}:${GID} . .
# RUN composer dump-autoload --no-scripts --no-dev --optimize
# Entrypoint
ENTRYPOINT [ "/usr/local/bin/entrypoint.sh" ]
COPY --chown=${UID}:${GID} docker/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod 500 /usr/local/bin/entrypoint.sh
ENV \
# You can change the name of the app
APP_NAME=2FAuth \
# You can leave this on "local". If you change it to production most console commands will ask for extra confirmation.
# Never set it to "testing".
APP_ENV=local \
# Set to true if you want to see debug information in error screens.
APP_DEBUG=false \
# This should be your email address
SITE_OWNER=mail@example.com \
# The encryption key for our database and sessions. Keep this very secure.
# If you generate a new one all existing data must be considered LOST.
# Change it to a string of exactly 32 chars or use command `php artisan key:generate` to generate it
APP_KEY=SomeRandomStringOf32CharsExactly \
# This variable must match your installation's external address but keep in mind that
# it's only used on the command line as a fallback value.
APP_URL=http://localhost \
# Turn this to true if you want your app to react like a demo.
# The Demo mode reset the app content every hours and set a generic demo user.
IS_DEMO_APP=false \
# The log channel defines where your log entries go to.
# 'daily' is the default logging mode giving you 5 daily rotated log files in /storage/logs/.
# Several other options exist. You can use 'single' for one big fat error log (not recommended).
# Also available are 'syslog', 'errorlog' and 'stdout' which will log to the system itself.
LOG_CHANNEL=daily \
# Log level. You can set this from least severe to most severe:
# debug, info, notice, warning, error, critical, alert, emergency
# If you set it to debug your logs will grow large, and fast. If you set it to emergency probably
# nothing will get logged, ever.
APP_LOG_LEVEL=notice \
# Database config & credentials
# DB_CONNECTION can only be sqlite
DB_CONNECTION=sqlite \
DB_DATABASE="/srv/database/database.sqlite" \
# If you're looking for performance improvements, you could install memcached.
CACHE_DRIVER=file \
SESSION_DRIVER=file \
# Mail settings
# Refer your email provider documentation to configure your mail settings
# Set a value for every available setting to avoid issue
MAIL_DRIVER=log \
MAIL_HOST=smtp.mailtrap.io \
MAIL_PORT=2525 \
MAIL_FROM=changeme@example.com \
MAIL_USERNAME=null \
MAIL_PASSWORD=null \
MAIL_ENCRYPTION=null \
MAIL_FROM_NAME=null \
MAIL_FROM_ADDRESS=null \
# Leave the following configuration vars as is.
# Unless you like to tinker and know what you're doing.
BROADCAST_DRIVER=log \
QUEUE_DRIVER=sync \
SESSION_LIFETIME=12 \
REDIS_HOST=127.0.0.1 \
REDIS_PASSWORD=null \
REDIS_PORT=6379 \
PUSHER_APP_ID= \
PUSHER_APP_KEY= \
PUSHER_APP_SECRET= \
PUSHER_APP_CLUSTER=mt1 \
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" \
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" \
MIX_ENV=local
ARG VERSION=unknown
ARG CREATED="an unknown date"
ARG COMMIT=unknown
ENV \
VERSION=${VERSION} \
CREATED=${CREATED} \
COMMIT=${COMMIT}
LABEL \
org.opencontainers.image.authors="https://github.com/Bubka" \
org.opencontainers.image.version=$VERSION \
org.opencontainers.image.created=$CREATED \
org.opencontainers.image.revision=$COMMIT \
org.opencontainers.image.url="https://github.com/Bubka/2FAuth" \
org.opencontainers.image.documentation="https://hub.docker.com/r/2fauth/2fauth" \
org.opencontainers.image.source="https://github.com/Bubka/2FAuth" \
org.opencontainers.image.title="2fauth" \
org.opencontainers.image.description="A web app to manage your Two-Factor Authentication (2FA) accounts and generate their security codes"

View File

@ -1,6 +1,7 @@
# 2FAuth
![https://travis-ci.com/github/Bubka/2FAuth](https://img.shields.io/travis/com/bubka/2fauth?style=flat-square)
[![Docker build status](https://github.com/Bubka/2fauth/actions/workflows/ci.yml/badge.svg)](https://github.com/Bubka/2fauth/actions/workflows/ci.yml)
![https://codecov.io/gh/Bubka/2FAuth](https://img.shields.io/codecov/c/github/Bubka/2FAuth?style=flat-square)
![https://github.com/Bubka/2FAuth/blob/master/LICENSE](https://img.shields.io/github/license/Bubka/2FAuth.svg?style=flat-square)
@ -10,6 +11,8 @@ A web app to manage your Two-Factor Authentication (2FA) accounts and generate t
[**2FAuth Demo**](https://demo.2fauth.app/)
[**Use it with Docker**](docker)
Credentials (login - password) : *demo@2fauth.app* - *demo*
## Purpose

117
docker/README.md Normal file
View File

@ -0,0 +1,117 @@
# Docker
[![Build status](https://github.com/Bubka/2fauth/actions/workflows/ci.yml/badge.svg)](https://github.com/Bubka/2fauth/actions/workflows/ci.yml)
[![dockeri.co](https://dockeri.co/image/2fauth/2fauth)](https://hub.docker.com/r/2fauth/2fauth)
You can run 2fauth in a single Docker container.
## Features
- [![Latest size](https://img.shields.io/docker/image-size/2fauth/2fauth/latest?label=Image%20size)](https://hub.docker.com/r/2fauth/2fauth/tags)
- Compatible with: `amd64`, `386`, `arm64`, `arm/v6` and `arm/v7`
- Stores data in an Sqlite database file
- Runs without root as user with id `1000` and group id `1000`
## Setup
We assume your current directory is `/yourpath`.
1. Create a directory on your host:
```sh
mkdir 2fauth
```
1. **If your host is not Windows**: since the container runs without root as user `1000:1000`, you need to fix the ownership and permissions of that directory:
```sh
chown 1000:1000 2fauth
chmod 700 2fauth
```
💁 if you feel like using another ID, you can [build the image with build arguments](#Build-the-image-with-build-arguments).
1. Run the container interactively:
```sh
docker run -it --rm -p 8000:8000/tcp \
-v /yourpath/2fauth:/2fauth 2fauth/2fauth
```
1. Access it at [http://localhost:8000](http://localhost:8000)
You can stop it with `CTRL+C`.
- You can also run it in the background by replacing `-it --rm` with `-d`.
- You can set environment variables available (see the [.env.example](.env.example)) with `-e`, for example `-e APP_NAME=2FAuth`.
- You can also use the [docker-compose.yml](docker-compose.yml) with `docker-compose` and modify it as you wish.
### Use an existing SQLite file
If you already have an SQLite file, move it to `/yourpath/2fauth/database.sqlite` on your host before starting the container. Don't forget to fix its ownership and permissions if you run on *nix:
```sh
chown 1000:1000 /yourpath/2fauth/database.sqlite
chmod 700 /yourpath/2fauth/database.sqlite
```
The container will automagically pick it up.
## Update
⚠️ At the very least, backup your `database.sqlite` file to avoid bad surprises!
The Docker image `2fauth/2fauth` is built on every commit pushed to the `master` branch.
You can therefore pull the image with `docker pull 2fauth/2fauth` and restart the container to update it.
You can also use tagged images, see [Docker Hub tags](https://hub.docker.com/r/2fauth/2fauth/tags?page=1&ordering=last_updated) which are produced on Github releases.
## Build the image
You can build the image from the `master` branch with `docker` and `git` using:
```sh
docker build -t 2fauth/2fauth https://github.com/Bubka/2FAuth.git
```
### Build the image for a specific release
You can build a [specific release](https://github.com/Bubka/2FAuth/releases) by appending the release tag with `#<release-tag>` to the command. For example:
```sh
docker build -t 2fauth/2fauth https://github.com/Bubka/2FAuth.git#v2.1.0
```
### Build the image for a specific commit
You can build a specific commit (see [master's commits](https://github.com/Bubka/2FAuth/commits/master)) by appending the commit hash with `#<commit-hash>` to the command. For example:
```sh
docker build -t 2fauth/2fauth https://github.com/Bubka/2FAuth.git#fba9e29bd4e3bb697296bb0bde60ae869537528b
```
### Build the image with build arguments
There are the following build arguments you can use to customize the image using `--build-arg key=value`:
| Build argument | Default | Description |
| --- | --- | --- |
| `UID` | 1000 | The UID of the user to run the container as |
| `GID` | 1000 | The GID of the user to run the container as |
| `DEBIAN_VERSION` | `buster-slim` | The Debian version to use |
| `PHP_VERSION` | `7.3-buster` | The PHP version to use to get composer dependencies |
| `COMPOSER_VERSION` | `2.1` | The version of composer to use |
| `SUPERVISORD_VERSION` | `v0.7.3` | The version of supervisord to use |
| `VERSION` | `unknown` | The version of the image |
| `CREATED` | `an unknown date` | The date of the image build time |
| `COMMIT` | `unknown` | The commit hash of the Git commit used |
## Implementation details
- The final Docker image is based on `alpine:3.14` with minimal packages installed
- The container runs [`supervisord`](https://github.com/ochinchina/supervisord) to handle both an Nginx server and a PHP-FPM server together
- The `/srv` directory holds the repository data and PHP code.
- The `/2fauth` directory is targeted for the container end users.
- By default the container logs the Nginx logs and the PHP-FPM logs. The application logs (if any) can be found in `/2fauth/storage/logs`.

71
docker/docker-compose.yml Normal file
View File

@ -0,0 +1,71 @@
version: "3"
services:
2fauth:
image: 2fauth/2fauth
container_name: 2fauth
volumes:
- ./2fauth:/2fauth
ports:
- 8000:8000/tcp
environment:
# You can change the name of the app
- APP_NAME=2FAuth
# You can leave this on "local". If you change it to production most console commands will ask for extra confirmation.
# Never set it to "testing".
- APP_ENV=local
# Set to true if you want to see debug information in error screens.
- APP_DEBUG=false
# This should be your email address
- SITE_OWNER=mail@example.com
# The encryption key for our database and sessions. Keep this very secure.
# If you generate a new one all existing data must be considered LOST.
# Change it to a string of exactly 32 chars or use command `php artisan key:generate` to generate it
- APP_KEY=SomeRandomStringOf32CharsExactly
# This variable must match your installation's external address but keep in mind that
# it's only used on the command line as a fallback value.
- APP_URL=http://localhost
# Turn this to true if you want your app to react like a demo.
# The Demo mode reset the app content every hours and set a generic demo user.
- IS_DEMO_APP=false
# The log channel defines where your log entries go to.
# 'daily' is the default logging mode giving you 5 daily rotated log files in /storage/logs/.
# Several other options exist. You can use 'single' for one big fat error log (not recommended).
# Also available are 'syslog', 'errorlog' and 'stdout' which will log to the system itself.
- LOG_CHANNEL=daily
# Log level. You can set this from least severe to most severe:
# debug, info, notice, warning, error, critical, alert, emergency
# If you set it to debug your logs will grow large, and fast. If you set it to emergency probably
# nothing will get logged, ever.
- APP_LOG_LEVEL=notice
# Database config (can only be sqlite)
- DB_DATABASE="/srv/database/database.sqlite"
# If you're looking for performance improvements, you could install memcached.
- CACHE_DRIVER=file
- SESSION_DRIVER=file
# Mail settings
# Refer your email provider documentation to configure your mail settings
# Set a value for every available setting to avoid issue
- MAIL_DRIVER=log
- MAIL_HOST=smtp.mailtrap.io
- MAIL_PORT=2525
- MAIL_FROM=changeme@example.com
- MAIL_USERNAME=null
- MAIL_PASSWORD=null
- MAIL_ENCRYPTION=null
- MAIL_FROM_NAME=null
- MAIL_FROM_ADDRESS=null
# Leave the following configuration vars as is.
# Unless you like to tinker and know what you're doing.
- BROADCAST_DRIVER=log
- QUEUE_DRIVER=sync
- SESSION_LIFETIME=12
- REDIS_HOST=127.0.0.1
- REDIS_PASSWORD=null
- REDIS_PORT=6379
- PUSHER_APP_ID=
- PUSHER_APP_KEY=
- PUSHER_APP_SECRET=
- PUSHER_APP_CLUSTER=mt1
- MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
- MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
- MIX_ENV=local

45
docker/entrypoint.sh Normal file
View File

@ -0,0 +1,45 @@
#!/bin/sh
set -e
echo "Running version ${VERSION} commit ${COMMIT} built on ${CREATED}"
# Show versions
echo "supervisord version: $(supervisord version)"
php-fpm7 -v | head -n 1
nginx -v
if [ "${DB_CONNECTION}" = "sqlite" ]; then
if [ ! -f /2fauth/database.sqlite ]; then
touch /2fauth/database.sqlite
fi
rm -f /srv/database/database.sqlite
ln -s /2fauth/database.sqlite /srv/database/database.sqlite
fi
# Inject storage in /2fauth and use it with a symlink
if [ ! -d /2fauth/storage ]; then
mv /srv/storage /2fauth/storage
else
rm -r /srv/storage
fi
ln -s /2fauth/storage /srv/storage
# Note: ${COMMIT} is set by the CI
if [ -f /2fauth/installed ]; then
INSTALLED_COMMIT="$(cat /2fauth/installed)"
if [ "${INSTALLED_COMMIT}" != "${COMMIT}" ]; then
echo "Installed commit ${INSTALLED_COMMIT} is different from program commit ${COMMIT}, we are migrating..."
php artisan migrate
php artisan config:clear
fi
else
php artisan migrate:refresh
php artisan passport:install
fi
echo "${COMMIT}" > /2fauth/installed
php artisan storage:link
php artisan config:cache
supervisord

36
docker/nginx.conf Normal file
View File

@ -0,0 +1,36 @@
events {}
http {
include mime.types;
access_log /dev/stdout;
error_log /dev/stderr;
server {
listen 8000;
server_name 2fAuth;
root /srv/public;
index index.php;
charset utf-8;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
error_page 404 /index.php;
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
}
}

19
docker/supervisord.conf Normal file
View File

@ -0,0 +1,19 @@
[supervisord]
nodaemon=true
pidfile=/run/supervisord.pid
loglevel=info
[program-default]
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
autorestart=false
startretries=0
[program:php-fpm]
command=/usr/sbin/php-fpm7 -F
[program:nginx]
command=/usr/sbin/nginx -g 'daemon off;'
depends_on=php-fpm