From fe458ce877f520cc7f20116db4ff2a0536ff319b Mon Sep 17 00:00:00 2001 From: Antoine GIRARD Date: Sun, 1 Nov 2020 01:58:22 +0100 Subject: [PATCH] docker: rootless image (#10154) * docker: rootless image * improve docs + remove check for write perm on custom * add more info on ssh passtrough * Add comment for internal ssh server in container config --- .drone.yml | 49 +++ Dockerfile.rootless | 68 ++++ docker/manifest.rootless.tmpl | 19 ++ docker/rootless/etc/templates/app.ini | 58 ++++ .../usr/local/bin/docker-entrypoint.sh | 11 + docker/rootless/usr/local/bin/docker-setup.sh | 48 +++ .../with-docker-rootless.en-us.md | 296 ++++++++++++++++++ 7 files changed, 549 insertions(+) create mode 100644 Dockerfile.rootless create mode 100644 docker/manifest.rootless.tmpl create mode 100644 docker/rootless/etc/templates/app.ini create mode 100755 docker/rootless/usr/local/bin/docker-entrypoint.sh create mode 100755 docker/rootless/usr/local/bin/docker-setup.sh create mode 100644 docs/content/doc/installation/with-docker-rootless.en-us.md diff --git a/.drone.yml b/.drone.yml index 872dcf750..3dc033b99 100644 --- a/.drone.yml +++ b/.drone.yml @@ -667,6 +667,25 @@ steps: exclude: - pull_request + - name: publish-rootless + pull: always + image: plugins/docker:linux-amd64 + settings: + dockerfile: Dockerfile.rootless + auto_tag: true + auto_tag_suffix: linux-amd64-rootless + repo: gitea/gitea + build_args: + - GOPROXY=off + password: + from_secret: docker_password + username: + from_secret: docker_username + when: + event: + exclude: + - pull_request + --- kind: pipeline name: docker-linux-arm64-dry-run @@ -745,6 +764,25 @@ steps: exclude: - pull_request + - name: publish-rootless + pull: always + image: plugins/docker:linux-arm64 + settings: + dockerfile: Dockerfile.rootless + auto_tag: true + auto_tag_suffix: linux-arm64-rootless + repo: gitea/gitea + build_args: + - GOPROXY=off + password: + from_secret: docker_password + username: + from_secret: docker_username + when: + event: + exclude: + - pull_request + --- kind: pipeline name: docker-manifest @@ -766,6 +804,17 @@ steps: username: from_secret: docker_username + - name: manifest-rootless + pull: always + image: plugins/manifest + settings: + auto_tag: true + ignore_missing: true + spec: docker/manifest.rootless.tmpl + password: + from_secret: docker_password + username: + from_secret: docker_username trigger: ref: - refs/heads/master diff --git a/Dockerfile.rootless b/Dockerfile.rootless new file mode 100644 index 000000000..6c98c099d --- /dev/null +++ b/Dockerfile.rootless @@ -0,0 +1,68 @@ + +################################### +#Build stage +FROM golang:1.15-alpine3.12 AS build-env + +ARG GOPROXY +ENV GOPROXY ${GOPROXY:-direct} + +ARG GITEA_VERSION +ARG TAGS="sqlite sqlite_unlock_notify" +ENV TAGS "bindata timetzdata $TAGS" +ARG CGO_EXTRA_CFLAGS + +#Build deps +RUN apk --no-cache add build-base git nodejs npm + +#Setup repo +COPY . ${GOPATH}/src/code.gitea.io/gitea +WORKDIR ${GOPATH}/src/code.gitea.io/gitea + +#Checkout version if set +RUN if [ -n "${GITEA_VERSION}" ]; then git checkout "${GITEA_VERSION}"; fi \ + && make clean-all build + +FROM alpine:3.12 +LABEL maintainer="maintainers@gitea.io" + +EXPOSE 2222 3000 + +RUN apk --no-cache add \ + bash \ + ca-certificates \ + gettext \ + git \ + gnupg + +RUN addgroup \ + -S -g 1000 \ + git && \ + adduser \ + -S -H -D \ + -h /var/lib/gitea/git \ + -s /bin/bash \ + -u 1000 \ + -G git \ + git && \ + echo "git:$(dd if=/dev/urandom bs=24 count=1 status=none | base64)" | chpasswd + +RUN mkdir -p /var/lib/gitea /etc/gitea +RUN chown git:git /var/lib/gitea /etc/gitea + +COPY docker/rootless / +COPY --from=build-env /go/src/code.gitea.io/gitea/gitea /usr/local/bin/gitea +RUN chown root:root /usr/local/bin/* && chmod 755 /usr/local/bin/* + +USER git:git +ENV GITEA_WORK_DIR /var/lib/gitea +ENV GITEA_CUSTOM /var/lib/gitea/custom +ENV GITEA_TEMP /tmp/gitea +#TODO add to docs the ability to define the ini to load (usefull to test and revert a config) +ENV GITEA_APP_INI /etc/gitea/app.ini +ENV HOME "/var/lib/gitea/git" +VOLUME ["/var/lib/gitea", "/etc/gitea"] +WORKDIR /var/lib/gitea + +ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] +CMD [] + diff --git a/docker/manifest.rootless.tmpl b/docker/manifest.rootless.tmpl new file mode 100644 index 000000000..2951be8b2 --- /dev/null +++ b/docker/manifest.rootless.tmpl @@ -0,0 +1,19 @@ +image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}-rootless +{{#if build.tags}} +tags: +{{#each build.tags}} + - {{this}} +{{/each}} +{{/if}} +manifests: + - + image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64-rootless + platform: + architecture: amd64 + os: linux + - + image: gitea/gitea:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64-rootless + platform: + architecture: arm64 + os: linux + variant: v8 diff --git a/docker/rootless/etc/templates/app.ini b/docker/rootless/etc/templates/app.ini new file mode 100644 index 000000000..e8a89cd27 --- /dev/null +++ b/docker/rootless/etc/templates/app.ini @@ -0,0 +1,58 @@ +APP_NAME = $APP_NAME +RUN_USER = $RUN_USER +RUN_MODE = $RUN_MODE + +[repository] +ROOT = $GITEA_WORK_DIR/git/repositories + +[repository.local] +LOCAL_COPY_PATH = $GITEA_TEMP/local-repo + +[repository.upload] +TEMP_PATH = $GITEA_TEMP/uploads + +[server] +APP_DATA_PATH = $GITEA_WORK_DIR +SSH_DOMAIN = $SSH_DOMAIN +HTTP_PORT = $HTTP_PORT +ROOT_URL = $ROOT_URL +DISABLE_SSH = $DISABLE_SSH +; In rootless gitea container only internal ssh server is supported +START_SSH_SERVER = true +SSH_PORT = $SSH_PORT +SSH_LISTEN_PORT = $SSH_LISTEN_PORT +BUILTIN_SSH_SERVER_USER = $RUN_USER +LFS_START_SERVER = $LFS_START_SERVER +LFS_CONTENT_PATH = $GITEA_WORK_DIR/git/lfs + +[database] +PATH = $GITEA_WORK_DIR/data/gitea.db +DB_TYPE = $DB_TYPE +HOST = $DB_HOST +NAME = $DB_NAME +USER = $DB_USER +PASSWD = $DB_PASSWD + +[indexer] +ISSUE_INDEXER_PATH = $GITEA_WORK_DIR/data/indexers/issues.bleve + +[session] +PROVIDER_CONFIG = $GITEA_WORK_DIR/data/sessions + +[picture] +AVATAR_UPLOAD_PATH = $GITEA_WORK_DIR/data/avatars +REPOSITORY_AVATAR_UPLOAD_PATH = $GITEA_WORK_DIR/data/gitea/repo-avatars + +[attachment] +PATH = $GITEA_WORK_DIR/data/attachments + +[log] +ROOT_PATH = $GITEA_WORK_DIR/data/log + +[security] +INSTALL_LOCK = $INSTALL_LOCK +SECRET_KEY = $SECRET_KEY + +[service] +DISABLE_REGISTRATION = $DISABLE_REGISTRATION +REQUIRE_SIGNIN_VIEW = $REQUIRE_SIGNIN_VIEW diff --git a/docker/rootless/usr/local/bin/docker-entrypoint.sh b/docker/rootless/usr/local/bin/docker-entrypoint.sh new file mode 100755 index 000000000..d05777adc --- /dev/null +++ b/docker/rootless/usr/local/bin/docker-entrypoint.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +if [ -x /usr/local/bin/docker-setup.sh ]; then + /usr/local/bin/docker-setup.sh || { echo 'docker setup failed' ; exit 1; } +fi + +if [ $# -gt 0 ]; then + exec "$@" +else + exec /usr/local/bin/gitea -c ${GITEA_APP_INI} web +fi diff --git a/docker/rootless/usr/local/bin/docker-setup.sh b/docker/rootless/usr/local/bin/docker-setup.sh new file mode 100755 index 000000000..1ee8c2c97 --- /dev/null +++ b/docker/rootless/usr/local/bin/docker-setup.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# Prepare git folder +mkdir -p ${HOME} && chmod 0700 ${HOME} +if [ ! -w ${HOME} ]; then echo "${HOME} is not writable"; exit 1; fi + +# Prepare custom folder +mkdir -p ${GITEA_CUSTOM} && chmod 0500 ${GITEA_CUSTOM} + +# Prepare temp folder +mkdir -p ${GITEA_TEMP} && chmod 0700 ${GITEA_TEMP} +if [ ! -w ${GITEA_TEMP} ]; then echo "${GITEA_TEMP} is not writable"; exit 1; fi + +#Prepare config file +if [ ! -f ${GITEA_APP_INI} ]; then + + #Prepare config file folder + GITEA_APP_INI_DIR=$(dirname ${GITEA_APP_INI}) + mkdir -p ${GITEA_APP_INI_DIR} && chmod 0700 ${GITEA_APP_INI_DIR} + if [ ! -w ${GITEA_APP_INI_DIR} ]; then echo "${GITEA_APP_INI_DIR} is not writable"; exit 1; fi + + # Set INSTALL_LOCK to true only if SECRET_KEY is not empty and + # INSTALL_LOCK is empty + if [ -n "$SECRET_KEY" ] && [ -z "$INSTALL_LOCK" ]; then + INSTALL_LOCK=true + fi + + # Substitude the environment variables in the template + APP_NAME=${APP_NAME:-"Gitea: Git with a cup of tea"} \ + RUN_MODE=${RUN_MODE:-"dev"} \ + RUN_USER=${USER:-"git"} \ + SSH_DOMAIN=${SSH_DOMAIN:-"localhost"} \ + HTTP_PORT=${HTTP_PORT:-"3000"} \ + ROOT_URL=${ROOT_URL:-""} \ + DISABLE_SSH=${DISABLE_SSH:-"false"} \ + SSH_PORT=${SSH_PORT:-"2222"} \ + SSH_LISTEN_PORT=${SSH_LISTEN_PORT:-$SSH_PORT} \ + DB_TYPE=${DB_TYPE:-"sqlite3"} \ + DB_HOST=${DB_HOST:-"localhost:3306"} \ + DB_NAME=${DB_NAME:-"gitea"} \ + DB_USER=${DB_USER:-"root"} \ + DB_PASSWD=${DB_PASSWD:-""} \ + INSTALL_LOCK=${INSTALL_LOCK:-"false"} \ + DISABLE_REGISTRATION=${DISABLE_REGISTRATION:-"false"} \ + REQUIRE_SIGNIN_VIEW=${REQUIRE_SIGNIN_VIEW:-"false"} \ + SECRET_KEY=${SECRET_KEY:-""} \ + envsubst < /etc/templates/app.ini > ${GITEA_APP_INI} +fi diff --git a/docs/content/doc/installation/with-docker-rootless.en-us.md b/docs/content/doc/installation/with-docker-rootless.en-us.md new file mode 100644 index 000000000..59a8538f5 --- /dev/null +++ b/docs/content/doc/installation/with-docker-rootless.en-us.md @@ -0,0 +1,296 @@ +--- +date: "2020-02-09T20:00:00+02:00" +title: "Installation with Docker (rootless)" +slug: "install-with-docker-rootless" +weight: 10 +toc: true +draft: true +menu: + sidebar: + parent: "installation" + name: "With Docker Rootless" + weight: 10 + identifier: "install-with-docker-rootless" +--- + +# Installation with Docker + +Gitea provides automatically updated Docker images within its Docker Hub organization. It is +possible to always use the latest stable tag or to use another service that handles updating +Docker images. + +The rootless image use Gitea internal ssh to provide git protocol and doesn't support openssh. + +This reference setup guides users through the setup based on `docker-compose`, but the installation +of `docker-compose` is out of scope of this documentation. To install `docker-compose` itself, follow +the official [install instructions](https://docs.docker.com/compose/install/). + +## Basics + +The most simple setup just creates a volume and a network and starts the `gitea/gitea:latest-rootless` +image as a service. Since there is no database available, one can be initialized using SQLite3. +Create a directory for `data` and `config` then paste the following content into a file named `docker-compose.yml`. +Note that the volume should be owned by the user/group with the UID/GID specified in the config file. By default Gitea in docker will use uid:1000 gid:1000. If needed you can set ownership on those folders with the command: `sudo chown 1000:1000 config/ data/` +If you don't give the volume correct permissions, the container may not start. +Also be aware that the tag `:latest-rootless` will install the current development version. +For a stable release you can use `:1-rootless` or specify a certain release like `:{{< version >}}-rootless`. + +```yaml +version: "2" + +services: + server: + image: gitea/gitea:latest-rootless + restart: always + volumes: + - ./data:/var/lib/gitea + - ./config:/etc/gitea + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + ports: + - "3000:3000" + - "2222:2222" +``` + +## Custom port + +To bind the integrated ssh and the webserver on a different port, adjust +the port section. It's common to just change the host port and keep the ports within +the container like they are. + +```diff +version: "2" + +services: + server: + image: gitea/gitea:latest-rootless + restart: always + volumes: + - ./data:/var/lib/gitea + - ./config:/etc/gitea + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + ports: +- - "3000:3000" +- - "2222:2222" ++ - "80:3000" ++ - "22:2222" +``` + +## MySQL database + +To start Gitea in combination with a MySQL database, apply these changes to the +`docker-compose.yml` file created above. + +```diff +version: "2" + +services: + server: + image: gitea/gitea:latest-rootless ++ environment: ++ - DB_TYPE=mysql ++ - DB_HOST=db:3306 ++ - DB_NAME=gitea ++ - DB_USER=gitea ++ - DB_PASSWD=gitea + restart: always + volumes: + - ./data:/var/lib/gitea + - ./config:/etc/gitea + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + ports: + - "3000:3000" + - "222:22" ++ depends_on: ++ - db ++ ++ db: ++ image: mysql:5.7 ++ restart: always ++ environment: ++ - MYSQL_ROOT_PASSWORD=gitea ++ - MYSQL_USER=gitea ++ - MYSQL_PASSWORD=gitea ++ - MYSQL_DATABASE=gitea ++ volumes: ++ - ./mysql:/var/lib/mysql +``` + +## PostgreSQL database + +To start Gitea in combination with a PostgreSQL database, apply these changes to +the `docker-compose.yml` file created above. + +```diff +version: "2" + +services: + server: + image: gitea/gitea:latest-rootless + environment: ++ - DB_TYPE=postgres ++ - DB_HOST=db:5432 ++ - DB_NAME=gitea ++ - DB_USER=gitea ++ - DB_PASSWD=gitea + restart: always + volumes: + - ./data:/var/lib/gitea + - ./config:/etc/gitea + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + ports: + - "3000:3000" + - "2222:2222" ++ depends_on: ++ - db ++ ++ db: ++ image: postgres:9.6 ++ restart: always ++ environment: ++ - POSTGRES_USER=gitea ++ - POSTGRES_PASSWORD=gitea ++ - POSTGRES_DB=gitea ++ volumes: ++ - ./postgres:/var/lib/postgresql/data +``` + +## Named volumes + +To use named volumes instead of host volumes, define and use the named volume +within the `docker-compose.yml` configuration. This change will automatically +create the required volume. You don't need to worry about permissions with +named volumes; Docker will deal with that automatically. + +```diff +version: "2" + ++volumes: ++ gitea: ++ driver: local ++ +services: + server: + image: gitea/gitea:latest-rootless + restart: always + volumes: +- - ./data:/var/lib/gitea ++ - gitea-data:/var/lib/gitea +- - ./config:/etc/gitea ++ - gitea-config:/etc/gitea + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + ports: + - "3000:3000" + - "2222:2222" +``` + +MySQL or PostgreSQL containers will need to be created separately. + +## Custom user + +You can choose to use a custom user (following --user flag definition https://docs.docker.com/engine/reference/run/#user). +As an example to clone the host user `git` definition use the command `id -u git` and add it to `docker-compose.yml` file: +Please make sure that the mounted folders are writable by the user. + +```diff +version: "2" + +services: + server: + image: gitea/gitea:latest-rootless + restart: always ++ user: 1001 + volumes: + - ./data:/var/lib/gitea + - ./config:/etc/gitea + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + ports: + - "3000:3000" + - "2222:2222" +``` + +## Start + +To start this setup based on `docker-compose`, execute `docker-compose up -d`, +to launch Gitea in the background. Using `docker-compose ps` will show if Gitea +started properly. Logs can be viewed with `docker-compose logs`. + +To shut down the setup, execute `docker-compose down`. This will stop +and kill the containers. The volumes will still exist. + +Notice: if using a non-3000 port on http, change app.ini to match +`LOCAL_ROOT_URL = http://localhost:3000/`. + +## Install + +After starting the Docker setup via `docker-compose`, Gitea should be available using a +favorite browser to finalize the installation. Visit http://server-ip:3000 and follow the +installation wizard. If the database was started with the `docker-compose` setup as +documented above, please note that `db` must be used as the database hostname. + +## Environments variables + +You can configure some of Gitea's settings via environment variables: + +(Default values are provided in **bold**) + +* `APP_NAME`: **"Gitea: Git with a cup of tea"**: Application name, used in the page title. +* `RUN_MODE`: **dev**: For performance and other purposes, change this to `prod` when deployed to a production environment. +* `SSH_DOMAIN`: **localhost**: Domain name of this server, used for the displayed clone URL in Gitea's UI. +* `SSH_PORT`: **2222**: SSH port displayed in clone URL. +* `SSH_LISTEN_PORT`: **%(SSH\_PORT)s**: Port for the built-in SSH server. +* `DISABLE_SSH`: **false**: Disable SSH feature when it's not available. +* `HTTP_PORT`: **3000**: HTTP listen port. +* `ROOT_URL`: **""**: Overwrite the automatically generated public URL. This is useful if the internal and the external URL don't match (e.g. in Docker). +* `LFS_START_SERVER`: **false**: Enables git-lfs support. +* `DB_TYPE`: **sqlite3**: The database type in use \[mysql, postgres, mssql, sqlite3\]. +* `DB_HOST`: **localhost:3306**: Database host address and port. +* `DB_NAME`: **gitea**: Database name. +* `DB_USER`: **root**: Database username. +* `DB_PASSWD`: **"\"**: Database user password. Use \`your password\` for quoting if you use special characters in the password. +* `INSTALL_LOCK`: **false**: Disallow access to the install page. +* `SECRET_KEY`: **""**: Global secret key. This should be changed. If this has a value and `INSTALL_LOCK` is empty, `INSTALL_LOCK` will automatically set to `true`. +* `DISABLE_REGISTRATION`: **false**: Disable registration, after which only admin can create accounts for users. +* `REQUIRE_SIGNIN_VIEW`: **false**: Enable this to force users to log in to view any page. + +# Customization + +Customization files described [here](https://docs.gitea.io/en-us/customizing-gitea/) should +be placed in `/var/lib/gitea/custom` directory. If using host volumes, it's quite easy to access these +files; for named volumes, this is done through another container or by direct access at +`/var/lib/docker/volumes/gitea_gitea/_/var_lib_gitea`. The configuration file will be saved at +`/etc/gitea/app.ini` after the installation. + +# Upgrading + +:exclamation::exclamation: **Make sure you have volumed data to somewhere outside Docker container** :exclamation::exclamation: + +To upgrade your installation to the latest release: +``` +# Edit `docker-compose.yml` to update the version, if you have one specified +# Pull new images +docker-compose pull +# Start a new container, automatically removes old one +docker-compose up -d +``` + +# Upgrading from standard image + +- Backup your setup +- Change volume mountpoint from /data to /var/lib/gitea +- If you used a custom app.ini move it to a new volume mounted to /etc/gitea +- Rename folder (inside volume) gitea to custom +- Edit app.ini if needed + - Set START_SSH_SERVER = true +- Use image gitea/gitea:latest-rootless + +# SSH Container Passthrough (not tested) + +This should be possible by forcing `authorized_keys` generation via `gitea admin regenerate keys`. + +We should use directly [SSH AuthorizedKeysCommand](https://docs.gitea.io/en-us/command-line/#keys) when it will be based on internal api.