Skip to content

Commit

Permalink
Merge pull request #1 from truenas/NAS-119343
Browse files Browse the repository at this point in the history
NAS-119343 / 23.10 / add tftpd-hpa container
  • Loading branch information
stavros-k authored May 22, 2023
2 parents 513fdd2 + f9596fc commit 5113a49
Show file tree
Hide file tree
Showing 6 changed files with 332 additions and 0 deletions.
76 changes: 76 additions & 0 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: Publish Docker image

on:
workflow_dispatch:
push:
branches:
- master
paths:
- apps/**
pull_request:
paths:
- apps/**

env:
REPOSITORY: ixsystems

jobs:
build:
permissions:
packages: write
contents: read
name: Build
runs-on: ubuntu-22.04
strategy:
matrix:
containers:
- app: tftpd-hpa
steps:
- name: Checkout
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3

# Login first so we can pull the manifest
# even if the repository is private
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}

- name: Prepare
id: prepare
shell: bash
run: |
# Grab the version from the VERSION file
VERSION=$(cat ./apps/${{ matrix.containers.app }}/VERSION )
result=$(docker manifest inspect "${{ env.REPOSITORY }}/${{ matrix.containers.app }}:$VERSION" || echo 1)
# Result contains 1 if the tag does not exist or a JSON object if it does.
# If the result is not 1, means the "production" tag exists.
# We should fail the build and ask for a version bump.
if [[ "$result" != 1 ]]; then
echo "Version $VERSION already exists, please bump the version in the VERSION file."
exit 1
fi
echo "Version $VERSION does not exist, proceeding with build."
# Initialize the output
OUTPUT_VERSION="$VERSION"
# If this is a pull request, append the PR number
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
OUTPUT_VERSION="unstable"
fi
# Set the output
echo "APP_VERSION=$OUTPUT_VERSION" >> $GITHUB_OUTPUT
- name: Build and push Docker images
if: steps.prepare.outputs.APP_VERSION != ''
uses: docker/build-push-action@91df6b874e498451163feb47610c87c4a218c1ee
with:
context: apps/${{ matrix.containers.app }}/
push: true
tags: |
${{ env.REPOSITORY }}/${{ matrix.containers.app }}:${{ steps.prepare.outputs.APP_VERSION }}
65 changes: 65 additions & 0 deletions apps/tftpd-hpa/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
FROM alpine:latest
LABEL maintainer="iXsystems <[email protected]>"
LABEL description="tftpd-hpa server on alpine linux"

# set UID/GID for tftp
ENV UID=9069 \
GID=9069

VOLUME /tftpboot
EXPOSE 69/udp

# add user tftp
RUN addgroup -g $GID -S tftp && \
adduser --disabled-password \
--gecos "" \
--shell /sbin/nologin \
--home /home/tftp \
--no-create-home \
--ingroup tftp \
--uid $UID tftp

# add tftp-hpa
RUN apk add --no-cache \
tftp-hpa \
runit \
tzdata && \
mkdir -p /tftpboot \
/runit-services \
/runit-services/tftpd-hpa \
/runit-services/syslogd && \
echo -e "#!/bin/sh\nbusybox syslogd -n -O /dev/stdout" > \
/runit-services/syslogd/run && \
echo -e "#!/bin/sh\n/usr/sbin/in.tftpd --foreground --address 0.0.0.0:69 \
--user tftp --verbose --secure /tftpboot" > \
/runit-services/tftpd-hpa/run && \
chmod +x /runit-services/syslogd/run \
/runit-services/tftpd-hpa/run

# tftpd-hpa environment variables
# (see: https://manpages.debian.org/testing/tftpd-hpa/tftpd.8.en.html)
ENV TZ="UTC" \
CREATE=0 \
MAPFILE="" \
PERMISSIVE=0 \
PORTRANGE="4096:32760" \
REFUSE="" \
RETRANSMIT="" \
SECURE=1 \
TIMEOUT="" \
UMASK="" \
VERBOSE=1 \
VERBOSITY=3

# add mapfile
# Currently its all empty
COPY mapfile $MAPFILE

# add docker-entrypoint.sh
COPY ./docker-entrypoint.sh /
RUN ["chmod", "+x", "/docker-entrypoint.sh"]

ENTRYPOINT ["/docker-entrypoint.sh"]

HEALTHCHECK --interval=5s --timeout=10s --retries=3 \
CMD getent services tftp || exit 1
117 changes: 117 additions & 0 deletions apps/tftpd-hpa/READMe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# tftpd-hpa (tftpd)

**A lightweight tftp-server (tftpd-hpa)**

⚠️ This container is used for the TrueNAS SCALE app `tftpd-hpa`. ⚠️

⚠️ While it should work, it's not intended to be used as a standalone container. ⚠️

`GitHub` - truenas/containers - https://github.com/truenas/containers/tree/master/apps/tftpd-hpa

## Index

1. [Usage](#1-usage)
1.1 [docker run](#11-docker-run)
2. [Environment Variables](#2-environment-variables)
3. [Volumes](#3-volumes)
4. [Ports](#4-ports)

## 1 Usage

### 1.1 docker run

**Example 1 - run without arguments (environment variables will be used):**
**This is the recommended way to use this container !!!**

```shell
docker run -d \
--name tftpd-hpa \
-e TZ="Europe/Berlin" \
-v /path/of/some/files:/tftpboot \
-p 69:69/udp \
truenas/tftpd-hpa:latest
```

**Example 2 - run with specified environment variables:**
**CREATE=1: allow uploads, even if file doesn't exist**
**MAPFILE="": do not use the mapfile**

```shell
docker run -d \
--name tftpd-hpa \
-e TZ="Europe/Berlin" \
-e CREATE=1 \
-e MAPFILE="" \
-v /path/of/some/files:/tftpboot \
-p 69:69/udp \
truenas/tftpd-hpa:latest
```

**Example 3 - run with arguments (environment variables will be ignored):**
**in.tftpd --foreground --address 0.0.0.0:69 --user tftp <your arguments>**

```shell
docker run -d \
--name tftpd-hpa \
-e TZ="Europe/Berlin" \
-v /path/of/some/files:/tftpboot \
-p 69:69/udp \
truenas/tftpd-hpa:latest \
--create --secure --verbose /tftpboot
```

**Example 4 - run with arguments with optional 'in.tftpd' as first argument:**
**in.tftpd --foreground --address 0.0.0.0:69 --user tftp <your arguments>**

```shell
docker run -d \
--name tftpd-hpa \
-e TZ="Europe/Berlin" \
-v /path/of/some/files:/tftpboot \
-p 69:69/udp \
truenas/tftpd-hpa:latest \
in.tftpd --create --secure --verbose /tftpboot
```

**Example 5 - run without arguments and custom MAPFILE:**
**you need to VOLUME your MAPFILE**

```shell
docker run -d \
--name tftpd-hpa \
-e TZ="Europe/Berlin" \
-e MAPFILE=/mapfile \
-v /path/of/some/files:/tftpboot \
-v /path/of/your/mapfile:/mapfile \
-p 69:69/udp \
truenas/tftpd-hpa:latest
```

### 2 Environment Variables

For more information, see [tftpd-hpa man pages](https://manpages.debian.org/testing/tftpd-hpa/tftpd.8.en.html)

- `TZ` - Specifies the server timezone - **Default: `UTC`**
- `BLOCKSIZE` - Specifies the maximum permitted block size
- `CREATE` - Allow new files to be created - **Default: `0`** (only upload files, if they already exist)
- `MAPFILE` - Specify the use of filename remapping - **Default: `""`**
(leave empty, if you don't want to use a mapfile)
- `PERMISSIVE` - Perform no additional permissions checks - **Default: `0`**
- `PORTRANGE` - Force the server port number (the Transaction ID) to be in the specified range of port numbers - **Default: `4096:32760`**
- `REFUSE` - Indicate that a specific RFC 2347 TFTP option should never be accepted
- `RETRANSMIT` - Determine the default timeout, in microseconds, before the first packet is retransmitted - **Default: `""`**
- `SECURE` - Change root directory on startup - **Default: `1`**
- `TIMEOUT` - This specifies how long, in seconds, to wait for a second connection before terminating the server - **Default: `""`**
- `UMASK` - Sets the umask for newly created files
- `VERBOSE` - Increase the logging verbosity of tftpd - **Default: `1`**
- `VERBOSITY` - Set the verbosity value from 0 to 4 - **Default: `3`**

### 3 Volumes

- `/tftpboot` - tftp root directory ->
**your directory needs to be at least 0555 (dr-xr-xr-x), owned by root or uid=9069, gid=9069** or **0757** when `CREATE=1`
- `/mapfile` - mapfile for tftpd-hpa -> your mapfile needs to be at least 0444 (-r--r--r--), owned by root or uid=9069, gid=9069

### 4 Ports

- `69/udp` - TFTP Port
1 change: 1 addition & 0 deletions apps/tftpd-hpa/VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1.0.0
38 changes: 38 additions & 0 deletions apps/tftpd-hpa/docker-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/bin/sh
set -e
# set timezone
ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# if started without args, exec in.tftpd
if [ "$#" = "0" ]; then
param=""
if [ "$BLOCKSIZE" != "" ]; then param="${param} --blocksize $BLOCKSIZE"; fi
if [ "$CREATE" = "1" ]; then param="${param} --create"; fi
if [ "$PORTRANGE" != "" ]; then param="${param} --port-range $PORTRANGE"; fi
if [ "$MAPFILE" != "" ]; then param="${param} --map-file $MAPFILE"; fi
if [ "$PERMISSIVE" = "1" ]; then param="${param} --permissive"; fi
if [ "$REFUSE" != "" ]; then param="${param} --refuse $REFUSE"; fi
if [ "$RETRANSMIT" != "" ]; then param="${param} --retransmit $RETRANSMIT"; fi
if [ "$SECURE" = "1" ]; then param="${param} --secure"; fi
if [ "$TIMEOUT" != "" ]; then param="${param} --timeout $TIMEOUT"; fi
if [ "$UMASK" != "" ]; then param="${param} --umask $UMASK"; fi
if [ "$VERBOSE" = "1" ]; then param="${param} --verbose"; fi
if [ "$VERBOSITY" != "" ]; then param="${param} --verbosity $VERBOSITY"; fi
param="--foreground --address 0.0.0.0:69 --user tftp ${param} /tftpboot"
echo -e "\nINFO: /usr/sbin/in.tftpd ${param}\n"
echo -e "#!/bin/sh\n/usr/sbin/in.tftpd ${param}" > /runit-services/tftpd-hpa/run
else
# if first arg looks like a flag, assume we want to run in.tftpd
if [ "$( echo "$1" | cut -c1 )" = "-" ]; then
echo -e "\nINFO: /usr/sbin/in.tftpd --foreground --address 0.0.0.0:69 --user tftp $@\n"
echo -e "#!/bin/sh\n/usr/sbin/in.tftpd --foreground --address 0.0.0.0:69 --user tftp $@" > /runit-services/tftpd-hpa/run
# if the first arg is "in.tftpd" ...
elif [ "$1" = "in.tftpd" ]; then
echo -e "\nINFO: /usr/sbin/in.tftpd --foreground --address 0.0.0.0:69 --user tftp $@\n"
echo -e "#!/bin/sh\n/usr/sbin/in.tftpd --foreground --address 0.0.0.0:69 --user tftp ${@:9}" > /runit-services/tftpd-hpa/run
# if first arg doesn't looks like a flag
else
printf "\nINFO: $@\n\n"
echo -e "#!/bin/sh\n$@" > /runit-services/tftpd-hpa/run
fi
fi
exec runsvdir /runit-services
35 changes: 35 additions & 0 deletions apps/tftpd-hpa/mapfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Rule file for the -m (remapping option)
#
# This file has three fields: operation, regex, remapping
#
# The operation is a combination of the following letters:
#
# r - rewrite the matched string with the remapping pattern
# i - case-insensitive matching
# g - repeat until no match (used with "r")
# e - exit (with success) if we match this pattern, do not process
# subsequent rules
# s - start over from the first rule if we match this pattern
# a - abort (refuse the request) if we match this rule
# G - this rule applies to TFTP GET requests only
# P - this rule applies to TFTP PUT requests only
#
# The regex is a regular expression in the style of egrep(1).
#
# The remapping is a pattern, all characters are verbatim except \
# \0 copies the full string that matched the regex
# \1..\9 copies the 9 first (..) expressions in the regex
# \\ is an escaped \
# See http://linux.die.net/man/8/tftpd for more info.
#
# "#" begins a comment, unless \-escaped
#

# ri ^[a-z]: # Remove "drive letters"
# rg \\ / # Convert backslashes to slashes
# rg ([A-Z]) \L\1 # Convert uppercase to lowercase
# rg \# @ # Convert hash marks to @ signs
# rg /../ /..no../ # Convert /../ to /..no../
# e ^ok/ # These are always ok
# r ^[^/] /\0 # Convert non-absolute files
# a \.pvt$ # Reject requests for private files

0 comments on commit 5113a49

Please sign in to comment.