From 2ff21ba11a076294491c7d6c99216c9b56b15378 Mon Sep 17 00:00:00 2001 From: Rob klein Gunnewiek Date: Fri, 28 May 2021 15:17:27 +0200 Subject: [PATCH] Docker image that generates PBKDF2 keys for the JSON file auth (#355) --- crates/unftp-auth-jsonfile/Makefile | 7 +- .../files/key-generator.sh | 212 ++++++++++++++++++ .../key-generator.Dockerfile | 6 + 3 files changed, 224 insertions(+), 1 deletion(-) create mode 100755 crates/unftp-auth-jsonfile/files/key-generator.sh create mode 100644 crates/unftp-auth-jsonfile/key-generator.Dockerfile diff --git a/crates/unftp-auth-jsonfile/Makefile b/crates/unftp-auth-jsonfile/Makefile index e46a9f3b..9fed9ad3 100644 --- a/crates/unftp-auth-jsonfile/Makefile +++ b/crates/unftp-auth-jsonfile/Makefile @@ -2,7 +2,7 @@ help: # Shows available `make` commands @echo 'Available `make` commands:' >/dev/stderr @echo >/dev/stderr - @awk -F'#' '/^[a-z][A-Za-z0-9]+/ {if (NF > 1) { sub(/:[^#]*/, ""); print $$1 "\t\t" $$2}}' Makefile + @awk -F'#' '/^[a-z][A-Za-z0-9]+/ {if (NF > 1) { sub(/:[^#]*/, ""); printf("%-25s %s\n", $$1, $$2)}}' Makefile .PHONY: docs docs: # Creates the API docs and opens it in the browser @@ -21,3 +21,8 @@ pr-prep: # Runs checks to ensure you're ready for a pull request .PHONY: publish publish: # Publishes the lib to crates.io cargo publish --verbose + +.PHONY: key-generator-image +key-generator-image: # Generate a Docker image for the unftp key generator script (files/run.sh) + docker build -f key-generator.Dockerfile -t bolcom/unftp-key-generator:latest . + diff --git a/crates/unftp-auth-jsonfile/files/key-generator.sh b/crates/unftp-auth-jsonfile/files/key-generator.sh new file mode 100755 index 00000000..b299c7fa --- /dev/null +++ b/crates/unftp-auth-jsonfile/files/key-generator.sh @@ -0,0 +1,212 @@ +#!/usr/bin/env bash + +function error { + RED='\033[0;31m' + NO_COLOR='\033[0m' + echo -e ${RED}ERROR: $*${NO_COLOR} >&2 +} + +function warning { + YELLOW='\033[0;33m' + NO_COLOR='\033[0m' + echo -e ${YELLOW}WARNING: $*${NO_COLOR} >&2 +} + +function exit_fail { + error $* + exit 1 +} + +[[ $BASH_VERSION =~ ^5 ]] || exit_fail "This script needs to run with Bash version 5" + +tty -s &>/dev/null || exit_fail "This script needs to run interactively. Use 'docker run -ti '" + +function read_password { + local -n opts=$1 + local valid_password + while true; do + valid_password=true + if ${opts[print]}; then + read -p "Enter password or press ENTER to generate one: " PASSWORD + else + read -s -p "Enter password or press ENTER to generate one: " PASSWORD + echo + fi + if [[ ${#PASSWORD} -eq 0 ]]; then + local output_length=16 + if [[ ${opts[length]} > 16 ]]; then + output_length=${opts[length]} + fi + PASSWORD=$(pwgen -c -n -y -s -B -v -1 $output_length) + if [[ $? -ne 0 ]]; then + exit 5 + fi + echo Generated password: $PASSWORD + break + fi + if [[ ${opts[length]} -eq 0 ]]; then + : # No complexity requirements (-d or -l 0 argument was used) + else + if [[ ${#PASSWORD} -lt ${opts[length]} ]]; then + valid_password=false + warning "Password must be at least ${opts[length]} characters long." + fi + if [[ ${opts[case]} == "yes" ]] && ! ( [[ $PASSWORD =~ [[:upper:]] ]] && [[ $PASSWORD =~ [[:lower:]] ]] ); then + valid_password=false + warning "Password complexity rules require a mixed case password. So make sure to include both lower and uppercase characters in your password." + fi + if [[ ${opts[symbols]} == "yes" && ! $PASSWORD =~ [[:punct:]] ]]; then + valid_password=false + warning "Password complexity rules require a symbolic character in the password." + fi + if [[ ${opts[digits]} == "yes" && ! $PASSWORD =~ [[:digit:]] ]]; then + valid_password=false + warning "Password complexity rules require a digit character in the password." + fi + fi + if $valid_password && ${options[print]}; then + return + fi + while true; do + if ! $valid_password; then + echo + warning "Password does not meet the above mentioned password complexity rules!\n To ignore this: Repeat the weak password at the next prompt.\n To be safe: press ENTER to try again." + fi + read -s -p "Repeat password (leave blank to re-enter initial password): " _PASSWORD + echo + if [[ -z $_PASSWORD ]]; then + break + elif [[ $_PASSWORD = $PASSWORD ]]; then + warning "Accepted a possibly insecure password." + return + else + error "Repeated password does not match" + error "Try again." + fi + done + done +} + +function generate_pbkdf2 { + local -n opts=$1 + local username=$2 + local salt=$(dd if=/dev/urandom bs=1 count=8 2>/dev/null | hexdump -v -e '"\\" "x" 1/1 "%02x"') + local b64_salt=$(echo -ne $salt | openssl base64 -A) + local pbkdf2=$(echo -n $PASSWORD | nettle-pbkdf2 -i 500000 -l 32 --hex-salt $(echo -ne $salt | xxd -p -c 80) --raw |openssl base64 -A) + + if [[ -n $username ]]; then + ENTRY="\"username\": \"$username\", \"pbkdf2_salt\": \"$b64_salt\", \"pbkdf2_key\": \"${pbkdf2}\", \"pbkdf2_iter\": ${options[iter]}" + else + printf "pbkdf2_salt: %s\npbkdf2_key: %s\n" $b64_salt $pbkdf2 + fi +} + +function validate_yes_no { + if [[ $1 =~ ^(yes|no)$ ]]; then + return 0 + else + return 1 + fi +} + +function usage { + cat <