diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ad2efc..e5bc513 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ 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). +## [2.1.0] - 2019-10-31 +### Added +* Ability to automatically load kernel modules. ([#18](https://github.com/ehough/docker-nfs-server/issues/18)). Credit to [@andyneff](https://github.com/andyneff). +### Fixed +* Minor bugs in `entrypoint.sh` + ## [2.0.0] - 2019-01-31 ### Changed diff --git a/README.md b/README.md index ef92c3d..b795b2b 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ This is the only containerized NFS server that offers **all** of the following f * [NFSv4 user ID mapping](doc/feature/nfsv4-user-id-mapping.md) * [AppArmor integration](doc/feature/apparmor.md) * Advanced + * [automatically load required kernel modules](doc/feature/auto-load-kernel-modules.md) * [custom server ports](doc/advanced/ports.md) * [custom NFS versions offered](doc/advanced/nfs-versions.md) * [performance tuning](doc/advanced/performance-tuning.md) @@ -42,7 +43,11 @@ This is the only containerized NFS server that offers **all** of the following f - `nfsd` - `rpcsec_gss_krb5` (*only if Kerberos is used*) - Usually you can enable these modules with: `modprobe {nfs,nfsd,rpcsec_gss_krb5}` + You can manually enable these modules on the Docker host with: + + `modprobe {nfs,nfsd,rpcsec_gss_krb5}` + + or you can just allow the container to [load them automatically](doc/feature/auto-load-kernel-modules.md). 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. @@ -145,6 +150,7 @@ If you pay close attention to each of the items in this section, the server shou ## Advanced + * [automatically load required kernel modules](doc/feature/auto-load-kernel-modules.md) * [customizing which ports are used](doc/advanced/ports.md) * [customizing NFS versions offered](doc/advanced/nfs-versions.md) * [performance tuning](doc/advanced/performance-tuning.md) diff --git a/doc/feature/auto-load-kernel-modules.md b/doc/feature/auto-load-kernel-modules.md new file mode 100644 index 0000000..3d1e8cd --- /dev/null +++ b/doc/feature/auto-load-kernel-modules.md @@ -0,0 +1,26 @@ +# Automatically load required kernel modules + +*Credit to Andy Neff [@andyneff](https://github.com/andyneff) for this idea.* + +As noted in the `README`, the Docker host kernel needs a few modules for proper operation of an NFS server. You can manually enable these on the host - i.e. with `modprobe` - or you can allow the container to do this on your behalf. Here's how: + +1. Add `--cap-add SYS_MODULE` to your Docker run command to allow the container to load/unload kernel modules. +1. Bind-mount the Docker host's `/lib/modules` directory into the container. e.g. `-v /lib/modules:/lib/modules:ro` + +Here's an example `docker-compose.yml`: + + ```YAML + version: 3 + services: + nfs: + image: erichough/nfs-server + volumes: + - /path/to/share:/nfs + - /path/to/exports.txt:/etc/exports:ro + - /lib/modules:/lib/modules:ro + cap_add: + - SYS_ADMIN + - SYS_MODULE + ports: + - 2049:2049 + ``` \ No newline at end of file diff --git a/entrypoint.sh b/entrypoint.sh index fb92484..34a4e07 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -263,6 +263,27 @@ is_idmapd_enabled() { return 1 } +is_kernel_module_loaded() { + + local -r module=$1 + + if lsmod | grep -Eq "^$module\\s+" || [[ -d "/sys/module/$module" ]]; then + log "kernel module $module is loaded" + return 0 + fi + + log "kernel module $module is missing" + return 1 +} + +has_linux_capability() { + + if capsh --print | grep -Eq "^Current: = .*,?${1}(,|$)"; then + return 0 + fi + + return 1 +} ###################################################################################### ### runtime configuration assertions @@ -279,11 +300,21 @@ assert_kernel_mod() { local -r module=$1 - log "checking for presence of kernel module: $module" + if is_kernel_module_loaded "$module"; then + return + fi + + if [[ ! -d /lib/modules ]] || ! has_linux_capability 'sys_module'; then + bail "$module module is not loaded in the Docker host's kernel (try: modprobe $module)" + fi - lsmod | grep -Eq "^$module\\s+" || [ -d "/sys/module/$module" ] + log "attempting to load kernel module $module" + modprobe -v "$module" + on_failure bail "unable to dynamically load kernel module $module. try modproble $module on the Docker host" - on_failure bail "$module module is not loaded in the Docker host's kernel (try: modprobe $module)" + if ! is_kernel_module_loaded "$module"; then + bail "modprobe claims that it loaded kernel module $module, but it still appears to be missing" + fi } assert_port() { @@ -303,7 +334,7 @@ assert_nfs_version() { echo "$requested_version" | grep -Eq '^3|4(\.[1-2])?$' on_failure bail "please set $ENV_VAR_NFS_VERSION to one of: 4.2, 4.1, 4, 3" - if [[ ( ! is_nfs3_enabled ) && "$requested_version" = '3' ]]; then + if ! is_nfs3_enabled && [[ "$requested_version" = '3' ]]; then bail 'you cannot simultaneously enable and disable NFS version 3' fi } @@ -322,10 +353,11 @@ assert_at_least_one_export() { on_failure bail "$PATH_FILE_ETC_EXPORTS has no exports" } -assert_linux_capabilities() { +assert_cap_sysadmin() { - 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' + if ! has_linux_capability 'cap_sys_admin'; then + bail 'missing CAP_SYS_ADMIN. be sure to run this image with --cap-add SYS_ADMIN or --privileged' + fi } @@ -424,7 +456,7 @@ init_assertions() { assert_at_least_one_export # ensure we have CAP_SYS_ADMIN - assert_linux_capabilities + assert_cap_sysadmin # perform Kerberos assertions if is_kerberos_enabled; then @@ -588,7 +620,7 @@ summarize_nfs_versions() { ;; esac - if [[ is_nfs3_enabled && "$reqd_version" =~ ^4 ]]; then + if is_nfs3_enabled && [[ "$reqd_version" =~ ^4 ]]; then versions="$versions, 3" fi