diff --git a/Dockerfile b/Dockerfile index c816de4..217112a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,42 +1,91 @@ -# 3DCityDB Web Map Client Dockerfile ########################################### +# 3DCityDB Web Map Client Dockerfile ########################################## # Official website https://www.3dcitydb.net # GitHub https://github.com/3dcitydb/3dcitydb-web-map ############################################################################### -# Base image -ARG baseimage_tag='9' -FROM "node:${baseimage_tag}" -# Maintainer ################################################################## -# Bruno Willenborg -# Chair of Geoinformatics -# Department of Civil, Geo and Environmental Engineering -# Technical University of Munich (TUM) -# -MAINTAINER Bruno Willenborg, Chair of Geoinformatics, Technical University of Munich (TUM) +FROM node:alpine +# Maintainers ################################################################# +# Bruno Willenborg +# Son H. Nguyen +# Chair of Geoinformatics +# Department of Civil, Geo and Environmental Engineering +# Technical University of Munich (TUM) +# +# +LABEL maintainer1="Bruno Willenborg, Chair of Geoinformatics, Technical University of Munich (TUM) " \ + maintainer2="Son H. Nguyen, Chair of Geoinformatics, Technical University of Munich (TUM) " # Setup 3DCityDB Web Map Client ############################################### -ARG webmapclient_version='v1.4.0' -RUN set -x \ - && BUILD_PACKAGES='ca-certificates git' \ - && apt-get update && apt-get install -y --no-install-recommends $BUILD_PACKAGES \ - && git clone -b "${webmapclient_version}" --depth 1 https://github.com/3dcitydb/3dcitydb-web-map.git /var/www \ - && cd /var/www \ - && rm -rf ./.git ./.gitignore ./LICENSE ./README.md ./build.xml \ - ./node_modules ./server.js ./theme \ - && mkdir -p /var/www/data \ - && apt-get purge -y --auto-remove $BUILD_PACKAGES \ - && rm -rf /var/lib/apt/lists/* - -WORKDIR /var/www/ -COPY package.json ./ -COPY html/* ./ -COPY server.js ./ -RUN set -x \ - && npm install --production - -RUN set -x \ - && chown -R node:node /var/www/ - -VOLUME /var/www/ -USER node -EXPOSE 8000 -CMD [ "node", "server.js", "--public"] +ARG webmapclient_version='v1.4.0' +RUN set -x && \ + BUILD_PACKAGES='ca-certificates git' && \ + apk update && apk add --no-cache --no-progress $BUILD_PACKAGES && \ + git clone -b "${webmapclient_version}" --depth 1 https://github.com/3dcitydb/3dcitydb-web-map.git /var/www && \ + cd /var/www && \ + rm -rf ./.git ./.gitignore ./LICENSE ./README.md ./build.xml \ + ./node_modules ./server.js ./theme && \ + mkdir -p /var/www/data && \ + apk del $BUILD_PACKAGES && \ + rm -rf /var/lib/apt/lists/* + +WORKDIR /var/www/ +COPY package.json ./ +COPY html/* ./ +COPY server.js ./ +RUN set -x && \ + npm install --production + +#RUN set -x && \ +# chown -R node:node /var/www/ + +VOLUME /var/www/ +#USER node +#EXPOSE 8000 +#CMD [ "node", "server.js", "--public"] + +# Install Samba based on dperson/samba +# https://github.com/dperson/samba +RUN apk --no-cache --no-progress upgrade && \ + apk --no-cache --no-progress add bash samba shadow && \ + adduser -D -G users -H -S -g 'Samba User' -h /tmp smbuser && \ + file="/etc/samba/smb.conf" && \ + sed -i 's|^;* *\(log file = \).*| \1/dev/stdout|' $file && \ + sed -i 's|^;* *\(load printers = \).*| \1no|' $file && \ + sed -i 's|^;* *\(printcap name = \).*| \1/dev/null|' $file && \ + sed -i 's|^;* *\(printing = \).*| \1bsd|' $file && \ + sed -i 's|^;* *\(unix password sync = \).*| \1no|' $file && \ + sed -i 's|^;* *\(preserve case = \).*| \1yes|' $file && \ + sed -i 's|^;* *\(short preserve case = \).*| \1yes|' $file && \ + sed -i 's|^;* *\(default case = \).*| \1lower|' $file && \ + sed -i '/Share Definitions/,$d' $file && \ + echo ' pam password change = yes' >>$file && \ + echo ' map to guest = bad user' >>$file && \ + echo ' usershare allow guests = yes' >>$file && \ + echo ' create mask = 0664' >>$file && \ + echo ' force create mode = 0664' >>$file && \ + echo ' directory mask = 0775' >>$file && \ + echo ' force directory mode = 0775' >>$file && \ + echo ' force user = smbuser' >>$file && \ + echo ' force group = users' >>$file && \ + echo ' follow symlinks = yes' >>$file && \ + echo ' load printers = no' >>$file && \ + echo ' printing = bsd' >>$file && \ + echo ' printcap name = /dev/null' >>$file && \ + echo ' disable spoolss = yes' >>$file && \ + echo ' socket options = TCP_NODELAY' >>$file && \ + echo ' strict locking = no' >>$file && \ + echo ' vfs objects = recycle' >>$file && \ + echo ' recycle:keeptree = yes' >>$file && \ + echo ' recycle:versions = yes' >>$file && \ + echo ' min protocol = SMB2' >>$file && \ + echo '' >>$file && \ + rm -rf /tmp/* + +COPY samba.sh /usr/bin/ + +EXPOSE 8000 137/udp 138/udp 139 445 + +#HEALTHCHECK --interval=60s --timeout=15s \ +# CMD smbclient -L '\\localhost\' -U 'guest%' -m SMB3 + +RUN chmod +x /usr/bin/samba.sh +ENTRYPOINT [ "/usr/bin/samba.sh" ] diff --git a/README.md b/README.md index 821f638..6981332 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ This repo contains a Dockerfile for the [3D City Database Web-Map-Client (3D web #### Special features * *Landing page* and *data listing* for convenient 3D web client project creation +* *Sharing data* across multiple platforms (Windows, Linux, MacOS, etc.) using *Samba* #### Image versions/tags * **latest** - Latest stable version based on latest version of the 3D web client. Built from [master](https://github.com/tum-gis/3dcitydb-web-map-docker/tree/master) branch. @@ -79,6 +80,120 @@ The data is now available in the data listing (e.g. `http://myDockerHost/data/`) ![Data listing view](https://github.com/tum-gis/3dcitydb-web-map-docker/blob/master/images/data-listing-01.png) +## Sharing data across multiple platforms (Windows, MacOS, Linux, etc.) +The 3DCityDB-Web-Map-Client Docker image also allows sharing data between different machines and operating systems such as Windows, MacOS, Linux, etc. using [Samba](https://en.wikipedia.org/wiki/Samba_(software)), which is a free software re-implementation of the SMB/CIFS networking protocol. Samba provides file and print services for various Microsoft Windows clients and can integrate with a Microsoft Windows Server domain, either as a Domain Controller (DC) or as a domain member. + +To enable Samba: +```bash +docker run -dit --name 3dwebmap-container -p 80:8000 -p 139:139 -p 445:445 \ + -v /home/docker/data/:/var/www/data/ tumgis/3dcitydb-web-map \ + -u "username;password!" \ + -s "smb_shared;/var/www/data/;yes;no;yes;all;username;username;comment" +``` +where: ++ The ports 139 and 445 are reserved for the Samba service. Do not change these numbers on Windows host machines. ++ In this example, the container data located in `/var/www/data` are mounted to `/home/docker/data` on the host machine using the [bind mount](https://docs.docker.com/storage/bind-mounts/) method. ++ The Samba service in this image is installed and configured based on [dperson/samba](https://github.com/dperson/samba). Optional Samba arguments can be provided in run-time, such as user credentials (parameter `-u`) and share information (parameter `-s`). These can be configured using the following syntax taken from [dperson/samba](https://github.com/dperson/samba): + ``` + Options (fields in '[]' are optional, '<>' are required): + -h + This help + -c "" + Setup character mapping for file/directory names + required arg: "" character mappings separated by ',' + -g "" + Provide global option for smb.conf + required arg: "" - IE: -g "log level = 2" + -i "" + Import smbpassword + required arg: "" - full file path in container + -n + Start the 'nmbd' daemon to advertise the shares + -p + Set ownership and permissions on the shares + -r + Disable recycle bin for shares + -S + Disable SMB2 minimum version + -s "[;browse;readonly;guest;users;admins;writelist;comment]" + Configure a share + required arg: ";" + is how it's called for clients + path to share + NOTE: for the default values, just leave blank + [browsable] default:'yes' or 'no' + [readonly] default:'yes' or 'no' + [guest] allowed default:'yes' or 'no' + [users] allowed default:'all' or list of allowed users + [admins] allowed default:'none' or list of admin users + [writelist] list of users that can write to a RO share + [comment] description of share + -u "[;ID;group]" + Add a user + required arg: ";" + for user + for user + [ID] for user + [group] for user + -w "" + Configure the workgroup (domain) samba should use + required arg: "" + for samba + -W + Allow access wide symbolic links + ``` + ++ Once the container has been started and Samba is running, the shared directory can be accessed using the following commands: + + In Windows: + ```bash + net use : \\\smb_shared + ``` + where the `` can be an arbitrary unoccupied letter (e.g. `R`) and `SERVER` is the name or IP address of the host machine, where the container is being hosted. The latter can be found using the command `ifconfig` or `ipconfig`. + + Once the connection has been established, open the shared folder in Windows Explorer and create a new text file `Test.txt`. This text file should now also be visible on the web client (e.g. `http://myDockerHost/data/`) as shown below: +

+ +

+ + + In Linux: + ```bash + # 1. Install Samba utils + sudo apt-get install cifs-utils + # 2. Create a local directory to mount shared data + mkdir local_smb_shared + # 3. Mount the shared data to this directory + sudo mount.cifs ///smb_shared \ + local_smb_shared -o user=username + ``` + + + In MacOS: + ```bash + mount_smbfs //username@/smb_shared /local_smb_shared + ``` + ++ To unmount the shared directory from the local machine (source data will not be removed): + + In Windows: Enter + ```bash + net use : /Delete + ``` + + + In Linux: + ```bash + umount ///smb_shared + ``` + + + In MacOS: + ```bash + umount /local_smb_shared + ``` + +To disable Samba, simply remove the Samba parameters from the `docker run` command: +```bash +docker run -dit --name 3dwebmap-container -p 80:8000 \ + -v /home/docker/data/:/var/www/data/ \ +tumgis/3dcitydb-web-map +``` + ## How to build To build a 3DCityDB-Web-Map-Client Docker image from the Dockerfile in this repo you need to download the source code from this repo and run the [`docker build`](https://docs.docker.com/engine/reference/commandline/build/) command. Follow the step below to build a 3DCityDB-Web-Map-Client Docker image or use the [`build.sh`](https://github.com/tum-gis/3dcitydb-web-map-docker/blob/master/build.sh) script. ```bash diff --git a/images/SambaTest.png b/images/SambaTest.png new file mode 100644 index 0000000..be98f86 Binary files /dev/null and b/images/SambaTest.png differ diff --git a/samba.sh b/samba.sh new file mode 100644 index 0000000..f265700 --- /dev/null +++ b/samba.sh @@ -0,0 +1,253 @@ +#!/usr/bin/env bash +#=============================================================================== +# FILE: samba.sh +# +# USAGE: ./samba.sh +# +# DESCRIPTION: Entrypoint for samba docker container +# +# OPTIONS: --- +# REQUIREMENTS: --- +# BUGS: --- +# NOTES: --- +# AUTHOR: David Personette (dperson@gmail.com), +# ORGANIZATION: +# CREATED: 09/28/2014 12:11 +# REVISION: 1.0 +# +# MODIFIED BY: Son H. Nguyen (son.nguyen@tum.de) +# MODIFIED ON: 04/03/2018 +#=============================================================================== + +set -o nounset # Treat unset variables as an error + +# Run NodeJs +nohup node server.js --public > output.log & +echo "NodeJS Server started." + +### charmap: setup character mapping for file/directory names +# Arguments: +# chars) from:to character mappings separated by ',' +# Return: configured character mapings +charmap() { local chars="$1" file=/etc/samba/smb.conf + grep -q catia $file || sed -i '/TCP_NODELAY/a \ +\ + vfs objects = catia\ + catia:mappings =\ + + ' $file + + sed -i '/catia:mappings/s/ =.*/ = '"$chars" $file +} + +### global: set a global config option +# Arguments: +# option) raw option +# Return: line added to smb.conf (replaces existing line with same key) +global() { local key="${1%%=*}" value="${1#*=}" file=/etc/samba/smb.conf + if grep -qE '^;*\s*'"$key" "$file"; then + sed -i 's|^;*\s*'"$key"'.*| '"${key% } = ${value# }"'|' "$file" + else + sed -i '/\[global\]/a \ '"${key% } = ${value# }" "$file" + fi +} + +### import: import a smbpasswd file +# Arguments: +# file) file to import +# Return: user(s) added to container +import() { local file="$1" name id + while read name id; do + grep -q "^$name:" /etc/passwd || adduser -D -H -u "$id" "$name" + done < <(cut -d: -f1,2 $file | sed 's/:/ /') + pdbedit -i smbpasswd:$file +} + +### perms: fix ownership and permissions of share paths +# Arguments: +# none) +# Return: result +perms() { local i file=/etc/samba/smb.conf + for i in $(awk -F ' = ' '/ path = / {print $2}' $file); do + chown -Rh smbuser. $i + find $i -type d ! -perm 775 -exec chmod 775 {} \; + find $i -type f ! -perm 0664 -exec chmod 0664 {} \; + done +} + +### recycle: disable recycle bin +# Arguments: +# none) +# Return: result +recycle() { local file=/etc/samba/smb.conf + sed -i '/recycle/d; /vfs/d' $file +} + +### share: Add share +# Arguments: +# share) share name +# path) path to share +# browsable) 'yes' or 'no' +# readonly) 'yes' or 'no' +# guest) 'yes' or 'no' +# users) list of allowed users +# admins) list of admin users +# writelist) list of users that can write to a RO share +# comment) description of share +# Return: result +share() { local share="$1" path="$2" browsable="${3:-yes}" ro="${4:-yes}" \ + guest="${5:-yes}" users="${6:-""}" admins="${7:-""}" \ + writelist="${8:-""}" comment="${9:-""}" file=/etc/samba/smb.conf + sed -i "/\\[$share\\]/,/^\$/d" $file + echo "[$share]" >>$file + echo " path = $path" >>$file + echo " browsable = $browsable" >>$file + echo " read only = $ro" >>$file + echo " guest ok = $guest" >>$file + echo -n " veto files = /._*/.apdisk/.AppleDouble/.DS_Store/" >>$file + echo -n ".TemporaryItems/.Trashes/desktop.ini/ehthumbs.db/" >>$file + echo "Network Trash Folder/Temporary Items/Thumbs.db/" >>$file + echo " delete veto files = yes" >>$file + [[ ${users:-""} && ! ${users:-""} =~ all ]] && + echo " valid users = $(tr ',' ' ' <<< $users)" >>$file + [[ ${admins:-""} && ! ${admins:-""} =~ none ]] && + echo " admin users = $(tr ',' ' ' <<< $admins)" >>$file + [[ ${writelist:-""} && ! ${writelist:-""} =~ none ]] && + echo " write list = $(tr ',' ' ' <<< $writelist)" >>$file + [[ ${comment:-""} && ! ${comment:-""} =~ none ]] && + echo " comment = $(tr ',' ' ' <<< $comment)" >>$file + echo "" >>$file + [[ -d $path ]] || mkdir -p $path +} + +### smb: disable SMB2 minimum +# Arguments: +# none) +# Return: result +smb() { local file=/etc/samba/smb.conf + sed -i '/min protocol/d' $file +} + +### user: add a user +# Arguments: +# name) for user +# password) for user +# id) for user +# group) for user +# Return: user added to container +user() { local name="$1" passwd="$2" id="${3:-""}" group="${4:-""}" + [[ "$group" ]] && { grep -q "^$group:" /etc/group || addgroup "$group"; } + grep -q "^$name:" /etc/passwd || + adduser -D -H ${group:+-G $group} ${id:+-u $id} "$name" + echo -e "$passwd\n$passwd" | smbpasswd -s -a "$name" +} + +### workgroup: set the workgroup +# Arguments: +# workgroup) the name to set +# Return: configure the correct workgroup +workgroup() { local workgroup="$1" file=/etc/samba/smb.conf + sed -i 's|^\( *workgroup = \).*|\1'"$workgroup"'|' $file +} + +### widelinks: allow access wide symbolic links +# Arguments: +# none) +# Return: result +widelinks() { local file=/etc/samba/smb.conf \ + replace='\1\n wide links = yes\n unix extensions = no' + sed -i 's/\(follow symlinks = yes\)/'"$replace"'/' $file +} + +### usage: Help +# Arguments: +# none) +# Return: Help text +usage() { local RC="${1:-0}" + echo "Usage: ${0##*/} [-opt] [command] +Options (fields in '[]' are optional, '<>' are required): + -h This help + -c \"\" setup character mapping for file/directory names + required arg: \"\" character mappings separated by ',' + -g \"\" Provide global option for smb.conf + required arg: \"\" - IE: -g \"log level = 2\" + -i \"\" Import smbpassword + required arg: \"\" - full file path in container + -n Start the 'nmbd' daemon to advertise the shares + -p Set ownership and permissions on the shares + -r Disable recycle bin for shares + -S Disable SMB2 minimum version + -s \"[;browse;readonly;guest;users;admins;writelist;comment]\" + Configure a share + required arg: \";\" + is how it's called for clients + path to share + NOTE: for the default value, just leave blank + [browsable] default:'yes' or 'no' + [readonly] default:'yes' or 'no' + [guest] allowed default:'yes' or 'no' + [users] allowed default:'all' or list of allowed users + [admins] allowed default:'none' or list of admin users + [writelist] list of users that can write to a RO share + [comment] description of share + -u \"[;ID;group]\" Add a user + required arg: \";\" + for user + for user + [ID] for user + [group] for user + -w \"\" Configure the workgroup (domain) samba should use + required arg: \"\" + for samba + -W Allow access wide symbolic links + +The 'command' (if provided and valid) will be run instead of samba +" >&2 + exit $RC +} + +[[ "${USERID:-""}" =~ ^[0-9]+$ ]] && usermod -u $USERID -o smbuser +[[ "${GROUPID:-""}" =~ ^[0-9]+$ ]] && groupmod -g $GROUPID -o users + +while getopts ":hc:g:i:nprs:Su:Ww:" opt; do + case "$opt" in + h) usage ;; + c) charmap "$OPTARG" ;; + g) global "$OPTARG" ;; + i) import "$OPTARG" ;; + n) NMBD="true" ;; + p) PERMISSIONS="true" ;; + r) recycle ;; + s) eval share $(sed 's/^/"/; s/$/"/; s/;/" "/g' <<< $OPTARG) ;; + S) smb ;; + u) eval user $(sed 's/^/"/; s/$/"/; s/;/" "/g' <<< $OPTARG) ;; + w) workgroup "$OPTARG" ;; + W) widelinks ;; + "?") echo "Unknown option: -$OPTARG"; usage 1 ;; + ":") echo "No argument value for option: -$OPTARG"; usage 2 ;; + esac +done +shift $(( OPTIND - 1 )) + +[[ "${CHARMAP:-""}" ]] && charmap "$CHARMAP" +[[ "${GLOBAL:-""}" ]] && global "$GLOBAL" +[[ "${IMPORT:-""}" ]] && import "$IMPORT" +[[ "${PERMISSIONS:-""}" ]] && perms +[[ "${RECYCLE:-""}" ]] && recycle +[[ "${SHARE:-""}" ]] && eval share $(sed 's/^/"/; s/$/"/; s/;/" "/g' <<< $SHARE) +[[ "${SMB:-""}" ]] && smb +[[ "${USER:-""}" ]] && eval user $(sed 's/^/"/; s/$/"/; s/;/" "/g' <<< $USER) +[[ "${WORKGROUP:-""}" ]] && workgroup "$WORKGROUP" +[[ "${WIDELINKS:-""}" ]] && widelinks + +if [[ $# -ge 1 && -x $(which $1 2>&-) ]]; then + exec "$@" +elif [[ $# -ge 1 ]]; then + echo "ERROR: command not found: $1" + exit 13 +elif ps -ef | egrep -v grep | grep -q smbd; then + echo "Service already running, please restart container to apply changes" +else + [[ ${NMBD:-""} ]] && ionice -c 3 nmbd -D + exec ionice -c 3 smbd -FS