Repository: fstab/cifs Branch: master Commit: 3b640936ef51 Files: 3 Total size: 11.4 KB Directory structure: gitextract_r1vlbl9p/ ├── LICENSE ├── README.md └── cifs ================================================ FILE CONTENTS ================================================ ================================================ FILE: LICENSE ================================================ Copyright 2019 Fabian Stäber (https://github.com/fstab/cifs) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ CIFS Flexvolume Plugin for Kubernetes ===================================== Driver for [CIFS][1] (SMB, Samba, Windows Share) network filesystems as [Kubernetes volumes][2]. Background ---------- Docker containers running in Kubernetes have an ephemeral file system: Once a container is terminated, all files are gone. In order to store persistent data in Kubernetes, you need to mount a [Persistent Volume][3] into your container. Kubernetes has built-in support for network filesystems found in the most common cloud providers, like [Amazon's EBS][4], [Microsoft's Azure disk][5], etc. However, some cloud hosting services, like the [Hetzner cloud][6], provide network storage using the CIFS (SMB, Samba, Windows Share) protocol, which is not natively supported in Kubernetes. Fortunately, Kubernetes provides [Flexvolume][7], which is a plugin mechanism enabling users to write their own drivers. There are a few flexvolume drivers for CIFS out there, but for different reasons none of them seemed to work for me. So I wrote my own, which can be found on [github.com/fstab/cifs][8]. Installing ---------- The flexvolume plugin is a single shell script named [cifs][8]. This shell script must be available on the Kubernetes master and on each of the Kubernetes nodes. By default, Kubernetes searches for third party volume plugins in `/usr/libexec/kubernetes/kubelet-plugins/volume/exec/`. The plugin directory can be configured with the kubelet's `--volume-plugin-dir` parameter, run `ps aux | grep kubelet` to learn the location of the plugin directory on your system (see [#1][9]). The `cifs` script must be located in a subdirectory named `fstab~cifs/`. The directory name `fstab~cifs/` will be [mapped][10] to the Flexvolume driver name `fstab/cifs`. On the Kubernetes master and on each Kubernetes node run the following commands: ```bash VOLUME_PLUGIN_DIR="/usr/libexec/kubernetes/kubelet-plugins/volume/exec" mkdir -p "$VOLUME_PLUGIN_DIR/fstab~cifs" cd "$VOLUME_PLUGIN_DIR/fstab~cifs" curl -L -O https://raw.githubusercontent.com/fstab/cifs/master/cifs chmod 755 cifs ``` The `cifs` script requires a few executables to be available on each host system: * `mount.cifs`, on Ubuntu this is in the [cifs-utils][11] package. * `jq`, on Ubuntu this is in the [jq][12] package. * `mountpoint`, on Ubuntu this is in the [util-linux][13] package. * `base64`, on Ubuntu this is in the [coreutils][14] package. To check if the installation was successful, run the following command: ```bash VOLUME_PLUGIN_DIR="/usr/libexec/kubernetes/kubelet-plugins/volume/exec" $VOLUME_PLUGIN_DIR/fstab~cifs/cifs init ``` It should output a JSON string containing `"status": "Success"`. This command is also run by Kubernetes itself when the cifs plugin is detected on the file system. Running ------- The plugin takes the CIFS username and password from a [Kubernetes Secret][15]. To create the secret, you first have to convert your username and password to base64 encoding: ```bash echo -n username | base64 echo -n password | base64 ``` Then, create a file `secret.yml` and use the ouput of the above commands as username and password: ```yaml apiVersion: v1 kind: Secret metadata: name: cifs-secret namespace: default type: fstab/cifs data: username: 'ZXhhbXBsZQ==' password: 'bXktc2VjcmV0LXBhc3N3b3Jk' ``` Apply the secret: ```bash kubectl apply -f secret.yml ``` You can check if the secret was installed successfully using `kubectl describe secret cifs-secret`. Next, create a file `pod.yml` with a test pod (replace `//server/share` with the network path of your CIFS share): ```yaml apiVersion: v1 kind: Pod metadata: name: busybox namespace: default spec: containers: - name: busybox image: busybox command: - sleep - "3600" imagePullPolicy: IfNotPresent volumeMounts: - name: test mountPath: /data volumes: - name: test flexVolume: driver: "fstab/cifs" fsType: "cifs" secretRef: name: "cifs-secret" options: networkPath: "//server/share" mountOptions: "dir_mode=0755,file_mode=0644,noperm" ``` Start the pod: ```yaml kubectl apply -f pod.yml ``` You can verify that the volume was mounted successfully using `kubectl describe pod busybox`. Testing ------- If everything is fine, start a shell inside the container to see if it worked: ```bash kubectl exec -ti busybox /bin/sh ``` Inside the container, you should see the CIFS share mounted to `/data`. [1]: https://en.wikipedia.org/wiki/Server_Message_Block [2]: https://kubernetes.io/docs/concepts/storage/volumes/ [3]: https://kubernetes.io/docs/concepts/storage/volumes/ [4]: https://aws.amazon.com/ebs [5]: https://azure.microsoft.com/en-us/services/storage/unmanaged-disks/ [6]: https://hetzner.cloud [7]: https://github.com/kubernetes/community/blob/master/contributors/devel/flexvolume.md [8]: https://github.com/fstab/cifs [9]: https://github.com/fstab/cifs/issues/1 [10]: https://github.com/kubernetes/community/blob/master/contributors/devel/flexvolume.md#prerequisites [11]: https://packages.ubuntu.com/bionic/cifs-utils [12]: https://packages.ubuntu.com/bionic/jq [13]: https://packages.ubuntu.com/bionic/util-linux [14]: https://packages.ubuntu.com/bionic/coreutils [15]: https://kubernetes.io/docs/concepts/configuration/secret/ ================================================ FILE: cifs ================================================ #!/bin/bash set -u # ==================================================================== # Example configuration: # ==================================================================== # -------------------------------------------------------------------- # secret.yml: # -------------------------------------------------------------------- # apiVersion: v1 # kind: Secret # metadata: # name: cifs-secret # namespace: default # type: fstab/cifs # data: # username: 'ZXhhbXBsZQo=' # password: 'c2VjcmV0Cg==' # # -------------------------------------------------------------------- # pod.yml: # -------------------------------------------------------------------- # apiVersion: v1 # kind: Pod # metadata: # name: busybox # namespace: default # spec: # containers: # - name: busybox # image: busybox # command: # - sleep # - "3600" # imagePullPolicy: IfNotPresent # volumeMounts: # - name: test # mountPath: /data # volumes: # - name: test # flexVolume: # driver: "fstab/cifs" # fsType: "cifs" # secretRef: # name: "cifs-secret" # options: # networkPath: "//example-server/backup" # mountOptions: "dir_mode=0755,file_mode=0644,noperm" # -------------------------------------------------------------------- # Uncomment the following lines to see how this plugin is called: # echo >> /tmp/cifs.log # date >> /tmp/cifs.log # echo "$@" >> /tmp/cifs.log init() { assertBinaryInstalled mount.cifs cifs-utils assertBinaryInstalled jq jq assertBinaryInstalled mountpoint util-linux assertBinaryInstalled base64 coreutils echo '{ "status": "Success", "message": "The fstab/cifs flexvolume plugin was initialized successfully", "capabilities": { "attach": false } }' exit 0 } assertBinaryInstalled() { binary="$1" package="$2" if ! which "$binary" > /dev/null ; then errorExit "Failed to initialize the fstab/cifs flexvolume plugin. $binary command not found. Please install the $package package." fi } errorExit() { if [[ $# -ne 1 ]] ; then echo '{ "status": "Failure", "message": "Unknown error in the fstab/cifs flexvolume plugin." }' else jq -Mcn --arg message "$1" '{ "status": "Failure", "message": $message }' fi exit 1 } doMount() { if [[ -z ${1:-} || -z ${2:-} ]] ; then errorExit "cifs mount: syntax error. usage: cifs mount " fi mountPoint="$1" shift json=$(printf '%s ' "${@}") if ! jq -e . > /dev/null 2>&1 <<< "$json" ; then errorExit "cifs mount: syntax error. invalid json: '$json'" fi networkPath="$(jq --raw-output -e '.networkPath' <<< "$json" 2>/dev/null)" if [[ $? -ne 0 ]] ; then errorExit "cifs mount: option networkPath missing in flexvolume configuration." fi mountOptions="$(jq --raw-output -e '.mountOptions' <<< "$json" 2>/dev/null)" if [[ $? -ne 0 ]] ; then errorExit "cifs mount: option mountOptions missing in flexvolume configuration." fi cifsUsernameBase64="$(jq --raw-output -e '.["kubernetes.io/secret/username"]' <<< "$json" 2>/dev/null)" if [[ $? -ne 0 ]] ; then errorExit "cifs mount: username not found. the flexVolume definition must contain a secretRef to a secret with username and password." fi cifsPasswordBase64="$(jq --raw-output -e '.["kubernetes.io/secret/password"]' <<< "$json" 2>/dev/null)" if [[ $? -ne 0 ]] ; then errorExit "cifs mount: password not found. the flexVolume definition must contain a secretRef to a secret with username and password." fi cifsUsername="$(base64 --decode <<< "$cifsUsernameBase64" 2>/dev/null)" if [[ $? -ne 0 ]] ; then errorExit "cifs mount: username secret is not base64 encoded." fi cifsPassword="$(base64 --decode <<< "$cifsPasswordBase64" 2>/dev/null)" if [[ $? -ne 0 ]] ; then errorExit "cifs mount: password secret is not base64 encoded." fi if ! mkdir -p "$mountPoint" > /dev/null 2>&1 ; then errorExit "cifs mount: failed to create mount directory: '$mountPoint'" fi if [[ $(mountpoint "$mountPoint") = *"is a mountpoint"* ]] ; then errorExit "cifs mount: there is already a filesystem mounted under the mount directory: '$mountPoint'" fi if [[ ! -z $(ls -A "$mountPoint" 2>/dev/null) ]] ; then errorExit "cifs mount: mount directory is not an empty directory: '$mountPoint'" fi export PASSWD="$cifsPassword" result=$(mount -t cifs "$networkPath" "$mountPoint" -o "username=$cifsUsername,$mountOptions" 2>&1) if [[ $? -ne 0 ]] ; then errorExit "cifs mount: failed to mount the network path: $result" fi echo '{ "status": "Success" }' exit 0 } doUnmount() { if [[ -z ${1:-} ]] ; then errorExit "cifs unmount: syntax error. usage: cifs unmount " fi mountPoint="$1" if [[ $(mountpoint "$mountPoint") != *"is a mountpoint"* ]] ; then errorExit "cifs unmount: no filesystem mounted under directory: '$mountPoint'" fi result=$(umount "$mountPoint" 2>&1) if [[ $? -ne 0 ]] ; then errorExit "cifs unmount: failed to unmount the network path: $result" fi echo '{ "status": "Success" }' exit 0 } not_supported() { echo '{ "status": "Not supported" }' exit 1 } command=${1:-} if [[ -n $command ]]; then shift fi case "$command" in init) init "$@" ;; mount) doMount "$@" ;; unmount) doUnmount "$@" ;; *) not_supported "$@" ;; esac