From 7ad40b830820cfb2525bd94e9e0c428bc7d726ba Mon Sep 17 00:00:00 2001 From: Eric Hough Date: Wed, 12 Sep 2018 13:40:21 -0700 Subject: [PATCH 01/11] try to clarify where container paths should be used. see #12 --- README.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 6f7469a..fb5c96c 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ This is the only containerized NFS server that offers **all** of the following f - `rpcsec_gss_krb5` (*only if Kerberos is used*) Usually you can enable these modules with: `modprobe {nfs,nfsd,rpcsec_gss_krb5}` -1. The container will need to run with `CAP_SYS_ADMIN` (or `--privileged`). This is necessary as the server needs to mount several filesystems inside the container to support its operation, and performing mounts from inside a container is impossible without these capabilities. +1. The container will need to run with `CAP_SYS_ADMIN` (or `--privileged`). This is necessary as the server needs to mount several filesystems *inside* the container to support its operation, and performing mounts from inside a container is impossible without these capabilities. 1. The container will need local access to the files you'd like to serve via NFS. You can use Docker volumes, bind mounts, files baked into a custom image, or virtually any other means of supplying files to a Docker container. ## Usage @@ -50,11 +50,11 @@ This is the only containerized NFS server that offers **all** of the following f Starting the `erichough/nfs-server` image will launch an NFS server. You'll need to supply some information upon container startup, which we'll cover below, but briefly speaking your `docker run` command might look something like this: - docker run \ - -v /host/path/to/shared/files:/nfs \ - -v /host/path/to/exports.txt:/etc/exports:ro \ - --cap-add SYS_ADMIN \ - -p 2049:2049 \ + docker run \ + -v /host/path/to/shared/files:/some/container/path \ + -v /host/path/to/exports.txt:/etc/exports:ro \ + --cap-add SYS_ADMIN \ + -p 2049:2049 \ erichough/nfs-server Let's break that command down into its individual pieces to see what's required for a successful server startup. @@ -63,15 +63,15 @@ Let's break that command down into its individual pieces to see what's required As noted in the [requirements](#requirements), the container will need local access to the files you'd like to share over NFS. Some ideas for supplying these files: - * [bind mounts](https://docs.docker.com/storage/bind-mounts/) (`-v /host/path/to/shared/files:/nfs`) - * [volumes](https://docs.docker.com/storage/volumes/) (`-v some_volume:/nfs`) - * files [baked into](https://docs.docker.com/engine/reference/builder/#copy) custom image (e.g. in a `Dockerfile`: `COPY /host/files /nfs`) + * [bind mounts](https://docs.docker.com/storage/bind-mounts/) (`-v /host/path/to/shared/files:/some/container/path`) + * [volumes](https://docs.docker.com/storage/volumes/) (`-v some_volume:/some/container/path`) + * files [baked into](https://docs.docker.com/engine/reference/builder/#copy) custom image (e.g. in a `Dockerfile`: `COPY /host/files /some/container/path`) You may use any combination of the above, or any other means to supply files to the container. 1. **Provide your desired [NFS exports](https://linux.die.net/man/5/exports) (`/etc/exports`)** - You'll need to tell the server which container directories to export. You have *three options* for this; choose whichever one you prefer: + You'll need to tell the server which **container directories** to share. You have *three options* for this; choose whichever one you prefer: 1. bind mount `/etc/exports` into the container @@ -84,10 +84,10 @@ Let's break that command down into its individual pieces to see what's required The container will look for environment variables that start with `NFS_EXPORT_` and end with an integer. e.g. `NFS_EXPORT_0`, `NFS_EXPORT_1`, etc. - docker run \ - -e NFS_EXPORT_0='/nfs/foo *(ro,no_subtree_check)' \ - -e NFS_EXPORT_1='/nfs/bar 123.123.123.123/32(rw,no_subtree_check)' \ - ... \ + docker run \ + -e NFS_EXPORT_0='/container/path/foo *(ro,no_subtree_check)' \ + -e NFS_EXPORT_1='/container/path/bar 123.123.123.123/32(rw,no_subtree_check)' \ + ... \ erichough/nfs-server 1. bake `/etc/exports` into a custom image @@ -153,7 +153,7 @@ Please [open an issue](https://github.com/ehough/docker-nfs-server/issues) if yo ## Remaining tasks -- switch to Alpine Linux once [this bug](https://bugs.alpinelinux.org/issues/8470) in `nfs-utils` is fixed +- switch to Alpine Linux once `nfs-utils` version 2.3.1-r4 (or higher) is released in a stable repo (maybe Alpine 3.9?). See [this bug](https://bugs.alpinelinux.org/issues/8470) for details - figure out why `rpc.nfsd` takes 5 minutes to startup/timeout unless `rpcbind` is running - add more examples, including Docker Compose From 89350c067580aa7dea75a7853264e74b21478c06 Mon Sep 17 00:00:00 2001 From: Eric Hough Date: Thu, 20 Sep 2018 23:18:30 -0700 Subject: [PATCH 02/11] snake case all function names --- entrypoint.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/entrypoint.sh b/entrypoint.sh index 8e4c785..16c0540 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -63,7 +63,7 @@ log() { echo "----> $1" } -logHeader() { +log_header() { echo '' echo '==================================================================' @@ -142,7 +142,7 @@ stop_exportfs() { stop() { - logHeader 'terminating ...' + log_header 'terminating ...' kill_process_if_running "$PATH_BIN_RPC_SVCGSSD" stop_nfsd @@ -154,7 +154,7 @@ stop() { stop_mount "$MOUNT_PATH_NFSD" stop_mount "$MOUNT_PATH_RPC_PIPEFS" - logHeader 'terminated' + log_header 'terminated' exit 0 } @@ -516,7 +516,7 @@ boot_main_svcgssd() { boot_main_print_ready_message() { - logHeader "ready and waiting for connections on port $(get_reqd_nfsd_port)" + log_header "ready and waiting for connections on port $(get_reqd_nfsd_port)" log 'list of exports:' cat $PATH_FILE_ETC_EXPORTS } @@ -528,7 +528,7 @@ boot_main_print_ready_message() { init() { - logHeader 'setting up' + log_header 'setting up' init_trap init_exports @@ -539,7 +539,7 @@ init() { boot() { - logHeader 'starting services' + log_header 'starting services' boot_main_mounts boot_main_rpcbind From 05f61a72600eeb0d882bb140123684a1963c673a Mon Sep 17 00:00:00 2001 From: Eric Hough Date: Wed, 26 Sep 2018 11:22:03 -0700 Subject: [PATCH 03/11] overhaul logging and add summary upon successful startup --- CHANGELOG.md | 8 ++ entrypoint.sh | 212 ++++++++++++++++++++++++++++++++++---------------- 2 files changed, 155 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6659a7..cd33ef1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Added +* upon successful server startup, log NFS versions, exports, and ports + +### Fixed +### Changed + ## [1.1.1] - 2018-08-21 ### Fixed diff --git a/entrypoint.sh b/entrypoint.sh index 16c0540..5f62e83 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -55,7 +55,7 @@ readonly MOUNT_PATH_RPC_PIPEFS='/var/lib/nfs/rpc_pipefs' ###################################################################################### -### general purpose utilities +### logging ###################################################################################### log() { @@ -63,35 +63,63 @@ log() { echo "----> $1" } +log_warning() { + + log "WARNING: $1" +} + +log_error() { + + log '' + log "ERROR: $1" + log '' +} + log_header() { - echo '' - echo '==================================================================' - echo " $1" | awk '{print toupper($0)}' - echo '==================================================================' + echo "\ + +================================================================== + $(echo "$1" | awk '{print toupper($0)}') +==================================================================" } + +###################################################################################### +### error handling +###################################################################################### + bail() { - log "ERROR: $1" + log_error "$1" exit 1 } -warn_on_failure() { +on_failure() { # shellcheck disable=SC2181 - if [[ $? -ne 0 ]]; then - log "WARNING: $1" + if [[ $? -eq 0 ]]; then + return fi + + case "$1" in + warn) + log_warning "$2" + ;; + stop) + log_error "$2" + stop + ;; + *) + bail "$2" + ;; + esac } -exit_on_failure() { - # shellcheck disable=SC2181 - if [[ $? -ne 0 ]]; then - bail "$1" - fi -} +###################################################################################### +### process control +###################################################################################### kill_process_if_running() { @@ -101,7 +129,7 @@ kill_process_if_running() { if [[ -n $pid ]]; then log "killing $base" kill -TERM "$pid" - warn_on_failure "unable to kill $base" + on_failure warn "unable to kill $base" else log "$base was not running" fi @@ -118,11 +146,11 @@ stop_mount() { local -r type=$(basename "$path") if mount | grep -Eq ^"$type on $path\\s+"; then - log "un-mounting $type from $path" + log "un-mounting $type filesystem from $path" umount -v "$path" - warn_on_failure "unable to un-mount $type from $path" + on_failure warn "unable to un-mount $type filesystem from $path" else - log "$type was not mounted on $path" + log "no active mount at $path" fi } @@ -130,14 +158,14 @@ stop_nfsd() { log 'stopping nfsd' $PATH_BIN_NFSD 0 - warn_on_failure 'unable to stop nfsd. if it had started already, check Docker host for lingering [nfsd] processes' + on_failure warn 'unable to stop nfsd. if it had started already, check Docker host for lingering [nfsd] processes' } stop_exportfs() { - log 'un-exporting filesystems' + log 'un-exporting filesystem(s)' $PATH_BIN_EXPORTFS -ua - warn_on_failure 'unable to un-export filesystems' + on_failure warn 'unable to un-export filesystem(s)' } stop() { @@ -159,15 +187,6 @@ stop() { exit 0 } -stop_on_failure() { - - # shellcheck disable=SC2181 - if [[ $? -ne 0 ]]; then - log "$1" - stop - fi -} - ###################################################################################### ### runtime environment detection @@ -244,7 +263,7 @@ assert_kernel_mod() { lsmod | grep -Eq "^$moduleName\\s+" || [ -d "/sys/module/$moduleName" ] - exit_on_failure "$moduleName module is not loaded on the Docker host's kernel (try: modprobe $moduleName)" + on_failure bail "$moduleName module is not loaded in the Docker host's kernel (try: modprobe $moduleName)" } assert_port() { @@ -253,14 +272,14 @@ assert_port() { local -r value=${!envName} if [[ -n "$value" && ( "$value" -lt 1 || "$value" -gt 65535 ) ]]; then - bail "please set $1 to a value between 1 and 65535 inclusive" + bail "please set $1 to an integer between 1 and 65535 inclusive" fi } assert_nfs_version() { get_reqd_nfs_version | grep -Eq '^(3|4|4\.1|4\.2)$' - exit_on_failure "please set $ENV_VAR_NFS_VERSION to 3, 4, 4.1, or 4.2" + on_failure bail "please set $ENV_VAR_NFS_VERSION to one of: 4.2, 4.1, 4, 3" } assert_disabled_nfs3() { @@ -275,7 +294,7 @@ assert_nfsd_threads() { local -r requested=$(get_reqd_nfsd_threads) if [[ "$requested" -lt 1 ]]; then - bail "please set $ENV_VAR_NFS_SERVER_THREAD_COUNT to a positive value" + bail "please set $ENV_VAR_NFS_SERVER_THREAD_COUNT to a positive integer" fi } @@ -320,9 +339,13 @@ init_exports() { local candidateExportVariables candidateExportVariables=$(compgen -A variable | grep -E 'NFS_EXPORT_[0-9]+' | sort) - exit_on_failure "please provide $PATH_FILE_ETC_EXPORTS or set NFS_EXPORT_* environment variables" + on_failure bail 'failed to detect NFS_EXPORT_* variables' + + if [[ -z "$candidateExportVariables" ]]; then + bail "please provide $PATH_FILE_ETC_EXPORTS to the container or set at least one NFS_EXPORT_* environment variable" + fi - log "building $PATH_FILE_ETC_EXPORTS" + log "building $PATH_FILE_ETC_EXPORTS from environment variables" for exportVariable in $candidateExportVariables; do @@ -332,7 +355,7 @@ init_exports() { local dir="${lineAsArray[0]}" if [[ ! -d "$dir" ]]; then - log "skipping $line since $dir is not a directory" + log_warning "skipping $exportVariable since $dir is not a container directory" continue fi @@ -348,13 +371,14 @@ init_exports() { done + log "collected $collected valid export(s) from NFS_EXPORT_* environment variables" + if [[ $collected -eq 0 ]]; then bail 'no valid exports' fi - log "will export $collected filesystem(s)" - echo "$exports" > $PATH_FILE_ETC_EXPORTS + on_failure bail "unable to write to $PATH_FILE_ETC_EXPORTS" } init_assertions() { @@ -374,11 +398,11 @@ init_assertions() { # ensure /etc/exports has at least one line grep -Evq '^\s*#|^\s*$' $PATH_FILE_ETC_EXPORTS - exit_on_failure "$PATH_FILE_ETC_EXPORTS has no exports" + on_failure bail "$PATH_FILE_ETC_EXPORTS has no exports" # ensure we have CAP_SYS_ADMIN capsh --print | grep -Eq "^Current: = .*,?cap_sys_admin(,|$)" - exit_on_failure 'missing CAP_SYS_ADMIN. be sure to run Docker with --cap-add SYS_ADMIN or --privileged' + on_failure bail 'missing CAP_SYS_ADMIN. be sure to run this image with --cap-add SYS_ADMIN or --privileged' # perform Kerberos assertions assert_kerberos_requirements @@ -395,17 +419,15 @@ boot_helper_mount() { local -r type=$(basename "$path") local -r args=('-vt' "$type" "$path") - log "mounting $type onto $path" + log "mounting $type filesystem onto $path" mount "${args[@]}" - stop_on_failure "unable to mount $type onto $path" + on_failure stop "unable to mount $type filesystem onto $path" } boot_helper_get_version_flags() { - local versionFlags local -r requestedVersion="$(get_reqd_nfs_version)" - - versionFlags=('--nfs-version' "$requestedVersion" '--no-nfs-version' 2) + local versionFlags=('--nfs-version' "$requestedVersion" '--no-nfs-version' 2) if [[ -z "$(is_nfs3_enabled)" ]]; then versionFlags+=('--no-nfs-version' 3) @@ -432,9 +454,9 @@ boot_main_mounts() { boot_main_exportfs() { - log 'exporting filesystems' + log 'exporting filesystem(s)' $PATH_BIN_EXPORTFS -arv - stop_on_failure 'exportfs failed' + on_failure stop 'exportfs failed' } boot_main_mountd() { @@ -442,13 +464,12 @@ boot_main_mountd() { local versionFlags read -r -a versionFlags <<< "$(boot_helper_get_version_flags)" local -r port=$(get_reqd_mountd_port) - local -r version=$(get_reqd_nfs_version) local -r args=('--debug' 'all' '--port' "$port" "${versionFlags[@]}") # yes, rpc.mountd is required even for NFS v4: https://forums.gentoo.org/viewtopic-p-7724856.html#7724856 - log "starting rpc.mountd for NFS version $version on port $port" + log "starting rpc.mountd on port $port" $PATH_BIN_MOUNTD "${args[@]}" - stop_on_failure 'rpc.mountd failed' + on_failure stop 'rpc.mountd failed' } boot_main_rpcbind() { @@ -458,7 +479,7 @@ boot_main_rpcbind() { # so as a workaround we start rpcbind now and (in v4-only scenarios) kill it after nfsd starts up log 'starting rpcbind' $PATH_BIN_RPCBIND -ds - stop_on_failure 'rpcbind failed' + on_failure stop 'rpcbind failed' } boot_main_idmapd() { @@ -466,7 +487,7 @@ boot_main_idmapd() { if [[ "$(get_reqd_nfs_version)" != '3' && -f "$PATH_FILE_ETC_IDMAPD_CONF" ]]; then log 'starting idmapd' $PATH_BIN_IDMAPD -v -S - stop_on_failure 'idmapd failed' + on_failure stop 'idmapd failed' fi } @@ -480,9 +501,9 @@ boot_main_statd() { local -r outPort=$(get_reqd_statd_out_port) local -r args=('--no-notify' '--port' "$inPort" '--outgoing-port' "$outPort") - log "starting statd on port $inPort (outgoing connections on port $outPort)" + log "starting statd on port $inPort (outgoing connections from port $outPort)" $PATH_BIN_STATD "${args[@]}" - stop_on_failure 'statd failed' + on_failure stop 'statd failed' } boot_main_nfsd() { @@ -491,12 +512,11 @@ boot_main_nfsd() { read -r -a versionFlags <<< "$(boot_helper_get_version_flags)" local -r threads=$(get_reqd_nfsd_threads) local -r port=$(get_reqd_nfsd_port) - local -r version=$(get_reqd_nfs_version) local -r args=('--debug' 8 '--port' "$port" "${versionFlags[@]}" "$threads") - log "starting rpc.nfsd on port $port with version $version and $threads server thread(s)" + log "starting rpc.nfsd on port $port with $threads server thread(s)" $PATH_BIN_NFSD "${args[@]}" - stop_on_failure 'rpc.nfsd failed' + on_failure stop 'rpc.nfsd failed' if [ -z "$(is_nfs3_enabled)" ]; then kill_process_if_running "$PATH_BIN_RPCBIND" @@ -511,14 +531,63 @@ boot_main_svcgssd() { log 'starting rpc.svcgssd' $PATH_BIN_RPC_SVCGSSD -f & - stop_on_failure 'rpc.svcgssd failed' + on_failure stop 'rpc.svcgssd failed' +} + + +###################################################################################### +### boot summary +###################################################################################### + +summarize_nfs_versions() { + + local -r reqd_version="$(get_reqd_nfs_version)" + local versions='' + + case "$reqd_version" in + 4\.2) + versions='4.2, 4.1, 4' + ;; + 4\.1) + versions='4.1, 4' + ;; + 4) + versions='4' + ;; + *) + versions='3' + ;; + esac + + if [[ -n "$(is_nfs3_enabled)" && "$reqd_version" =~ ^4 ]]; then + versions="$versions, 3" + fi + + log "list of enabled NFS protocol versions: $versions" +} + +summarize_exports() { + + log 'list of container exports:' + + while read -r export; do + log " $export" + done < "$PATH_FILE_ETC_EXPORTS" } -boot_main_print_ready_message() { +summarize_ports() { + + local -r nfsdPort="$(get_reqd_nfsd_port)" - log_header "ready and waiting for connections on port $(get_reqd_nfsd_port)" - log 'list of exports:' - cat $PATH_FILE_ETC_EXPORTS + if [[ -z "$(is_nfs3_enabled)" ]]; then + log "list of container ports that should be exposed: $nfsdPort (TCP)" + else + log 'list of container ports that should be exposed:' + log ' 111 (TCP and UDP)' + log " $nfsdPort (TCP and UDP)" + log " $(get_reqd_statd_in_port) (TCP and UDP)" + log " $(get_reqd_mountd_port) (TCP and UDP)" + fi } @@ -549,11 +618,23 @@ boot() { boot_main_idmapd boot_main_nfsd boot_main_svcgssd - boot_main_print_ready_message + + log 'all services started normally' +} + +summarize() { + + log_header 'server startup complete' + + summarize_nfs_versions + summarize_exports + summarize_ports } hangout() { + log_header 'ready and waiting for NFS client connections' + # wait forever or until we get SIGTERM or SIGINT # https://stackoverflow.com/a/41655546/229920 # https://stackoverflow.com/a/27694965/229920 @@ -564,6 +645,7 @@ main() { init boot + summarize hangout } From 434497b3241b55496efb74e1387bc892b529d1a5 Mon Sep 17 00:00:00 2001 From: Eric Hough Date: Wed, 26 Sep 2018 11:29:09 -0700 Subject: [PATCH 04/11] snake case all variables --- entrypoint.sh | 85 ++++++++++++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/entrypoint.sh b/entrypoint.sh index 5f62e83..db6f638 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -257,22 +257,22 @@ assert_file_provided() { assert_kernel_mod() { - local -r moduleName=$1 + local -r module=$1 - log "checking for presence of kernel module: $moduleName" + log "checking for presence of kernel module: $module" - lsmod | grep -Eq "^$moduleName\\s+" || [ -d "/sys/module/$moduleName" ] + lsmod | grep -Eq "^$module\\s+" || [ -d "/sys/module/$module" ] - on_failure bail "$moduleName module is not loaded in the Docker host's kernel (try: modprobe $moduleName)" + on_failure bail "$module module is not loaded in the Docker host's kernel (try: modprobe $module)" } assert_port() { - local -r envName=$1 - local -r value=${!envName} + local -r variable_name=$1 + local -r value=${!variable_name} if [[ -n "$value" && ( "$value" -lt 1 || "$value" -gt 65535 ) ]]; then - bail "please set $1 to an integer between 1 and 65535 inclusive" + bail "please set $variable_name to an integer between 1 and 65535 inclusive" fi } @@ -291,9 +291,9 @@ assert_disabled_nfs3() { assert_nfsd_threads() { - local -r requested=$(get_reqd_nfsd_threads) + local -r reqd_thread_count=$(get_reqd_nfsd_threads) - if [[ "$requested" -lt 1 ]]; then + if [[ "$reqd_thread_count" -lt 1 ]]; then bail "please set $ENV_VAR_NFS_SERVER_THREAD_COUNT to a positive integer" fi } @@ -334,46 +334,47 @@ init_exports() { return fi - local collected=0 + local count_valid_exports=0 local exports='' - local candidateExportVariables + local candidate_export_vars + local candidate_export_var - candidateExportVariables=$(compgen -A variable | grep -E 'NFS_EXPORT_[0-9]+' | sort) + candidate_export_vars=$(compgen -A variable | grep -E 'NFS_EXPORT_[0-9]+' | sort) on_failure bail 'failed to detect NFS_EXPORT_* variables' - if [[ -z "$candidateExportVariables" ]]; then + if [[ -z "$candidate_export_vars" ]]; then bail "please provide $PATH_FILE_ETC_EXPORTS to the container or set at least one NFS_EXPORT_* environment variable" fi log "building $PATH_FILE_ETC_EXPORTS from environment variables" - for exportVariable in $candidateExportVariables; do + for candidate_export_var in $candidate_export_vars; do - local line=${!exportVariable} - local lineAsArray - read -r -a lineAsArray <<< "$line" - local dir="${lineAsArray[0]}" + local line=${!candidate_export_var} + local line_as_array + read -r -a line_as_array <<< "$line" + local dir="${line_as_array[0]}" if [[ ! -d "$dir" ]]; then - log_warning "skipping $exportVariable since $dir is not a container directory" + log_warning "skipping $candidate_export_var since $dir is not a container directory" continue fi log "will export $line" - if [[ $collected -gt 0 ]]; then + if [[ $count_valid_exports -gt 0 ]]; then exports=$exports$'\n' fi exports=$exports$line - (( collected++ )) + (( count_valid_exports++ )) done - log "collected $collected valid export(s) from NFS_EXPORT_* environment variables" + log "collected $count_valid_exports valid export(s) from NFS_EXPORT_* environment variables" - if [[ $collected -eq 0 ]]; then + if [[ $count_valid_exports -eq 0 ]]; then bail 'no valid exports' fi @@ -426,18 +427,18 @@ boot_helper_mount() { boot_helper_get_version_flags() { - local -r requestedVersion="$(get_reqd_nfs_version)" - local versionFlags=('--nfs-version' "$requestedVersion" '--no-nfs-version' 2) + local -r reqd_version="$(get_reqd_nfs_version)" + local flags=('--nfs-version' "$reqd_version" '--no-nfs-version' 2) if [[ -z "$(is_nfs3_enabled)" ]]; then - versionFlags+=('--no-nfs-version' 3) + flags+=('--no-nfs-version' 3) fi - if [[ "$requestedVersion" == '3' ]]; then - versionFlags+=('--no-nfs-version' 4) + if [[ "$reqd_version" == '3' ]]; then + flags+=('--no-nfs-version' 4) fi - echo "${versionFlags[@]}" + echo "${flags[@]}" } @@ -461,10 +462,10 @@ boot_main_exportfs() { boot_main_mountd() { - local versionFlags - read -r -a versionFlags <<< "$(boot_helper_get_version_flags)" + local version_flags + read -r -a version_flags <<< "$(boot_helper_get_version_flags)" local -r port=$(get_reqd_mountd_port) - local -r args=('--debug' 'all' '--port' "$port" "${versionFlags[@]}") + local -r args=('--debug' 'all' '--port' "$port" "${version_flags[@]}") # yes, rpc.mountd is required even for NFS v4: https://forums.gentoo.org/viewtopic-p-7724856.html#7724856 log "starting rpc.mountd on port $port" @@ -497,22 +498,22 @@ boot_main_statd() { return fi - local -r inPort=$(get_reqd_statd_in_port) - local -r outPort=$(get_reqd_statd_out_port) - local -r args=('--no-notify' '--port' "$inPort" '--outgoing-port' "$outPort") + local -r port_in=$(get_reqd_statd_in_port) + local -r port_out=$(get_reqd_statd_out_port) + local -r args=('--no-notify' '--port' "$port_in" '--outgoing-port' "$port_out") - log "starting statd on port $inPort (outgoing connections from port $outPort)" + log "starting statd on port $port_in (outgoing connections from port $port_out)" $PATH_BIN_STATD "${args[@]}" on_failure stop 'statd failed' } boot_main_nfsd() { - local versionFlags - read -r -a versionFlags <<< "$(boot_helper_get_version_flags)" + local version_flags + read -r -a version_flags <<< "$(boot_helper_get_version_flags)" local -r threads=$(get_reqd_nfsd_threads) local -r port=$(get_reqd_nfsd_port) - local -r args=('--debug' 8 '--port' "$port" "${versionFlags[@]}" "$threads") + local -r args=('--debug' 8 '--port' "$port" "${version_flags[@]}" "$threads") log "starting rpc.nfsd on port $port with $threads server thread(s)" $PATH_BIN_NFSD "${args[@]}" @@ -577,14 +578,14 @@ summarize_exports() { summarize_ports() { - local -r nfsdPort="$(get_reqd_nfsd_port)" + local -r port_nfsd="$(get_reqd_nfsd_port)" if [[ -z "$(is_nfs3_enabled)" ]]; then - log "list of container ports that should be exposed: $nfsdPort (TCP)" + log "list of container ports that should be exposed: $port_nfsd (TCP)" else log 'list of container ports that should be exposed:' log ' 111 (TCP and UDP)' - log " $nfsdPort (TCP and UDP)" + log " $port_nfsd (TCP and UDP)" log " $(get_reqd_statd_in_port) (TCP and UDP)" log " $(get_reqd_mountd_port) (TCP and UDP)" fi From 411f9ffce8396d820a1be48fc7200bd989eef42e Mon Sep 17 00:00:00 2001 From: Eric Hough Date: Wed, 26 Sep 2018 11:29:49 -0700 Subject: [PATCH 05/11] ditch unused function --- entrypoint.sh | 7 ------- 1 file changed, 7 deletions(-) diff --git a/entrypoint.sh b/entrypoint.sh index db6f638..04fe96d 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -236,13 +236,6 @@ is_nfs3_enabled() { fi } -is_nfs4_enabled() { - - if [[ "$(get_reqd_nfs_version)" =~ '^4' ]]; then - echo 1 - fi -} - ###################################################################################### ### runtime configuration assertions From 7109a2b84ab9309e8c3aece6532d9c2677b89ee5 Mon Sep 17 00:00:00 2001 From: Eric Hough Date: Wed, 26 Sep 2018 11:36:25 -0700 Subject: [PATCH 06/11] touch up assertions --- entrypoint.sh | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/entrypoint.sh b/entrypoint.sh index 04fe96d..cfd512c 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -273,9 +273,6 @@ assert_nfs_version() { get_reqd_nfs_version | grep -Eq '^(3|4|4\.1|4\.2)$' on_failure bail "please set $ENV_VAR_NFS_VERSION to one of: 4.2, 4.1, 4, 3" -} - -assert_disabled_nfs3() { if [[ -z "$(is_nfs3_enabled)" && "$(get_reqd_nfs_version)" == '3' ]]; then bail 'you cannot simultaneously enable and disable NFS version 3' @@ -291,16 +288,17 @@ assert_nfsd_threads() { fi } -assert_kerberos_requirements() { +assert_at_least_one_export() { - if [[ -n "$(is_kerberos_enabled)" ]]; then + # ensure /etc/exports has at least one line + grep -Evq '^\s*#|^\s*$' $PATH_FILE_ETC_EXPORTS + on_failure bail "$PATH_FILE_ETC_EXPORTS has no exports" +} - assert_file_provided "$PATH_FILE_ETC_IDMAPD_CONF" - assert_file_provided "$PATH_FILE_ETC_KRB5_KEYTAB" - assert_file_provided "$PATH_FILE_ETC_KRB5_CONF" +assert_linux_capabilities() { - assert_kernel_mod rpcsec_gss_krb5 - fi + capsh --print | grep -Eq "^Current: = .*,?cap_sys_admin(,|$)" + on_failure bail 'missing CAP_SYS_ADMIN. be sure to run this image with --cap-add SYS_ADMIN or --privileged' } @@ -383,23 +381,27 @@ init_assertions() { assert_port "$ENV_VAR_NFS_PORT_STATD_IN" assert_port "$ENV_VAR_NFS_PORT_STATD_OUT" assert_nfs_version - assert_disabled_nfs3 assert_nfsd_threads # check kernel modules assert_kernel_mod nfs assert_kernel_mod nfsd - # ensure /etc/exports has at least one line - grep -Evq '^\s*#|^\s*$' $PATH_FILE_ETC_EXPORTS - on_failure bail "$PATH_FILE_ETC_EXPORTS has no exports" + # make sure we have at least one export + assert_at_least_one_export # ensure we have CAP_SYS_ADMIN - capsh --print | grep -Eq "^Current: = .*,?cap_sys_admin(,|$)" - on_failure bail 'missing CAP_SYS_ADMIN. be sure to run this image with --cap-add SYS_ADMIN or --privileged' + assert_linux_capabilities # perform Kerberos assertions - assert_kerberos_requirements + if [[ -n "$(is_kerberos_enabled)" ]]; then + + assert_file_provided "$PATH_FILE_ETC_IDMAPD_CONF" + assert_file_provided "$PATH_FILE_ETC_KRB5_KEYTAB" + assert_file_provided "$PATH_FILE_ETC_KRB5_CONF" + + assert_kernel_mod rpcsec_gss_krb5 + fi } From 2ba982a3fab6facf22b88f37702e7ab53a80f1a8 Mon Sep 17 00:00:00 2001 From: Eric Hough Date: Wed, 26 Sep 2018 11:39:30 -0700 Subject: [PATCH 07/11] trap just before it's necessary --- entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/entrypoint.sh b/entrypoint.sh index cfd512c..43ac480 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -595,9 +595,9 @@ init() { log_header 'setting up' - init_trap init_exports init_assertions + init_trap log 'setup complete' } From 4c932b9260aa1e5ef687849b01165be9dc778741 Mon Sep 17 00:00:00 2001 From: Eric Hough Date: Wed, 26 Sep 2018 12:43:50 -0700 Subject: [PATCH 08/11] ignore comments and empty lines for /etc/exports --- entrypoint.sh | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/entrypoint.sh b/entrypoint.sh index 43ac480..a700111 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -53,6 +53,8 @@ readonly PATH_FILE_ETC_KRB5_KEYTAB='/etc/krb5.keytab' readonly MOUNT_PATH_NFSD='/proc/fs/nfsd' readonly MOUNT_PATH_RPC_PIPEFS='/var/lib/nfs/rpc_pipefs' +readonly REGEX_EXPORTS_LINES_TO_SKIP='^\s*#|^\s*$' + ###################################################################################### ### logging @@ -291,7 +293,7 @@ assert_nfsd_threads() { assert_at_least_one_export() { # ensure /etc/exports has at least one line - grep -Evq '^\s*#|^\s*$' $PATH_FILE_ETC_EXPORTS + grep -Evq "$REGEX_EXPORTS_LINES_TO_SKIP" $PATH_FILE_ETC_EXPORTS on_failure bail "$PATH_FILE_ETC_EXPORTS has no exports" } @@ -330,6 +332,7 @@ init_exports() { local candidate_export_vars local candidate_export_var + # collect all candidate environment variable names candidate_export_vars=$(compgen -A variable | grep -E 'NFS_EXPORT_[0-9]+' | sort) on_failure bail 'failed to detect NFS_EXPORT_* variables' @@ -341,18 +344,23 @@ init_exports() { for candidate_export_var in $candidate_export_vars; do - local line=${!candidate_export_var} + local line="${!candidate_export_var}" + + # skip comments and empty lines + if [[ "$line" =~ $REGEX_EXPORTS_LINES_TO_SKIP ]]; then + log_warning "skipping $candidate_export_var environment variable since it contains only whitespace or a comment" + continue; + fi + local line_as_array read -r -a line_as_array <<< "$line" local dir="${line_as_array[0]}" if [[ ! -d "$dir" ]]; then - log_warning "skipping $candidate_export_var since $dir is not a container directory" + log_warning "skipping $candidate_export_var environment variable since $dir is not a container directory" continue fi - log "will export $line" - if [[ $count_valid_exports -gt 0 ]]; then exports=$exports$'\n' fi @@ -567,7 +575,15 @@ summarize_exports() { log 'list of container exports:' while read -r export; do - log " $export" + + # skip comments and empty lines + if [[ "$export" =~ $REGEX_EXPORTS_LINES_TO_SKIP ]]; then + continue; + fi + + # log it w/out leading and trailing whitespace + log " $(echo -e "$export" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" + done < "$PATH_FILE_ETC_EXPORTS" } From eb1df94a90eb8b25a3e0d2cae98250b8c3ac3c69 Mon Sep 17 00:00:00 2001 From: Eric Hough Date: Wed, 26 Sep 2018 12:44:11 -0700 Subject: [PATCH 09/11] update CHANGELOG --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd33ef1..4fcc655 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] ### Added -* upon successful server startup, log NFS versions, exports, and ports +* upon successful server startup, log: + * list of enabled NFS versions + * list of exports + * list of ports that should be exposed +* improved error detection and logging ### Fixed ### Changed From e07827b785f45fc485c2211708e231b9f0ef575c Mon Sep 17 00:00:00 2001 From: Eric Hough Date: Wed, 26 Sep 2018 12:54:29 -0700 Subject: [PATCH 10/11] update CHANGELOG for 1.2.0 release --- CHANGELOG.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fcc655..dd0d1ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [1.2.0] - 2018-09-26 ### Added * upon successful server startup, log: @@ -13,9 +13,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. * list of ports that should be exposed * improved error detection and logging -### Fixed -### Changed - ## [1.1.1] - 2018-08-21 ### Fixed From 4b501010aea18af42b1ed8fac38e1e6e1603c3ba Mon Sep 17 00:00:00 2001 From: Eric Hough Date: Wed, 26 Sep 2018 12:56:33 -0700 Subject: [PATCH 11/11] add relevant URLs to entrypoint.sh header --- entrypoint.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/entrypoint.sh b/entrypoint.sh index a700111..22a77b5 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -2,6 +2,9 @@ # # ehough/docker-nfs-server: A lightweight, robust, flexible, and containerized NFS server. # +# https://hub.docker.com/r/erichough/nfs-server +# https://github.com/ehough/docker-nfs-server +# # Copyright (C) 2017-2018 Eric D. Hough # # This program is free software: you can redistribute it and/or modify