1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-04-17 11:46:39 +02:00

Add known hosts checking for SFTP storage driver.

By default require a known hosts match as part of the SFTP storage driver's authentication process, i.e. repo-sftp-host-key-check-type=strict. The match is expected to be found in the default list or in a list of known hosts files provided by the user. An exception is made if a fingerprint has been manually configured with repo-sftp-host-fingerprint or repo-sftp-host-key-check-type=accept-new can be used to automatically add new hosts.

Also allow host key verification to be skipped, as before, but require the user to explicitly set this (repo-sftp-host-key-check-type=none) rather than it being the default.
This commit is contained in:
Reid Thompson 2023-09-15 20:22:38 -04:00 committed by GitHub
parent f5c730fd03
commit ce9ba0fade
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 3855 additions and 231 deletions

View File

@ -15,6 +15,18 @@
<p>Add <br-option>--repo-storage-tag</br-option> option to create object tags.</p>
</release-item>
<release-item>
<github-pull-request id="2125"/>
<release-item-contributor-list>
<release-item-contributor id="reid.thompson"/>
<release-item-reviewer id="stephen.frost"/>
<release-item-reviewer id="david.steele"/>
</release-item-contributor-list>
<p>Add known hosts checking for <proper>SFTP</proper> storage driver.</p>
</release-item>
<release-item>
<commit subject="Adjust Wait object to be more accurate when nested."/>
<commit subject="Aggregate error retries in ErrorRetry output."/>

View File

@ -2579,6 +2579,14 @@
<p>Commands are run exactly as if the repository were stored on a local disk.</p>
<execute-list host="{[host-pg1]}">
<title>Add sftp-server fingerprint to known_hosts file since <br-option>repo4-sftp-host-key-check-type</br-option> defaults to <quote>strict</quote></title>
<execute user="postgres" user-force="y">
<exe-cmd>ssh-keyscan -H {[host-sftp]} >> {[pg-home-path]}/.ssh/known_hosts 2>/dev/null</exe-cmd>
</execute>
</execute-list>
<execute-list host="{[host-pg1]}">
<title>Create the stanza</title>

View File

@ -2379,6 +2379,23 @@ option:
type: string
required: false
command: repo-type
depend:
option: repo-sftp-host-key-check-type
list:
- fingerprint
- strict
repo-sftp-host-key-check-type:
section: global
group: repo
type: string-id
default: strict
allow-list:
- accept-new
- fingerprint
- none
- strict
command: repo-type
depend: repo-sftp-host
repo-sftp-host-key-hash-type:
@ -2408,6 +2425,18 @@ option:
command: repo-type
depend: repo-sftp-host
repo-sftp-known-host:
section: global
group: repo
type: list
required: false
command: repo-type
depend:
option: repo-sftp-host-key-check-type
list:
- accept-new
- strict
repo-sftp-private-key-file:
section: global
group: repo

View File

@ -1021,17 +1021,34 @@
<summary>SFTP repository host fingerprint.</summary>
<text>
<p>SFTP repository host fingerprint generation should match the <setting>repo-sftp-host-key-hash-type</setting>. Generate the fingeprint via <code>awk '{print $2}' ssh_host_xxx_key.pub | base64 -d | (md5sum or sha1sum) -b</code>. The ssh host keys are normally found in the <path>/etc/ssh</path> directory.</p>
<p>SFTP repository host fingerprint generation should match the <setting>repo-sftp-host-key-hash-type</setting>. Generate the fingerprint via <code>awk '{print $2}' ssh_host_xxx_key.pub | base64 -d | (md5sum or sha1sum) -b</code>. The ssh host keys are normally found in the <path>/etc/ssh</path> directory.</p>
</text>
<example>f84e172dfead7aeeeae6c1fdfb5aa8cf</example>
</config-key>
<config-key id="repo-sftp-host-key-check-type" name="SFTP Host Key Check Type">
<summary>SFTP host key check type.</summary>
<text>
<p>The following SFTP host key check types are supported:</p>
<list>
<list-item><id>strict</id> - <backrest/> will never automatically add host keys to the ~/.ssh/known_hosts file, and refuses to connect to hosts whose host key has changed or is not found in the known hosts files. This option forces the user to manually add all new hosts.</list-item>
<list-item><id>accept-new</id> - <backrest/> will automatically add new host keys to the user's known hosts file, but will not permit connections to hosts with changed host keys.</list-item>
<list-item><id>fingerprint</id> - <backrest/> will check the host key against the fingerprint specified by the <br-option>repo-sftp-host-fingerprint</br-option> option.</list-item>
<list-item><id>none</id> - no host key checking will be performed.</list-item>
</list>
</text>
<default>yes</default>
<example>accept-new</example>
</config-key>
<config-key id="repo-sftp-host-key-hash-type" name="SFTP Repository Host Key Hash Type">
<summary>SFTP repository host key hash type.</summary>
<text>
<p>SFTP repository host key hash type. Declares the hash type to be used to compute the digest of the remote system's host key on SSH startup. Newer versions of libssh2 support <id>sha256</id> in addition to md5 and sha1.</p>
<p>SFTP repository host key hash type. Declares the hash type to be used to compute the digest of the remote system's host key on SSH startup. Newer versions of <id>libssh2</id> support <id>sha256</id> in addition to md5 and sha1.</p>
</text>
<example>sha256</example>
@ -1057,6 +1074,16 @@
<example>pg-backup</example>
</config-key>
<config-key id="repo-sftp-known-host" name="SFTP known hosts file">
<summary>SFTP known hosts file.</summary>
<text>
<p>A known hosts file to search for an SFTP host match during authentication. When unspecified, <backrest/> will default to searching <file>~/.ssh/known_hosts</file>, <file>~/.ssh/known_hosts2</file>, <file>/etc/ssh/ssh_known_hosts</file>, and <file>/etc/ssh/ssh_known_hosts2</file>. If configured with one or more file paths, <backrest/> will search those for a match. File paths must be full or leading tilde paths. The <setting>repo-sftp-known-host</setting> option can be passed multiple times to specify more than one known hosts file to search. To utilize known hosts file checking <setting>repo-sftp-host-fingerprint</setting> must not be specified. See also <setting>repo-sftp-host-check-type</setting> option.</p>
</text>
<example>/home/postgres/.ssh/known_hosts</example>
</config-key>
<config-key id="repo-sftp-private-key-file" name="SFTP Repository Private Key File">
<summary>SFTP private key file.</summary>

View File

@ -121,7 +121,7 @@ groupNameFromId(gid_t groupId)
}
/**********************************************************************************************************************************/
// Currently userHome() and userHomeFromId() are only used when building with libssh2
// Currently userHome() and userHomeFromId() are only used if we are building with libssh2
#ifdef HAVE_LIBSSH2
FN_EXTERN const String *

View File

@ -136,7 +136,7 @@ Option constants
#define CFGOPT_TYPE "type"
#define CFGOPT_VERBOSE "verbose"
#define CFG_OPTION_TOTAL 177
#define CFG_OPTION_TOTAL 179
/***********************************************************************************************************************************
Option value constants
@ -270,6 +270,15 @@ Option value constants
#define CFGOPTVAL_REPO_S3_URI_STYLE_PATH STRID5("path", 0x450300)
#define CFGOPTVAL_REPO_S3_URI_STYLE_PATH_Z "path"
#define CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_ACCEPT_NEW STRID5("accept-new", 0x2e576e9028c610)
#define CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_ACCEPT_NEW_Z "accept-new"
#define CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_FINGERPRINT STRID5("fingerprint", 0x51c9942453b9260)
#define CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_FINGERPRINT_Z "fingerprint"
#define CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_NONE STRID5("none", 0x2b9ee0)
#define CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_NONE_Z "none"
#define CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_STRICT STRID5("strict", 0x2834ca930)
#define CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_STRICT_Z "strict"
#define CFGOPTVAL_REPO_SFTP_HOST_KEY_HASH_TYPE_MD5 STRID5("md5", 0x748d0)
#define CFGOPTVAL_REPO_SFTP_HOST_KEY_HASH_TYPE_MD5_Z "md5"
#define CFGOPTVAL_REPO_SFTP_HOST_KEY_HASH_TYPE_SHA1 STRID6("sha1", 0x7412131)
@ -511,9 +520,11 @@ typedef enum
cfgOptRepoS3UriStyle,
cfgOptRepoSftpHost,
cfgOptRepoSftpHostFingerprint,
cfgOptRepoSftpHostKeyCheckType,
cfgOptRepoSftpHostKeyHashType,
cfgOptRepoSftpHostPort,
cfgOptRepoSftpHostUser,
cfgOptRepoSftpKnownHost,
cfgOptRepoSftpPrivateKeyFile,
cfgOptRepoSftpPrivateKeyPassphrase,
cfgOptRepoSftpPublicKeyFile,

View File

@ -389,6 +389,57 @@ cfgLoadUpdateOption(void)
}
}
// Error if repo-sftp--host-key-check-type is explicitly set to anything other than fingerprint and repo-sftp-host-fingerprint
// is also specified. For backward compatibility we need to allow repo-sftp-host-fingerprint when
// repo-sftp-host-key-check-type defaults to yes, but emit a warning to let the user know to change the configuration. Also
// set repo-sftp-host-key-check-type=fingerprint so other code does not need to know about this exception.
for (unsigned int repoIdx = 0; repoIdx < cfgOptionGroupIdxTotal(cfgOptGrpRepo); repoIdx++)
{
if (cfgOptionIdxTest(cfgOptRepoSftpHostKeyCheckType, repoIdx))
{
if (cfgOptionIdxTest(cfgOptRepoSftpHostFingerprint, repoIdx))
{
if (cfgOptionIdxSource(cfgOptRepoSftpHostKeyCheckType, repoIdx) == cfgSourceDefault)
{
LOG_WARN_FMT(
"option '%s' without option '%s' = '" CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_FINGERPRINT_Z "' is"
" deprecated\n"
"HINT: set option '%s=" CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_FINGERPRINT_Z "'",
cfgOptionIdxName(cfgOptRepoSftpHostFingerprint, repoIdx),
cfgOptionIdxName(cfgOptRepoSftpHostKeyCheckType, repoIdx),
cfgOptionIdxName(cfgOptRepoSftpHostKeyCheckType, repoIdx));
cfgOptionIdxSet(
cfgOptRepoSftpHostKeyCheckType, repoIdx, cfgSourceDefault,
VARSTRZ(CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_FINGERPRINT_Z));
}
else
{
if (cfgOptionIdxStrId(cfgOptRepoSftpHostKeyCheckType, repoIdx) !=
CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_FINGERPRINT)
{
THROW_FMT(
OptionInvalidError,
"option '%s' not valid without option '%s' = '" CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_FINGERPRINT_Z
"'",
cfgOptionIdxName(cfgOptRepoSftpHostFingerprint, repoIdx),
cfgOptionIdxName(cfgOptRepoSftpHostKeyCheckType, repoIdx));
}
}
}
else
{
if (cfgOptionIdxStrId(cfgOptRepoSftpHostKeyCheckType, repoIdx) ==
CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_FINGERPRINT)
{
THROW_FMT(
OptionRequiredError, "%s command requires option: %s", cfgCommandName(),
cfgOptionIdxName(cfgOptRepoSftpHostFingerprint, repoIdx));
}
}
}
}
FUNCTION_LOG_RETURN_VOID();
}

View File

@ -53,6 +53,7 @@ static const StringPub parseRuleValueStr[] =
PARSE_RULE_STRPUB("shared"), // val/str
PARSE_RULE_STRPUB("ssh"), // val/str
PARSE_RULE_STRPUB("storage.googleapis.com"), // val/str
PARSE_RULE_STRPUB("strict"), // val/str
PARSE_RULE_STRPUB("text"), // val/str
PARSE_RULE_STRPUB("warn"), // val/str
PARSE_RULE_STRPUB(CFGOPTDEF_CONFIG_PATH), // val/str
@ -104,6 +105,7 @@ typedef enum
parseRuleValStrQT_shared_QT, // val/str/enum
parseRuleValStrQT_ssh_QT, // val/str/enum
parseRuleValStrQT_storage_DT_googleapis_DT_com_QT, // val/str/enum
parseRuleValStrQT_strict_QT, // val/str/enum
parseRuleValStrQT_text_QT, // val/str/enum
parseRuleValStrQT_warn_QT, // val/str/enum
parseRuleValStrCFGOPTDEF_CONFIG_PATH, // val/str/enum
@ -118,6 +120,7 @@ Rule StringIds
static const StringId parseRuleValueStrId[] =
{
STRID5("accept-new", 0x2e576e9028c610), // val/strid
STRID5("aes-256-cbc", 0xc43dfbbcdcca10), // val/strid
STRID5("asc", 0xe610), // val/strid
STRID5("auto", 0x7d2a10), // val/strid
@ -131,6 +134,7 @@ static const StringId parseRuleValueStrId[] =
STRID5("detail", 0x1890d0a40), // val/strid
STRID5("diff", 0x319240), // val/strid
STRID5("error", 0x127ca450), // val/strid
STRID5("fingerprint", 0x51c9942453b9260), // val/strid
STRID5("full", 0x632a60), // val/strid
STRID5("gcs", 0x4c670), // val/strid
STRID5("gz", 0x3470), // val/strid
@ -162,6 +166,7 @@ static const StringId parseRuleValueStrId[] =
STRID5("shutdown", 0x75de4a55130), // val/strid
STRID5("ssh", 0x22730), // val/strid
STRID5("standby", 0x6444706930), // val/strid
STRID5("strict", 0x2834ca930), // val/strid
STRID5("text", 0xa60b40), // val/strid
STRID5("time", 0x2b5340), // val/strid
STRID5("tls", 0x4d940), // val/strid
@ -175,6 +180,7 @@ static const StringId parseRuleValueStrId[] =
typedef enum
{
parseRuleValStrIdAcceptNew, // val/strid/enum
parseRuleValStrIdAes256Cbc, // val/strid/enum
parseRuleValStrIdAsc, // val/strid/enum
parseRuleValStrIdAuto, // val/strid/enum
@ -188,6 +194,7 @@ typedef enum
parseRuleValStrIdDetail, // val/strid/enum
parseRuleValStrIdDiff, // val/strid/enum
parseRuleValStrIdError, // val/strid/enum
parseRuleValStrIdFingerprint, // val/strid/enum
parseRuleValStrIdFull, // val/strid/enum
parseRuleValStrIdGcs, // val/strid/enum
parseRuleValStrIdGz, // val/strid/enum
@ -219,6 +226,7 @@ typedef enum
parseRuleValStrIdShutdown, // val/strid/enum
parseRuleValStrIdSsh, // val/strid/enum
parseRuleValStrIdStandby, // val/strid/enum
parseRuleValStrIdStrict, // val/strid/enum
parseRuleValStrIdText, // val/strid/enum
parseRuleValStrIdTime, // val/strid/enum
parseRuleValStrIdTls, // val/strid/enum
@ -8041,13 +8049,108 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] =
( // opt/repo-sftp-host-fingerprint
PARSE_RULE_OPTIONAL_DEPEND // opt/repo-sftp-host-fingerprint
( // opt/repo-sftp-host-fingerprint
PARSE_RULE_VAL_OPT(cfgOptRepoType), // opt/repo-sftp-host-fingerprint
PARSE_RULE_VAL_STRID(parseRuleValStrIdSftp), // opt/repo-sftp-host-fingerprint
PARSE_RULE_VAL_OPT(cfgOptRepoSftpHostKeyCheckType), // opt/repo-sftp-host-fingerprint
PARSE_RULE_VAL_STRID(parseRuleValStrIdFingerprint), // opt/repo-sftp-host-fingerprint
PARSE_RULE_VAL_STRID(parseRuleValStrIdStrict), // opt/repo-sftp-host-fingerprint
), // opt/repo-sftp-host-fingerprint
), // opt/repo-sftp-host-fingerprint
), // opt/repo-sftp-host-fingerprint
), // opt/repo-sftp-host-fingerprint
// -----------------------------------------------------------------------------------------------------------------------------
PARSE_RULE_OPTION // opt/repo-sftp-host-key-check-type
( // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_NAME("repo-sftp-host-key-check-type"), // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_TYPE(cfgOptTypeStringId), // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_RESET(true), // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_REQUIRED(true), // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_SECTION(cfgSectionGlobal), // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_GROUP_MEMBER(true), // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_GROUP_ID(cfgOptGrpRepo), // opt/repo-sftp-host-key-check-type
// opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND_ROLE_MAIN_VALID_LIST // opt/repo-sftp-host-key-check-type
( // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdAnnotate) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdCheck) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdExpire) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdInfo) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdManifest) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoCreate) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoGet) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoLs) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoPut) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoRm) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaCreate) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaDelete) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaUpgrade) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-host-key-check-type
), // opt/repo-sftp-host-key-check-type
// opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND_ROLE_ASYNC_VALID_LIST // opt/repo-sftp-host-key-check-type
( // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-host-key-check-type
), // opt/repo-sftp-host-key-check-type
// opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND_ROLE_LOCAL_VALID_LIST // opt/repo-sftp-host-key-check-type
( // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-host-key-check-type
), // opt/repo-sftp-host-key-check-type
// opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND_ROLE_REMOTE_VALID_LIST // opt/repo-sftp-host-key-check-type
( // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdAnnotate) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdCheck) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdInfo) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdManifest) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoCreate) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoGet) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoLs) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoPut) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoRm) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaCreate) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaDelete) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaUpgrade) // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-host-key-check-type
), // opt/repo-sftp-host-key-check-type
// opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTIONAL // opt/repo-sftp-host-key-check-type
( // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTIONAL_GROUP // opt/repo-sftp-host-key-check-type
( // opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTIONAL_DEPEND // opt/repo-sftp-host-key-check-type
( // opt/repo-sftp-host-key-check-type
PARSE_RULE_VAL_OPT(cfgOptRepoType), // opt/repo-sftp-host-key-check-type
PARSE_RULE_VAL_STRID(parseRuleValStrIdSftp), // opt/repo-sftp-host-key-check-type
), // opt/repo-sftp-host-key-check-type
// opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTIONAL_ALLOW_LIST // opt/repo-sftp-host-key-check-type
( // opt/repo-sftp-host-key-check-type
PARSE_RULE_VAL_STRID(parseRuleValStrIdAcceptNew), // opt/repo-sftp-host-key-check-type
PARSE_RULE_VAL_STRID(parseRuleValStrIdFingerprint), // opt/repo-sftp-host-key-check-type
PARSE_RULE_VAL_STRID(parseRuleValStrIdNone), // opt/repo-sftp-host-key-check-type
PARSE_RULE_VAL_STRID(parseRuleValStrIdStrict), // opt/repo-sftp-host-key-check-type
), // opt/repo-sftp-host-key-check-type
// opt/repo-sftp-host-key-check-type
PARSE_RULE_OPTIONAL_DEFAULT // opt/repo-sftp-host-key-check-type
( // opt/repo-sftp-host-key-check-type
PARSE_RULE_VAL_STRID(parseRuleValStrIdStrict), // opt/repo-sftp-host-key-check-type
PARSE_RULE_VAL_STR(parseRuleValStrQT_strict_QT), // opt/repo-sftp-host-key-check-type
), // opt/repo-sftp-host-key-check-type
), // opt/repo-sftp-host-key-check-type
), // opt/repo-sftp-host-key-check-type
), // opt/repo-sftp-host-key-check-type
// -----------------------------------------------------------------------------------------------------------------------------
PARSE_RULE_OPTION // opt/repo-sftp-host-key-hash-type
( // opt/repo-sftp-host-key-hash-type
PARSE_RULE_OPTION_NAME("repo-sftp-host-key-hash-type"), // opt/repo-sftp-host-key-hash-type
@ -8307,6 +8410,88 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] =
), // opt/repo-sftp-host-user
), // opt/repo-sftp-host-user
// -----------------------------------------------------------------------------------------------------------------------------
PARSE_RULE_OPTION // opt/repo-sftp-known-host
( // opt/repo-sftp-known-host
PARSE_RULE_OPTION_NAME("repo-sftp-known-host"), // opt/repo-sftp-known-host
PARSE_RULE_OPTION_TYPE(cfgOptTypeList), // opt/repo-sftp-known-host
PARSE_RULE_OPTION_RESET(true), // opt/repo-sftp-known-host
PARSE_RULE_OPTION_REQUIRED(false), // opt/repo-sftp-known-host
PARSE_RULE_OPTION_SECTION(cfgSectionGlobal), // opt/repo-sftp-known-host
PARSE_RULE_OPTION_MULTI(true), // opt/repo-sftp-known-host
PARSE_RULE_OPTION_GROUP_MEMBER(true), // opt/repo-sftp-known-host
PARSE_RULE_OPTION_GROUP_ID(cfgOptGrpRepo), // opt/repo-sftp-known-host
// opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND_ROLE_MAIN_VALID_LIST // opt/repo-sftp-known-host
( // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdAnnotate) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdCheck) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdExpire) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdInfo) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdManifest) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoCreate) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoGet) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoLs) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoPut) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoRm) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaCreate) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaDelete) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaUpgrade) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-known-host
), // opt/repo-sftp-known-host
// opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND_ROLE_ASYNC_VALID_LIST // opt/repo-sftp-known-host
( // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-known-host
), // opt/repo-sftp-known-host
// opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND_ROLE_LOCAL_VALID_LIST // opt/repo-sftp-known-host
( // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-known-host
), // opt/repo-sftp-known-host
// opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND_ROLE_REMOTE_VALID_LIST // opt/repo-sftp-known-host
( // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdAnnotate) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdCheck) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdInfo) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdManifest) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoCreate) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoGet) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoLs) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoPut) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoRm) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaCreate) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaDelete) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaUpgrade) // opt/repo-sftp-known-host
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-known-host
), // opt/repo-sftp-known-host
// opt/repo-sftp-known-host
PARSE_RULE_OPTIONAL // opt/repo-sftp-known-host
( // opt/repo-sftp-known-host
PARSE_RULE_OPTIONAL_GROUP // opt/repo-sftp-known-host
( // opt/repo-sftp-known-host
PARSE_RULE_OPTIONAL_DEPEND // opt/repo-sftp-known-host
( // opt/repo-sftp-known-host
PARSE_RULE_VAL_OPT(cfgOptRepoSftpHostKeyCheckType), // opt/repo-sftp-known-host
PARSE_RULE_VAL_STRID(parseRuleValStrIdAcceptNew), // opt/repo-sftp-known-host
PARSE_RULE_VAL_STRID(parseRuleValStrIdStrict), // opt/repo-sftp-known-host
), // opt/repo-sftp-known-host
), // opt/repo-sftp-known-host
), // opt/repo-sftp-known-host
), // opt/repo-sftp-known-host
// -----------------------------------------------------------------------------------------------------------------------------
PARSE_RULE_OPTION // opt/repo-sftp-private-key-file
( // opt/repo-sftp-private-key-file
PARSE_RULE_OPTION_NAME("repo-sftp-private-key-file"), // opt/repo-sftp-private-key-file
@ -10795,10 +10980,11 @@ static const uint8_t optionResolveOrder[] =
cfgOptRepoS3Token, // opt-resolve-order
cfgOptRepoS3UriStyle, // opt-resolve-order
cfgOptRepoSftpHost, // opt-resolve-order
cfgOptRepoSftpHostFingerprint, // opt-resolve-order
cfgOptRepoSftpHostKeyCheckType, // opt-resolve-order
cfgOptRepoSftpHostKeyHashType, // opt-resolve-order
cfgOptRepoSftpHostPort, // opt-resolve-order
cfgOptRepoSftpHostUser, // opt-resolve-order
cfgOptRepoSftpKnownHost, // opt-resolve-order
cfgOptRepoSftpPrivateKeyFile, // opt-resolve-order
cfgOptRepoSftpPrivateKeyPassphrase, // opt-resolve-order
cfgOptRepoSftpPublicKeyFile, // opt-resolve-order
@ -10826,4 +11012,5 @@ static const uint8_t optionResolveOrder[] =
cfgOptRepoHostKeyFile, // opt-resolve-order
cfgOptRepoS3Key, // opt-resolve-order
cfgOptRepoS3KeySecret, // opt-resolve-order
cfgOptRepoSftpHostFingerprint, // opt-resolve-order
};

View File

@ -22,17 +22,30 @@ storageSftpHelper(const unsigned int repoIdx, const bool write, StoragePathExpre
ASSERT(cfgOptionIdxStrId(cfgOptRepoType, repoIdx) == STORAGE_SFTP_TYPE);
FUNCTION_LOG_RETURN(
STORAGE,
storageSftpNewP(
cfgOptionIdxStr(cfgOptRepoPath, repoIdx), cfgOptionIdxStr(cfgOptRepoSftpHost, repoIdx),
cfgOptionIdxUInt(cfgOptRepoSftpHostPort, repoIdx), cfgOptionIdxStr(cfgOptRepoSftpHostUser, repoIdx),
cfgOptionUInt64(cfgOptIoTimeout), cfgOptionIdxStr(cfgOptRepoSftpPrivateKeyFile, repoIdx),
cfgOptionIdxStrId(cfgOptRepoSftpHostKeyHashType, repoIdx), .write = write,
.pathExpressionFunction = pathExpressionCallback, .modeFile = STORAGE_MODE_FILE_DEFAULT,
.modePath = STORAGE_MODE_PATH_DEFAULT, .keyPub = cfgOptionIdxStrNull(cfgOptRepoSftpPublicKeyFile, repoIdx),
.keyPassphrase = cfgOptionIdxStrNull(cfgOptRepoSftpPrivateKeyPassphrase, repoIdx),
.hostFingerprint = cfgOptionIdxStrNull(cfgOptRepoSftpHostFingerprint, repoIdx)));
Storage *result = NULL;
MEM_CONTEXT_TEMP_BEGIN()
{
const StringList *const knownHosts = strLstNewVarLst(cfgOptionIdxLst(cfgOptRepoSftpKnownHost, repoIdx));
MEM_CONTEXT_PRIOR_BEGIN()
{
result = storageSftpNewP(
cfgOptionIdxStr(cfgOptRepoPath, repoIdx), cfgOptionIdxStr(cfgOptRepoSftpHost, repoIdx),
cfgOptionIdxUInt(cfgOptRepoSftpHostPort, repoIdx), cfgOptionIdxStr(cfgOptRepoSftpHostUser, repoIdx),
cfgOptionUInt64(cfgOptIoTimeout), cfgOptionIdxStr(cfgOptRepoSftpPrivateKeyFile, repoIdx),
cfgOptionIdxStrId(cfgOptRepoSftpHostKeyHashType, repoIdx), .write = write,
.pathExpressionFunction = pathExpressionCallback, .modeFile = STORAGE_MODE_FILE_DEFAULT,
.modePath = STORAGE_MODE_PATH_DEFAULT, .keyPub = cfgOptionIdxStrNull(cfgOptRepoSftpPublicKeyFile, repoIdx),
.keyPassphrase = cfgOptionIdxStrNull(cfgOptRepoSftpPrivateKeyPassphrase, repoIdx),
.hostKeyCheckType = cfgOptionIdxStrId(cfgOptRepoSftpHostKeyCheckType, repoIdx),
.hostFingerprint = cfgOptionIdxStrNull(cfgOptRepoSftpHostFingerprint, repoIdx), .knownHosts = knownHosts);
}
MEM_CONTEXT_PRIOR_END();
}
MEM_CONTEXT_TEMP_END();
FUNCTION_LOG_RETURN(STORAGE, result);
}
#endif // HAVE_LIBSSH2

View File

@ -12,6 +12,7 @@ SFTP Storage
#include "common/log.h"
#include "common/regExp.h"
#include "common/user.h"
#include "storage/posix/storage.h"
#include "storage/sftp/read.h"
#include "storage/sftp/storage.intern.h"
#include "storage/sftp/write.h"
@ -37,6 +38,199 @@ struct StorageSftp
TimeMSec timeout; // Session timeout
};
/***********************************************************************************************************************************
Return known host key type based on host key type
***********************************************************************************************************************************/
static int
storageSftpKnownHostKeyType(const int hostKeyType)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(INT, hostKeyType);
FUNCTION_TEST_END();
int result;
switch (hostKeyType)
{
case LIBSSH2_HOSTKEY_TYPE_RSA:
result = LIBSSH2_KNOWNHOST_KEY_SSHRSA;
break;
case LIBSSH2_HOSTKEY_TYPE_DSS:
result = LIBSSH2_KNOWNHOST_KEY_SSHDSS;
break;
#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256
case LIBSSH2_HOSTKEY_TYPE_ECDSA_256:
result = LIBSSH2_KNOWNHOST_KEY_ECDSA_256;
break;
#endif
#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_384
case LIBSSH2_HOSTKEY_TYPE_ECDSA_384:
result = LIBSSH2_KNOWNHOST_KEY_ECDSA_384;
break;
#endif
#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_521
case LIBSSH2_HOSTKEY_TYPE_ECDSA_521:
result = LIBSSH2_KNOWNHOST_KEY_ECDSA_521;
break;
#endif
#ifdef LIBSSH2_HOSTKEY_TYPE_ED25519
case LIBSSH2_HOSTKEY_TYPE_ED25519:
result = LIBSSH2_KNOWNHOST_KEY_ED25519;
break;
#endif
default:
result = 0;
break;
}
FUNCTION_TEST_RETURN(INT, result);
}
/***********************************************************************************************************************************
Return a match failed message based on known host check failure type
***********************************************************************************************************************************/
static const char *
storageSftpKnownHostCheckpFailureMsg(const int rc)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(INT, rc);
FUNCTION_TEST_END();
const char *result;
switch (rc)
{
case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
result = "LIBSSH2_KNOWNHOST_CHECK_FAILURE";
break;
case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
result = "not found in known hosts files: LIBSSH2_KNOWNHOST_CHECK_NOTFOUND";
break;
case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
result = "mismatch in known hosts files: LIBSSH2_KNOWNHOST_CHECK_MISMATCH";
break;
default:
result = "unknown failure";
break;
}
FUNCTION_TEST_RETURN_CONST(STRINGZ, result);
}
/***********************************************************************************************************************************
Rewrite the user's known_hosts file with a new entry
***********************************************************************************************************************************/
static void
storageSftpUpdateKnownHostsFile(
StorageSftp *const this, const int hostKeyType, const String *const host, const char *const hostKey, const size_t hostKeyLen)
{
FUNCTION_LOG_BEGIN(logLevelDebug);
FUNCTION_LOG_PARAM(STORAGE_SFTP, this);
FUNCTION_LOG_PARAM(INT, hostKeyType);
FUNCTION_LOG_PARAM(STRING, host);
FUNCTION_LOG_PARAM(STRINGZ, hostKey);
FUNCTION_LOG_PARAM(SIZE, hostKeyLen);
FUNCTION_LOG_END();
MEM_CONTEXT_TEMP_BEGIN()
{
char *libSsh2ErrMsg;
int libSsh2ErrMsgLen;
int rc;
// Init a known host collection for the user's known_hosts file
const char *const userKnownHostsFile = strZ(strNewFmt("%s%s", strZ(userHome()), "/.ssh/known_hosts"));
LIBSSH2_KNOWNHOSTS *const userKnownHostsList = libssh2_knownhost_init(this->session);
LOG_WARN_FMT("host '%s' not found in known hosts files, attempting to add host to '%s'", strZ(host), userKnownHostsFile);
if (userKnownHostsList == NULL)
{
// Get the libssh2 error message and emit warning
rc = libssh2_session_last_error(this->session, &libSsh2ErrMsg, &libSsh2ErrMsgLen, 0);
LOG_WARN_FMT(
"libssh2_knownhost_init failed for '%s' for update: libssh2 errno [%d] %s", userKnownHostsFile, rc, libSsh2ErrMsg);
}
else
{
// Read the user's known_hosts file entries into the collection. libssh2_knownhost_readfile() returns the number of
// successfully loaded hosts or a negative value on error, an empty known hosts file will return 0.
if ((rc = libssh2_knownhost_readfile(userKnownHostsList, userKnownHostsFile, LIBSSH2_KNOWNHOST_FILE_OPENSSH)) < 0)
{
// Missing known_hosts file will return LIBSSH2_ERROR_FILE. Possibly issues other than missing may return this.
if (rc == LIBSSH2_ERROR_FILE)
{
// If user's known_hosts file is non-existant, create an empty one for libssh2 to operate on
const Storage *const sshStorage =
storagePosixNewP(
strNewFmt("%s%s", strZ(userHome()), "/.ssh"), .modeFile = 0600, .modePath = 0700, .write = true);
if (!storageExistsP(sshStorage, strNewFmt("%s", "known_hosts")))
storagePutP(storageNewWriteP(sshStorage, strNewFmt("%s", "known_hosts")), NULL);
// Try to load the user's known_hosts file entries into the collection again
rc = libssh2_knownhost_readfile(userKnownHostsList, userKnownHostsFile, LIBSSH2_KNOWNHOST_FILE_OPENSSH);
}
}
// If the user's known_hosts file was read successfully, add the host to the collection and rewrite the file
if (rc >= 0)
{
// Check for a supported known host key type
const int knownHostKeyType = storageSftpKnownHostKeyType(hostKeyType);
if (knownHostKeyType != 0)
{
// Add host to the internal list
if (libssh2_knownhost_addc(
userKnownHostsList, strZ(host), NULL, hostKey, hostKeyLen,
strZ(strNewZ("Generated from " PROJECT_NAME)), strlen(strZ(strNewZ("Generated from " PROJECT_NAME))),
LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW | knownHostKeyType, NULL) == 0)
{
// Rewrite the updated known_hosts file
rc = libssh2_knownhost_writefile(userKnownHostsList, userKnownHostsFile, LIBSSH2_KNOWNHOST_FILE_OPENSSH);
if (rc != 0)
LOG_WARN_FMT(PROJECT_NAME " unable to write '%s' for update", userKnownHostsFile);
else
LOG_WARN_FMT(PROJECT_NAME " added new host '%s' to '%s'", strZ(host), userKnownHostsFile);
}
else
LOG_WARN_FMT(PROJECT_NAME " failed to add '%s' to known_hosts internal list", strZ(host));
}
else
LOG_WARN_FMT("unsupported key type [%d], unable to update knownhosts for '%s'", hostKeyType, strZ(host));
}
else
{
// On readfile failure warn that we're unable to update the user's known_hosts file
rc = libssh2_session_last_error(this->session, &libSsh2ErrMsg, &libSsh2ErrMsgLen, 0);
LOG_WARN_FMT(
"libssh2 unable to read '%s' for update: libssh2 errno [%d] %s\n"
"HINT: does '%s' exist with proper permissions?", userKnownHostsFile, rc, libSsh2ErrMsg, userKnownHostsFile);
}
}
// Free the user's known hosts list
if (userKnownHostsList)
libssh2_knownhost_free(userKnownHostsList);
}
MEM_CONTEXT_TEMP_END();
FUNCTION_LOG_RETURN_VOID();
}
/***********************************************************************************************************************************
Free libssh2 resources
***********************************************************************************************************************************/
@ -307,6 +501,53 @@ storageSftpInfo(THIS_VOID, const String *const file, const StorageInfoLevel leve
FUNCTION_LOG_RETURN(STORAGE_INFO, result);
}
/***********************************************************************************************************************************
Build known hosts file list. If knownHosts is empty build the default file list, otherwise build the list provided. knownHosts
requires full path and/or leading tilde path entries.
***********************************************************************************************************************************/
static StringList *
storageSftpKnownHostsFilesList(const StringList *const knownHosts)
{
FUNCTION_LOG_BEGIN(logLevelDebug);
FUNCTION_LOG_PARAM(STRING_LIST, knownHosts);
FUNCTION_LOG_END();
StringList *const result = strLstNew();
MEM_CONTEXT_TEMP_BEGIN()
{
if (strLstEmpty(knownHosts))
{
// Create default file list
strLstAddFmt(result, "%s%s", strZ(userHome()), "/.ssh/known_hosts");
strLstAddFmt(result, "%s%s", strZ(userHome()), "/.ssh/known_hosts2");
strLstAddZ(result, "/etc/ssh/ssh_known_hosts");
strLstAddZ(result, "/etc/ssh/ssh_known_hosts2");
}
else
{
// Process the known host list entries and add them to the result list
for (unsigned int listIdx = 0; listIdx < strLstSize(knownHosts); listIdx++)
{
// Get the trimmed file path and add it to the result list
const String *const filePath = strTrim(strLstGet(knownHosts, listIdx));
if (strBeginsWithZ(filePath, "~/"))
{
// Replace leading tilde with space, trim space, prepend user home path and add to the result list
strLstAddFmt(
result, "%s%s", strZ(userHome()), strZ(strTrim(strSub(filePath, (size_t)strChr(filePath, '~') + 1))));
}
else
strLstAdd(result, filePath);
}
}
}
MEM_CONTEXT_TEMP_END();
FUNCTION_LOG_RETURN(STRING_LIST, result);
}
/**********************************************************************************************************************************/
static String *
storageSftpExpandTildePath(const String *const tildePath)
@ -664,6 +905,7 @@ storageSftpPathCreate(
FUNCTION_LOG_RETURN_VOID();
}
/**********************************************************************************************************************************/
static bool
storageSftpPathRemove(THIS_VOID, const String *const path, const bool recurse, const StorageInterfacePathRemoveParam param)
{
@ -793,7 +1035,9 @@ storageSftpNew(
FUNCTION_LOG_PARAM(STRING_ID, hostKeyHashType);
FUNCTION_LOG_PARAM(STRING, param.keyPub);
FUNCTION_TEST_PARAM(STRING, param.keyPassphrase);
FUNCTION_LOG_PARAM(STRING_ID, param.hostKeyCheckType);
FUNCTION_LOG_PARAM(STRING, param.hostFingerprint);
FUNCTION_LOG_PARAM(STRING_LIST, param.knownHosts);
FUNCTION_LOG_PARAM(MODE, param.modeFile);
FUNCTION_LOG_PARAM(MODE, param.modePath);
FUNCTION_LOG_PARAM(BOOL, param.write);
@ -806,7 +1050,6 @@ storageSftpNew(
ASSERT(user != NULL);
ASSERT(keyPriv != NULL);
ASSERT(hostKeyHashType != 0);
// Initialize user module
userInit();
@ -871,19 +1114,21 @@ storageSftpNew(
#endif // LIBSSH2_HOSTKEY_HASH_SHA256
default:
THROW_FMT(
ServiceError, "requested ssh2 hostkey hash type (%s) not available", strZ(strIdToStr(hostKeyHashType)));
THROW_FMT(ServiceError, "requested ssh2 hostkey hash type (%s) not available", strZ(strIdToStr(hostKeyHashType)));
break;
}
const char *binaryFingerprint = libssh2_hostkey_hash(this->session, hashType);
if (binaryFingerprint == NULL)
THROW_FMT(ServiceError, "libssh2 hostkey hash failed: libssh2 errno [%d]", libssh2_session_last_errno(this->session));
// Compare fingerprint if provided
if (param.hostFingerprint != NULL)
// Compare fingerprint if provided else check known hosts files for a match
if (param.hostKeyCheckType == SFTP_STRICT_HOSTKEY_CHECKING_FINGERPRINT)
{
const char *const binaryFingerprint = libssh2_hostkey_hash(this->session, hashType);
if (binaryFingerprint == NULL)
{
THROW_FMT(
ServiceError, "libssh2 hostkey hash failed: libssh2 errno [%d]", libssh2_session_last_errno(this->session));
}
// 256 bytes is large enough to hold the hex representation of currently supported hash types. The hex encoded version
// requires twice as much space (hashSize * 2) as the raw version.
char fingerprint[256];
@ -897,6 +1142,115 @@ storageSftpNew(
fingerprint, strZ(param.hostFingerprint));
}
}
else if (param.hostKeyCheckType != SFTP_STRICT_HOSTKEY_CHECKING_NONE)
{
// Init the known host collection
LIBSSH2_KNOWNHOSTS *const knownHostsList = libssh2_knownhost_init(this->session);
if (knownHostsList == NULL)
THROW_FMT(ServiceError, "failure during libssh2_knownhost_init");
// Get the list of known host files to search
const StringList *const knownHostsPathList = storageSftpKnownHostsFilesList(param.knownHosts);
// Loop through the list of known host files
for (unsigned int listIdx = 0; listIdx < strLstSize(knownHostsPathList); listIdx++)
{
const char *const currentKnownHostFile = strZNull(strLstGet(knownHostsPathList, listIdx));
// Read the known hosts file entries into the collection, log message for readfile status.
// libssh2_knownhost_readfile() returns the number of successfully loaded hosts or a negative value on error, an
// empty known hosts file will return 0.
if ((rc = libssh2_knownhost_readfile(knownHostsList, currentKnownHostFile, LIBSSH2_KNOWNHOST_FILE_OPENSSH)) <= 0)
{
if (rc == 0)
LOG_DETAIL_FMT("libssh2 '%s' file is empty", currentKnownHostFile);
else
{
char *libSsh2ErrMsg;
int libSsh2ErrMsgLen;
// Get the libssh2 error message
rc = libssh2_session_last_error(this->session, &libSsh2ErrMsg, &libSsh2ErrMsgLen, 0);
LOG_DETAIL_FMT(
"libssh2 read '%s' failed: libssh2 errno [%d] %s", currentKnownHostFile, rc, libSsh2ErrMsg);
}
}
else
LOG_DETAIL_FMT("libssh2 read '%s' succeeded", currentKnownHostFile);
}
// Get the remote host key
size_t hostKeyLen;
int hostKeyType;
const char *const hostKey = libssh2_session_hostkey(this->session, &hostKeyLen, &hostKeyType);
// Check for a match in known hosts files else throw an error if no host key was retrieved
if (hostKey != NULL)
{
rc = libssh2_knownhost_checkp(
knownHostsList, strZ(host), (int)port, hostKey, hostKeyLen,
LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW, NULL);
// Handle check success/failure
if (rc == LIBSSH2_KNOWNHOST_CHECK_MATCH)
LOG_DETAIL_FMT("known hosts match found for '%s'", strZ(host));
else
{
// Handle failure to match in a similar manner as ssh_config StrictHostKeyChecking. If this flag is set to
// "strict", never automatically add host keys to the ~/.ssh/known_hosts file, and refuse to connect to hosts
// whose host key has changed. This option forces the user to manually add all new hosts. If this flag is set to
// "accept-new" then automatically add new host keys to the user known hosts files, but do not permit
// connections to hosts with changed host keys.
switch (param.hostKeyCheckType)
{
case SFTP_STRICT_HOSTKEY_CHECKING_STRICT:
{
// Throw an error when set to strict and we have any result other than match
libssh2_knownhost_free(knownHostsList);
THROW_FMT(
ServiceError, "known hosts failure: '%s' %s [%d]: check type [%s]", strZ(host),
storageSftpKnownHostCheckpFailureMsg(rc), rc, strZ(strIdToStr(param.hostKeyCheckType)));
break;
}
default:
{
ASSERT(param.hostKeyCheckType == SFTP_STRICT_HOSTKEY_CHECKING_ACCEPT_NEW);
// Throw an error when set to accept-new and match fails or mismatches else add the new host key to the
// user's known_hosts file
if (rc == LIBSSH2_KNOWNHOST_CHECK_MISMATCH || rc == LIBSSH2_KNOWNHOST_CHECK_FAILURE)
{
// Free the known hosts list
libssh2_knownhost_free(knownHostsList);
THROW_FMT(
ServiceError, "known hosts failure: '%s': %s [%d]: check type [%s]", strZ(host),
storageSftpKnownHostCheckpFailureMsg(rc), rc,
strZ(strIdToStr(param.hostKeyCheckType)));
}
else
storageSftpUpdateKnownHostsFile(this, hostKeyType, host, hostKey, hostKeyLen);
break;
}
}
}
}
else
{
THROW_FMT(
ServiceError,
"libssh2_session_hostkey failed to get hostkey: libssh2 error [%d]", libssh2_session_last_errno(this->session));
}
// Free the known hosts list
libssh2_knownhost_free(knownHostsList);
}
// Perform public key authorization, expand leading tilde key file paths if needed
String *const privKeyPath = regExpMatchOne(STRDEF("^ *~"), keyPriv) ? storageSftpExpandTildePath(keyPriv) : strDup(keyPriv);
@ -929,6 +1283,7 @@ storageSftpNew(
" generate the keypair"));
}
// Init the sftp session
do
{
this->sftpSession = libssh2_sftp_init(this->session);

View File

@ -13,6 +13,14 @@ Storage type
#ifdef HAVE_LIBSSH2
/***********************************************************************************************************************************
SFTP StrictHostKeyChecking
***********************************************************************************************************************************/
#define SFTP_STRICT_HOSTKEY_CHECKING_ACCEPT_NEW STRID5("accept-new", 0x2e576e9028c610)
#define SFTP_STRICT_HOSTKEY_CHECKING_FINGERPRINT STRID5("fingerprint", 0x51c9942453b9260)
#define SFTP_STRICT_HOSTKEY_CHECKING_NONE STRID5("none", 0x2b9ee0)
#define SFTP_STRICT_HOSTKEY_CHECKING_STRICT STRID5("strict", 0x2834ca930)
/***********************************************************************************************************************************
Constructors
***********************************************************************************************************************************/
@ -25,7 +33,9 @@ typedef struct StorageSftpNewParam
StoragePathExpressionCallback *pathExpressionFunction;
const String *keyPub;
const String *keyPassphrase;
StringId hostKeyCheckType;
const String *hostFingerprint;
const StringList *knownHosts;
} StorageSftpNewParam;
#define storageSftpNewP(path, host, port, user, timeout, keyPriv, hostKeyHashType, ...) \

View File

@ -1322,6 +1322,7 @@ sub configCreate
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo1-sftp-host-user'} = TEST_USER;
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo1-sftp-private-key-file'} = testRunGet()->basePath() . SSH_PRIVATE_KEY;
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo1-sftp-public-key-file'} = testRunGet()->basePath() . SSH_PUBLIC_KEY;
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo1-sftp-host-key-check-type'} = "none";
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo1-path'} = $self->repoPath();
# At what count do we hit diminishing returns

View File

@ -165,6 +165,180 @@ libssh2_init(int flags)
return hrnLibSsh2->resultInt;
}
/***********************************************************************************************************************************
Shim for libssh2_knownhost_addc
***********************************************************************************************************************************/
int
libssh2_knownhost_addc(
LIBSSH2_KNOWNHOSTS *hosts, const char *host, const char *salt, const char *key, size_t keylen, const char *comment,
size_t commentlen, int typemask, struct libssh2_knownhost **store)
{
// Avoid compiler complaining of unused param
(void)store;
if (hosts == NULL)
{
snprintf(
hrnLibSsh2ScriptError, sizeof(hrnLibSsh2ScriptError),
"libssh2 script function 'libssh2_knownhost_adddc', expects hosts to be not NULL");
THROW(AssertError, hrnLibSsh2ScriptError);
}
HrnLibSsh2 *hrnLibSsh2 = NULL;
MEM_CONTEXT_TEMP_BEGIN()
{
hrnLibSsh2 = hrnLibSsh2ScriptRun(
HRNLIBSSH2_KNOWNHOST_ADDC,
varLstAdd(
varLstAdd(
varLstAdd(
varLstAdd(
varLstAdd(
varLstAdd(
varLstAdd(
varLstNew(), varNewStrZ(host)),
varNewStrZ(salt)),
varNewStrZ(key)),
varNewUInt64(keylen)),
varNewStrZ(comment)),
varNewUInt64(commentlen)),
varNewInt(typemask)),
(HrnLibSsh2 *)hosts);
}
MEM_CONTEXT_TEMP_END();
return hrnLibSsh2->resultInt;
}
/***********************************************************************************************************************************
Shim for libssh2_knownhost_checkp
***********************************************************************************************************************************/
int
libssh2_knownhost_checkp(
LIBSSH2_KNOWNHOSTS *hosts, const char *host, int port, const char *key, size_t keylen, int typemask,
struct libssh2_knownhost **knownhost)
{
// Avoid compiler complaining of unused param
(void)knownhost;
if (hosts == NULL)
{
snprintf(
hrnLibSsh2ScriptError, sizeof(hrnLibSsh2ScriptError),
"libssh2 script function 'libssh2_knownhost_checkp', expects hosts to be not NULL");
THROW(AssertError, hrnLibSsh2ScriptError);
}
HrnLibSsh2 *hrnLibSsh2 = NULL;
MEM_CONTEXT_TEMP_BEGIN()
{
hrnLibSsh2 = hrnLibSsh2ScriptRun(
HRNLIBSSH2_KNOWNHOST_CHECKP,
varLstAdd(
varLstAdd(
varLstAdd(
varLstAdd(
varLstAdd(
varLstNew(), varNewStrZ(host)),
varNewInt(port)),
varNewStrZ(key)),
varNewUInt64(keylen)),
varNewInt(typemask)),
(HrnLibSsh2 *)hosts);
}
MEM_CONTEXT_TEMP_END();
return hrnLibSsh2->resultInt;
}
/***********************************************************************************************************************************
Shim for libssh2_knownhost_free
***********************************************************************************************************************************/
void
libssh2_knownhost_free(LIBSSH2_KNOWNHOSTS *hosts)
{
if (hosts == NULL)
{
snprintf(
hrnLibSsh2ScriptError, sizeof(hrnLibSsh2ScriptError),
"libssh2 script function 'libssh2_session_knownhost_free', expects hosts to be not NULL");
THROW(AssertError, hrnLibSsh2ScriptError);
}
}
/***********************************************************************************************************************************
Shim for libssh2_knownhost_init
***********************************************************************************************************************************/
LIBSSH2_KNOWNHOSTS *
libssh2_knownhost_init(LIBSSH2_SESSION *session)
{
HrnLibSsh2 *hrnLibSsh2 = hrnLibSsh2ScriptRun(HRNLIBSSH2_KNOWNHOST_INIT, NULL, (HrnLibSsh2 *)session);
return hrnLibSsh2->resultNull ? NULL : (LIBSSH2_KNOWNHOSTS *)hrnLibSsh2;
}
/***********************************************************************************************************************************
Shim for libssh2_knownhost_readfile
***********************************************************************************************************************************/
int
libssh2_knownhost_readfile(LIBSSH2_KNOWNHOSTS *hosts, const char *filename, int type)
{
HrnLibSsh2 *hrnLibSsh2 = NULL;
MEM_CONTEXT_TEMP_BEGIN()
{
hrnLibSsh2 = hrnLibSsh2ScriptRun(
HRNLIBSSH2_KNOWNHOST_READFILE,
varLstAdd(
varLstAdd(
varLstNew(), varNewStrZ(filename)),
varNewInt(type)),
(HrnLibSsh2 *)hosts);
}
MEM_CONTEXT_TEMP_END();
return hrnLibSsh2->resultInt;
}
/***********************************************************************************************************************************
Shim for libssh2_knownhost_writefile
***********************************************************************************************************************************/
int
libssh2_knownhost_writefile(LIBSSH2_KNOWNHOSTS *hosts, const char *filename, int type)
{
HrnLibSsh2 *hrnLibSsh2 = NULL;
MEM_CONTEXT_TEMP_BEGIN()
{
hrnLibSsh2 = hrnLibSsh2ScriptRun(
HRNLIBSSH2_KNOWNHOST_WRITEFILE,
varLstAdd(
varLstAdd(
varLstNew(), varNewStrZ(filename)),
varNewInt(type)),
(HrnLibSsh2 *)hosts);
}
MEM_CONTEXT_TEMP_END();
return hrnLibSsh2->resultInt;
}
/***********************************************************************************************************************************
Shim for libssh2_session_hostkey
***********************************************************************************************************************************/
const char *
libssh2_session_hostkey(LIBSSH2_SESSION *session, size_t *len, int *type)
{
HrnLibSsh2 *hrnLibSsh2 = hrnLibSsh2ScriptRun(HRNLIBSSH2_SESSION_HOSTKEY, NULL, (HrnLibSsh2 *)session);
*len = (size_t)hrnLibSsh2->len;
*type = (int)hrnLibSsh2->type;
return hrnLibSsh2->resultNull ? NULL : (const char *)hrnLibSsh2->resultZ;
}
/***********************************************************************************************************************************
Shim for libssh2_session_init
***********************************************************************************************************************************/
@ -197,28 +371,21 @@ libssh2_session_init_ex(
}
/***********************************************************************************************************************************
Shim for libssh2_session_set_blocking
Shim for libssh2_session_last_error
***********************************************************************************************************************************/
void
libssh2_session_set_blocking(LIBSSH2_SESSION *session, int blocking)
int
libssh2_session_last_error(LIBSSH2_SESSION *session, char **errmsg, int *errmsg_len, int want_buf)
{
if (session == NULL)
{
snprintf(
hrnLibSsh2ScriptError, sizeof(hrnLibSsh2ScriptError),
"libssh2 script function 'libssh2_session_set_blocking', expects session to be not NULL");
THROW(AssertError, hrnLibSsh2ScriptError);
}
// Avoid compiler complaining of unused params
(void)errmsg_len;
(void)want_buf;
if (!(INT_MIN <= blocking && blocking <= INT_MAX))
{
snprintf(
hrnLibSsh2ScriptError, sizeof(hrnLibSsh2ScriptError),
"libssh2 script function 'libssh2_session_set_blocking', expects blocking to be an integer value");
THROW(AssertError, hrnLibSsh2ScriptError);
}
HrnLibSsh2 *hrnLibSsh2 = hrnLibSsh2ScriptRun(HRNLIBSSH2_SESSION_LAST_ERROR, NULL, (HrnLibSsh2 *)session);
return;
if (hrnLibSsh2->errMsg != NULL)
*errmsg = hrnLibSsh2->errMsg;
return hrnLibSsh2->resultInt;
}
/***********************************************************************************************************************************
@ -268,6 +435,14 @@ libssh2_userauth_publickey_fromfile_ex(
{
HrnLibSsh2 *hrnLibSsh2 = NULL;
if (privatekey == NULL)
{
snprintf(
hrnLibSsh2ScriptError, sizeof(hrnLibSsh2ScriptError),
"libssh2 script function 'libssh2_userauth_publickey_fromfile_ex', expects privatekey to be not NULL");
THROW(AssertError, hrnLibSsh2ScriptError);
}
MEM_CONTEXT_TEMP_BEGIN()
{
hrnLibSsh2 = hrnLibSsh2ScriptRun(

View File

@ -25,19 +25,31 @@ libssh2 authorization constants
#define KEYPUB STRDEF("/home/" TEST_USER "/.ssh/id_rsa.pub")
#define KEYPRIV_CSTR "/home/" TEST_USER "/.ssh/id_rsa"
#define KEYPUB_CSTR "/home/" TEST_USER "/.ssh/id_rsa.pub"
#define KNOWNHOSTS_FILE_CSTR "/home/" TEST_USER "/.ssh/known_hosts"
#define KNOWNHOSTS2_FILE_CSTR "/home/" TEST_USER "/.ssh/known_hosts2"
#define ETC_KNOWNHOSTS_FILE_CSTR "/etc/ssh/ssh_known_hosts"
#define ETC_KNOWNHOSTS2_FILE_CSTR "/etc/ssh/ssh_known_hosts2"
#define HOSTKEY "12345678901234567890"
/***********************************************************************************************************************************
Function constants
***********************************************************************************************************************************/
#define HRNLIBSSH2_HOSTKEY_HASH "libssh2_hostkey_hash"
#define HRNLIBSSH2_INIT "libssh2_init"
#define HRNLIBSSH2_SESSION_DISCONNECT_EX "libssh2_session_disconnect_ex"
#define HRNLIBSSH2_KNOWNHOST_ADDC "libssh2_knownhost_addc"
#define HRNLIBSSH2_KNOWNHOST_CHECKP "libssh2_knownhost_checkp"
#define HRNLIBSSH2_KNOWNHOST_FREE "libssh2_knownhost_free"
#define HRNLIBSSH2_KNOWNHOST_INIT "libssh2_knownhost_init"
#define HRNLIBSSH2_KNOWNHOST_READFILE "libssh2_knownhost_readfile"
#define HRNLIBSSH2_KNOWNHOST_WRITEFILE "libssh2_knownhost_writefile"
#define HRNLIBSSH2_SESSION_BLOCK_DIRECTIONS "libssh2_session_block_directions"
#define HRNLIBSSH2_SESSION_DISCONNECT_EX "libssh2_session_disconnect_ex"
#define HRNLIBSSH2_SESSION_FREE "libssh2_session_free"
#define HRNLIBSSH2_SESSION_HANDSHAKE "libssh2_session_handshake"
#define HRNLIBSSH2_SESSION_HOSTKEY "libssh2_session_hostkey"
#define HRNLIBSSH2_SESSION_INIT_EX "libssh2_session_init_ex"
#define HRNLIBSSH2_SESSION_LAST_ERRNO "libssh2_session_last_errno"
#define HRNLIBSSH2_SESSION_SET_BLOCKING "libssh2_session_set_blocking"
#define HRNLIBSSH2_SESSION_LAST_ERROR "libssh2_session_last_error"
#define HRNLIBSSH2_SFTP_CLOSE_HANDLE "libssh2_sftp_close_handle"
#define HRNLIBSSH2_SFTP_FSYNC "libssh2_sftp_fsync"
#define HRNLIBSSH2_SFTP_INIT "libssh2_sftp_init"
@ -64,7 +76,11 @@ Macros for defining groups of functions that implement commands
{.function = HRNLIBSSH2_INIT, .param = "[0]", .resultInt = 0}, \
{.function = HRNLIBSSH2_SESSION_INIT_EX, .param = "[null,null,null,null]"}, \
{.function = HRNLIBSSH2_SESSION_HANDSHAKE, .param = HANDSHAKE_PARAM, .resultInt = 0}, \
{.function = HRNLIBSSH2_HOSTKEY_HASH, .param = "[2]", .resultZ = "12345678910123456789"}, \
{.function = HRNLIBSSH2_KNOWNHOST_INIT}, \
{.function = HRNLIBSSH2_KNOWNHOST_READFILE, .param = "[\"" KNOWNHOSTS_FILE_CSTR "\",1]", .resultInt = 5}, \
{.function = HRNLIBSSH2_SESSION_HOSTKEY, .len = 20, .type = LIBSSH2_HOSTKEY_TYPE_RSA, .resultZ = HOSTKEY}, \
{.function = HRNLIBSSH2_KNOWNHOST_CHECKP, .param = "[\"localhost\",22,\"" HOSTKEY "\",20,65537]", \
.resultInt = LIBSSH2_KNOWNHOST_CHECK_MATCH}, \
{.function = HRNLIBSSH2_USERAUTH_PUBLICKEY_FROMFILE_EX, \
.param = "[\"" TEST_USER "\"," TEST_USER_LEN ",\"" KEYPUB_CSTR "\",\"" KEYPRIV_CSTR "\",null]", \
.resultInt = 0}, \
@ -77,7 +93,7 @@ Macros for defining groups of functions that implement commands
{.function = HRNLIBSSH2_SESSION_FREE, .resultInt = 0}, \
{.function = NULL} \
// older systems do not support LIBSSH2_HOSTKEY_HASH_SHA256
// Older systems do not support LIBSSH2_HOSTKEY_HASH_SHA256
#ifdef LIBSSH2_HOSTKEY_HASH_SHA256
#define HOSTKEY_HASH_ENTRY() \
{.function = HRNLIBSSH2_HOSTKEY_HASH, .param = "[3]", .resultZ = "12345678910123456789"}
@ -108,6 +124,9 @@ typedef struct HrnLibSsh2
const String *fileName; // libssh2_readdir* libssh2_stat* filename
const String *readBuffer; // what to copy into read buffer
TimeMSec sleep; // Sleep specified milliseconds before returning from function
size_t len; // libssh2_session_hostkey len
int type; // libssh2_session_hostkey type
char *errMsg; // libssh2_session_last_error error msg
} HrnLibSsh2;
/***********************************************************************************************************************************

View File

@ -319,9 +319,11 @@ testRun(void)
" --repo-s3-uri-style S3 URI Style [default=host]\n"
" --repo-sftp-host SFTP repository host\n"
" --repo-sftp-host-fingerprint SFTP repository host fingerprint\n"
" --repo-sftp-host-key-check-type SFTP host key check type [default=strict]\n"
" --repo-sftp-host-key-hash-type SFTP repository host key hash type\n"
" --repo-sftp-host-port SFTP repository host port [default=22]\n"
" --repo-sftp-host-user SFTP repository host user\n"
" --repo-sftp-known-host SFTP known hosts file\n"
" --repo-sftp-private-key-file SFTP private key file\n"
" --repo-sftp-private-key-passphrase SFTP private key passphrase\n"
" --repo-sftp-public-key-file SFTP public key file\n"

View File

@ -640,6 +640,102 @@ testRun(void)
hrnCfgEnvKeyRemoveRaw(cfgOptRepoAzureAccount, 1);
hrnCfgEnvKeyRemoveRaw(cfgOptRepoAzureKey, 1);
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("error on missing SFTP fingerprint");
argList = strLstNew();
hrnCfgArgRawZ(argList, cfgOptStanza, "db");
hrnCfgArgRawZ(argList, cfgOptPgPath, "/path/to/pg");
hrnCfgArgKeyRawZ(argList, cfgOptRepoType, 1, "sftp");
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 1, "/repo");
hrnCfgArgRawZ(argList, cfgOptRepoSftpHost, "localhost");
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostKeyHashType, "sha1");
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostUser, TEST_USER);
hrnCfgArgRawZ(argList, cfgOptRepoSftpPrivateKeyFile, "/privatekey");
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostKeyCheckType, "fingerprint");
TEST_ERROR(
hrnCfgLoadP(cfgCmdArchivePush, argList), OptionRequiredError,
"archive-push command requires option: repo1-sftp-host-fingerprint");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("success on SFTP fingerprint not needed");
argList = strLstNew();
hrnCfgArgRawZ(argList, cfgOptStanza, "db");
hrnCfgArgRawZ(argList, cfgOptPgPath, "/path/to/pg");
hrnCfgArgKeyRawZ(argList, cfgOptRepoType, 1, "sftp");
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 1, "/repo");
hrnCfgArgRawZ(argList, cfgOptRepoSftpHost, "localhost");
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostKeyHashType, "sha1");
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostUser, TEST_USER);
hrnCfgArgRawZ(argList, cfgOptRepoSftpPrivateKeyFile, "/privatekey");
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostKeyCheckType, "accept-new");
HRN_CFG_LOAD(cfgCmdArchivePush, argList);
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("error on incorrect SFTP fingerprint check type");
argList = strLstNew();
hrnCfgArgRawZ(argList, cfgOptStanza, "db");
hrnCfgArgRawZ(argList, cfgOptPgPath, "/path/to/pg");
hrnCfgArgKeyRawZ(argList, cfgOptRepoType, 1, "sftp");
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 1, "/repo");
hrnCfgArgRawZ(argList, cfgOptRepoSftpHost, "localhost");
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostKeyHashType, "sha1");
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostUser, TEST_USER);
hrnCfgArgRawZ(argList, cfgOptRepoSftpPrivateKeyFile, "/privatekey");
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostFingerprint, "xxx");
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostKeyCheckType, "strict");
TEST_ERROR(
hrnCfgLoadP(cfgCmdArchivePush, argList), OptionInvalidError,
"option 'repo1-sftp-host-fingerprint' not valid without option 'repo1-sftp-host-key-check-type' = 'fingerprint'");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("warn on default SFTP fingerprint check type");
argList = strLstNew();
hrnCfgArgRawZ(argList, cfgOptStanza, "db");
hrnCfgArgRawZ(argList, cfgOptPgPath, "/path/to/pg");
hrnCfgArgKeyRawZ(argList, cfgOptRepoType, 1, "sftp");
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 1, "/repo");
hrnCfgArgRawZ(argList, cfgOptRepoSftpHost, "localhost");
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostKeyHashType, "sha1");
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostUser, TEST_USER);
hrnCfgArgRawZ(argList, cfgOptRepoSftpPrivateKeyFile, "/privatekey");
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostFingerprint, "xxx");
HRN_CFG_LOAD(cfgCmdArchivePush, argList);
TEST_RESULT_LOG(
"P00 WARN: option 'repo1-sftp-host-fingerprint' without option 'repo1-sftp-host-key-check-type' = 'fingerprint' is"
" deprecated\n"
" HINT: set option 'repo1-sftp-host-key-check-type=fingerprint'");
TEST_RESULT_UINT(
cfgOptionIdxStrId(cfgOptRepoSftpHostKeyCheckType, 0), CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_FINGERPRINT,
"check type updated");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("success on correct SFTP fingerprint check type");
argList = strLstNew();
hrnCfgArgRawZ(argList, cfgOptStanza, "db");
hrnCfgArgRawZ(argList, cfgOptPgPath, "/path/to/pg");
hrnCfgArgKeyRawZ(argList, cfgOptRepoType, 1, "sftp");
hrnCfgArgKeyRawZ(argList, cfgOptRepoPath, 1, "/repo");
hrnCfgArgRawZ(argList, cfgOptRepoSftpHost, "localhost");
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostKeyHashType, "sha1");
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostUser, TEST_USER);
hrnCfgArgRawZ(argList, cfgOptRepoSftpPrivateKeyFile, "/privatekey");
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostFingerprint, "xxx");
hrnCfgArgRawZ(argList, cfgOptRepoSftpHostKeyCheckType, "fingerprint");
HRN_CFG_LOAD(cfgCmdArchivePush, argList);
TEST_RESULT_UINT(
cfgOptionIdxStrId(cfgOptRepoSftpHostKeyCheckType, 0), CFGOPTVAL_REPO_SFTP_HOST_KEY_CHECK_TYPE_FINGERPRINT,
"check type");
}
// *****************************************************************************************************************************

File diff suppressed because it is too large Load Diff