From 64b747ff2fba751a04bde42c763019d4137825ad Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 11 Mar 2019 07:18:20 -0500 Subject: [PATCH 1/5] Add ssh key fingerprint as unique column in api-db --- .../docker-entrypoint-initdb.d/00-tables.sql | 11 ++++++----- .../01-migrations.sql | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/services/api-db/docker-entrypoint-initdb.d/00-tables.sql b/services/api-db/docker-entrypoint-initdb.d/00-tables.sql index 8ad9133bed..42591ee1b5 100644 --- a/services/api-db/docker-entrypoint-initdb.d/00-tables.sql +++ b/services/api-db/docker-entrypoint-initdb.d/00-tables.sql @@ -3,11 +3,12 @@ USE infrastructure; -- Tables CREATE TABLE IF NOT EXISTS ssh_key ( - id int NOT NULL auto_increment PRIMARY KEY, - name varchar(100) NOT NULL, - key_value varchar(5000) NOT NULL, - key_type ENUM('ssh-rsa', 'ssh-ed25519') NOT NULL DEFAULT 'ssh-rsa', - created timestamp DEFAULT CURRENT_TIMESTAMP + id int NOT NULL auto_increment PRIMARY KEY, + name varchar(100) NOT NULL, + key_value varchar(5000) NOT NULL, + key_type ENUM('ssh-rsa', 'ssh-ed25519') NOT NULL DEFAULT 'ssh-rsa', + key_fingerprint char(51) NULL UNIQUE, + created timestamp DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS user ( diff --git a/services/api-db/docker-entrypoint-initdb.d/01-migrations.sql b/services/api-db/docker-entrypoint-initdb.d/01-migrations.sql index c5438ec42e..b454806a48 100644 --- a/services/api-db/docker-entrypoint-initdb.d/01-migrations.sql +++ b/services/api-db/docker-entrypoint-initdb.d/01-migrations.sql @@ -588,6 +588,24 @@ CREATE OR REPLACE PROCEDURE END; $$ +CREATE OR REPLACE PROCEDURE + add_key_fingerprint_to_ssh_key() + + BEGIN + IF NOT EXISTS( + SELECT NULL + FROM INFORMATION_SCHEMA.COLUMNS + WHERE + table_name = 'ssh_key' + AND table_schema = 'infrastructure' + AND column_name = 'key_fingerprint' + ) THEN + ALTER TABLE `ssh_key` + ADD `key_fingerprint` char(51) NULL UNIQUE; + END IF; + END; +$$ + DELIMITER ; CALL add_production_environment_to_project(); @@ -618,6 +636,7 @@ CALL add_default_value_to_task_status(); CALL add_scope_to_env_vars(); CALL add_deleted_to_environment_backup(); CALL convert_task_command_to_text(); +CALL add_key_fingerprint_to_ssh_key(); -- Drop legacy SSH key procedures DROP PROCEDURE IF EXISTS CreateProjectSshKey; From 9fedd48fefbef6c0eb7f1fbd566882c2cdc8074f Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Mon, 11 Mar 2019 07:50:51 -0500 Subject: [PATCH 2/5] Update api to get/save ssh key fingerprints --- services/api/src/resources/sshKey/index.js | 6 +++++ .../api/src/resources/sshKey/resolvers.js | 22 ++++++++++++------- services/api/src/resources/sshKey/sql.js | 3 +++ services/api/src/typeDefs.js | 1 + 4 files changed, 24 insertions(+), 8 deletions(-) diff --git a/services/api/src/resources/sshKey/index.js b/services/api/src/resources/sshKey/index.js index 6ba2948ce4..205b69eeef 100644 --- a/services/api/src/resources/sshKey/index.js +++ b/services/api/src/resources/sshKey/index.js @@ -14,6 +14,12 @@ const validateSshKey = (key /* : string */) /* : boolean */ => { } }; +const getSshKeyFingerprint = (key /* : string */) /* : string */ => { + const parsed = sshpk.parseKey(key, 'ssh'); + return parsed.fingerprint('sha256', 'ssh').toString(); +}; + module.exports = { validateSshKey, + getSshKeyFingerprint, }; diff --git a/services/api/src/resources/sshKey/resolvers.js b/services/api/src/resources/sshKey/resolvers.js index acddae8d60..6f5570abf4 100644 --- a/services/api/src/resources/sshKey/resolvers.js +++ b/services/api/src/resources/sshKey/resolvers.js @@ -3,7 +3,7 @@ const R = require('ramda'); const sqlClient = require('../../clients/sqlClient'); const { isPatchEmpty, prepare, query } = require('../../util/db'); -const { validateSshKey } = require('.'); +const { validateSshKey, getSshKeyFingerprint } = require('.'); const Sql = require('./sql'); /* :: @@ -66,8 +66,9 @@ const addSshKey = async ( { credentials: { role, userId: credentialsUserId } }, ) => { const keyType = sshKeyTypeToString(unformattedKeyType); + const keyFormatted = formatSshKey({ keyType, keyValue }); - if (!validateSshKey(formatSshKey({ keyType, keyValue }))) { + if (!validateSshKey(keyFormatted)) { throw new Error('Invalid SSH key format! Please verify keyType + keyValue'); } @@ -86,6 +87,7 @@ const addSshKey = async ( name, keyValue, keyType, + keyFingerprint: getSshKeyFingerprint(keyFormatted), }), ); await query(sqlClient, Sql.addSshKeyToUser({ sshKeyId: insertId, userId })); @@ -125,16 +127,20 @@ const updateSshKey = async ( throw new Error('Input patch requires at least 1 attribute'); } - if ( - (keyType || keyValue) && - !validateSshKey(formatSshKey({ keyType, keyValue })) - ) { - throw new Error('Invalid SSH key format! Please verify keyType + keyValue'); + let keyFingerprint = null; + if ((keyType || keyValue)) { + const keyFormatted = formatSshKey({ keyType, keyValue }); + + if (!validateSshKey(keyFormatted)) { + throw new Error('Invalid SSH key format! Please verify keyType + keyValue'); + } + + keyFingerprint = getSshKeyFingerprint(keyFormatted); } await query( sqlClient, - Sql.updateSshKey({ id, patch: { name, keyType, keyValue } }), + Sql.updateSshKey({ id, patch: { name, keyType, keyValue, keyFingerprint } }), ); const rows = await query(sqlClient, Sql.selectSshKey(id)); diff --git a/services/api/src/resources/sshKey/sql.js b/services/api/src/resources/sshKey/sql.js index ce5aa717eb..21269b55e7 100644 --- a/services/api/src/resources/sshKey/sql.js +++ b/services/api/src/resources/sshKey/sql.js @@ -58,11 +58,13 @@ const Sql /* : SqlObj */ = { name, keyValue, keyType, + keyFingerprint, } /* : { id: number, name: string, keyValue: string, keyType: string, + keyFingerprint: string, } */, ) => knex('ssh_key') @@ -71,6 +73,7 @@ const Sql /* : SqlObj */ = { name, key_value: keyValue, key_type: keyType, + key_fingerprint: keyFingerprint, }) .toString(), addSshKeyToUser: ( diff --git a/services/api/src/typeDefs.js b/services/api/src/typeDefs.js index 222eb3d9c3..00f45cde45 100644 --- a/services/api/src/typeDefs.js +++ b/services/api/src/typeDefs.js @@ -77,6 +77,7 @@ const typeDefs = gql` name: String keyValue: String keyType: String + keyFingerprint: String created: String } From b3997e3940f02e300accc682896846e5ba0f7781 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 12 Mar 2019 10:19:09 -0500 Subject: [PATCH 3/5] Add script to generate fingerprints for existing keys in api-db --- ...577-api-db-generate-ssh-key-fingerprints.sh | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100755 helpers/577-api-db-generate-ssh-key-fingerprints.sh diff --git a/helpers/577-api-db-generate-ssh-key-fingerprints.sh b/helpers/577-api-db-generate-ssh-key-fingerprints.sh new file mode 100755 index 0000000000..adeaaa74cf --- /dev/null +++ b/helpers/577-api-db-generate-ssh-key-fingerprints.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -eu -o pipefail + +# disable globbing +set -f; +# set field separator to NL (only) +IFS=$'\n'; +# get all ssh keys from api-db into a bash array +SSHKEY_RECORDS=( $(mysql -u$MARIADB_USER -p$MARIADB_PASSWORD -h$HOSTNAME $MARIADB_DATABASE --batch -sse "select id, key_type, key_value from ssh_key") ); + +for SSHKEY_RECORD in "${SSHKEY_RECORDS[@]}"; +do + RECORD_ID=$(awk '{print $1}' <<< "$SSHKEY_RECORD"); + SSHKEY=$(awk '{print $2, $3}' <<< "$SSHKEY_RECORD"); + FINGERPRINT=$(ssh-keygen -lE sha256 -f - <<< "$SSHKEY" | awk '{print $2}'); + mysql -u$MARIADB_USER -p$MARIADB_PASSWORD -h$HOSTNAME $MARIADB_DATABASE -e "UPDATE ssh_key SET key_fingerprint = '$FINGERPRINT' WHERE id = $RECORD_ID"; +done; From cbd7e68124bcc0bdebc6bc75598f96ec7eb4bebc Mon Sep 17 00:00:00 2001 From: Schnitzel Date: Tue, 19 Mar 2019 15:04:31 -0500 Subject: [PATCH 4/5] move generate-ssh-key-fingerprint into initdb of api-db --- ...77-api-db-generate-ssh-key-fingerprints.sh | 18 --------- services/api-db/Dockerfile | 2 + .../04-generate-ssh-key-fingerprints.sh | 38 +++++++++++++++++++ services/api-db/rerun_initdb.sh | 13 +++++-- 4 files changed, 49 insertions(+), 22 deletions(-) delete mode 100755 helpers/577-api-db-generate-ssh-key-fingerprints.sh create mode 100755 services/api-db/docker-entrypoint-initdb.d/04-generate-ssh-key-fingerprints.sh diff --git a/helpers/577-api-db-generate-ssh-key-fingerprints.sh b/helpers/577-api-db-generate-ssh-key-fingerprints.sh deleted file mode 100755 index adeaaa74cf..0000000000 --- a/helpers/577-api-db-generate-ssh-key-fingerprints.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -set -eu -o pipefail - -# disable globbing -set -f; -# set field separator to NL (only) -IFS=$'\n'; -# get all ssh keys from api-db into a bash array -SSHKEY_RECORDS=( $(mysql -u$MARIADB_USER -p$MARIADB_PASSWORD -h$HOSTNAME $MARIADB_DATABASE --batch -sse "select id, key_type, key_value from ssh_key") ); - -for SSHKEY_RECORD in "${SSHKEY_RECORDS[@]}"; -do - RECORD_ID=$(awk '{print $1}' <<< "$SSHKEY_RECORD"); - SSHKEY=$(awk '{print $2, $3}' <<< "$SSHKEY_RECORD"); - FINGERPRINT=$(ssh-keygen -lE sha256 -f - <<< "$SSHKEY" | awk '{print $2}'); - mysql -u$MARIADB_USER -p$MARIADB_PASSWORD -h$HOSTNAME $MARIADB_DATABASE -e "UPDATE ssh_key SET key_fingerprint = '$FINGERPRINT' WHERE id = $RECORD_ID"; -done; diff --git a/services/api-db/Dockerfile b/services/api-db/Dockerfile index c8f5bd2f1e..3f65575402 100644 --- a/services/api-db/Dockerfile +++ b/services/api-db/Dockerfile @@ -1,6 +1,8 @@ ARG IMAGE_REPO FROM ${IMAGE_REPO:-lagoon}/mariadb +RUN apk add --no-cache openssh-keygen + ENV MARIADB_DATABASE=infrastructure \ MARIADB_USER=api \ MARIADB_PASSWORD=api \ diff --git a/services/api-db/docker-entrypoint-initdb.d/04-generate-ssh-key-fingerprints.sh b/services/api-db/docker-entrypoint-initdb.d/04-generate-ssh-key-fingerprints.sh new file mode 100755 index 0000000000..924f860699 --- /dev/null +++ b/services/api-db/docker-entrypoint-initdb.d/04-generate-ssh-key-fingerprints.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +set -eu -o pipefail + +# disable globbing +set -f; +# set field separator to NL (only) +IFS=$'\n'; + +DUPLICATE_SSHKEY_RECORDS=( $(mysql infrastructure --batch -sse "SELECT count(*) count, key_value FROM ssh_key GROUP BY key_value HAVING count > 1") ); + +if [ ${#DUPLICATE_SSHKEY_RECORDS[@]} -ne 0 ]; then + echo "====== FOUND DUPLICATE SSH KEYS IN LAGOON API DATABASE!" + for DUPLICATE_SSHKEY_RECORD in "${DUPLICATE_SSHKEY_RECORDS[@]}"; + do + echo "" + echo $(awk '{print $2}' <<< "$DUPLICATE_SSHKEY_RECORD"); + done; + echo "" + echo "====== PLEASE REMOVE DUPLICATED SSH KEYS AND RUN INITIALIZATION OF DB AGAIN" + #exit 1 +fi + +echo "=== Starting SSH KEY Fingerprint generation" + +# get all ssh keys which have no fingerprint yet from api-db into a bash array +SSHKEY_RECORDS=( $(mysql infrastructure --batch -sse "SELECT id, key_type, key_value FROM ssh_key WHERE key_fingerprint is NULL") ); + +for SSHKEY_RECORD in "${SSHKEY_RECORDS[@]}"; +do + RECORD_ID=$(awk '{print $1}' <<< "$SSHKEY_RECORD"); + SSHKEY=$(awk '{print $2, $3}' <<< "$SSHKEY_RECORD"); + FINGERPRINT=$(ssh-keygen -lE sha256 -f - <<< "$SSHKEY" | awk '{print $2}'); + echo "Adding SSH Key Fingerprint for SSH KEY '$RECORD_ID': $FINGERPRINT" + mysql infrastructure -e "UPDATE ssh_key SET key_fingerprint = '$FINGERPRINT' WHERE id = $RECORD_ID"; +done; + +echo "=== Finished SSH KEY Fingerprint generation" diff --git a/services/api-db/rerun_initdb.sh b/services/api-db/rerun_initdb.sh index 2690dc95d3..b482adfedb 100755 --- a/services/api-db/rerun_initdb.sh +++ b/services/api-db/rerun_initdb.sh @@ -1,5 +1,10 @@ -#!/bin/sh +#!/bin/bash -INITDB_DIR="/docker-entrypoint-initdb.d" - -for sql_file in `ls $INITDB_DIR`; do mysql --verbose < "$INITDB_DIR/$sql_file" ; done +for f in `ls /docker-entrypoint-initdb.d/*`; do + case "$f" in + *.sh) echo "$0: running $f"; . "$f" ;; + *.sql) echo "$0: running $f"; cat $f| tee | mysql --verbose; echo ;; + *) echo "$0: ignoring $f" ;; + esac +echo +done \ No newline at end of file From 941e82b45aea4908bc10af8da00ca9a2aa0c4a9f Mon Sep 17 00:00:00 2001 From: Schnitzel Date: Wed, 20 Mar 2019 13:46:04 -0500 Subject: [PATCH 5/5] switch to root to install openssh --- services/api-db/Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/api-db/Dockerfile b/services/api-db/Dockerfile index 3f65575402..d95f048580 100644 --- a/services/api-db/Dockerfile +++ b/services/api-db/Dockerfile @@ -1,7 +1,9 @@ ARG IMAGE_REPO FROM ${IMAGE_REPO:-lagoon}/mariadb +USER root RUN apk add --no-cache openssh-keygen +USER mysql ENV MARIADB_DATABASE=infrastructure \ MARIADB_USER=api \