From c30e34cd66ea0f538c0782f4cb15fdd6647146ba Mon Sep 17 00:00:00 2001 From: Thomas Weinzettl Date: Tue, 15 Feb 2022 13:39:42 +0400 Subject: [PATCH] initial commit --- .gitignore | 1 + CHANGELOG.md | 13 ++ CONTRIBUTING.md | 84 ++++++++++++ Dockerfile | 55 ++++++++ LICENSE | 2 +- MAINTAINERS.md | 3 + README.md | 97 +++++++++++++- containeradm | 323 +++++++++++++++++++++++++++++++++++++++++++++++ entrypoint.sh | 176 ++++++++++++++++++++++++++ sftp.batch | 8 ++ ssh-functions.sh | 42 ++++++ ssh-key.sh | 110 ++++++++++++++++ 12 files changed, 912 insertions(+), 2 deletions(-) create mode 100755 .gitignore create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md create mode 100755 Dockerfile create mode 100644 MAINTAINERS.md create mode 100755 containeradm create mode 100755 entrypoint.sh create mode 100644 sftp.batch create mode 100755 ssh-functions.sh create mode 100755 ssh-key.sh diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..e43b0f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..1bc0034 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,13 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [Unreleased] + +## [0.3.1] - 2022-02-08 + +### Added +- Added a changelog + +[unreleased]: https://github.com/ibm/repo-template/compare/v0.0.1...HEAD +[0.3.1]: https://github.com/ibm/repo-template/releases/tag/v0.0.1 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..5d7dab9 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,84 @@ +## Contributing In General +Our project welcomes external contributions. If you have an itch, please feel +free to scratch it. + +To contribute code or documentation, please submit a [pull request](https://github.com/ibm/sftp-only-container/pulls). + +A good way to familiarize yourself with the codebase and contribution process is +to look for and tackle low-hanging fruit in the [issue tracker](https://github.com/ibm/sftp-only-container/issues). +Before embarking on a more ambitious contribution, please quickly [get in touch](#communication) with us. + +**Note: We appreciate your effort, and want to avoid a situation where a contribution +requires extensive rework (by you or by us), sits in backlog for a long time, or +cannot be accepted at all!** + +### Proposing new features + +If you would like to implement a new feature, please [raise an issue](https://github.com/ibm/sftp-only-container/issues) +before sending a pull request so the feature can be discussed. This is to avoid +you wasting your valuable time working on a feature that the project developers +are not interested in accepting into the code base. + +### Fixing bugs + +If you would like to fix a bug, please [raise an issue](https://github.com/ibm/sftp-only-container/issues) before sending a +pull request so it can be tracked. + +### Merge approval + +The project maintainers use LGTM (Looks Good To Me) in comments on the code +review to indicate acceptance. A change requires LGTMs from two of the +maintainers of each component affected. + +For a list of the maintainers, see the [MAINTAINERS.md](MAINTAINERS.md) page. + +## Legal + +Each source file must include a license header for the Apache +Software License 2.0. Using the SPDX format is the simplest approach. +e.g. + +``` +/* +Copyright All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ +``` + +We have tried to make it as easy as possible to make contributions. This +applies to how we handle the legal aspects of contribution. We use the +same approach - the [Developer's Certificate of Origin 1.1 (DCO)](https://github.com/hyperledger/fabric/blob/master/docs/source/DCO1.1.txt) - that the Linux® Kernel [community](https://elinux.org/Developer_Certificate_Of_Origin) +uses to manage code contributions. + +We simply ask that when submitting a patch for review, the developer +must include a sign-off statement in the commit message. + +Here is an example Signed-off-by line, which indicates that the +submitter accepts the DCO: + +``` +Signed-off-by: John Doe +``` + +You can include this automatically when you commit a change to your +local git repository using the following command: + +``` +git commit -s +``` + +## Communication +**FIXME** Please feel free to connect with us on our [Slack channel](link). + +## Setup +**FIXME** Please add any special setup instructions for your project to help the developer +become productive quickly. + +## Testing +**FIXME** Please provide information that helps the developer test any changes they make +before submitting. + +## Coding style guidelines +**FIXME** Optional, but recommended: please share any specific style guidelines you might +have for your project. diff --git a/Dockerfile b/Dockerfile new file mode 100755 index 0000000..1cc99f7 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,55 @@ +# +# SFTP only Container - thomasw64/sshd +# +# Under Apache 2.0 License see LICENSE file. +# +# Copyright IBM 2021,2022 +# SPDX-License-Identifier: Apache2.0 +# +# Authors: +# - Thomas Weinzettl +# +#=============================================================================== + +# Choose from one of the two: +# ubi8-minimal:latest ..... if you have RH licenses +# fedora-minimal:latest ... if you prefer complete open source +# FROM registry.access.redhat.com/ubi8-minimal:latest +FROM registry.fedoraproject.org/fedora-minimal:latest + +LABEL org.opencontainers.image.title="SFTP only Container" +LABEL org.opencontainers.image.description="A container that allows to share docker/podman volumes via a secure SFTP only connection." +LABEL org.opencontainers.image.authors="thomasw@ae.ibm.com" +LABEL org.opencontainers.image.source="https://github.com/IBM/sftp-only-container.git" +LABEL org.opencontainers.image.vendor="IBM" +LABEL org.opencontainers.image.licenses="Apache-2.0" +#LABEL description="A ssh container with an simple method to import public keys" +LABEL org.opencontainers.image.version="0.3.1" + +RUN microdnf --nodocs -y install openssh-server sudo && \ + microdnf clean all + +RUN mkdir -p /home/.sshd/ && \ + chmod 700 /home/.sshd + +RUN sed -i "s/#PubkeyAuthentication yes/PubkeyAuthentication yes/g" /etc/ssh/sshd_config && \ + sed -i "s/PermitRootLogin yes/PermitRootLogin no/g" /etc/ssh/sshd_config && \ + sed -i "s/PasswordAuthentication yes/PasswordAuthentication no/g" /etc/ssh/sshd_config && \ + sed -i "s/GSSAPIAuthentication yes/GSSAPIAuthentication no/g" /etc/ssh/sshd_config && \ + sed -i "s/#PermitEmptyPasswords no/PermitEmptyPasswords no/g" /etc/ssh/sshd_config + +COPY entrypoint.sh /entrypoint.sh +COPY ssh-key.sh /bin/ssh-key.sh +COPY ssh-functions.sh /bin/ssh-functions.sh +COPY containeradm /bin/containeradm + +ENV SFTP_ONLY=no +ENV DEBUG=0 + +VOLUME ["/Volume","/home/"] + +EXPOSE 22 + +ENTRYPOINT ["/entrypoint.sh"] + +CMD ["/sbin/sshd","-D","-e"] diff --git a/LICENSE b/LICENSE index 261eeb9..7cb4cc8 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright 2021, 2022 IBM Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/MAINTAINERS.md b/MAINTAINERS.md new file mode 100644 index 0000000..5fda93c --- /dev/null +++ b/MAINTAINERS.md @@ -0,0 +1,3 @@ +# MAINTAINERS + +Thomas Weinzettl - thomasw@ae.ibm.com diff --git a/README.md b/README.md index 3b90813..08a6ac8 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,97 @@ + # sftp-only-container -container files and shell scripts for the IBM Developer tutorial sftp-only container for IBM zCX (or any other appliance-like container runtime). + + + + + +## Scope + +container files and shell scripts for the IBM Developer tutorial sftp-only +container for IBM zCX (or any other appliance-like container runtime). + +*TODO* add link to public site, once published. + +## Usage - Start the container + +### Requirements + +- IBM Z Container Extension (zCX) or other remote container runtime (docker or podman) e.g. podman machines on MacOS +- volume for `/home` to contain _authorized_keys_ for ssh public key +authentication +- volume for `/Volume` to host the hub of container volumes to mount onto + +### Start the container + +Here is an example to start the container. The `dummy_volume` is an example of +how to add another container volume to the sftp_only container. + +``` +$ docker run --name sftp-only --hostname sftp-only --rm -d -p 2022:22 \ +-v sftp-home:/home -v sftp-volume:/Volume -v dummy_volume:/Volume/dummy \ +-e SFTP_ONLY=yes thomasw/sftp-only:latest +``` + +| Environment Variable | Values | description | +| --- | --- | --- | +| SFTP_ONLY | yes / **no** | **Default:** no
Set to _yes_ if the container should restrict the access to sftp, and change the root to `/Volume` | +| DEBUG | **0** numeric | **Default:** 0 (for no output)
1 or higher is more verbose | + +## Usage User Administration + +This can be done on the running container with `docker exec` or on the `/home` +volume while stopped. + +``` +$ docker exec sftp-only containeradm +... +``` + +or + +``` +$ docker run --rm -v sftp-home:/home thomasw/sftp-only:latest containeradm +... +``` + +To get started you need to add a user and add his ssh public key like this: + +``` +$ docker exec sftp-only containeradm user add username +User username was added. +$ docker exec sftp-only containeradm key add \ +"username:ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFHe4Aqe5RbyC1d7Zco+EI9Q4VUvtwcLEHHURK02pe+B test-key" +added key to user username +$ +``` + +Here is a list of the most important commands: + +| Task | Command | +| --- | --- | +| Add a user | `... containeradm user add username` or
`... containeradm user add username:1000:1000` | +| Delete a user | `... containeradm user del username` | +| List users | `... containeradm user list` | +| Add user to a group | `... containeradm user addgrp username groupname` | +| Remove user from a group | `... containeradm user rmgrp username groupname` | +| Add ssh public key | `... containeradm key "username:ssh-ed25119 AAAA...."` | +| List keys | `... containeradm key list username` | +| Dump the ssh config | `... containeradm showconfig` | +| Regenerate the hostkeys | `... containeradm hostkey refresh` + +## License + +The Dockerfiles and associated shell scripts are licensed under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0.html) + +All source files must include a Copyright and License header. The SPDX license header is preferred because it can be easily scanned. + +If you would like to see the detailed LICENSE click [here](LICENSE). + +```text +# +# Copyright 2020- IBM Inc. All rights reserved +# SPDX-License-Identifier: Apache2.0 +# +``` + +[issues]: https://github.com/IBM/sftp-only-container/issues/new diff --git a/containeradm b/containeradm new file mode 100755 index 0000000..662d1cf --- /dev/null +++ b/containeradm @@ -0,0 +1,323 @@ +#!/bin/bash +# Under Apache 2.0 License see LICENSE file. +# +# Copyright IBM 2021,2022 +# SPDX-License-Identifier: Apache2.0 +# +# Authors: +# - Thomas Weinzettl +# +#=============================================================================== + +# We need to set DEBUG to 0 if it is not set in order to get the debug level +# right. +[ -z $DEBUG ] && DEBUG=0 || [ $DEBUG -ge 1 ] && echo "Container: Debug level=${DEBUG}" + + +# This shell script is helps to administer the container users as well as the +# ssh keys of the user and the host. +# +source /bin/ssh-functions.sh + +###### +# This function will print all possible users with their uid:gid settings +# the container is aware of in `id` format. +# +list_users() { + users=`ls /home/` + for user in $users ; do + if [ -d /home/${user} ]; then + # the `id` command helps us to print out the groups and ids + id ${user} + fi + done +} + +###### +# This function adds a ssh-key to a users authorized_keys file. It is split in two +# for security reasons. The acual add is executed with as the user whos key is to +# be added. +# +# We only parse the parameters, the format is "username:sshkey" +# +add_key(){ + IFS=":" read -r user key <<< "$@" + [ $DEBUG -ge 1 ] && echo "Container: DEBUG > add_key for user=${user} with key=${key:0:24}..." + + # + # We call here `sudo` as the user to add the key, to avoid any permission bit, or + # ownership issues. + sudo -E -u $user ssh-key.sh add $key +} + +###### +# This function lists the keys installed for one user +# $1 .. is the user name. +# +list_keys(){ + user=$1 + echo "Container: list keys for ${user}" + + # + # To avoid permission or ownership issues we call the list script as the user + sudo -E -u ${user} ssh-key.sh list +} + +###### +# This functions adds a new user to the container. It requires a username as parameter. +# Optionally uid and gid can be specified. +# +add_user() { + IFS=":" read -r user uid gid <<<$@ + + [ -z "$uid" ] && uid_cmd="" || uid_cmd="-u ${uid}" + + if [ -z "$gid" ]; then + gid_cmd="" + else + gid_cmd="-g ${gid}" + groupadd -g $gid $user + fi + + # -m needed to ensure we have the home-dir + useradd -m $uid_cmd $gid_cmd $user + echo "User $user was added." +} + +###### +# This functions adds a new group to the data. It requires a group name as +# parameter. Optionally a gid can be specified. +# +add_group() { + IFS=":" read -r group gid <<<$@ + + [ -z "$gid" ] && gid_cmd="" || gid_cmd="-g ${gid}" + + groupadd $gid_cmd $group + + echo "Group $group was added." +} + + +###### +# remove a user from a group +# +remove_from_group() { + IFS=" " read -r user group force <<< "$@" + [ $DEBUG -ge 1 ] && echo "Container: DEBUG > add_to_group user=${user},group=${group}" + + [ "$user" == "$group" ] && { + echo "Error: cannot remove primary group" + return + } + + if [ -f /home/${user}/.groups.d/${group} ]; then + rm /home/${user}/.groups.d/${group} + gpasswd -d $user $group + fi +} + +###### +# This function checks if a group exists +# +group_is_existing(){ + IFS=" " read -r group trash <<<$@ + result=0 + + [ -z "$group" ] || { + getent group $group >/dev/null && result=1 + } + + return $result +} + +###### +# This function adds a user to a group. It stores the group associations in +# $HOME/.groups.d/${groupname} by owning it by the user. +# +add_to_group() { + IFS=" " read -r user group <<< "$@" + [ $DEBUG -ge 1 ] && echo "Container: DEBUG > add_to_group user=${user},group=${group}" + + # We need to have an .groups.d directoy inside $HOME + if [ ! -d /home/${user}/.groups.d ]; then + sudo -u $user mkdir -p /home/${user}/.groups.d + sudo -u $user chmod 700 /home/${user}/.groups.d + fi + + group_is_existing $group + isexisting=$? + + [ $isexisting == 1 ] && { + gpasswd -a ${user} ${group} + sudo -u ${user} -g ${group} touch /home/${user}/.groups.d/${group} + sudo -u ${user} -g ${group} chmod 400 /home/${user}/.groups.d/${group} + } || { + echo "Group $group does not exist." + } + +} + +###### +# This function removes users +# +del_user(){ + IFS=" " read -r user <<<$@ + + userdel ${user} + + if [ -d /home/$user ]; then + rm -Rf /home/${user} + fi + + echo "User $user was deleted." +} + +###### +# This function regenerates the hostkeys. +# +hostkey_refresh(){ + rm -r /etc/ssh/ssh_host_*_key /etc/ssh/ssh_host_*_key.pub + ssh-keygen -A + store +} + +##### +# Print usage info +# +usage(){ + echo "containeradm ... usage" + echo "" + echo "# containeradm [object] [command] [parameter]" + echo "" + echo "Objects:" + echo " - user ....... to work with users." + echo " - group ...... to work with groups." + echo " - key ........ to work with public ssh keys." + echo " - hostkey .... to work with host keys." + echo " - showconfig . to show the current sshd_config" +} + +##### +# Print Usage info for user subcommand +usage_user(){ + echo "" + echo "Commands:" + echo " - list ..... to list users" + echo " no parameters" + echo " - add ...... to add a user" + echo " parameters: USERNAME[:[UID]:[GID]])" + echo " - del ...... to delete a user" + echo " parameters: USERNAME" + echo " - addgrp ... add to group" + echo " parameters: USERNAME GROUPNAME" + echo " - rmgrp .... remove from a group" + echo " parameters: USERNAME GROUPNAME" +} + +###### +# Print usage for key subcommand +# +usage_key(){ + echo "" + echo "Commands:" + echo " - list ..... to list a users key" + echo " parameters: USERNAME" + echo " - add ...... to add a key to a user" + echo " parameters: USERNAME:PUBLIC-SSH-KEY" +} + +###### +# Print usage for hostkey subcommand +usage_hostkey() { + echo "" + echo "Commands:" + echo " - refresh .. generates new host keys" +} + +###### +# Print usage for adding grouops +usage_group() { + echo "" + echo "Commands:" + echo " - add .... GROUPNAME[:GID] to add a group" +} + +################################################################################ +# main + +# Lets get the arguments and load them into our variables +read -r object command parameters <<<$@ + +#TODO: switch to case (replacing if) +case "$object" in + "user") + case "$command" in + "list") + list_users + ;; + + "add") + add_user ${parameters} + ;; + + "del") + del_user ${parameters} + ;; + + "addgrp") + add_to_group ${parameters} + ;; + + "rmgrp") + remove_from_group ${parameters} + ;; + + *|"help") + usage + usage_user + ;; + esac + ;; + + "group") + case "$command" in + "add") + add_group ${parameters} + ;; + + *|"help") + usage + usage_group + ;; + esac + ;; + + "key") + if [[ -z "$command" || "$command" == "help" ]]; then + usage + usage_key + elif [ "$command" == "list" ]; then + list_keys $parameters + elif [ "$command" == "add" ]; then + add_key $parameters + fi + ;; + + "hostkey") + if [[ -z "$command" || "$command" == "help" ]]; then + usage + usage_hostkey + elif [ "$command" == "refresh" ]; then + hostkey_refresh + fi + ;; + + "showconfig") + cat /etc/sshd/config + ;; + + *|"help") + usage + ;; +esac diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100755 index 0000000..e54e4d6 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,176 @@ +#!/bin/bash +# Under Apache 2.0 License see LICENSE file. +# +# Copyright IBM 2021,2022 +# SPDX-License-Identifier: Apache2.0 +# +# Authors: +# - Thomas Weinzettl +# +#=============================================================================== + +# +# We rely on having /run/pids available, as we will place the sshd pid file +# in there. +# +mkdir -p /run/pids/ + +source /bin/ssh-functions.sh + +[ -z $DEBUG ] && DEBUG=0 || echo "Container: Debug level=${DEBUG}" + +# +# This functions is called for every invocation of the container to re-construct +# the /etc/passwd and all users we need to have in there. +# This will create the group and user based on the following parameters: +# $1 ... is the username +# $2 ... is the uid +# $3 ... is the gid +# +# The password field will be set to '!locked', the ! will prevent from users +# using a password for login. +make_user() { + IFS=" " read -r user uid gid <<< "$@" + [ $DEBUG -ge 1 ] && echo "Container: DEBUG > make_user user=${user},uid=${uid},gid=${gid}" + + groupadd -g ${gid} ${user} 2>/dev/null + useradd -m -u ${uid} -g ${gid} ${user} 2>/dev/null + echo "${user}:!locked" | chpasswd -e +} + +# +# This function orchestrates the recreation of all passwd entries for all users +# we need. The volume mounted on /home and the directory in there (with their +# uid:gid settings will describe which users to create) +# +init_users() { + users=`ls /home/` + for user in $users ; do + if [ -d /home/${user} ]; then + uid=`stat -c '%u' /home/${user}` + gid=`stat -c '%g' /home/${user}` + + make_user ${user} ${uid} ${gid} + + if [ -d "/home/${user}/.groups.d" ]; then + groups=`ls /home/${user}/.groups.d/` + [ $DEBUG -ge 1 ] && echo "Container: DEBUG> user=${user} has this groups=(${groups})" + + for group in $groups ; do + #TODO: Need to fix. What if a group does not exist. + + # To make sure we are not tricked into a group that the user + # is not added to, we read it from the file + grp=`stat -c '%G' /home/${user}/.groups.d/${group}` + + # gpasswd needs a silencer (>/dev/null) or writes a message + # like "Adding xxx to group yyy" + gpasswd -a ${user} ${grp} >/dev/null + done + fi + fi + done +} + +# +# This functions modifies the sshd_config to support sftp only access in a chrooted +# environment. +# +set_config_sftp_only(){ + sed -i "s/Subsystem sftp \/usr\/libexec\/openssh\/sftp-server/Subsystem sftp internal-sftp/g" /etc/ssh/sshd_config + sed -i "s/#ChrootDirectory none/ChrootDirectory \/Volume\//g" /etc/ssh/sshd_config + sed -i "s/X11Forwarding yes/X11Forwarding no/g" /etc/ssh/sshd_config + sed -i "s/#AllowTcpForwarding yes/AllowTcpForwarding no/g" /etc/ssh/sshd_config + echo "ForceCommand internal-sftp" >> /etc/ssh/sshd_config + + owner=`stat -c '%u' /Volume` + if [ "${owner}" != "0" ]; then + echo "Container: WARNING /Volume has not the proper ownership" + fi + + owngrp=`stat -c '%g' /Volume` + if [ "${owngrp}" != "0" ]; then + echo "Container: WARNING /Volume has not the proper group ownership" + fi + + perm=`stat -c '%a' /Volume` + if [ "${perm}" != "755" ]; then + echo "Container: WARNING /Volume has not the proper permission bits" + fi +} + +# +# Stop the SSHD Daemon +# +stop_ssh() { + pid=`cat /run/pids/sshd.pid` + echo "Container: stopping sshd pid=${pid}" + kill -s TERM $pid + wait $pid +} + +# +# Start the SSHD Daemon +# +start_ssh() { + echo "Container: starting ssh with command '$@'" + $@ & + pid=$! + echo -n $pid >/run/pids/sshd.pid + echo "Container: sshd (pid=${pid}) started..." + wait ${pid} + exit $? +} + +# +# running the command +# + +# +# First we need to recreate our /etc/passwd and all users that has to be in there +init_users + +# +# If `-e SFTP_ONLY=yes` was specified we change the sshd_config to reflect that. +if [ "$SFTP_ONLY" == "yes" ]; then + echo "Container: SFTP_ONLY is set to ${SFTP_ONLY}" + set_config_sftp_only +fi + +# +# We need to make sure we have ssh keys. +[ $DEBUG -ge 1 ] && echo "Container: DEBUG> checking host keys." +load_host_keys +if [ ! -e /etc/ssh/ssh_host_ed25519_key ]; then + [ $DEBUG -ge 1 ] && echo "Container: DEBUG> generate host keys." + /bin/ssh-keygen -A + store_host_keys +fi + +# +# Now let us figure out what the container is suppose to do today. +if [ "$1" == "help" ]; then + echo "HELP:" + echo "" + echo "supported commands:" + echo "sshd or nothing ... start sshd daemon" + echo "containeradm ...... add or delete users, add keys, ..." + echo "show-config ... show the sshd config file" +elif [ "`basename $1`" == "sshd" ];then + trap stop_ssh SIGTERM SIGINT + + start_ssh $@ +elif [ "$1" == "containeradm" ]; then + shift + /bin/containeradm $@ +elif [ "$1" == "show-config" ]; then + cat /etc/ssh/sshd_config +else + [ "$SFTP_ONLY" == "yes" ] && { + echo "Container runs in SFTP only mode. Command access restricted." + echo "use 'containeradm' command to manage." + } || { + exec $@ + } + +fi diff --git a/sftp.batch b/sftp.batch new file mode 100644 index 0000000..25c4d6f --- /dev/null +++ b/sftp.batch @@ -0,0 +1,8 @@ +mkdir Containers +mkdir Containers/sftp-only +cd Containers/sftp-only +put Dockerfile +put entrypoint.sh +put containeradm +put ssh-functions.sh +put ssh-key.sh diff --git a/ssh-functions.sh b/ssh-functions.sh new file mode 100755 index 0000000..81223b8 --- /dev/null +++ b/ssh-functions.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# Under Apache 2.0 License see LICENSE file. +# +# Copyright IBM 2021,2022 +# SPDX-License-Identifier: Apache2.0 +# +# Authors: +# - Thomas Weinzettl +# +#=============================================================================== + + +store_host_key() { + read -r keyname to loc other <<<$@ + + [ -e /etc/ssh/ssh_host_${keyname}_key ] && \ + cp -p -f /etc/ssh/ssh_host_${keyname}_key* /home/.sshd/ +} + +load_host_key() { + read -r keyname to loc other <<<$@ + + [ -e /home/.sshd/ssh_host_${keyname}_key ] && \ + cp -p -n /home/.sshd/ssh_host_${keyname}_key* /etc/ssh/ + +} +# +# +# +store_host_keys() { + keys="dsa rsa ecdsa ed25519" + for key in $keys ; do + store_host_key $key + done +} + +load_host_keys(){ + keys="dsa rsa ecdsa ed25519" + for key in $keys ; do + load_host_key $key + done +} diff --git a/ssh-key.sh b/ssh-key.sh new file mode 100755 index 0000000..e0ab5f3 --- /dev/null +++ b/ssh-key.sh @@ -0,0 +1,110 @@ +#!/bin/bash +# THIS IS TO BE RUN AS USER *** NOT AS ROOT +# +# Under Apache 2.0 License see LICENSE file. +# +# Copyright IBM 2021,2022 +# SPDX-License-Identifier: Apache2.0 +# +# Authors: +# - Thomas Weinzettl +# +#=============================================================================== + +# +# The ssh directory must exist and has to have permissions +# u:rwx g:--- o:--- aka 700 +# +fix_ssh_dir() { + # this helps us to capture othersuff someone could send over what we do not + # want to pass to chmod. + IFS=" " read -r sshdir otherstuff <<<"$@" + + if [ ! -d $sshdir ]; then + mkdir -p $sshdir + fi + + chmod 700 $sshdir +} + +# +# Add a public ssh key to the authorized_keys file, but only if it is a real +# key file. +add_key() { + # key needs to capture all the rest. In this case it is save, as we stuff it + # into a temp keyfile and check it with ssh-keygen -lf, so garbage keys + # won't pass the test. + IFS=" " read -r sshdir key <<<"$@" + + [ $DEBUG -gt 1 ] \ + && echo "ssh-key ($USER) DEBUG: add_key dir=${sshdir} key=${key:0:24}" + + fix_ssh_dir $sshdir + + # We want a temp keyfile, just to check if the key we got passed over is + # acutally a ssh public key. + keyfile=`mktemp /tmp/keyfile.XXXXXXXX` + echo "$key" >>$keyfile + + # -lf will verify the keys and hash them. It will come back with none-0 + # return code if the key is not valid. + ssh-keygen -lf $keyfile >/dev/null 2>&1 || { + echo "ERROR: add_key ${key:0:24}... is not a valid public ssh key." + rm $keyfile + exit 1 + } + + authorized_keys=${sshdir}/authorized_keys + # if the keyfile is there we make it writable for short. + [ -e $authorized_keys ] && chmod 600 $authorized_keys + # let us not trust the temp file, as someone could have written to it. We + # take the key that we have written into the temp file. + echo ${key} >> $authorized_keys + chmod 644 $keyfile + rm $keyfile + + # lets make the keyfile read-only again + chmod 400 $authorized_keys + echo "added key to user $USER" +} + +# +# List all the keys for the current user. +list_keys(){ + # we make sure we do not pass othergarbage to ssh-keygen + IFS=" " read -r sshdir othergarbage <<<"$@" + + [ $DEBUG -ge 1 ] && echo "DEBUG: list_keys dir=${sshdir} " + + fix_ssh_dir $sshdir + authorized_keys=${sshdir}/authorized_keys + [ -e $authorized_keys ] \ + && ssh-keygen -lf $authorized_keys \ + || echo "No keys found." +} + +# +# We will not allow to run this as root, so that no-one introduces a ssh +# key to a root user. +effective_user=`id -u` +if [ "${effective_user}" -le 0 ]; then + echo "$0 : please run as user" + echo "$0 : Current user is root, this is not permited." + exit 1 +fi + +# +# Here comes the main program ... +function=$1 +shift + +if [ -z $function ]; then + echo "usage: ssh-key.sh [command]" + echo " " + echo "add .... add a key to a user" + echo "list ... list the keys of a user" +elif [ "$function" == "add" ]; then + add_key $HOME/.ssh "$@" +elif [ "$function" == "list" ]; then + list_keys $HOME/.ssh +fi