1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-07-15 01:04:37 +02:00

Add TLS Server.

The TLS server is an alternative to using SSH for protocol connections to remote hosts.

This command is currently experimental and intended only for trial and testing. As such, the new commands and options will not show up in the command-line help unless directly requested.
This commit is contained in:
David Steele
2021-10-18 14:32:41 -04:00
parent 09fb9393f1
commit ccc255d3e0
90 changed files with 4332 additions and 560 deletions

View File

@ -130,6 +130,21 @@
<p>Add hints to standby replay timeout message.</p>
</release-item>
</release-improvement-list>
<release-development-list>
<release-item>
<github-pull-request id="1504"/>
<release-item-contributor-list>
<release-item-contributor id="david.steele"/>
<release-item-reviewer id="stephen.frost"/>
<!-- Actually tester, but we don't have a tag for that yet -->
<release-item-reviewer id="andrew.lecuyer"/>
</release-item-contributor-list>
<p><proper>Add TLS</proper> server.</p>
</release-item>
</release-development-list>
</release-core-list>
</release>

View File

@ -78,6 +78,8 @@ SRCS = \
command/restore/protocol.c \
command/restore/restore.c \
command/remote/remote.c \
command/server/ping.c \
command/server/server.c \
command/stanza/common.c \
command/stanza/create.c \
command/stanza/delete.c \
@ -116,11 +118,15 @@ SRCS = \
common/io/http/response.c \
common/io/http/session.c \
common/io/http/url.c \
common/io/server.c \
common/io/session.c \
common/io/socket/client.c \
common/io/socket/common.c \
common/io/socket/server.c \
common/io/socket/session.c \
common/io/tls/client.c \
common/io/tls/common.c \
common/io/tls/server.c \
common/io/tls/session.c \
common/lock.c \
common/stat.c \

View File

@ -119,6 +119,14 @@ command:
local: {}
remote: {}
server-ping:
internal: true
log-file: false
parameter-allowed: true
server-start:
internal: true
stanza-create:
command-role:
remote: {}
@ -599,6 +607,8 @@ option:
repo-put: {}
repo-rm: {}
restore: {}
server-ping: {}
server-start: {}
stanza-create: {}
stanza-delete: {}
stanza-upgrade: {}
@ -822,6 +832,7 @@ option:
repo-put: {}
repo-rm: {}
restore: {}
server-start: {}
stanza-create: {}
stanza-delete: {}
stanza-upgrade: {}
@ -889,6 +900,61 @@ option:
command: buffer-size
depend: tcp-keep-alive-count
tls-server-ca-file:
internal: true
section: global
type: path
command:
server-start:
internal: false
tls-server-cert-file:
internal: true
section: global
type: path
command:
server-start:
internal: false
tls-server-key-file:
internal: true
section: global
type: path
command:
server-start:
internal: false
tls-server-auth:
internal: true
section: global
type: hash
command:
server-start:
internal: false
tls-server-address:
internal: true
section: global
type: string
default: localhost
command:
server-ping:
internal: false
server-start:
internal: false
tls-server-port:
internal: true
section: global
type: integer
default: 8432
allow-range: [1, 65535]
command:
server-ping:
internal: false
server-start:
internal: false
# Logging options
#---------------------------------------------------------------------------------------------------------------------------------
log-level-console:
@ -1265,6 +1331,32 @@ option:
db-host: {}
db?-host: {}
pg-host-cert-file:
internal: true
section: global
group: pg
type: string
command: pg-local
command-role:
async: {}
main: {}
local: {}
depend:
option: pg-host-type
list:
- tls
pg-host-key-file:
inherit: pg-host-cert-file
pg-host-ca-file:
inherit: pg-host-cert-file
required: false
pg-host-ca-path:
inherit: pg-host-cert-file
required: false
pg-host-cmd:
section: stanza
group: pg
@ -1324,6 +1416,25 @@ option:
db-user: {}
db?-user: {}
pg-host-type:
internal: true
section: global
group: pg
type: string
default: ssh
allow-list:
- ssh
- tls
command: pg-local
command-role:
async: {}
main: {}
local: {}
depend:
option: pg-local
list:
- false
pg-path:
section: stanza
group: pg
@ -1699,6 +1810,25 @@ option:
deprecate:
hardlink: {}
repo-host-type:
internal: true
section: global
group: repo
type: string
default: ssh
allow-list:
- ssh
- tls
command: repo-local
command-role:
async: {}
main: {}
local: {}
depend:
option: repo-local
list:
- false
repo-host:
section: global
group: repo
@ -1709,13 +1839,36 @@ option:
async: {}
main: {}
local: {}
depend:
option: repo-local
list:
- false
depend: repo-host-type
deprecate:
backup-host: {}
repo-host-cert-file:
internal: true
section: global
group: repo
type: string
command: repo-local
command-role:
async: {}
main: {}
local: {}
depend:
option: repo-host-type
list:
- tls
repo-host-key-file:
inherit: repo-host-cert-file
repo-host-ca-file:
inherit: repo-host-cert-file
required: false
repo-host-ca-path:
inherit: repo-host-cert-file
required: false
repo-host-cmd:
section: global
group: repo
@ -1724,6 +1877,7 @@ option:
command:
archive-get: {}
archive-push: {}
backup: {}
check: {}
info: {}
repo-create: {}

View File

@ -102,6 +102,9 @@ repo-invalid: 103
# The command encountered one or more errors
command: 104
# Unable to authenticate or authorize
access: 105
# This error should not be thrown directly -- it serves as a parent for the C errors
runtime: 122

View File

@ -550,6 +550,22 @@
<example>none</example>
</config-key>
<!-- ======================================================================================================= -->
<config-key id="repo-host-type" name="Repository Host Protocol Type">
<summary>Repository host protocol type.</summary>
<text>
<p>The following protocol types are supported:</p>
<list>
<list-item><id>ssh</id> - Secure Shell.</list-item>
<list-item><id>tls</id> - <backrest/> TLS server.</list-item>
</list>
</text>
<example>tls</example>
</config-key>
<!-- CONFIG - REPO SECTION - REPO-HOST KEY -->
<config-key id="repo-host" name="Repository Host">
<summary>Repository host when operating remotely via SSH.</summary>
@ -563,6 +579,50 @@
<example>repo1.domain.com</example>
</config-key>
<!-- ======================================================================================================= -->
<config-key id="repo-host-ca-file" name="Repository Host Certificate Authority File">
<summary>Repository host certificate authority file.</summary>
<text>
<p>Use a CA file other than the system default for connecting to the repository host.</p>
</text>
<example>/etc/pki/tls/certs/ca-bundle.crt</example>
</config-key>
<!-- ======================================================================================================= -->
<config-key id="repo-host-ca-path" name="Repository Host Certificate Authority Path">
<summary>Repository host certificate authority path.</summary>
<text>
<p>Use a CA path other than the system default for connecting to the repository host.</p>
</text>
<example>/etc/pki/tls/certs</example>
</config-key>
<!-- ======================================================================================================= -->
<config-key id="repo-host-cert-file" name="Repository Host Certificate File">
<summary>Repository host certificate file.</summary>
<text>
<p>Sent to repository host to prove client indentity.</p>
</text>
<example>/path/to/client.crt</example>
</config-key>
<!-- ======================================================================================================= -->
<config-key id="repo-host-key-file" name="Repository Host Key File">
<summary>Repository host key file.</summary>
<text>
<p>Proves client certificate was sent by owner.</p>
</text>
<example>/path/to/client.key</example>
</config-key>
<!-- CONFIG - REPO SECTION - REPO-HOST-CMD KEY -->
<config-key id="repo-host-cmd" name="Repository Host Command">
<summary><backrest/> exe path on the repository host.</summary>
@ -1326,6 +1386,75 @@
<example>/data/tablespace</example>
</config-key>
<!-- For now the server options must be here so they do not show up in the documentation -->
<!-- ======================================================================================================= -->
<config-key id="tls-server-auth" name="TLS Server Authorized Clients">
<summary>TLS server authorized clients.</summary>
<text>
<p>Clients are authorized on the server by verifying their certificate and checking the certificate CN (Common name) against a list on the server configured with the <br-option>tls-server-auth</br-option>.</p>
<p>A CN can be authorized for as many stanzas as needed by repeating the <br-option>tls-server-auth</br-option> option, or for all stanzas by specifying <br-option>tls-server-auth=*</br-option>.</p>
</text>
<example>stanza1</example>
</config-key>
<!-- ======================================================================================================= -->
<config-key id="tls-server-ca-file" name="TLS Server Certificate Authorities">
<summary>TLS server certificate authorities.</summary>
<text>
<p>Checks that client certificates are signed by a trusted certificate authority.</p>
</text>
<example>/path/to/server.ca</example>
</config-key>
<!-- ======================================================================================================= -->
<config-key id="tls-server-cert-file" name="TLS Server Certificate">
<summary>TLS server certificate file.</summary>
<text>
<p>Sent to the client to show the server identity.</p>
</text>
<example>/path/to/server.crt</example>
</config-key>
<!-- ======================================================================================================= -->
<config-key id="tls-server-key-file" name="TLS Server Key">
<summary>TLS server key file.</summary>
<text>
<p>Proves server certificate was sent by the owner.</p>
</text>
<example>/path/to/server.key</example>
</config-key>
<!-- ======================================================================================================= -->
<config-key id="tls-server-address" name="TLS Server Address">
<summary>TLS server address.</summary>
<text>
<p>IP address the server will listen on for client requests.</p>
</text>
<example>::*</example>
</config-key>
<!-- ======================================================================================================= -->
<config-key id="tls-server-port" name="TLS Server Port">
<summary>TLS server port.</summary>
<text>
<p>Port the server will listen on for client requests.</p>
</text>
<example>8000</example>
</config-key>
</config-key-list>
</config-section>
@ -1394,6 +1523,66 @@
<example>db.domain.com</example>
</config-key>
<!-- ======================================================================================================= -->
<config-key id="pg-host-ca-file" name="PostgreSQL Host Certificate Authority File">
<summary><postgres/> host certificate authority file.</summary>
<text>
<p>Use a CA file other than the system default for connecting to the <postgres/> host.</p>
</text>
<example>/etc/pki/tls/certs/ca-bundle.crt</example>
</config-key>
<!-- ======================================================================================================= -->
<config-key id="pg-host-ca-path" name="PostgreSQL Host Certificate Authority Path">
<summary><postgres/> host certificate authority path.</summary>
<text>
<p>Use a CA path other than the system default for connecting to the <postgres/> host.</p>
</text>
<example>/etc/pki/tls/certs</example>
</config-key>
<!-- ======================================================================================================= -->
<config-key id="pg-host-cert-file" name="PostgreSQL Host Certificate File">
<summary><postgres/> host certificate file.</summary>
<text>
<p>Sent to <postgres/> host to prove client indentity.</p>
</text>
<example>/path/to/client.crt</example>
</config-key>
<!-- ======================================================================================================= -->
<config-key id="pg-host-key-file" name="PostgreSQL Host Key File">
<summary><postgres/> host key file.</summary>
<text>
<p>Proves client certificate was sent by owner.</p>
</text>
<example>/path/to/client.key</example>
</config-key>
<!-- ======================================================================================================= -->
<config-key id="pg-host-type" name="PostgreSQL Host Protocol Type">
<summary><postgres/> host protocol type.</summary>
<text>
<p>The following protocol types are supported:</p>
<list>
<list-item><id>ssh</id> - Secure Shell.</list-item>
<list-item><id>tls</id> - <backrest/> TLS server.</list-item>
</list>
</text>
<example>tls</example>
</config-key>
<!-- CONFIG - STANZA SECTION - PG-HOST-USER KEY -->
<config-key id="pg-host-user" name="PostgreSQL Host User">
<summary><postgres/> host logon user when <setting>pg-host</setting> is set.</summary>
@ -2065,6 +2254,26 @@
</text>
</command>
<!-- =============================================================================================================== -->
<command id="server-start" name="Server Start">
<summary>Start <backrest/> server.</summary>
<text>
<p>The <backrest/> server allows access to remote hosts without using the <proper>SSH</proper> protocol.</p>
</text>
</command>
<!-- =============================================================================================================== -->
<command id="server-ping" name="Server Ping">
<summary>Ping <backrest/> server.</summary>
<text>
<p>Ping a <backrest/> <proper>TLS</proper> server to ensure it is accepting connections. This serves as an aliveness check only since no authentication is attempted.</p>
<p>If no host is specified on the command-line then the <br-option>tls-server-host</br-option> option will be used.</p>
</text>
</command>
<!-- OPERATION - START COMMAND -->
<command id="start" name="Start">
<summary>Allow <backrest/> processes to run.</summary>

49
src/command/server/ping.c Normal file
View File

@ -0,0 +1,49 @@
/***********************************************************************************************************************************
Server Ping Command
***********************************************************************************************************************************/
#include "build.auto.h"
#include "command/server/ping.h"
#include "common/debug.h"
#include "common/io/tls/client.h"
#include "common/io/socket/client.h"
#include "config/config.h"
#include "protocol/client.h"
#include "protocol/helper.h"
void
cmdServerPing(void)
{
FUNCTION_LOG_VOID(logLevelDebug);
MEM_CONTEXT_TEMP_BEGIN()
{
// Check for user-specified host
const String *host = cfgOptionStr(cfgOptTlsServerAddress);
const StringList *commandParam = cfgCommandParam();
if (strLstSize(commandParam) == 1)
host = strLstGet(commandParam, 0);
else if (strLstSize(commandParam) > 1)
THROW(ParamInvalidError, "extra parameters found");
// Connect to server without any verification
const TimeMSec timeout = cfgOptionUInt64(cfgOptIoTimeout);
IoClient *const tlsClient = tlsClientNew(
sckClientNew(host, cfgOptionUInt(cfgOptTlsServerPort), timeout, timeout), host, timeout, timeout, false, NULL, NULL,
NULL, NULL);
IoSession *const tlsSession = ioClientOpen(tlsClient);
// Send ping
ProtocolClient *const protocolClient = protocolClientNew(
strNewFmt(PROTOCOL_SERVICE_REMOTE " socket protocol on '%s'", strZ(host)), PROTOCOL_SERVICE_REMOTE_STR,
ioSessionIoRead(tlsSession), ioSessionIoWrite(tlsSession));
protocolClientNoExit(protocolClient);
protocolClientNoOp(protocolClient);
protocolClientFree(protocolClient);
}
MEM_CONTEXT_TEMP_END();
FUNCTION_LOG_RETURN_VOID();
}

12
src/command/server/ping.h Normal file
View File

@ -0,0 +1,12 @@
/***********************************************************************************************************************************
Server Ping Command
***********************************************************************************************************************************/
#ifndef COMMAND_SERVER_PING_H
#define COMMAND_SERVER_PING_H
/***********************************************************************************************************************************
Functions
***********************************************************************************************************************************/
void cmdServerPing(void);
#endif

View File

@ -0,0 +1,81 @@
/***********************************************************************************************************************************
Server Command
***********************************************************************************************************************************/
#include "build.auto.h"
#include <sys/wait.h>
#include "command/remote/remote.h"
#include "command/server/server.h"
#include "common/debug.h"
#include "common/fork.h"
#include "common/io/socket/server.h"
#include "common/io/tls/server.h"
#include "config/config.h"
#include "protocol/helper.h"
/**********************************************************************************************************************************/
void
cmdServer(uint64_t connectionMax)
{
FUNCTION_LOG_VOID(logLevelDebug);
ASSERT(connectionMax > 0);
MEM_CONTEXT_TEMP_BEGIN()
{
IoServer *const tlsServer = tlsServerNew(
cfgOptionStr(cfgOptTlsServerAddress), cfgOptionStr(cfgOptTlsServerCaFile), cfgOptionStr(cfgOptTlsServerKeyFile),
cfgOptionStr(cfgOptTlsServerCertFile), cfgOptionUInt64(cfgOptProtocolTimeout));
IoServer *const socketServer = sckServerNew(
cfgOptionStr(cfgOptTlsServerAddress), cfgOptionUInt(cfgOptTlsServerPort), cfgOptionUInt64(cfgOptProtocolTimeout));
// Accept connections until connection max is reached
do
{
// Accept a new connection
IoSession *const socketSession = ioServerAccept(socketServer, NULL);
// Fork off the child process
pid_t pid = forkSafe();
if (pid == 0)
{
// Close the server socket so we don't hold the port open if the parent exits first
ioServerFree(socketServer);
// Disable logging and close log file
logClose();
// Detach from parent process
forkDetach();
// Start standard remote processing if a server is returned
ProtocolServer *server = protocolServer(tlsServer, socketSession);
if (server != NULL)
cmdRemote(server);
break;
}
// Wait for first fork to exit
else
{
// The process that was just forked should return immediately
int processStatus;
THROW_ON_SYS_ERROR(waitpid(pid, &processStatus, 0) == -1, ExecuteError, "unable to wait for forked process");
// The first fork should exit with success. If not, something went wrong during the second fork.
CHECK(WIFEXITED(processStatus) && WEXITSTATUS(processStatus) == 0);
}
// Free the socket since the child is now using it
ioSessionFree(socketSession);
}
while (--connectionMax > 0);
}
MEM_CONTEXT_TEMP_END();
FUNCTION_LOG_RETURN_VOID();
}

View File

@ -0,0 +1,14 @@
/***********************************************************************************************************************************
Server Command
***********************************************************************************************************************************/
#ifndef COMMAND_SERVER_SERVER_H
#define COMMAND_SERVER_SERVER_H
#include <inttypes.h>
/***********************************************************************************************************************************
Functions
***********************************************************************************************************************************/
void cmdServer(uint64_t connectionMax);
#endif

View File

@ -85,6 +85,7 @@ ERROR_DEFINE(101, ServiceError, RuntimeError);
ERROR_DEFINE(102, ExecuteError, RuntimeError);
ERROR_DEFINE(103, RepoInvalidError, RuntimeError);
ERROR_DEFINE(104, CommandError, RuntimeError);
ERROR_DEFINE(105, AccessError, RuntimeError);
ERROR_DEFINE(122, RuntimeError, RuntimeError);
ERROR_DEFINE(123, InvalidError, RuntimeError);
ERROR_DEFINE(124, UnhandledError, RuntimeError);
@ -173,6 +174,7 @@ static const ErrorType *errorTypeList[] =
&ExecuteError,
&RepoInvalidError,
&CommandError,
&AccessError,
&RuntimeError,
&InvalidError,
&UnhandledError,

View File

@ -87,6 +87,7 @@ ERROR_DECLARE(ServiceError);
ERROR_DECLARE(ExecuteError);
ERROR_DECLARE(RepoInvalidError);
ERROR_DECLARE(CommandError);
ERROR_DECLARE(AccessError);
ERROR_DECLARE(RuntimeError);
ERROR_DECLARE(InvalidError);
ERROR_DECLARE(UnhandledError);

56
src/common/io/server.c Normal file
View File

@ -0,0 +1,56 @@
/***********************************************************************************************************************************
Io Server Interface
***********************************************************************************************************************************/
#include "build.auto.h"
#include "common/debug.h"
#include "common/io/server.h"
#include "common/log.h"
#include "common/memContext.h"
/***********************************************************************************************************************************
Object type
***********************************************************************************************************************************/
struct IoServer
{
IoServerPub pub; // Publicly accessible variables
};
/**********************************************************************************************************************************/
IoServer *
ioServerNew(void *const driver, const IoServerInterface *const interface)
{
FUNCTION_LOG_BEGIN(logLevelTrace)
FUNCTION_LOG_PARAM_P(VOID, driver);
FUNCTION_LOG_PARAM(IO_SERVER_INTERFACE, interface);
FUNCTION_LOG_END();
ASSERT(driver != NULL);
ASSERT(interface != NULL);
ASSERT(interface->type != 0);
ASSERT(interface->name != NULL);
ASSERT(interface->accept != NULL);
ASSERT(interface->toLog != NULL);
IoServer *this = memNew(sizeof(IoServer));
*this = (IoServer)
{
.pub =
{
.memContext = memContextCurrent(),
.driver = driver,
.interface = interface,
},
};
FUNCTION_LOG_RETURN(IO_SERVER, this);
}
/**********************************************************************************************************************************/
String *
ioServerToLog(const IoServer *const this)
{
return strNewFmt(
"{type: %s, driver: %s}", strZ(strIdToStr(this->pub.interface->type)), strZ(this->pub.interface->toLog(this->pub.driver)));
}

72
src/common/io/server.h Normal file
View File

@ -0,0 +1,72 @@
/***********************************************************************************************************************************
Io Server Interface
Create sessions for protocol servers. For example, a TLS server can be created with tlsServerNew() and then new TLS sessions can be
accepted with ioServerAccept().
***********************************************************************************************************************************/
#ifndef COMMON_IO_SERVER_H
#define COMMON_IO_SERVER_H
/***********************************************************************************************************************************
Object type
***********************************************************************************************************************************/
typedef struct IoServer IoServer;
#include "common/io/server.intern.h"
#include "common/io/session.h"
#include "common/type/object.h"
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
typedef struct IoServerPub
{
MemContext *memContext; // Mem context
void *driver; // Driver object
const IoServerInterface *interface; // Driver interface
} IoServerPub;
// Name that identifies the server
__attribute__((always_inline)) static inline const String *
ioServerName(const IoServer *const this)
{
return THIS_PUB(IoServer)->interface->name(THIS_PUB(IoServer)->driver);
}
/***********************************************************************************************************************************
Functions
***********************************************************************************************************************************/
// Move to a new parent mem context
__attribute__((always_inline)) static inline IoServer *
ioServerMove(IoServer *const this, MemContext *const parentNew)
{
return objMoveContext(this, parentNew);
}
// Open session
__attribute__((always_inline)) static inline IoSession *
ioServerAccept(IoServer *const this, IoSession *const session)
{
return THIS_PUB(IoServer)->interface->accept(THIS_PUB(IoServer)->driver, session);
}
/***********************************************************************************************************************************
Destructor
***********************************************************************************************************************************/
__attribute__((always_inline)) static inline void
ioServerFree(IoServer *const this)
{
objFreeContext(this);
}
/***********************************************************************************************************************************
Macros for function logging
***********************************************************************************************************************************/
String *ioServerToLog(const IoServer *this);
#define FUNCTION_LOG_IO_SERVER_TYPE \
IoServer *
#define FUNCTION_LOG_IO_SERVER_FORMAT(value, buffer, bufferSize) \
FUNCTION_LOG_STRING_OBJECT_FORMAT(value, ioServerToLog, buffer, bufferSize)
#endif

View File

@ -0,0 +1,42 @@
/***********************************************************************************************************************************
Io Server Interface Internal
***********************************************************************************************************************************/
#ifndef COMMON_IO_SERVER_INTERN_H
#define COMMON_IO_SERVER_INTERN_H
#include "common/io/server.h"
#include "common/io/session.h"
#include "common/type/string.h"
/***********************************************************************************************************************************
Interface
***********************************************************************************************************************************/
typedef struct IoServerInterface
{
// Type used to identify the server
StringId type;
// Server name, usually address:port or some other unique indentifier
const String *(*name)(void *driver);
// Accept a session
IoSession *(*accept)(void *driver, IoSession *session);
// Driver log function
String *(*toLog)(const void *driver);
} IoServerInterface;
/***********************************************************************************************************************************
Constructors
***********************************************************************************************************************************/
IoServer *ioServerNew(void *driver, const IoServerInterface *interface);
/***********************************************************************************************************************************
Macros for function logging
***********************************************************************************************************************************/
#define FUNCTION_LOG_IO_SERVER_INTERFACE_TYPE \
IoServerInterface *
#define FUNCTION_LOG_IO_SERVER_INTERFACE_FORMAT(value, buffer, bufferSize) \
objToLog(&value, "IoServerInterface", buffer, bufferSize)
#endif

View File

@ -48,6 +48,20 @@ ioSessionNew(void *driver, const IoSessionInterface *interface)
FUNCTION_LOG_RETURN(IO_SESSION, this);
}
/**********************************************************************************************************************************/
void
ioSessionAuthenticatedSet(IoSession *const this, const bool authenticated)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(IO_SESSION, this);
FUNCTION_TEST_PARAM(BOOL, authenticated);
FUNCTION_TEST_END();
this->pub.authenticated = authenticated;
FUNCTION_TEST_RETURN_VOID();
}
/**********************************************************************************************************************************/
int
ioSessionFd(IoSession *this)
@ -61,6 +75,24 @@ ioSessionFd(IoSession *this)
FUNCTION_TEST_RETURN(this->pub.interface->fd == NULL ? -1 : this->pub.interface->fd(this->pub.driver));
}
/**********************************************************************************************************************************/
void
ioSessionPeerNameSet(IoSession *const this, const String *const peerName) // {vm_covered}
{
FUNCTION_TEST_BEGIN(); // {vm_covered}
FUNCTION_TEST_PARAM(IO_SESSION, this); // {vm_covered}
FUNCTION_TEST_PARAM(STRING, peerName); // {vm_covered}
FUNCTION_TEST_END(); // {vm_covered}
MEM_CONTEXT_BEGIN(this->pub.memContext) // {vm_covered}
{
this->pub.peerName = strDup(peerName); // {vm_covered}
}
MEM_CONTEXT_END(); // {vm_covered}
FUNCTION_TEST_RETURN_VOID(); // {vm_covered}
} // {vm_covered}
/**********************************************************************************************************************************/
String *
ioSessionToLog(const IoSession *this)

View File

@ -36,8 +36,17 @@ typedef struct IoSessionPub
MemContext *memContext; // Mem context
void *driver; // Driver object
const IoSessionInterface *interface; // Driver interface
const String *peerName; // Name of peer (exact meaning depends on driver)
bool authenticated; // Is the session authenticated?
} IoSessionPub;
// Is the session authenticated? The exact meaning of "authenticated" will vary by driver type.
__attribute__((always_inline)) static inline bool
ioSessionAuthenticated(const IoSession *const this)
{
return THIS_PUB(IoSession)->authenticated;
}
// Session file descriptor, -1 if none
int ioSessionFd(IoSession *this);
@ -55,6 +64,14 @@ ioSessionIoWrite(IoSession *const this)
return THIS_PUB(IoSession)->interface->ioWrite(THIS_PUB(IoSession)->driver);
}
// The peer name, if any. The exact meaning will vary by driver type, for example the peer name will be the client CN for the
// TlsSession driver.
__attribute__((always_inline)) static inline const String *
ioSessionPeerName(const IoSession *const this)
{
return THIS_PUB(IoSession)->peerName;
}
// Session role
__attribute__((always_inline)) static inline IoSessionRole
ioSessionRole(const IoSession *const this)

View File

@ -39,6 +39,15 @@ Constructors
***********************************************************************************************************************************/
IoSession *ioSessionNew(void *driver, const IoSessionInterface *interface);
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
// Has the session been authenticated?
void ioSessionAuthenticatedSet(IoSession *this, bool authenticated);
// Set the peer name
void ioSessionPeerNameSet(IoSession *this, const String *peerName);
/***********************************************************************************************************************************
Macros for function logging
***********************************************************************************************************************************/

View File

@ -33,7 +33,8 @@ typedef struct SocketClient
String *host; // Hostname or IP address
unsigned int port; // Port to connect to host on
String *name; // Socket name (host:port)
TimeMSec timeout; // Timeout for any i/o operation (connect, read, etc.)
TimeMSec timeoutConnect; // Timeout for connection
TimeMSec timeoutSession; // Timeout passed to session
} SocketClient;
/***********************************************************************************************************************************
@ -44,7 +45,9 @@ sckClientToLog(const THIS_VOID)
{
THIS(const SocketClient);
return strNewFmt("{host: %s, port: %u, timeout: %" PRIu64 "}", strZ(this->host), this->port, this->timeout);
return strNewFmt(
"{host: %s, port: %u, timeoutConnect: %" PRIu64 ", timeoutSession: %" PRIu64 "}", strZ(this->host), this->port,
this->timeoutConnect, this->timeoutSession);
}
#define FUNCTION_LOG_SOCKET_CLIENT_TYPE \
@ -69,7 +72,7 @@ sckClientOpen(THIS_VOID)
MEM_CONTEXT_TEMP_BEGIN()
{
bool retry;
Wait *wait = waitNew(this->timeout);
Wait *wait = waitNew(this->timeoutConnect);
do
{
@ -79,48 +82,28 @@ sckClientOpen(THIS_VOID)
TRY_BEGIN()
{
// Set hints that narrow the type of address we are looking for -- we'll take ipv4 or ipv6
struct addrinfo hints = (struct addrinfo)
{
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM,
.ai_protocol = IPPROTO_TCP,
};
// Convert the port to a zero-terminated string for use with getaddrinfo()
char port[CVT_BASE10_BUFFER_SIZE];
cvtUIntToZ(this->port, port, sizeof(port));
// Get an address for the host. We are only going to try the first address returned.
struct addrinfo *hostAddress;
int resultAddr;
if ((resultAddr = getaddrinfo(strZ(this->host), port, &hints, &hostAddress)) != 0)
{
THROW_FMT(
HostConnectError, "unable to get address for '%s': [%d] %s", strZ(this->host), resultAddr,
gai_strerror(resultAddr));
}
// Get an address for the host. We are only going to try the first address returned.
struct addrinfo *addressFound = sckHostLookup(this->host, this->port);
// Connect to the host
TRY_BEGIN()
{
fd = socket(hostAddress->ai_family, hostAddress->ai_socktype, hostAddress->ai_protocol);
fd = socket(addressFound->ai_family, addressFound->ai_socktype, addressFound->ai_protocol);
THROW_ON_SYS_ERROR(fd == -1, HostConnectError, "unable to create socket");
sckOptionSet(fd);
sckConnect(fd, this->host, this->port, hostAddress, waitRemaining(wait));
sckConnect(fd, this->host, this->port, addressFound, waitRemaining(wait));
}
FINALLY()
{
freeaddrinfo(hostAddress);
freeaddrinfo(addressFound);
}
TRY_END();
// Create the session
MEM_CONTEXT_PRIOR_BEGIN()
{
result = sckSessionNew(ioSessionRoleClient, fd, this->host, this->port, this->timeout);
result = sckSessionNew(ioSessionRoleClient, fd, this->host, this->port, this->timeoutSession);
}
MEM_CONTEXT_PRIOR_END();
}
@ -176,12 +159,13 @@ static const IoClientInterface sckClientInterface =
};
IoClient *
sckClientNew(const String *host, unsigned int port, TimeMSec timeout)
sckClientNew(const String *const host, const unsigned int port, const TimeMSec timeoutConnect, const TimeMSec timeoutSession)
{
FUNCTION_LOG_BEGIN(logLevelDebug)
FUNCTION_LOG_PARAM(STRING, host);
FUNCTION_LOG_PARAM(UINT, port);
FUNCTION_LOG_PARAM(TIME_MSEC, timeout);
FUNCTION_LOG_PARAM(TIME_MSEC, timeoutConnect);
FUNCTION_LOG_PARAM(TIME_MSEC, timeoutSession);
FUNCTION_LOG_END();
ASSERT(host != NULL);
@ -197,7 +181,8 @@ sckClientNew(const String *host, unsigned int port, TimeMSec timeout)
.host = strDup(host),
.port = port,
.name = strNewFmt("%s:%u", strZ(host), port),
.timeout = timeout,
.timeoutConnect = timeoutConnect,
.timeoutSession = timeoutSession,
};
statInc(SOCKET_STAT_CLIENT_STR);

View File

@ -27,6 +27,6 @@ Statistics constants
/***********************************************************************************************************************************
Constructors
***********************************************************************************************************************************/
IoClient *sckClientNew(const String *host, unsigned int port, TimeMSec timeout);
IoClient *sckClientNew(const String *host, unsigned int port, TimeMSec timeoutConnect, TimeMSec timeoutSession);
#endif

View File

@ -55,6 +55,41 @@ sckInit(bool block, bool keepAlive, int tcpKeepAliveCount, int tcpKeepAliveIdle,
FUNCTION_LOG_RETURN_VOID();
}
/**********************************************************************************************************************************/
struct addrinfo *
sckHostLookup(const String *const host, unsigned int port)
{
FUNCTION_LOG_BEGIN(logLevelDebug);
FUNCTION_LOG_PARAM(STRING, host);
FUNCTION_LOG_PARAM(UINT, port);
FUNCTION_LOG_END();
ASSERT(host != NULL);
ASSERT(port != 0);
// Set hints that narrow the type of address we are looking for -- we'll take ipv4 or ipv6
struct addrinfo hints = (struct addrinfo)
{
.ai_family = AF_UNSPEC,
.ai_flags = AI_PASSIVE,
.ai_socktype = SOCK_STREAM,
.ai_protocol = IPPROTO_TCP,
};
// Convert the port to a zero-terminated string for use with getaddrinfo()
char portZ[CVT_BASE10_BUFFER_SIZE];
cvtUIntToZ(port, portZ, sizeof(portZ));
// Do the lookup
struct addrinfo *result;
int error;
if ((error = getaddrinfo(strZ(host), portZ, &hints, &result)) != 0)
THROW_FMT(HostConnectError, "unable to get address for '%s': [%d] %s", strZ(host), error, gai_strerror(error));
FUNCTION_LOG_RETURN_P(VOID, result);
}
/**********************************************************************************************************************************/
void
sckOptionSet(int fd)

View File

@ -15,6 +15,9 @@ Functions
// Initialize settings for socket connections (some are used only for TCP)
void sckInit(bool block, bool keepAlive, int tcpKeepAliveCount, int tcpKeepAliveIdle, int tcpKeepAliveInterval);
// Get address info for a host/address. The caller is reponsible for freeing addrinfo.
struct addrinfo *sckHostLookup(const String *const host, unsigned int port);
// Set options on a socket
void sckOptionSet(int fd);

View File

@ -0,0 +1,205 @@
/***********************************************************************************************************************************
Socket Server
***********************************************************************************************************************************/
#include "build.auto.h"
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include "common/debug.h"
#include "common/log.h"
#include "common/io/server.h"
#include "common/io/socket/server.h"
#include "common/io/socket/common.h"
#include "common/io/socket/session.h"
#include "common/memContext.h"
#include "common/stat.h"
#include "common/type/object.h"
#include "common/wait.h"
/***********************************************************************************************************************************
Statistics constants
***********************************************************************************************************************************/
STRING_EXTERN(SOCKET_STAT_SERVER_STR, SOCKET_STAT_SERVER);
/***********************************************************************************************************************************
Object type
***********************************************************************************************************************************/
typedef struct SocketServer
{
MemContext *memContext; // Mem context
String *address; // Address to listen on
unsigned int port; // Port to listen on
int socket; // Socket used to listen for new connections
String *name; // Socket name (address:port)
TimeMSec timeout; // Timeout for any i/o operation (connect, read, etc.)
} SocketServer;
/***********************************************************************************************************************************
Macros for function logging
***********************************************************************************************************************************/
static String *
sckServerToLog(const THIS_VOID)
{
THIS(const SocketServer);
return strNewFmt("{address: %s, port: %u, timeout: %" PRIu64 "}", strZ(this->address), this->port, this->timeout);
}
#define FUNCTION_LOG_SOCKET_SERVER_TYPE \
SocketServer *
#define FUNCTION_LOG_SOCKET_SERVER_FORMAT(value, buffer, bufferSize) \
FUNCTION_LOG_STRING_OBJECT_FORMAT(value, sckServerToLog, buffer, bufferSize)
/***********************************************************************************************************************************
Free connection
***********************************************************************************************************************************/
static void
sckServerFreeResource(THIS_VOID)
{
THIS(SocketServer);
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(SOCKET_SERVER, this);
FUNCTION_LOG_END();
ASSERT(this != NULL);
close(this->socket);
FUNCTION_LOG_RETURN_VOID();
}
/**********************************************************************************************************************************/
static IoSession *
sckServerAccept(THIS_VOID, IoSession *const session)
{
THIS(SocketServer);
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(SOCKET_SERVER, this);
(void)session; // Not used by this server
FUNCTION_LOG_END();
ASSERT(this != NULL);
ASSERT(session == NULL);
IoSession *result = NULL;
MEM_CONTEXT_TEMP_BEGIN()
{
// Accept the socket connection
struct sockaddr_in addr;
unsigned int len = sizeof(addr);
int serverSocket = accept(this->socket, (struct sockaddr *)&addr, &len);
THROW_ON_SYS_ERROR(serverSocket == -1, FileOpenError, "unable to accept socket");
// Create socket session
sckOptionSet(serverSocket);
MEM_CONTEXT_PRIOR_BEGIN()
{
result = sckSessionNew(ioSessionRoleServer, serverSocket, this->address, this->port, this->timeout);
}
MEM_CONTEXT_PRIOR_END();
statInc(SOCKET_STAT_SESSION_STR);
}
MEM_CONTEXT_TEMP_END();
FUNCTION_LOG_RETURN(IO_SESSION, result);
}
/**********************************************************************************************************************************/
static const String *
sckServerName(THIS_VOID) // {vm_covered}
{
THIS(SocketServer); // {vm_covered}
FUNCTION_TEST_BEGIN(); // {vm_covered}
FUNCTION_TEST_PARAM(SOCKET_SERVER, this); // {vm_covered}
FUNCTION_TEST_END(); // {vm_covered}
ASSERT(this != NULL); // {vm_covered}
FUNCTION_TEST_RETURN(this->name); // {vm_covered}
}
/**********************************************************************************************************************************/
static const IoServerInterface sckServerInterface =
{
.type = IO_SERVER_SOCKET_TYPE,
.name = sckServerName,
.accept = sckServerAccept,
.toLog = sckServerToLog,
};
IoServer *
sckServerNew(const String *const address, const unsigned int port, const TimeMSec timeout)
{
FUNCTION_LOG_BEGIN(logLevelDebug)
FUNCTION_LOG_PARAM(STRING, address);
FUNCTION_LOG_PARAM(UINT, port);
FUNCTION_LOG_PARAM(TIME_MSEC, timeout);
FUNCTION_LOG_END();
ASSERT(address != NULL);
ASSERT(port > 0);
IoServer *this = NULL;
MEM_CONTEXT_NEW_BEGIN("SocketServer")
{
SocketServer *driver = memNew(sizeof(SocketServer));
*driver = (SocketServer)
{
.memContext = MEM_CONTEXT_NEW(),
.address = strDup(address),
.port = port,
.name = strNewFmt("%s:%u", strZ(address), port),
.timeout = timeout,
};
// Lookup address
struct addrinfo *addressFound = sckHostLookup(driver->address, driver->port);
TRY_BEGIN()
{
// Create socket
THROW_ON_SYS_ERROR(
(driver->socket = socket(addressFound->ai_family, SOCK_STREAM, 0)) == -1, FileOpenError, "unable to create socket");
// Set the address as reusable so we can bind again quickly after a restart or crash
int reuseAddr = 1;
setsockopt(driver->socket, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr));
// Ensure file descriptor is closed
memContextCallbackSet(driver->memContext, sckServerFreeResource, driver);
// Bind the address
THROW_ON_SYS_ERROR(
bind(driver->socket, addressFound->ai_addr, addressFound->ai_addrlen) == -1, FileOpenError,
"unable to bind socket");
}
FINALLY()
{
freeaddrinfo(addressFound);
}
TRY_END();
// Listen for client connections. It might be a good idea to make the backlog configurable but this value seems OK for now.
THROW_ON_SYS_ERROR(listen(driver->socket, 100) == -1, FileOpenError, "unable to listen on socket");
statInc(SOCKET_STAT_SERVER_STR);
this = ioServerNew(driver, &sckServerInterface);
}
MEM_CONTEXT_NEW_END();
FUNCTION_LOG_RETURN(IO_SERVER, this);
}

View File

@ -0,0 +1,29 @@
/***********************************************************************************************************************************
Socket Server
A simple socket server intended to expose services by accepting a socket.
***********************************************************************************************************************************/
#ifndef COMMON_IO_SOCKET_SERVER_H
#define COMMON_IO_SOCKET_SERVER_H
#include "common/io/server.h"
#include "common/io/socket/client.h"
#include "common/time.h"
/***********************************************************************************************************************************
Io server type
***********************************************************************************************************************************/
#define IO_SERVER_SOCKET_TYPE IO_CLIENT_SOCKET_TYPE
/***********************************************************************************************************************************
Statistics constants
***********************************************************************************************************************************/
#define SOCKET_STAT_SERVER "socket.server" // Servers created
STRING_DECLARE(SOCKET_STAT_SERVER_STR);
/***********************************************************************************************************************************
Constructors
***********************************************************************************************************************************/
IoServer *sckServerNew(const String *address, unsigned int port, TimeMSec timeout);
#endif

View File

@ -22,7 +22,7 @@ typedef struct SocketSession
int fd; // File descriptor
String *host; // Hostname or IP address
unsigned int port; // Port to connect to host on
TimeMSec timeout; // Timeout for any i/o operation (connect, read, etc.)
TimeMSec timeout; // Timeout for any i/o operation (read, write, etc.)
IoRead *read; // IoRead interface to the file descriptor
IoWrite *write; // IoWrite interface to the file descriptor

View File

@ -3,17 +3,15 @@ TLS Client
***********************************************************************************************************************************/
#include "build.auto.h"
#include <string.h>
#include <strings.h>
#include <openssl/x509v3.h>
#include "common/crypto/common.h"
#include "common/debug.h"
#include "common/log.h"
#include "common/io/client.h"
#include "common/io/io.h"
#include "common/io/tls/client.h"
#include "common/io/tls/common.h"
#include "common/io/tls/session.h"
#include "common/stat.h"
#include "common/type/object.h"
@ -32,7 +30,8 @@ Object type
typedef struct TlsClient
{
const String *host; // Host to use for peer verification
TimeMSec timeout; // Timeout for any i/o operation (connect, read, etc.)
TimeMSec timeoutConnect; // Timeout for connection
TimeMSec timeoutSession; // Timeout passed to session
bool verifyPeer; // Should the peer (server) certificate be verified?
IoClient *ioClient; // Underlying client (usually a SocketClient)
@ -48,8 +47,8 @@ tlsClientToLog(const THIS_VOID)
THIS(const TlsClient);
return strNewFmt(
"{ioClient: %s, timeout: %" PRIu64", verifyPeer: %s}",
objMemContextFreeing(this) ? NULL_Z : strZ(ioClientToLog(this->ioClient)), this->timeout,
"{ioClient: %s, timeoutConnect: %" PRIu64 ", timeoutSession: %" PRIu64 ", verifyPeer: %s}",
objMemContextFreeing(this) ? NULL_Z : strZ(ioClientToLog(this->ioClient)), this->timeoutConnect, this->timeoutSession,
cvtBoolToConstZ(this->verifyPeer));
}
@ -77,30 +76,6 @@ tlsClientFreeResource(THIS_VOID)
FUNCTION_LOG_RETURN_VOID();
}
/***********************************************************************************************************************************
Convert an ASN1 string used in certificates to a String
***********************************************************************************************************************************/
static String *
asn1ToStr(ASN1_STRING *nameAsn1)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM_P(VOID, nameAsn1);
FUNCTION_TEST_END();
// The name should not be null
if (nameAsn1 == NULL) // {vm_covered}
THROW(CryptoError, "TLS certificate name entry is missing");
FUNCTION_TEST_RETURN( // {vm_covered}
strNewZN(
#if OPENSSL_VERSION_NUMBER < 0x10100000L
(const char *)ASN1_STRING_data(nameAsn1),
#else
(const char *)ASN1_STRING_get0_data(nameAsn1),
#endif
(size_t)ASN1_STRING_length(nameAsn1)));
}
/***********************************************************************************************************************************
Check if a name from the server certificate matches the hostname
@ -117,9 +92,8 @@ tlsClientHostVerifyName(const String *host, const String *name)
ASSERT(host != NULL);
ASSERT(name != NULL);
// Reject embedded nulls in certificate common or alternative name to prevent attacks like CVE-2009-4034
if (strlen(strZ(name)) != strSize(name))
THROW(CryptoError, "TLS certificate name contains embedded null");
// Check for NULLs in the name
tlsCertNameVerify(name);
bool result = false;
@ -185,7 +159,7 @@ tlsClientHostVerify(const String *host, X509 *certificate)
altNameFound = true; // {vm_covered}
if (name->type == GEN_DNS) // {vm_covered}
result = tlsClientHostVerifyName(host, asn1ToStr(name->d.dNSName)); // {vm_covered}
result = tlsClientHostVerifyName(host, tlsAsn1ToStr(name->d.dNSName)); // {vm_covered}
if (result != false) // {vm_covered}
break; // {vm_covered}
@ -197,17 +171,7 @@ tlsClientHostVerify(const String *host, X509 *certificate)
// If no subject alternative name was found then check the common name. Per RFC 2818 and RFC 6125, if the subjectAltName
// extension of type dNSName is present the CN must be ignored.
if (!altNameFound) // {vm_covered}
{
X509_NAME *subjectName = X509_get_subject_name(certificate); // {vm_covered}
CHECK(subjectName != NULL); // {vm_covered}
int commonNameIndex = X509_NAME_get_index_by_NID(subjectName, NID_commonName, -1); // {vm_covered}
CHECK(commonNameIndex >= 0); // {vm_covered}
result = tlsClientHostVerifyName( // {vm_covered}
host, // {vm_covered}
asn1ToStr(X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subjectName, commonNameIndex)))); // {vm_covered}
}
result = tlsClientHostVerifyName(host, tlsCertCommonName(certificate)); // {vm_covered}
}
MEM_CONTEXT_TEMP_END(); // {vm_covered}
@ -215,46 +179,110 @@ tlsClientHostVerify(const String *host, X509 *certificate)
}
/***********************************************************************************************************************************
Open connection if this is a new client or if the connection was closed by the server
Authenticate server
Adapted from PostgreSQL open_client_SSL() in src/interfaces/libpq/fe-secure-openssl.c.
***********************************************************************************************************************************/
static bool
tlsClientAuth(const TlsClient *const this, SSL *const tlsSession)
{
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(TLS_CLIENT, this);
FUNCTION_LOG_PARAM_P(VOID, tlsSession);
FUNCTION_LOG_END();
bool result = false;
MEM_CONTEXT_TEMP_BEGIN()
{
// Verify that the certificate presented by the server is valid
if (this->verifyPeer) // {vm_covered}
{
// Verify that the chain of trust leads to a valid CA
long int verifyResult = SSL_get_verify_result(tlsSession); // {vm_covered}
if (verifyResult != X509_V_OK) // {vm_covered}
{
THROW_FMT( // {vm_covered}
CryptoError, "unable to verify certificate presented by '%s': [%ld] %s", // {vm_covered}
strZ(ioClientName(this->ioClient)), verifyResult, // {vm_covered}
X509_verify_cert_error_string(verifyResult)); // {vm_covered}
}
// Verify that the hostname appears in the certificate
X509 *certificate = SSL_get_peer_certificate(tlsSession); // {vm_covered}
bool nameResult = tlsClientHostVerify(this->host, certificate); // {vm_covered}
X509_free(certificate); // {vm_covered}
if (!nameResult) // {vm_covered}
{
THROW_FMT( // {vm_covered}
CryptoError, // {vm_covered}
"unable to find hostname '%s' in certificate common name or subject alternative names", // {vm_covered}
strZ(this->host)); // {vm_covered}
}
}
result = true;
}
MEM_CONTEXT_TEMP_END();
FUNCTION_LOG_RETURN(BOOL, result);
}
/***********************************************************************************************************************************
Open TLS session on a socket
***********************************************************************************************************************************/
static IoSession *
tlsClientOpen(THIS_VOID)
{
THIS(TlsClient);
FUNCTION_LOG_BEGIN(logLevelTrace)
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(TLS_CLIENT, this);
FUNCTION_LOG_END();
ASSERT(this != NULL);
IoSession *result = NULL;
SSL *session = NULL;
MEM_CONTEXT_TEMP_BEGIN()
{
bool retry;
Wait *wait = waitNew(this->timeout);
Wait *wait = waitNew(this->timeoutConnect);
SSL *tlsSession = NULL;
do
{
// Assume there will be no retry
retry = false;
// Create the TLS session
tlsSession = SSL_new(this->context);
cryptoError(tlsSession == NULL, "unable to create TLS session");
// Set server host name used for validation
cryptoError(SSL_set_tlsext_host_name(tlsSession, strZ(this->host)) != 1, "unable to set TLS host name");
// Open TLS session
TRY_BEGIN()
{
// Open the underlying session first since this is mostly likely to fail
IoSession *ioSession = ioClientOpen(this->ioClient);
IoSession *ioSession = NULL;
// Create internal TLS session. If there is a failure before the TlsSession object is created there may be a leak
// of the TLS session but this is likely to result in program termination so it doesn't seem worth coding for.
cryptoError((session = SSL_new(this->context)) == NULL, "unable to create TLS session");
TRY_BEGIN()
{
ioSession = ioClientOpen(this->ioClient);
}
CATCH_ANY()
{
SSL_free(tlsSession);
RETHROW();
}
TRY_END();
// Set server host name used for validation
cryptoError(SSL_set_tlsext_host_name(session, strZ(this->host)) != 1, "unable to set TLS host name");
// Create the TLS session
result = tlsSessionNew(session, ioSession, this->timeout);
// Open session
result = tlsSessionNew(tlsSession, ioSession, this->timeoutSession);
}
CATCH_ANY()
{
@ -275,39 +303,16 @@ tlsClientOpen(THIS_VOID)
}
while (retry);
// Authenticate TLS session
ioSessionAuthenticatedSet(result, tlsClientAuth(this, tlsSession));
// Move session
ioSessionMove(result, memContextPrior());
}
MEM_CONTEXT_TEMP_END();
statInc(TLS_STAT_SESSION_STR);
// Verify that the certificate presented by the server is valid
if (this->verifyPeer) // {vm_covered}
{
// Verify that the chain of trust leads to a valid CA
long int verifyResult = SSL_get_verify_result(session); // {vm_covered}
if (verifyResult != X509_V_OK) // {vm_covered}
{
THROW_FMT( // {vm_covered}
CryptoError, "unable to verify certificate presented by '%s': [%ld] %s", // {vm_covered}
strZ(ioClientName(this->ioClient)), verifyResult, X509_verify_cert_error_string(verifyResult)); // {vm_covered}
}
// Verify that the hostname appears in the certificate
X509 *certificate = SSL_get_peer_certificate(session); // {vm_covered}
bool nameResult = tlsClientHostVerify(this->host, certificate); // {vm_covered}
X509_free(certificate); // {vm_covered}
if (!nameResult) // {vm_covered}
{
THROW_FMT( // {vm_covered}
CryptoError, // {vm_covered}
"unable to find hostname '%s' in certificate common name or subject alternative names", // {vm_covered}
strZ(this->host)); // {vm_covered}
}
}
FUNCTION_LOG_RETURN(IO_SESSION, result);
}
@ -326,7 +331,11 @@ tlsClientName(THIS_VOID)
FUNCTION_TEST_RETURN(ioClientName(this->ioClient));
}
/**********************************************************************************************************************************/
/***********************************************************************************************************************************
Initialize TLS session with all required security features
Adapted from PostgreSQL initialize_SSL() in src/interfaces/libpq/fe-secure-openssl.c.
***********************************************************************************************************************************/
static const IoClientInterface tlsClientInterface =
{
.type = IO_CLIENT_TLS_TYPE,
@ -336,15 +345,21 @@ static const IoClientInterface tlsClientInterface =
};
IoClient *
tlsClientNew(IoClient *ioClient, const String *host, TimeMSec timeout, bool verifyPeer, const String *caFile, const String *caPath)
tlsClientNew(
IoClient *const ioClient, const String *const host, const TimeMSec timeoutConnect, const TimeMSec timeoutSession,
const bool verifyPeer, const String *const caFile, const String *const caPath, const String *const certFile,
const String *const keyFile)
{
FUNCTION_LOG_BEGIN(logLevelDebug)
FUNCTION_LOG_BEGIN(logLevelDebug);
FUNCTION_LOG_PARAM(IO_CLIENT, ioClient);
FUNCTION_LOG_PARAM(STRING, host);
FUNCTION_LOG_PARAM(TIME_MSEC, timeout);
FUNCTION_LOG_PARAM(TIME_MSEC, timeoutConnect);
FUNCTION_LOG_PARAM(TIME_MSEC, timeoutSession);
FUNCTION_LOG_PARAM(BOOL, verifyPeer);
FUNCTION_LOG_PARAM(STRING, caFile);
FUNCTION_LOG_PARAM(STRING, caPath);
FUNCTION_LOG_PARAM(STRING, certFile);
FUNCTION_LOG_PARAM(STRING, keyFile);
FUNCTION_LOG_END();
ASSERT(ioClient != NULL);
@ -359,33 +374,19 @@ tlsClientNew(IoClient *ioClient, const String *host, TimeMSec timeout, bool veri
{
.ioClient = ioClientMove(ioClient, MEM_CONTEXT_NEW()),
.host = strDup(host),
.timeout = timeout,
.timeoutConnect = timeoutConnect,
.timeoutSession = timeoutSession,
.verifyPeer = verifyPeer,
.context = tlsContext(),
};
// Setup TLS context
// -------------------------------------------------------------------------------------------------------------------------
cryptoInit();
// Select the TLS method to use. To maintain compatibility with older versions of OpenSSL we need to use an SSL method,
// but SSL versions will be excluded in SSL_CTX_set_options().
const SSL_METHOD *method = SSLv23_method();
cryptoError(method == NULL, "unable to load TLS method");
// Create the TLS context
driver->context = SSL_CTX_new(method);
cryptoError(driver->context == NULL, "unable to create TLS context");
// Set callback to free context
memContextCallbackSet(objMemContext(driver), tlsClientFreeResource, driver);
// Exclude SSL versions to only allow TLS and also disable compression
SSL_CTX_set_options(driver->context, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION);
// Disable auto-retry to prevent SSL_read() from hanging
SSL_CTX_clear_mode(driver->context, SSL_MODE_AUTO_RETRY);
// Enable safe compatibility options
SSL_CTX_set_options(driver->context, SSL_OP_ALL);
// Set location of CA certificates if the server certificate will be verified
// -------------------------------------------------------------------------------------------------------------------------
if (driver->verifyPeer)
{
// If the user specified a location
@ -403,6 +404,10 @@ tlsClientNew(IoClient *ioClient, const String *host, TimeMSec timeout, bool veri
}
}
// Load certificate and key, if specified
tlsCertKeyLoad(driver->context, certFile, keyFile);
// Increment stat
statInc(TLS_STAT_CLIENT_STR);
// Create client interface

View File

@ -31,7 +31,8 @@ Statistics constants
Constructors
***********************************************************************************************************************************/
IoClient *tlsClientNew(
IoClient *ioClient, const String *host, TimeMSec timeout, bool verifyPeer, const String *caFile, const String *caPath);
IoClient *ioClient, const String *host, TimeMSec timeoutConnect, TimeMSec timeoutSession, bool verifyPeer, const String *caFile,
const String *caPath, const String *certFile, const String *keyFile);
/***********************************************************************************************************************************
Functions

186
src/common/io/tls/common.c Normal file
View File

@ -0,0 +1,186 @@
/***********************************************************************************************************************************
TLS Common
***********************************************************************************************************************************/
#include "build.auto.h"
#include <string.h>
#include <sys/stat.h>
#include <openssl/ssl.h>
#include "common/crypto/common.h"
#include "common/debug.h"
#include "common/io/tls/common.h"
#include "common/user.h"
#include "storage/posix/storage.h"
/**********************************************************************************************************************************/
String *
tlsAsn1ToStr(ASN1_STRING *const nameAsn1)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM_P(VOID, nameAsn1);
FUNCTION_TEST_END();
// The name should not be null
if (nameAsn1 == NULL) // {vm_covered}
THROW(CryptoError, "TLS certificate name entry is missing");
FUNCTION_TEST_RETURN( // {vm_covered}
strNewZN(
#if OPENSSL_VERSION_NUMBER < 0x10100000L
(const char *)ASN1_STRING_data(nameAsn1),
#else
(const char *)ASN1_STRING_get0_data(nameAsn1),
#endif
(size_t)ASN1_STRING_length(nameAsn1)));
}
/**********************************************************************************************************************************/
void
tlsCertNameVerify(const String *const name)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING, name);
FUNCTION_TEST_END();
// Check for embedded NULLs
if (strlen(strZ(name)) != strSize(name))
THROW(CryptoError, "TLS certificate name contains embedded null");
FUNCTION_TEST_RETURN_VOID();
}
/**********************************************************************************************************************************/
String *
tlsCertCommonName(X509 *const certificate) // {vm_covered}
{
FUNCTION_TEST_BEGIN(); // {vm_covered}
FUNCTION_TEST_PARAM_P(VOID, certificate); // {vm_covered}
FUNCTION_TEST_END(); // {vm_covered}
X509_NAME *const subjectName = X509_get_subject_name(certificate); // {vm_covered}
CHECK(subjectName != NULL); // {vm_covered}
const int commonNameIndex = X509_NAME_get_index_by_NID(subjectName, NID_commonName, -1); // {vm_covered}
CHECK(commonNameIndex >= 0); // {vm_covered}
String *result = tlsAsn1ToStr(X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subjectName, commonNameIndex))); // {vm_covered}
// Check for NULLs in the name
tlsCertNameVerify(result); // {vm_covered}
FUNCTION_TEST_RETURN(result); // {vm_covered}
}
/**********************************************************************************************************************************/
// Callback to process cert passwords
static int
tlsCertPwd(char *buffer, const int size, const int rwFlag, void *const userData)
{
CHECK(size > 0);
(void)rwFlag; (void)userData;
// No password is currently supplied
buffer[0] = '\0';
return 0;
}
void
tlsCertKeyLoad(SSL_CTX *const context, const String *const certFile, const String *const keyFile)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM_P(VOID, context);
FUNCTION_TEST_PARAM(STRING, certFile);
FUNCTION_TEST_PARAM(STRING, keyFile);
FUNCTION_TEST_END();
ASSERT(context != NULL);
ASSERT((certFile == NULL && keyFile == NULL) || (certFile != NULL && keyFile != NULL));
if (certFile != NULL)
{
userInit();
MEM_CONTEXT_TEMP_BEGIN()
{
// Set cert password callback
SSL_CTX_set_default_passwd_cb(context, tlsCertPwd);
// Load certificate
cryptoError(
SSL_CTX_use_certificate_chain_file(context, strZ(certFile)) != 1,
strZ(strNewFmt("unable to load cert file '%s'", strZ(certFile))));
// Check that key has the correct permissions
const StorageInfo keyInfo = storageInfoP(
storagePosixNewP(FSLASH_STR), keyFile, .ignoreMissing = true, .followLink = true);
if (keyInfo.exists)
{
if (keyInfo.userId != userId() && keyInfo.userId != 0) // {vm_covered}
{
THROW_FMT( // {vm_covered}
FileReadError, "key file '%s' must be owned by the '%s' user or root", strZ(keyFile), strZ(userName()));
}
if ((keyInfo.userId == userId() && keyInfo.mode & (S_IRWXG | S_IRWXO)) || // {vm_covered}
(keyInfo.userId == 0 && keyInfo.mode & (S_IWGRP | S_IXGRP | S_IRWXO))) // {vm_covered}
{
THROW_FMT(
FileReadError,
"key file '%s' has group or other permissions\n"
"HINT: file must have permissions u=rw (0600) or less if owned by the '%s' user\n"
"HINT: file must have permissions u=rw, g=r (0640) or less if owned by root\n",
strZ(keyFile), strZ(userName()));
}
}
// Load key and verify that the key and cert go together
cryptoError(
SSL_CTX_use_PrivateKey_file(context, strZ(keyFile), SSL_FILETYPE_PEM) != 1,
strZ(strNewFmt("unable to load key file '%s'", strZ(keyFile))));
// Verify again that the cert and key go together. It is not clear why this is needed since the key has already been
// verified in SSL_CTX_use_PrivateKey_file(), but it may be that older versions of OpenSSL need it.
cryptoError(
SSL_CTX_check_private_key(context) != 1,
strZ(strNewFmt("cert '%s' and key '%s' do not match", strZ(certFile), strZ(keyFile))));
}
MEM_CONTEXT_TEMP_END();
}
FUNCTION_TEST_RETURN_VOID();
}
/**********************************************************************************************************************************/
SSL_CTX *
tlsContext(void)
{
FUNCTION_TEST_VOID();
cryptoInit();
// Select the TLS method to use. To maintain compatibility with older versions of OpenSSL we need to use an SSL method, but
// SSL versions will be excluded in SSL_CTX_set_options().
const SSL_METHOD *const method = SSLv23_method();
cryptoError(method == NULL, "unable to load TLS method");
// Create TLS context
SSL_CTX *const result = SSL_CTX_new(method);
cryptoError(result == NULL, "unable to create TLS context");
// Set options
SSL_CTX_set_options(
result,
// Disable SSL
SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
// Disable compression
SSL_OP_NO_COMPRESSION);
// Disable auto-retry to prevent SSL_read() from hanging
SSL_CTX_clear_mode(result, SSL_MODE_AUTO_RETRY);
FUNCTION_TEST_RETURN(result);
}

View File

@ -0,0 +1,29 @@
/***********************************************************************************************************************************
TLS Common
***********************************************************************************************************************************/
#ifndef COMMON_IO_TLS_COMMON_H
#define COMMON_IO_TLS_COMMON_H
#include <openssl/x509v3.h>
#include "common/type/string.h"
/***********************************************************************************************************************************
Functions
***********************************************************************************************************************************/
// Convert an ASN1 string used in certificates to a String
String *tlsAsn1ToStr(ASN1_STRING *nameAsn1);
// Get common name from a certificate
String *tlsCertCommonName(X509 *certificate);
// Reject embedded nulls in certificate common or alternative name to prevent attacks like CVE-2009-4034
void tlsCertNameVerify(const String *name);
// Load certificate and key and check that they match
void tlsCertKeyLoad(SSL_CTX *context, const String *certFile, const String *keyFile);
// Create TLS context
SSL_CTX *tlsContext(void);
#endif

357
src/common/io/tls/server.c Normal file
View File

@ -0,0 +1,357 @@
/***********************************************************************************************************************************
TLS Server
***********************************************************************************************************************************/
#include "build.auto.h"
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <openssl/err.h>
#include "common/crypto/common.h"
#include "common/debug.h"
#include "common/log.h"
#include "common/io/server.h"
#include "common/io/tls/common.h"
#include "common/io/tls/server.h"
#include "common/io/tls/session.h"
#include "common/stat.h"
#include "common/type/object.h"
/***********************************************************************************************************************************
Statistics constants
***********************************************************************************************************************************/
STRING_EXTERN(TLS_STAT_SERVER_STR, TLS_STAT_SERVER);
/***********************************************************************************************************************************
Object type
***********************************************************************************************************************************/
typedef struct TlsServer
{
String *host; // Host
SSL_CTX *context; // TLS context
TimeMSec timeout; // Timeout for any i/o operation (connect, read, etc.)
bool verifyPeer; // Will the client cert be verified?
} TlsServer;
/***********************************************************************************************************************************
Macros for function logging
***********************************************************************************************************************************/
static String *
tlsServerToLog(const THIS_VOID)
{
THIS(const TlsServer);
return strNewFmt("{host: %s, timeout: %" PRIu64 "}", strZ(this->host), this->timeout);
}
#define FUNCTION_LOG_TLS_SERVER_TYPE \
TlsServer *
#define FUNCTION_LOG_TLS_SERVER_FORMAT(value, buffer, bufferSize) \
FUNCTION_LOG_STRING_OBJECT_FORMAT(value, tlsServerToLog, buffer, bufferSize)
/***********************************************************************************************************************************
Free context
***********************************************************************************************************************************/
static void
tlsServerFreeResource(THIS_VOID)
{
THIS(TlsServer);
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(TLS_SERVER, this);
FUNCTION_LOG_END();
ASSERT(this != NULL);
SSL_CTX_free(this->context);
FUNCTION_LOG_RETURN_VOID();
}
/***********************************************************************************************************************************
Set DH parameters for generating ephemeral DH keys. The DH parameters can take a long time to compute, so they must be precomputed
using parameters provided by the OpenSSL project.
These values can be static since the OpenSSL library can efficiently generate random keys from the information provided.
Adapted from PostgreSQL initialize_dh() in src/backend/libpq/be-secure-openssl.c. Also see https://weakdh.org and
https://en.wikipedia.org/wiki/Logjam_(computer_security).
***********************************************************************************************************************************/
// Hardcoded DH parameters, used in ephemeral DH keying. This is the 2048-bit DH parameter from RFC 3526. The generation of the
// prime is specified in RFC 2412 Appendix E, which also discusses the design choice of the generator. Note that when loaded with
// OpenSSL this causes DH_check() to fail on DH_NOT_SUITABLE_GENERATOR, where leaking a bit is preferred.
#define DH_2048 \
"-----BEGIN DH PARAMETERS-----\n" \
"MIIBCAKCAQEA///////////JD9qiIWjCNMTGYouA3BzRKQJOCIpnzHQCC76mOxOb\n" \
"IlFKCHmONATd75UZs806QxswKwpt8l8UN0/hNW1tUcJF5IW1dmJefsb0TELppjft\n" \
"awv/XLb0Brft7jhr+1qJn6WunyQRfEsf5kkoZlHs5Fs9wgB8uKFjvwWY2kg2HFXT\n" \
"mmkWP6j9JM9fg2VdI9yjrZYcYvNWIIVSu57VKQdwlpZtZww1Tkq8mATxdGwIyhgh\n" \
"fDKQXkYuNs474553LBgOhgObJ4Oi7Aeij7XFXfBvTFLJ3ivL9pVYFxg5lUl86pVq\n" \
"5RXSJhiY+gUQFXKOWoqsqmj//////////wIBAg==\n" \
"-----END DH PARAMETERS-----"
static void
tlsServerDh(SSL_CTX *const context)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM_P(VOID, context);
FUNCTION_TEST_END();
SSL_CTX_set_options(context, SSL_OP_SINGLE_DH_USE);
BIO *const bio = BIO_new_mem_buf(DH_2048, sizeof(DH_2048));
cryptoError(bio == NULL, "unable create buffer for DH parameters");
TRY_BEGIN()
{
DH *const dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
TRY_BEGIN()
{
cryptoError(SSL_CTX_set_tmp_dh(context, dh) != 1, "unable to set temp dh parameters");
}
FINALLY()
{
DH_free(dh);
}
TRY_END();
}
FINALLY()
{
BIO_free(bio);
}
TRY_END();
FUNCTION_TEST_RETURN_VOID();
}
/***********************************************************************************************************************************
Set ECDH parameters for generating ephemeral Elliptic Curve DH keys.
Adapted from PostgreSQL initialize_ecdh() in src/backend/libpq/be-secure-openssl.c.
***********************************************************************************************************************************/
#define ECHD_CURVE "prime256v1"
static void
tlsServerEcdh(SSL_CTX *const context)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM_P(VOID, context);
FUNCTION_TEST_END();
const int nid = OBJ_sn2nid(ECHD_CURVE);
cryptoError(nid == NID_undef, "unrecognized ECDH curve " ECHD_CURVE);
EC_KEY *const ecdh = EC_KEY_new_by_curve_name(nid);
cryptoError(ecdh == NULL, "could not create ecdh key");
SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE);
TRY_BEGIN()
{
cryptoError(SSL_CTX_set_tmp_ecdh(context, ecdh) != 1, "unable to set temp ecdh key");
}
FINALLY()
{
EC_KEY_free(ecdh);
}
TRY_END();
FUNCTION_TEST_RETURN_VOID();
}
/***********************************************************************************************************************************
Authenticate client
Adapted from PostgreSQL be_tls_open_server() in src/backend/libpq/be-secure-openssl.c.
***********************************************************************************************************************************/
static void
tlsServerAuth(const TlsServer *const this, IoSession *const ioSession, SSL *const tlsSession)
{
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(TLS_SERVER, this);
FUNCTION_LOG_PARAM(IO_SESSION, ioSession);
FUNCTION_LOG_PARAM_P(VOID, tlsSession);
FUNCTION_LOG_END();
MEM_CONTEXT_TEMP_BEGIN()
{
// If peer verification requested
if (this->verifyPeer) // {vm_covered}
{
// If the client cert was presented then the session is authenticated. An error will be thrown automatically if the
// client cert is not valid.
X509 *const clientCert = SSL_get_peer_certificate(tlsSession); // {vm_covered}
ioSessionAuthenticatedSet(ioSession, clientCert != NULL); // {vm_covered}
// Set the peer name to the client cert common name
if (clientCert != NULL) // {vm_covered}
ioSessionPeerNameSet(ioSession, tlsCertCommonName(clientCert)); // {vm_covered}
// Free the cert
X509_free(clientCert); // {vm_covered}
}
}
MEM_CONTEXT_TEMP_END();
FUNCTION_LOG_RETURN_VOID();
}
/**********************************************************************************************************************************/
static IoSession *
tlsServerAccept(THIS_VOID, IoSession *const ioSession)
{
THIS(TlsServer);
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(TLS_SERVER, this);
FUNCTION_LOG_PARAM(IO_SESSION, ioSession);
FUNCTION_LOG_END();
ASSERT(this != NULL);
ASSERT(ioSession != NULL);
IoSession *result = NULL;
MEM_CONTEXT_TEMP_BEGIN()
{
// Open TLS session
SSL *tlsSession = SSL_new(this->context);
result = tlsSessionNew(tlsSession, ioSession, this->timeout);
// Authenticate TLS session
tlsServerAuth(this, result, tlsSession);
// Move session
ioSessionMove(result, memContextPrior());
}
MEM_CONTEXT_TEMP_END();
statInc(TLS_STAT_SESSION_STR);
FUNCTION_LOG_RETURN(IO_SESSION, result);
}
/**********************************************************************************************************************************/
static const String *
tlsServerName(THIS_VOID) // {vm_covered}
{
THIS(TlsServer); // {vm_covered}
FUNCTION_TEST_BEGIN(); // {vm_covered}
FUNCTION_TEST_PARAM(TLS_SERVER, this); // {vm_covered}
FUNCTION_TEST_END(); // {vm_covered}
ASSERT(this != NULL); // {vm_covered}
FUNCTION_TEST_RETURN(this->host); // {vm_covered}
}
/***********************************************************************************************************************************
Initialize TLS context with all required security features
Adapted from PostgreSQL be_tls_init() in src/backend/libpq/be-secure-openssl.c.
***********************************************************************************************************************************/
static const IoServerInterface tlsServerInterface =
{
.type = IO_SERVER_TLS_TYPE,
.name = tlsServerName,
.accept = tlsServerAccept,
.toLog = tlsServerToLog,
};
IoServer *
tlsServerNew(
const String *const host, const String *const caFile, const String *const keyFile, const String *const certFile,
const TimeMSec timeout)
{
FUNCTION_LOG_BEGIN(logLevelDebug);
FUNCTION_LOG_PARAM(STRING, host);
FUNCTION_LOG_PARAM(STRING, caFile);
FUNCTION_LOG_PARAM(STRING, keyFile);
FUNCTION_LOG_PARAM(STRING, certFile);
FUNCTION_LOG_PARAM(TIME_MSEC, timeout);
FUNCTION_LOG_END();
ASSERT(host != NULL);
ASSERT(keyFile != NULL);
ASSERT(certFile != NULL);
IoServer *this = NULL;
OBJ_NEW_BEGIN(TlsServer)
{
TlsServer *const driver = OBJ_NEW_ALLOC();
*driver = (TlsServer)
{
.host = strDup(host),
.context = tlsContext(),
.timeout = timeout,
};
// Set callback to free context
memContextCallbackSet(objMemContext(driver), tlsServerFreeResource, driver);
// Set options
SSL_CTX_set_options(driver->context,
// Disable SSL and TLS v1/v1.1
SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 |
// Let server set cipher order
SSL_OP_CIPHER_SERVER_PREFERENCE |
#ifdef SSL_OP_NO_RENEGOTIATION
// Disable renegotiation, available since 1.1.0h. This affects only TLSv1.2 and older protocol versions as TLSv1.3 has
// no support for renegotiation.
SSL_OP_NO_RENEGOTIATION |
#endif
// Disable session tickets
SSL_OP_NO_TICKET);
// Disable session caching
SSL_CTX_set_session_cache_mode(driver->context, SSL_SESS_CACHE_OFF);
// Setup ephemeral DH and ECDH keys
tlsServerDh(driver->context);
tlsServerEcdh(driver->context);
// Load certificate and key
tlsCertKeyLoad(driver->context, certFile, keyFile);
// If a CA store is specified then client certificates will be verified
// -------------------------------------------------------------------------------------------------------------------------
if (caFile != NULL) // {vm_covered}
{
// Load CA store
cryptoError( // {vm_covered}
SSL_CTX_load_verify_locations(driver->context, strZ(caFile), NULL) != 1, // {vm_covered}
strZ(strNewFmt("unable to load CA file '%s'", strZ(caFile)))); // {vm_covered}
// Tell OpenSSL to send the list of root certs we trust to clients in CertificateRequests. This lets a client with a
// keystore select the appropriate client certificate to send to us. Also, this ensures that the SSL context will own
// the rootCertList and free it when no longer needed.
STACK_OF(X509_NAME) *rootCertList = SSL_load_client_CA_file(strZ(caFile)); // {vm_covered}
cryptoError( // {vm_covered}
rootCertList == NULL, strZ(strNewFmt("unable to generate CA list from '%s'", strZ(caFile)))); // {vm_covered}
SSL_CTX_set_client_CA_list(driver->context, rootCertList); // {vm_covered}
// Always ask for SSL client cert, but don't fail when not presented. In this case the server will disconnect after
// sending a data end message to the client. The client can use this to verify that the server is running without the
// need to authenticate.
SSL_CTX_set_verify(driver->context, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, NULL); // {vm_covered}
// Set a flag so the client cert will be checked later
driver->verifyPeer = true; // {vm_covered}
}
statInc(TLS_STAT_SERVER_STR);
this = ioServerNew(driver, &tlsServerInterface);
}
OBJ_NEW_END();
FUNCTION_LOG_RETURN(IO_SERVER, this);
}

View File

@ -0,0 +1,30 @@
/***********************************************************************************************************************************
TLS Server
A simple TLS server intended to expose services by accepting TLS over an IoSession.
***********************************************************************************************************************************/
#ifndef COMMON_IO_TLS_SERVER_H
#define COMMON_IO_TLS_SERVER_H
#include "common/io/server.h"
#include "common/io/tls/client.h"
#include "common/time.h"
/***********************************************************************************************************************************
Io server type
***********************************************************************************************************************************/
#define IO_SERVER_TLS_TYPE IO_CLIENT_TLS_TYPE
/***********************************************************************************************************************************
Statistics constants
***********************************************************************************************************************************/
#define TLS_STAT_SERVER "tls.server" // Servers created
STRING_DECLARE(TLS_STAT_SERVER_STR);
/***********************************************************************************************************************************
Constructors
***********************************************************************************************************************************/
IoServer *tlsServerNew(
const String *host, const String *caFile, const String *keyFile, const String *certFile, TimeMSec timeout);
#endif

View File

@ -23,7 +23,7 @@ typedef struct TlsSession
{
IoSession *ioSession; // Io session
SSL *session; // TLS session on the file descriptor
TimeMSec timeout; // Timeout for any i/o operation (connect, read, etc.)
TimeMSec timeout; // Timeout for any i/o operation (read, write, etc.)
bool shutdownOnClose; // Shutdown the TLS connection when closing the session
IoRead *read; // Read interface

View File

@ -22,6 +22,8 @@ Command constants
#define CFGCMD_REPO_PUT "repo-put"
#define CFGCMD_REPO_RM "repo-rm"
#define CFGCMD_RESTORE "restore"
#define CFGCMD_SERVER_PING "server-ping"
#define CFGCMD_SERVER_START "server-start"
#define CFGCMD_STANZA_CREATE "stanza-create"
#define CFGCMD_STANZA_DELETE "stanza-delete"
#define CFGCMD_STANZA_UPGRADE "stanza-upgrade"
@ -30,7 +32,7 @@ Command constants
#define CFGCMD_VERIFY "verify"
#define CFGCMD_VERSION "version"
#define CFG_COMMAND_TOTAL 20
#define CFG_COMMAND_TOTAL 22
/***********************************************************************************************************************************
Option group constants
@ -115,9 +117,15 @@ Option constants
#define CFGOPT_TCP_KEEP_ALIVE_COUNT "tcp-keep-alive-count"
#define CFGOPT_TCP_KEEP_ALIVE_IDLE "tcp-keep-alive-idle"
#define CFGOPT_TCP_KEEP_ALIVE_INTERVAL "tcp-keep-alive-interval"
#define CFGOPT_TLS_SERVER_ADDRESS "tls-server-address"
#define CFGOPT_TLS_SERVER_AUTH "tls-server-auth"
#define CFGOPT_TLS_SERVER_CA_FILE "tls-server-ca-file"
#define CFGOPT_TLS_SERVER_CERT_FILE "tls-server-cert-file"
#define CFGOPT_TLS_SERVER_KEY_FILE "tls-server-key-file"
#define CFGOPT_TLS_SERVER_PORT "tls-server-port"
#define CFGOPT_TYPE "type"
#define CFG_OPTION_TOTAL 132
#define CFG_OPTION_TOTAL 148
/***********************************************************************************************************************************
Option value constants
@ -188,6 +196,11 @@ Option value constants
#define CFGOPTVAL_OUTPUT_TEXT STRID5("text", 0xa60b40)
#define CFGOPTVAL_OUTPUT_TEXT_Z "text"
#define CFGOPTVAL_PG_HOST_TYPE_SSH STRID5("ssh", 0x22730)
#define CFGOPTVAL_PG_HOST_TYPE_SSH_Z "ssh"
#define CFGOPTVAL_PG_HOST_TYPE_TLS STRID5("tls", 0x4d940)
#define CFGOPTVAL_PG_HOST_TYPE_TLS_Z "tls"
#define CFGOPTVAL_REMOTE_TYPE_PG STRID5("pg", 0xf00)
#define CFGOPTVAL_REMOTE_TYPE_PG_Z "pg"
#define CFGOPTVAL_REMOTE_TYPE_REPO STRID5("repo", 0x7c0b20)
@ -215,6 +228,11 @@ Option value constants
#define CFGOPTVAL_REPO_GCS_KEY_TYPE_TOKEN STRID5("token", 0xe2adf40)
#define CFGOPTVAL_REPO_GCS_KEY_TYPE_TOKEN_Z "token"
#define CFGOPTVAL_REPO_HOST_TYPE_SSH STRID5("ssh", 0x22730)
#define CFGOPTVAL_REPO_HOST_TYPE_SSH_Z "ssh"
#define CFGOPTVAL_REPO_HOST_TYPE_TLS STRID5("tls", 0x4d940)
#define CFGOPTVAL_REPO_HOST_TYPE_TLS_Z "tls"
#define CFGOPTVAL_REPO_RETENTION_ARCHIVE_TYPE_DIFF STRID5("diff", 0x319240)
#define CFGOPTVAL_REPO_RETENTION_ARCHIVE_TYPE_DIFF_Z "diff"
#define CFGOPTVAL_REPO_RETENTION_ARCHIVE_TYPE_FULL STRID5("full", 0x632a60)
@ -305,6 +323,8 @@ typedef enum
cfgCmdRepoPut,
cfgCmdRepoRm,
cfgCmdRestore,
cfgCmdServerPing,
cfgCmdServerStart,
cfgCmdStanzaCreate,
cfgCmdStanzaDelete,
cfgCmdStanzaUpgrade,
@ -380,11 +400,16 @@ typedef enum
cfgOptPg,
cfgOptPgDatabase,
cfgOptPgHost,
cfgOptPgHostCaFile,
cfgOptPgHostCaPath,
cfgOptPgHostCertFile,
cfgOptPgHostCmd,
cfgOptPgHostConfig,
cfgOptPgHostConfigIncludePath,
cfgOptPgHostConfigPath,
cfgOptPgHostKeyFile,
cfgOptPgHostPort,
cfgOptPgHostType,
cfgOptPgHostUser,
cfgOptPgLocal,
cfgOptPgPath,
@ -413,11 +438,16 @@ typedef enum
cfgOptRepoGcsKeyType,
cfgOptRepoHardlink,
cfgOptRepoHost,
cfgOptRepoHostCaFile,
cfgOptRepoHostCaPath,
cfgOptRepoHostCertFile,
cfgOptRepoHostCmd,
cfgOptRepoHostConfig,
cfgOptRepoHostConfigIncludePath,
cfgOptRepoHostConfigPath,
cfgOptRepoHostKeyFile,
cfgOptRepoHostPort,
cfgOptRepoHostType,
cfgOptRepoHostUser,
cfgOptRepoLocal,
cfgOptRepoPath,
@ -460,6 +490,12 @@ typedef enum
cfgOptTcpKeepAliveCount,
cfgOptTcpKeepAliveIdle,
cfgOptTcpKeepAliveInterval,
cfgOptTlsServerAddress,
cfgOptTlsServerAuth,
cfgOptTlsServerCaFile,
cfgOptTlsServerCertFile,
cfgOptTlsServerKeyFile,
cfgOptTlsServerPort,
cfgOptType,
} ConfigOption;

View File

@ -286,6 +286,34 @@ cfgLoadUpdateOption(void)
}
}
// Set pg-host-port/repo-host-port default when pg-host-type/repo-host-type is tls. ??? This should be handled in the parser but
// it requires a default that depends on another option value and that is not currently possible.
#define HOST_PORT_TLS 8432
if (cfgOptionValid(cfgOptRepoHostPort))
{
for (unsigned int repoIdx = 0; repoIdx < cfgOptionGroupIdxTotal(cfgOptGrpRepo); repoIdx++)
{
if (cfgOptionIdxStrId(cfgOptRepoHostType, repoIdx) == CFGOPTVAL_REPO_HOST_TYPE_TLS &&
cfgOptionIdxSource(cfgOptRepoHostPort, repoIdx) == cfgSourceDefault)
{
cfgOptionIdxSet(cfgOptRepoHostPort, repoIdx, cfgSourceDefault, VARINT64(HOST_PORT_TLS));
}
}
}
if (cfgOptionValid(cfgOptPgHostPort))
{
for (unsigned int pgIdx = 0; pgIdx < cfgOptionGroupIdxTotal(cfgOptGrpPg); pgIdx++)
{
if (cfgOptionIdxStrId(cfgOptPgHostType, pgIdx) == CFGOPTVAL_PG_HOST_TYPE_TLS &&
cfgOptionIdxSource(cfgOptPgHostPort, pgIdx) == cfgSourceDefault)
{
cfgOptionIdxSet(cfgOptPgHostPort, pgIdx, cfgSourceDefault, VARINT64(HOST_PORT_TLS));
}
}
}
// Check/update compress-type if compress is valid. There should be no references to the compress option outside this block.
if (cfgOptionValid(cfgOptCompress))
{

File diff suppressed because it is too large Load Diff

View File

@ -25,6 +25,8 @@ Main
#include "command/repo/put.h"
#include "command/repo/rm.h"
#include "command/restore/restore.h"
#include "command/server/ping.h"
#include "command/server/server.h"
#include "command/stanza/create.h"
#include "command/stanza/delete.h"
#include "command/stanza/upgrade.h"
@ -231,6 +233,18 @@ main(int argListSize, const char *argList[])
cmdStorageRemove();
break;
// Server start command
// -----------------------------------------------------------------------------------------------------------------
case cfgCmdServerStart:
cmdServer(UINT64_MAX);
break;
// Server ping command
// -----------------------------------------------------------------------------------------------------------------
case cfgCmdServerPing:
cmdServerPing();
break;
// Restore command
// -----------------------------------------------------------------------------------------------------------------
case cfgCmdRestore:

View File

@ -42,6 +42,7 @@ Constants
#define PROTOCOL_GREETING_VERSION "version"
STRING_DECLARE(PROTOCOL_GREETING_VERSION_STR);
#define PROTOCOL_COMMAND_CONFIG STRID5("config", 0xe9339e30)
#define PROTOCOL_COMMAND_EXIT STRID5("exit", 0xa27050)
#define PROTOCOL_COMMAND_NOOP STRID5("noop", 0x83dee0)
@ -91,6 +92,13 @@ protocolClientMove(ProtocolClient *const this, MemContext *const parentNew)
return objMove(this, parentNew);
}
// Do not send exit command to the server when the client is freed
__attribute__((always_inline)) static inline void
protocolClientNoExit(ProtocolClient *const this)
{
memContextCallbackClear(objMemContext(this));
}
// Send noop to test connection or keep it alive
void protocolClientNoOp(ProtocolClient *this);

View File

@ -8,9 +8,15 @@ Protocol Helper
#include "common/crypto/common.h"
#include "common/debug.h"
#include "common/exec.h"
#include "common/io/client.h"
#include "common/io/socket/client.h"
#include "common/io/socket/server.h"
#include "common/io/tls/client.h"
#include "common/io/tls/server.h"
#include "common/memContext.h"
#include "config/config.intern.h"
#include "config/exec.h"
#include "config/load.h"
#include "config/parse.h"
#include "config/protocol.h"
#include "postgres/version.h"
@ -29,6 +35,8 @@ Local variables
typedef struct ProtocolHelperClient
{
Exec *exec; // Executed client
IoClient *ioClient; // Io client, e.g. TlsClient
IoSession *ioSession; // Io session, e.g. TlsSession
ProtocolClient *client; // Protocol client
} ProtocolHelperClient;
@ -282,6 +290,12 @@ protocolHelperClientFree(ProtocolHelperClient *protocolHelperClient)
}
TRY_END();
// Free the io client/session (there should be no errors)
ioSessionFree(protocolHelperClient->ioSession);
ioClientFree(protocolHelperClient->ioClient);
protocolHelperClient->ioSession = NULL;
protocolHelperClient->ioClient = NULL;
protocolHelperClient->client = NULL;
protocolHelperClient->exec = NULL;
}
@ -306,6 +320,117 @@ protocolLocalFree(unsigned int processId)
FUNCTION_LOG_RETURN_VOID();
}
/**********************************************************************************************************************************/
// Helper to check if client is authorized for a stanza
static bool
protocolServerAuthorize(const String *authListStr, const String *const stanza)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING, authListStr);
FUNCTION_LOG_PARAM(STRING, stanza);
FUNCTION_TEST_END();
ASSERT(authListStr != NULL);
// Empty list is not valid. ??? It would be better if this were done during config parsing.
authListStr = strTrim(strDup(authListStr));
if (strEmpty(authListStr))
THROW(OptionInvalidValueError, "'" CFGOPT_TLS_SERVER_AUTH "' option must have a value");
// If * then all stanzas are authorized
if (strEqZ(authListStr, "*"))
FUNCTION_TEST_RETURN(true);
// Check the list of stanzas for a match with the specified stanza. Each entry will need to be trimmed before comparing.
if (stanza != NULL)
{
StringList *authList = strLstNewSplitZ(authListStr, COMMA_Z);
for (unsigned int authListIdx = 0; authListIdx < strLstSize(authList); authListIdx++)
{
if (strEq(strTrim(strLstGet(authList, authListIdx)), stanza))
FUNCTION_TEST_RETURN(true);
}
}
FUNCTION_TEST_RETURN(false);
}
ProtocolServer *
protocolServer(IoServer *const tlsServer, IoSession *const socketSession)
{
FUNCTION_LOG_BEGIN(logLevelDebug);
FUNCTION_LOG_PARAM(IO_SERVER, tlsServer);
FUNCTION_LOG_PARAM(IO_SESSION, socketSession);
FUNCTION_LOG_END();
ProtocolServer *result = NULL;
MEM_CONTEXT_TEMP_BEGIN()
{
// Start TLS
IoSession *const tlsSession = ioServerAccept(tlsServer, socketSession);
result = protocolServerNew(
PROTOCOL_SERVICE_REMOTE_STR, PROTOCOL_SERVICE_REMOTE_STR, ioSessionIoRead(tlsSession),
ioSessionIoWrite(tlsSession));
// If session is authenticated
if (ioSessionAuthenticated(tlsSession))
{
TRY_BEGIN()
{
// Get list of authorized stanzas for this client
CHECK(cfgOptionTest(cfgOptTlsServerAuth));
const String *const clientAuthList = strDup(
varStr(kvGet(cfgOptionKv(cfgOptTlsServerAuth), VARSTR(ioSessionPeerName(tlsSession)))));
// Error if the client is not authorized for anything
if (clientAuthList == NULL)
THROW(AccessError, "access denied");
// Get parameter list from the client and load it
const ProtocolServerCommandGetResult command = protocolServerCommandGet(result);
CHECK(command.id == PROTOCOL_COMMAND_CONFIG);
StringList *const paramList = pckReadStrLstP(pckReadNew(command.param));
strLstInsert(paramList, 0, cfgExe());
cfgLoad(strLstSize(paramList), strLstPtr(paramList));
// Error if the client is authorized for the requested stanza
if (!protocolServerAuthorize(clientAuthList, cfgOptionStrNull(cfgOptStanza)))
THROW(AccessError, "access denied");
}
CATCH_ANY()
{
protocolServerError(result, errorCode(), STR(errorMessage()), STR(errorStackTrace()));
RETHROW();
}
TRY_END();
// Ack the config command
protocolServerDataEndPut(result);
ioSessionMove(tlsSession, memContextPrior());
protocolServerMove(result, memContextPrior());
}
// Else the client can only detect that the server is alive
else
{
// Send a data end message and return a NULL server. Do not waste time looking at what the client wrote.
protocolServerDataEndPut(result);
// Set result to NULL so there is no server for the caller to use. The TLS session will be freed when the temp mem
// context ends.
result = NULL;
}
}
MEM_CONTEXT_TEMP_END();
FUNCTION_LOG_RETURN(PROTOCOL_SERVER, result);
}
/***********************************************************************************************************************************
Get the command line required for remote protocol execution
***********************************************************************************************************************************/
@ -511,22 +636,84 @@ protocolRemoteExec(
ASSERT(helper != NULL);
// Execute the protocol command
const char *const host =
strZ(cfgOptionIdxStr(protocolStorageType == protocolStorageTypeRepo ? cfgOptRepoHost : cfgOptPgHost, hostIdx));
// Get remote info
const bool isRepo = protocolStorageType == protocolStorageTypeRepo;
const StringId remoteType = cfgOptionIdxStrId(isRepo ? cfgOptRepoHostType : cfgOptPgHostType, hostIdx);
const String *const host = cfgOptionIdxStr(isRepo ? cfgOptRepoHost : cfgOptPgHost, hostIdx);
helper->exec = execNew(
cfgOptionStr(cfgOptCmdSsh), protocolRemoteParamSsh(protocolStorageType, hostIdx),
strNewFmt(PROTOCOL_SERVICE_REMOTE "-%u process on '%s'", processId, host), cfgOptionUInt64(cfgOptProtocolTimeout));
execOpen(helper->exec);
// Handle remote types
IoRead *read;
IoWrite *write;
switch (remoteType)
{
// SSH remote
case CFGOPTVAL_REPO_HOST_TYPE_SSH:
{
// Exec SSH
helper->exec = execNew(
cfgOptionStr(cfgOptCmdSsh), protocolRemoteParamSsh(protocolStorageType, hostIdx),
strNewFmt(PROTOCOL_SERVICE_REMOTE "-%u process on '%s'", processId, strZ(host)),
cfgOptionUInt64(cfgOptProtocolTimeout));
execOpen(helper->exec);
read = execIoRead(helper->exec);
write = execIoWrite(helper->exec);
break;
}
// TLS remote
default:
{
ASSERT(remoteType == CFGOPTVAL_REPO_HOST_TYPE_TLS);
// Negotiate TLS
helper->ioClient = tlsClientNew(
sckClientNew(
host, cfgOptionIdxUInt(isRepo ? cfgOptRepoHostPort : cfgOptPgHostPort, hostIdx),
cfgOptionUInt64(cfgOptIoTimeout), cfgOptionUInt64(cfgOptProtocolTimeout)),
host, cfgOptionUInt64(cfgOptIoTimeout), cfgOptionUInt64(cfgOptProtocolTimeout), true,
cfgOptionIdxStrNull(isRepo ? cfgOptRepoHostCaFile : cfgOptPgHostCaFile, hostIdx),
cfgOptionIdxStrNull(isRepo ? cfgOptRepoHostCaPath : cfgOptPgHostCaPath, hostIdx),
cfgOptionIdxStr(isRepo ? cfgOptRepoHostCertFile : cfgOptPgHostCertFile, hostIdx),
cfgOptionIdxStr(isRepo ? cfgOptRepoHostKeyFile : cfgOptPgHostKeyFile, hostIdx));
helper->ioSession = ioClientOpen(helper->ioClient);
read = ioSessionIoRead(helper->ioSession);
write = ioSessionIoWrite(helper->ioSession);
break;
}
}
// Create protocol object
helper->client = protocolClientNew(
strNewFmt(PROTOCOL_SERVICE_REMOTE "-%u protocol on '%s'", processId, host), PROTOCOL_SERVICE_REMOTE_STR,
execIoRead(helper->exec), execIoWrite(helper->exec));
strNewFmt(PROTOCOL_SERVICE_REMOTE "-%u %s protocol on '%s'", processId, strZ(strIdToStr(remoteType)), strZ(host)),
PROTOCOL_SERVICE_REMOTE_STR, read, write);
// Move client to exec context so they are freed together
protocolClientMove(helper->client, execMemContext(helper->exec));
// Remote initialization
switch (remoteType)
{
// SSH remote
case CFGOPTVAL_REPO_HOST_TYPE_SSH:
// Move client to exec context so they are freed together
protocolClientMove(helper->client, execMemContext(helper->exec));
break;
// TLS remote
default:
{
ASSERT(remoteType == CFGOPTVAL_REPO_HOST_TYPE_TLS);
// Pass parameters to server
ProtocolCommand *command = protocolCommandNew(PROTOCOL_COMMAND_CONFIG);
pckWriteStrLstP(protocolCommandParam(command), protocolRemoteParam(protocolStorageType, hostIdx));
protocolClientExecute(helper->client, command, false);
break;
}
}
FUNCTION_TEST_RETURN_VOID();
}

View File

@ -15,7 +15,8 @@ typedef enum
protocolStorageTypeRepo = STRID5("repo", 0x7c0b20),
} ProtocolStorageType;
#include "protocol/client.h"
#include "common/io/server.h"
#include "protocol/server.h"
/***********************************************************************************************************************************
Constants
@ -59,6 +60,9 @@ ProtocolClient *protocolRemoteGet(ProtocolStorageType protocolStorageType, unsig
// Free (shutdown) a remote
void protocolRemoteFree(unsigned int hostId);
// Initialize a server
ProtocolServer *protocolServer(IoServer *const tlsServer, IoSession *const socketSession);
/***********************************************************************************************************************************
Destructor
***********************************************************************************************************************************/

View File

@ -737,7 +737,10 @@ storageAzureNew(
// Create the http client used to service requests
driver->httpClient = httpClientNew(
tlsClientNew(sckClientNew(driver->host, port, timeout), driver->host, timeout, verifyPeer, caFile, caPath), timeout);
tlsClientNew(
sckClientNew(driver->host, port, timeout, timeout), driver->host, timeout, timeout, verifyPeer, caFile, caPath,
NULL, NULL),
timeout);
// Create list of redacted headers
driver->headerRedactList = strLstNew();

View File

@ -955,7 +955,7 @@ storageGcsNew(
STRDEF("metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token"),
.type = httpProtocolTypeHttp);
driver->authClient = httpClientNew(
sckClientNew(httpUrlHost(driver->authUrl), httpUrlPort(driver->authUrl), timeout), timeout);
sckClientNew(httpUrlHost(driver->authUrl), httpUrlPort(driver->authUrl), timeout, timeout), timeout);
break;
}
@ -970,8 +970,8 @@ storageGcsNew(
driver->authClient = httpClientNew(
tlsClientNew(
sckClientNew(httpUrlHost(driver->authUrl), httpUrlPort(driver->authUrl), timeout),
httpUrlHost(driver->authUrl), timeout, verifyPeer, caFile, caPath),
sckClientNew(httpUrlHost(driver->authUrl), httpUrlPort(driver->authUrl), timeout, timeout),
httpUrlHost(driver->authUrl), timeout, timeout, verifyPeer, caFile, caPath, NULL, NULL),
timeout);
break;
@ -990,7 +990,8 @@ storageGcsNew(
// Create the http client used to service requests
driver->httpClient = httpClientNew(
tlsClientNew(
sckClientNew(driver->endpoint, httpUrlPort(url), timeout), driver->endpoint, timeout, verifyPeer, caFile, caPath),
sckClientNew(driver->endpoint, httpUrlPort(url), timeout, timeout), driver->endpoint, timeout, timeout, verifyPeer,
caFile, caPath, NULL, NULL),
timeout);
// Create list of redacted headers

View File

@ -1004,7 +1004,9 @@ storageS3New(
host = driver->bucketEndpoint;
driver->httpClient = httpClientNew(
tlsClientNew(sckClientNew(host, port, timeout), host, timeout, verifyPeer, caFile, caPath), timeout);
tlsClientNew(
sckClientNew(host, port, timeout, timeout), host, timeout, timeout, verifyPeer, caFile, caPath, NULL, NULL),
timeout);
// Initialize authentication
switch (driver->keyType)
@ -1017,7 +1019,8 @@ storageS3New(
driver->credRole = strDup(credRole);
driver->credHost = S3_CREDENTIAL_HOST_STR;
driver->credExpirationTime = time(NULL);
driver->credHttpClient = httpClientNew(sckClientNew(driver->credHost, S3_CREDENTIAL_PORT, timeout), timeout);
driver->credHttpClient = httpClientNew(
sckClientNew(driver->credHost, S3_CREDENTIAL_PORT, timeout, timeout), timeout);
break;
}

View File

@ -1,2 +1,3 @@
*.csr
*.srl
index.*

View File

@ -14,35 +14,44 @@ openssl req -new -x509 -extensions v3_ca -key pgbackrest-test-ca.key -out pgback
openssl x509 -in pgbackrest-test-ca.crt -text -noout
```
## Generating the Test Key (pgbackrest-test.key)
## Generating the Server Test Key (pgbackrest-test-server.key)
This key will be used for all server certificates to keep things simple.
```
cd [pgbackrest-root]/test/certificate
openssl genrsa -out pgbackrest-test.key 4096
openssl genrsa -out pgbackrest-test-server.key 4096
```
## Generating the Alt Name Test Certificate (pgbackrest-test-alt-name.crt)
This certificate will include alternate names and will only be used in unit tests to check alternate name verification functionality.
```
cd [pgbackrest-root]/test/certificate
openssl req -new -sha256 -nodes -out pgbackrest-test-alt-name.csr -key pgbackrest-test.key -config pgbackrest-test-alt-name.cnf
openssl x509 -req -in pgbackrest-test-alt-name.csr -CA pgbackrest-test-ca.crt -CAkey pgbackrest-test-ca.key -CAcreateserial \
-out pgbackrest-test-alt-name.crt -days 99999 -extensions v3_req -extfile pgbackrest-test-alt-name.cnf
openssl x509 -in pgbackrest-test-alt-name.crt -text -noout
```
## Generating the Test Certificate (pgbackrest-test.crt)
## Generating the Server Test Certificate (pgbackrest-test-server.crt/key)
This certificate will be used in unit and integration tests. It is expected to pass verification but won't be subjected to extensive testing.
```
cd [pgbackrest-root]/test/certificate
openssl req -new -sha256 -nodes -out pgbackrest-test.csr -key pgbackrest-test.key -config pgbackrest-test.cnf
openssl x509 -req -in pgbackrest-test.csr -CA pgbackrest-test-ca.crt -CAkey pgbackrest-test-ca.key -CAcreateserial \
-out pgbackrest-test.crt -days 99999 -extensions v3_req -extfile pgbackrest-test.cnf
openssl x509 -in pgbackrest-test.crt -text -noout
openssl req -new -sha256 -nodes -out pgbackrest-test-server.csr -key pgbackrest-test-server.key -config pgbackrest-test-server.cnf
openssl x509 -req -in pgbackrest-test-server.csr -CA pgbackrest-test-ca.crt -CAkey pgbackrest-test-ca.key -CAcreateserial \
-out pgbackrest-test-server.crt -days 99999 -extensions v3_req -extfile pgbackrest-test-server.cnf
openssl x509 -in pgbackrest-test-server.crt -text -noout
```
## Generating the Client Test Key (pgbackrest-test-client.key)
This key will be used for all client certificates to keep things simple.
```
cd [pgbackrest-root]/test/certificate
openssl genrsa -out pgbackrest-test-client.key 4096
```
## Generating the Client Test Certificate (pgbackrest-test-client.crt/key)
This certificate will be used in unit and integration tests. It is expected to pass verification but won't be subjected to extensive testing.
```
cd [pgbackrest-root]/test/certificate
openssl req -new -sha256 -nodes -out pgbackrest-test-client.csr -key pgbackrest-test-client.key -config pgbackrest-test-client.cnf
openssl x509 -req -in pgbackrest-test-client.csr -CA pgbackrest-test-ca.crt -CAkey pgbackrest-test-ca.key -CAcreateserial \
-out pgbackrest-test-client.crt -days 99999 -extensions v3_req -extfile pgbackrest-test-client.cnf
openssl x509 -in pgbackrest-test-client.crt -text -noout
```

View File

@ -1,34 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIF4jCCA8qgAwIBAgIJAN+gSjwW7yR+MA0GCSqGSIb3DQEBCwUAMFwxCzAJBgNV
BAYTAlVTMQwwCgYDVQQIDANBbGwxDDAKBgNVBAcMA0FsbDETMBEGA1UECgwKcGdC
YWNrUmVzdDEcMBoGA1UEAwwTdGVzdC5wZ2JhY2tyZXN0Lm9yZzAgFw0xODExMjAx
MzU2MjlaGA8yMjkyMDkwMzEzNTYyOVowejELMAkGA1UEBhMCVVMxDDAKBgNVBAgM
A0FsbDEMMAoGA1UEBwwDQWxsMRMwEQYDVQQKDApwZ0JhY2tSZXN0MRwwGgYDVQQL
DBNVbml0IFRlc3RpbmcgRG9tYWluMRwwGgYDVQQDDBN0ZXN0LnBnYmFja3Jlc3Qu
b3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwzNZDX/VhTA6lALX
DZ4AOHv4OQH5wTZipa97XdTrI2TIfMGEffLmv5wzN85pku5HXBuHGJUaUENXt1Ss
GwdfBx/gZZEA8oONqkrxOoJTrABWIAs5k6TTUd+f3Y39rlsyQj076f1sw6Mw9qoC
h+JKXDDqw8kGwQHifXdtCrxL9OfV4eq+gYKrqdlyFM08WfKxe0Js8bB5cZ4Bt/GC
2JhQzQ9bMjYJlxSXIXivP/FFunVT5hZ8gsUVAH+/sm8xlQ4sedW7mIBKkjT3tgL0
FvchB3XyoZ6Sr0JKVaMOcQjIsTzOqdgawgArO541ZwUWHdJH+DODr/gBWXSnnzhH
ED5DAvRMPdO/t353qS/ihpacTqQ91B4UKxK1pVNC84ch3spCLnQncl7kn7RhcdCc
b5g4ZfahRmq79QSoMDvN4+7MtyERLXtSttSWiBzQVVj/jcFNDeGeDjKp6Z55xoso
tMZ3yVajl4IbuQS1pfTLjp7WdJ58y5hQ+8O/ebjUYIxOo5kZhRZV/jxqoR7Ga9MG
bAQ7BPcTuItpfGqiWcdYU+ZdyyFwvpXov6qNoCYt58nj7s+FAbed7EzRHa2Z3RVG
kcqv2iX5EddydHmqKip+QUUR4cPLUXn+kvOHtJEOgAWDURh0DVfhrMD5dX1d+9de
BUwZ89gYvxkkErPL1o8OPRxyiucCAwEAAaOBhjCBgzAJBgNVHRMEAjAAMAsGA1Ud
DwQEAwIF4DBpBgNVHREEYjBgghN0ZXN0LnBnYmFja3Jlc3Qub3JnghUqLnRlc3Qu
cGdiYWNrcmVzdC5vcmeHBH8AAAGCFHRlc3QyLnBnYmFja3Jlc3Qub3JnghYqLnRl
c3QyLnBnYmFja3Jlc3Qub3JnMA0GCSqGSIb3DQEBCwUAA4ICAQABqorGy+U2CBBl
ei2Q0zTccJVPqz3xL6UpZVh0X/SoZFisZNyLPskxAh070q8C11g4uqyPoXPp2eJt
smwop2pY6glzO+7b9kEUo2527s8BJejlIzAe9bQ8eZ1Hbqlh52B+wzEE7Ci22KDC
O3RZCya6WiMHXxFCjPR7m4qaoodU4XtRefepg/bs0qUq5rrsHDRfvaitISUOeXnj
NSsLyiVLbwkCW3mL7bEFAG1Th8GWa809zpw8fJiUkhavtDe35UesHM5fOZ8oqY28
BIcSc5yVgOxDAHDQI9lf9umAN5ZakT9lo5WdndG9eUZGOK72s8Vy1ReiKH+vX2qw
3cI69akD+UBLqvO9NFMJPurDLdM82B453EaBqDhm1oPPwHsXc8wiNG6jYmIAHlvN
qX1qXcdJjp54PSv6RFm/ZxcS6CCRRzPv46Zi86cynY32qmEYU5Et3tGVwx6KywRx
HKF206NUzyHYW8cuDywTpQZSYuAHrjcWGzS/Fg1aTIGULRffFLTvmt6URHjpfFwJ
bRkMcw+k1xdV01rTA2jnDbFAC5f6V7QOGwcqSzc25cz1PDHWweCw4oDwpAxRFx14
X98jbWjo6YYsc9wWpcQt5NICO5wHoaJfhawf7suJmuluQTzOsH2ZS6O0zVJmbsVs
KveXMVBWdJoaJjUqEBy6tJHB5uRxJg==
-----END CERTIFICATE-----

View File

@ -0,0 +1,26 @@
[req]
default_bits=4096
prompt=no
default_md=sha256
req_extensions=v3_req
distinguished_name=dn
[ca]
default_ca=ca_pgbackrest
[ca_pgbackrest]
database = index.txt
unique_subject = no
default_md = sha256
[dn]
C=US
ST=All
L=All
O=pgBackRest
OU=Unit Testing Domain
CN=pgbackrest-client
[v3_req]
basicConstraints=CA:FALSE
keyUsage=nonRepudiation,digitalSignature,keyEncipherment

View File

@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFfjCCA2agAwIBAgIUJCya0E5vFzyH2AgiM3HSAHmpZ1IwDQYJKoZIhvcNAQEL
BQAwXDELMAkGA1UEBhMCVVMxDDAKBgNVBAgMA0FsbDEMMAoGA1UEBwwDQWxsMRMw
EQYDVQQKDApwZ0JhY2tSZXN0MRwwGgYDVQQDDBN0ZXN0LnBnYmFja3Jlc3Qub3Jn
MCAXDTIxMDgyNDIxMDA1M1oYDzIyOTUwNjA4MjEwMDUzWjB4MQswCQYDVQQGEwJV
UzEMMAoGA1UECAwDQWxsMQwwCgYDVQQHDANBbGwxEzARBgNVBAoMCnBnQmFja1Jl
c3QxHDAaBgNVBAsME1VuaXQgVGVzdGluZyBEb21haW4xGjAYBgNVBAMMEXBnYmFj
a3Jlc3QtY2xpZW50MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsrlq
GJfMWq+FrbzKp7dIZ17OzghEQdQOBufpN6b2TUZrMT8ix0hD3CP0od2VfBv/Z+x1
CTWGQ9dripEFH8DJ4E9Evd3RpnNT6lONXqf10n4ohqK+4DGjLJjn1czfoKpNnF7d
WpfB5FY+fjS3MNxTFJUh+KkmkW4ra/GnAQd2X9wVDfz+xGgdOPHrWfGhu3hEWIQw
AULHEKT+oPJghIcmYfrt0hQTDu6MHi+f+CmTl/VYFjmaxkyBPeklsW/EPx1GBS8y
kgzmmhUV+EeBGr4vZ3FDaMcPkCeWljqfxetbLhBBrcAWxaCcPTDRafpzJTSDkCQi
U/7SYw0L0sg9Vz0sYpROupyIGDHxHKzLeaySDykSUR7Wi5A6L9nHxQAbdFeq+PFc
uQyndLs8qCuvjy5kcCyjn36TfcSQX9x4uyvevdVjVZayKzMN8r69uPoFvG+2/9LZ
lhcwDuczZiy7bQGkeb0sujjLweoN6LGrDl6c7C4MpJ4oPWkmAnkJRipFsD9xRjEc
nfo248PF5z5uGqImrxExHikYZwnsG+KsnFg8lfQuoh9McvW3Z1Wb/8CZ4wM2Izaq
4DOkGiyO9pZpFRxqeRgTV0vn15W6CgMoI0I+DvYuNcEHts9ZlPZeGRxyjR62u4hh
gmZDcs7S8Z72k5gG7YUfHkVjvZvyCmyGb66Z0vkCAwEAAaMaMBgwCQYDVR0TBAIw
ADALBgNVHQ8EBAMCBeAwDQYJKoZIhvcNAQELBQADggIBAJH9UKmWwm662dwBLBF0
hsZ2gsQckKTu2nySazQ6JXiJFobStoRjixIjM6hmTbxNtndQqqDbymMhl8L++Gvo
LOocWXZWgWskly/vAjEZCRxq0ZmQcgZcyKz1Xms9WzCkdSCaZGpAXPma6PnHk/xS
cqBIXo0h0zMHfIon9n+kNGJqZ82V2KFZjPrbi0DDISheVGVvmC3cUm8eOh0jjHXS
1d28VXqXsQncegmjeHiehw88XrkbI9Vf8lveTMLN0p7fYmkaMWNRP0BCCw49+Xzy
rlHHD+2bXn0Uq9bMefkiR0ZAZFgzsP4GUUFx3IPSO8iOxfzBlg8qidXqgtKV1Y98
FUI5JQ49HSQ6CK5wBkUUA8oivsy7c7JVfb+8E48CgAzHpHH8PxkIFCpAD2jwGmsq
gMfMkUkS5cvC8M6RUvfmBwLPI/ZbqMtJuO50f/rC9eO5cYBT0S+MTfEwrQ/FpeRZ
w5v2Bpi1qPYHCjNXlzniIRfvMuYwia8p3S0ZB1UIcWTSTAxjy6j+sOVBxPAyKj6t
V0LgWtOgDwDqf9Xv3XiSIDqZU4t5suiEnIWbDXnZCrwBtQI3W8H1uCqtaWG7zERr
+MDNlqFC8Q+gjzGo6mZmzGxejEBYS5mNT/j37bqxGGB2TaWZVc9oPq0nAF7vBZl5
g8C/t+7nWYzBTv0EPl3jMDF4
-----END CERTIFICATE-----

View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
MIIJKgIBAAKCAgEAsrlqGJfMWq+FrbzKp7dIZ17OzghEQdQOBufpN6b2TUZrMT8i
x0hD3CP0od2VfBv/Z+x1CTWGQ9dripEFH8DJ4E9Evd3RpnNT6lONXqf10n4ohqK+
4DGjLJjn1czfoKpNnF7dWpfB5FY+fjS3MNxTFJUh+KkmkW4ra/GnAQd2X9wVDfz+
xGgdOPHrWfGhu3hEWIQwAULHEKT+oPJghIcmYfrt0hQTDu6MHi+f+CmTl/VYFjma
xkyBPeklsW/EPx1GBS8ykgzmmhUV+EeBGr4vZ3FDaMcPkCeWljqfxetbLhBBrcAW
xaCcPTDRafpzJTSDkCQiU/7SYw0L0sg9Vz0sYpROupyIGDHxHKzLeaySDykSUR7W
i5A6L9nHxQAbdFeq+PFcuQyndLs8qCuvjy5kcCyjn36TfcSQX9x4uyvevdVjVZay
KzMN8r69uPoFvG+2/9LZlhcwDuczZiy7bQGkeb0sujjLweoN6LGrDl6c7C4MpJ4o
PWkmAnkJRipFsD9xRjEcnfo248PF5z5uGqImrxExHikYZwnsG+KsnFg8lfQuoh9M
cvW3Z1Wb/8CZ4wM2Izaq4DOkGiyO9pZpFRxqeRgTV0vn15W6CgMoI0I+DvYuNcEH
ts9ZlPZeGRxyjR62u4hhgmZDcs7S8Z72k5gG7YUfHkVjvZvyCmyGb66Z0vkCAwEA
AQKCAgBB0LHXWJ8yjgyGTE28sWwCVRGHIdgw/awksuZZTizJjDMRMZTXGi+TM9J0
gSr5Rz4owRTgTUuiVAQQRRND/DTbW5WO7c60oye11QrsXqHR4A+AdRIhcWYLmEtX
mliHy+9EtFKDd/s44OGmjDch0SDvJXQbI/9F8vIekCDV6i+XYW6HdjFDlqucOspW
lvreFI3motciv7qa0IfMMTWF2Fc0IbnOcQILr6D9xL/+HSFzBjBoDtKGT8oQSHlO
/D+ill8grZ7L1eBdwYITqhFLDdpbF4ShYxzCEzILJI+NzPwI3IezOleoc1rV1dJo
WohonQwN2qChmedT/ovqFT8xjx92mCx2m5/cwD4VtlER7KNVPphGnrGx7i4L9QlM
x8YnG2Bv4534I7G4+tZIBoyZpMsCXU6qvp0BRNi1/g7KfsBCAFokt1SJs3913I1W
RkJ3j774AThCATmttGg3BKZ2ghmOHuKDulbW/AmbqJtqRQve8oXPYAPi8YC/+1BF
nIpOHI32W53Iv/chLw9c5zo47iJdo6tpzcyJPtieiDlK8BHkVdlet8AAMOmg5a0j
x6txJ7WwHSgODGuhnBb/nQuMhf/y9Dgk33tQ1rgtWxTqJqWo0Vj70sbmLixceNVc
b3chpMY4B5SRjrSPBDfVr0UdL0h01dsnxasMqgxX2E4ft+WA2QKCAQEA4eehhPZD
raJofwCZtMN9di9Lf5cAbzSX1yB7A8wotPGP8nqd2S5b1uLZiH7M0nAq5U3n2pla
XS92Upzw173NHE1MUwia9IHfydkp5t1vSFn4etXP0ug3qpMQESwH9jfEF2CqXvzT
ndKftOSb9EOxFJ/uwLfkBIry2ffEDRMkrioEeE9Kan+BVI7K405oCrg7/Kqe/YPm
uYfkllUU4iHwff3egJ61r+8+g3jBBkcN+APE1QOKYdWWB5nuJ6knWazGl6S0gKuU
olrgn63gQ+qV1pi+7N6dIjwt73wLNIdjXUIngOFhBTrCF27lHdmbR5dNaLkRETCT
/YmdHbgxCCVc8wKCAQEAyoi3Hu0dIl7kKaW0Lv6kr9ZCSU2qfGUEUnNV8HuwWQHj
UfDlRsJCnaDTUTA0oQXd37tLLS0pTZpKJJdzixwS+oLg4HLBJO2wxiTJ7eAAwbee
byhLJBnbtlZClK5w5dTJlcB+SVQ1vX3W5DfaNwc8Qw+6BmQ0QJCbiGiALMUiE/YU
Mv+PMvdVzz/PK4F0QiezRqqL4uTv3lIKnFhYHdH1uGe/Qke9Ircwh3tre46V9a0I
9h0WFcLpTMVjMH4wO1JGtQMg5i0hxS0YAA/QRJzsu9rTDWlvVJ8wMeiYpHsocP0h
QkfV0b9VCeLvo/CFHPj4Jj4Qwl+P0gjFLEoTZJHbYwKCAQEAuZF3yewtvhVaN3F7
0dSAXDQPlbip36/ZRxtkUcNEcRa5w1rcDYqaYJOqVAitIfSIGCL94UD1zPRvAv/u
i5pbQfNxTggUmeORvJKKbB3cCxf1GJpIhJzDFXiGy0RbKfO3PejnKu4Ou2dIT+U3
AtkUPrf9/QtR4DVvtoNj5cWi7PqQ/29K1t70k0xU95Hxv1SF2lEiF2hhgGhT6vXr
GLL+hdCkFQ3Ke8JQnNj13r1BiUk3SDAHXcSA4K+exa+bJnvaCrl+mO/ZQeDbAfBn
+AJcoZKJy7Q2aef7Z+2CmtzYT8RHBQSemRgiVfN/fBzEBHQSX4MetPstpiiECuTC
Skm+5wKCAQEAkH2Wvv6EsXenKTspX/mVPkkKQ0egYFylB7l2zzk+51EcfuHwgFmo
stIA0EQm19Xao3n4OQOmGtH/ITnXWaAuvf/PNPFJb/IPEl0+nnEXuRwpcL37VYcJ
I31IxM/qhV0Yb7jDn8bvDAAR5gEesfF8gY9ftJ2aVZGVFG0RPF9rX3hIktWVsNOZ
qWCpiS0y5ttx3/FeGQIhSqeXIFE6lBOtTODfJwcNHQ46Ff3sAVsJCxtmcEbx8ZZ7
YKTVjcwI1tlszkisGOqL1CLGo4yklb6qeZ60wTW+Ek37US/Sbk3a3TkJmEKtfZUV
sxbuxjOak7/fMVFIHZOVhE9zc5RpX29bZwKCAQEAgNInsAnvlePu/tNp9l/UyBpL
WJ92xirUpnooXyUXnw4Et65IKwAr4besgotwg8RsQy9xwsEgasI3eTgouZgVSTUI
rHnMiC+vy8nbwL13GnUQJqPRNZaZTBML/lYpPuBBHJ5rVSL19ciNZNLLi3avSwoz
kbTplnIALmHTYbjdirfRKii2t9qkrDU+SptPUmMfJeemZmwUD0YTzMLkpf+mFwGS
FF9omqBo3GRR/AcFxsMvpMbOXJl7VUzYeSsovtlZrVhhtXetJHnJLIU+2r/hEBRy
eaK4jPloDpoHD7y9BWc/UfloNaoszinl1DqMvUvTZK2yWHx2WyIQyREs11xOwA==
-----END RSA PRIVATE KEY-----

View File

@ -5,6 +5,14 @@ default_md = sha256
req_extensions = v3_req
distinguished_name = dn
[ca]
default_ca=ca_pgbackrest
[ca_pgbackrest]
database=index.txt
unique_subject=no
default_md=sha256
[ dn ]
C=US
ST=All
@ -19,8 +27,16 @@ keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[ alt_names ]
# Used in container unit tests
DNS.1 = test.pgbackrest.org
DNS.2 = *.test.pgbackrest.org
DNS.3 = *.test2.pgbackrest.org
# Used in non-container unit tests
DNS.4 = 127.0.0.1
IP.1 = 127.0.0.1
DNS.3 = test2.pgbackrest.org
DNS.4 = *.test2.pgbackrest.org
# Used in integration tests
DNS.5 = db-primary
DNS.6 = db-standby
DNS.7 = backup

View File

@ -0,0 +1,35 @@
-----BEGIN CERTIFICATE-----
MIIGAjCCA+qgAwIBAgIUW0gPWoZD5DqjIWIP3PliYA0IAOQwDQYJKoZIhvcNAQEL
BQAwXDELMAkGA1UEBhMCVVMxDDAKBgNVBAgMA0FsbDEMMAoGA1UEBwwDQWxsMRMw
EQYDVQQKDApwZ0JhY2tSZXN0MRwwGgYDVQQDDBN0ZXN0LnBnYmFja3Jlc3Qub3Jn
MCAXDTIxMDgyNjEyMjkwM1oYDzIyOTUwNjEwMTIyOTAzWjB6MQswCQYDVQQGEwJV
UzEMMAoGA1UECAwDQWxsMQwwCgYDVQQHDANBbGwxEzARBgNVBAoMCnBnQmFja1Jl
c3QxHDAaBgNVBAsME1VuaXQgVGVzdGluZyBEb21haW4xHDAaBgNVBAMME3Rlc3Qu
cGdiYWNrcmVzdC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDD
M1kNf9WFMDqUAtcNngA4e/g5AfnBNmKlr3td1OsjZMh8wYR98ua/nDM3zmmS7kdc
G4cYlRpQQ1e3VKwbB18HH+BlkQDyg42qSvE6glOsAFYgCzmTpNNR35/djf2uWzJC
PTvp/WzDozD2qgKH4kpcMOrDyQbBAeJ9d20KvEv059Xh6r6Bgqup2XIUzTxZ8rF7
QmzxsHlxngG38YLYmFDND1syNgmXFJcheK8/8UW6dVPmFnyCxRUAf7+ybzGVDix5
1buYgEqSNPe2AvQW9yEHdfKhnpKvQkpVow5xCMixPM6p2BrCACs7njVnBRYd0kf4
M4Ov+AFZdKefOEcQPkMC9Ew907+3fnepL+KGlpxOpD3UHhQrErWlU0LzhyHeykIu
dCdyXuSftGFx0JxvmDhl9qFGarv1BKgwO83j7sy3IREte1K21JaIHNBVWP+NwU0N
4Z4OMqnpnnnGiyi0xnfJVqOXghu5BLWl9MuOntZ0nnzLmFD7w795uNRgjE6jmRmF
FlX+PGqhHsZr0wZsBDsE9xO4i2l8aqJZx1hT5l3LIXC+lei/qo2gJi3nyePuz4UB
t53sTNEdrZndFUaRyq/aJfkR13J0eaoqKn5BRRHhw8tRef6S84e0kQ6ABYNRGHQN
V+GswPl1fV37114FTBnz2Bi/GSQSs8vWjw49HHKK5wIDAQABo4GbMIGYMAkGA1Ud
EwQCMAAwCwYDVR0PBAQDAgXgMH4GA1UdEQR3MHWCE3Rlc3QucGdiYWNrcmVzdC5v
cmeCFSoudGVzdC5wZ2JhY2tyZXN0Lm9yZ4IWKi50ZXN0Mi5wZ2JhY2tyZXN0Lm9y
Z4IJMTI3LjAuMC4xhwR/AAABggpkYi1wcmltYXJ5ggpkYi1zdGFuZGJ5ggZiYWNr
dXAwDQYJKoZIhvcNAQELBQADggIBAMbSq1/hjvQZJ2PFE/VVz9OcA7vlewq632eE
P5BalSJJgLVEsv1AxPx8VT08xfFQHQtEcCg/PFqT3RQ5yb1kHfa6glJkjYIdKQbn
lv9OVc/iutQwKPwk32QQjSgQFb/m0tXv9SlQ+gNTdkK4UKffXPj5rpgwaSiVwuLF
d+3TUpJihS48LLRC27kcL5Ur69/fu0ZD7xZSoCr/n8MUq4f9LwOhBqq+h64wM9cV
V79iPWmEJXoNAJrPYmK+XNhcro071c4m+HR4CCNikjxz/GUUf/NGHWT3pL0Ildku
X3dHmsNRVT/wLqi2v2oa6zr9FfVzjDAdCfnvTLOJ6H6dmofzQUFJBSWfhqGNDR8U
oblwirM2sjaOUjnkBS6Cb26yHSClStI+GZvS0KZfSVd2Qbe4YmtQMTNl/hdZGK3z
ZoqV++idVR+A0NQP8xR4VWqQdq0BR5eQOXDA4wtqvivqlIXpbJvqh1kBHPU9cAF+
g/t3Wa7EomwLazRaV9djLUpon6wGwScKJGzv+vyQSgXN1tQG9tLV4NCFUKDueUUZ
U/j1t64KF9hp5NU2A16zLp6V5GPIJhufXOYa66AFjV8c880eLd5YlkfzgyYwReOx
7vHkiLylbx2tc6aYUqdwjpMwnkxTsn52BBVxDvXToBIRdq/ea/LnZ/yhpnaac/Um
bJOTMee+
-----END CERTIFICATE-----

View File

@ -1,18 +0,0 @@
[req]
default_bits = 4096
prompt = no
default_md = sha256
req_extensions = v3_req
distinguished_name = dn
[ dn ]
C=US
ST=All
L=All
O=pgBackRest
OU=Unit Testing Domain
CN = *.test.pgbackrest.org
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment

View File

@ -1,32 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIFdzCCA1+gAwIBAgIJAN+gSjwW7yR/MA0GCSqGSIb3DQEBCwUAMFwxCzAJBgNV
BAYTAlVTMQwwCgYDVQQIDANBbGwxDDAKBgNVBAcMA0FsbDETMBEGA1UECgwKcGdC
YWNrUmVzdDEcMBoGA1UEAwwTdGVzdC5wZ2JhY2tyZXN0Lm9yZzAgFw0xODExMjAx
MzU2NDJaGA8yMjkyMDkwMzEzNTY0MlowfDELMAkGA1UEBhMCVVMxDDAKBgNVBAgM
A0FsbDEMMAoGA1UEBwwDQWxsMRMwEQYDVQQKDApwZ0JhY2tSZXN0MRwwGgYDVQQL
DBNVbml0IFRlc3RpbmcgRG9tYWluMR4wHAYDVQQDDBUqLnRlc3QucGdiYWNrcmVz
dC5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDDM1kNf9WFMDqU
AtcNngA4e/g5AfnBNmKlr3td1OsjZMh8wYR98ua/nDM3zmmS7kdcG4cYlRpQQ1e3
VKwbB18HH+BlkQDyg42qSvE6glOsAFYgCzmTpNNR35/djf2uWzJCPTvp/WzDozD2
qgKH4kpcMOrDyQbBAeJ9d20KvEv059Xh6r6Bgqup2XIUzTxZ8rF7QmzxsHlxngG3
8YLYmFDND1syNgmXFJcheK8/8UW6dVPmFnyCxRUAf7+ybzGVDix51buYgEqSNPe2
AvQW9yEHdfKhnpKvQkpVow5xCMixPM6p2BrCACs7njVnBRYd0kf4M4Ov+AFZdKef
OEcQPkMC9Ew907+3fnepL+KGlpxOpD3UHhQrErWlU0LzhyHeykIudCdyXuSftGFx
0JxvmDhl9qFGarv1BKgwO83j7sy3IREte1K21JaIHNBVWP+NwU0N4Z4OMqnpnnnG
iyi0xnfJVqOXghu5BLWl9MuOntZ0nnzLmFD7w795uNRgjE6jmRmFFlX+PGqhHsZr
0wZsBDsE9xO4i2l8aqJZx1hT5l3LIXC+lei/qo2gJi3nyePuz4UBt53sTNEdrZnd
FUaRyq/aJfkR13J0eaoqKn5BRRHhw8tRef6S84e0kQ6ABYNRGHQNV+GswPl1fV37
114FTBnz2Bi/GSQSs8vWjw49HHKK5wIDAQABoxowGDAJBgNVHRMEAjAAMAsGA1Ud
DwQEAwIF4DANBgkqhkiG9w0BAQsFAAOCAgEAsjmEXx1dc2bbQHUOZTqfTNZ38/ux
O4V8uXGcKER7bR7TKP6NYwYmgUqcwA50FJkBQ8wULF2C/tLsp1AyAu3wlhn+yVsP
zeZL+p/fOzXDhTyz0Ggx19tyol4o2q+bxyd+KS8rrcSvzC9Jz7Wjuhs9/M5r2gWr
WJydwYbGo4MbAcdPWWJdYRf93vlUqG3Qh3oBnwDntGr0xrBPRBbfcIg+f1c/fnAb
S94rkrmRCtCGqf09IKjVgnlNhXERM2YLoiLcMIwJCLPU8TWoar+paDBgevqIzMvD
k+WYc/NuHpwkDv2e7iVuWarB5r7BAB0olu4qOC8jyGeD8vrJgLah8qyW1slOAH9h
7h0vGQ6+Gjp14ZVWQUwjvnINyX2xUc3gkkQvaons+Ny7xtyu3tOy6v4Ukl/ryT87
IbgkoCj8T+9whp4PqHBDNUmEicJs18q/VRYa4yTE5hh2ecKaveb++YtdZxwJxZR4
Le1+GHj4wQJzQR+Hv42G7C444FIx0G266RIMRoNgbroRPN5mNwdiAg2oejB6qWlX
IOi4+eWbpQk2AM0tvB+eSY/JxHn+5XiVyXiN4hO7ndsAwT7Jj3VNKEjRZyZhTwM9
YO7FOaKbDLnn4IswCgw2iz916fghgBfKLLcjpe/Y9kJvY1g/Fd77zXMHMPs1Zbv3
4MleZ146f1S0En0=
-----END CERTIFICATE-----

View File

@ -305,17 +305,29 @@ unit:
coverage:
- common/io/client
- common/io/server
- common/io/session
- common/io/tls/client
- common/io/tls/common
- common/io/tls/server
- common/io/tls/session
- common/io/socket/client
- common/io/socket/common
- common/io/socket/server
- common/io/socket/session
include:
- common/io/fdRead
- common/io/read
depend:
- storage/posix/read
- storage/posix/storage
- storage/posix/write
- storage/read
- storage/storage
- storage/write
# ----------------------------------------------------------------------------------------------------------------------------
- name: io-http
total: 6
@ -344,14 +356,6 @@ unit:
coverage:
- common/ini
depend:
- storage/posix/read
- storage/posix/storage
- storage/posix/write
- storage/read
- storage/storage
- storage/write
# ----------------------------------------------------------------------------------------------------------------------------
- name: compress
total: 5
@ -448,7 +452,7 @@ unit:
test:
# ----------------------------------------------------------------------------------------------------------------------------
- name: protocol
total: 7
total: 8
harness:
name: protocol
shim:
@ -857,6 +861,14 @@ unit:
coverage:
- command/local/local
# ----------------------------------------------------------------------------------------------------------------------------
- name: server
total: 2
coverage:
- command/server/ping
- command/server/server
# **********************************************************************************************************************************
# Integration tests
#

View File

@ -1,5 +1,5 @@
run 001 - rmt 0, storage posix, enc 0, delta 1
==============================================
run 001 - rmt 0, tls 0, storage posix, enc 0, delta 1
=====================================================
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] version
------------------------------------------------------------------------------------------------------------------------------------

View File

@ -1,5 +1,5 @@
run 002 - rmt 1, storage s3, enc 1, delta 0
===========================================
run 002 - rmt 1, tls 1, storage s3, enc 1, delta 0
==================================================
stanza-create db - create required data for stanza (backup host)
> [CONTAINER-EXEC] backup [BACKREST-BIN] --config=[TEST_PATH]/backup/pgbackrest.conf --stanza=db --no-online stanza-create
@ -77,8 +77,11 @@ log-timestamp=n
process-max=2
protocol-timeout=60
repo1-host=backup
repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt
repo1-host-cmd=[BACKREST-BIN]
repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf
repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key
repo1-host-type=tls
repo1-host-user=[USER-1]
spool-path=[TEST_PATH]/db-primary/spool
@ -86,8 +89,11 @@ spool-path=[TEST_PATH]/db-primary/spool
-------------------------------------------------------
[db]
pg1-host=db-primary
pg1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt
pg1-host-cmd=[BACKREST-BIN]
pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf
pg1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key
pg1-host-type=tls
pg1-host-user=[USER-1]
pg1-path=[TEST_PATH]/db-primary/db/base
@ -272,8 +278,11 @@ log-timestamp=n
process-max=2
protocol-timeout=60
repo1-host=backup
repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt
repo1-host-cmd=[BACKREST-BIN]
repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf
repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key
repo1-host-type=tls
repo1-host-user=[USER-1]
spool-path=[TEST_PATH]/db-primary/spool
@ -281,8 +290,11 @@ spool-path=[TEST_PATH]/db-primary/spool
-------------------------------------------------------
[db]
pg1-host=db-primary
pg1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt
pg1-host-cmd=[BACKREST-BIN]
pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf
pg1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key
pg1-host-type=tls
pg1-host-user=[USER-1]
pg1-path=[TEST_PATH]/db-primary/db/base
@ -506,8 +518,11 @@ log-timestamp=n
process-max=2
protocol-timeout=60
repo1-host=backup
repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt
repo1-host-cmd=[BACKREST-BIN]
repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf
repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key
repo1-host-type=tls
repo1-host-user=[USER-1]
spool-path=[TEST_PATH]/db-primary/spool
@ -515,8 +530,11 @@ spool-path=[TEST_PATH]/db-primary/spool
-------------------------------------------------------
[db]
pg1-host=db-primary
pg1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt
pg1-host-cmd=[BACKREST-BIN]
pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf
pg1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key
pg1-host-type=tls
pg1-host-user=[USER-1]
pg1-path=[TEST_PATH]/db-primary/db/base
@ -713,8 +731,11 @@ log-timestamp=n
process-max=2
protocol-timeout=60
repo1-host=backup
repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt
repo1-host-cmd=[BACKREST-BIN]
repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf
repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key
repo1-host-type=tls
repo1-host-user=[USER-1]
spool-path=[TEST_PATH]/db-primary/spool
@ -722,8 +743,11 @@ spool-path=[TEST_PATH]/db-primary/spool
-------------------------------------------------------
[db]
pg1-host=db-primary
pg1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt
pg1-host-cmd=[BACKREST-BIN]
pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf
pg1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key
pg1-host-type=tls
pg1-host-user=[USER-1]
pg1-path=[TEST_PATH]/db-primary/db/base
@ -928,8 +952,11 @@ log-timestamp=n
process-max=2
protocol-timeout=60
repo1-host=backup
repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt
repo1-host-cmd=[BACKREST-BIN]
repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf
repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key
repo1-host-type=tls
repo1-host-user=[USER-1]
spool-path=[TEST_PATH]/db-primary/spool
@ -937,8 +964,11 @@ spool-path=[TEST_PATH]/db-primary/spool
-------------------------------------------------------
[db]
pg1-host=db-primary
pg1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt
pg1-host-cmd=[BACKREST-BIN]
pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf
pg1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key
pg1-host-type=tls
pg1-host-user=[USER-1]
pg1-path=[TEST_PATH]/db-primary/db/base
@ -1160,8 +1190,11 @@ log-timestamp=n
process-max=2
protocol-timeout=60
repo1-host=backup
repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt
repo1-host-cmd=[BACKREST-BIN]
repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf
repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key
repo1-host-type=tls
repo1-host-user=[USER-1]
spool-path=[TEST_PATH]/db-primary/spool
@ -1169,8 +1202,11 @@ spool-path=[TEST_PATH]/db-primary/spool
-------------------------------------------------------
[db]
pg1-host=db-primary
pg1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt
pg1-host-cmd=[BACKREST-BIN]
pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf
pg1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key
pg1-host-type=tls
pg1-host-user=[USER-1]
pg1-path=[TEST_PATH]/db-primary/db/base-2
@ -1372,8 +1408,11 @@ log-timestamp=n
process-max=2
protocol-timeout=60
repo1-host=backup
repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt
repo1-host-cmd=[BACKREST-BIN]
repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf
repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key
repo1-host-type=tls
repo1-host-user=[USER-1]
spool-path=[TEST_PATH]/db-primary/spool
@ -1381,8 +1420,11 @@ spool-path=[TEST_PATH]/db-primary/spool
-------------------------------------------------------
[db]
pg1-host=db-primary
pg1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt
pg1-host-cmd=[BACKREST-BIN]
pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf
pg1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key
pg1-host-type=tls
pg1-host-user=[USER-1]
pg1-path=[TEST_PATH]/db-primary/db/base-2
@ -1587,8 +1629,11 @@ log-timestamp=n
process-max=2
protocol-timeout=60
repo1-host=backup
repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt
repo1-host-cmd=[BACKREST-BIN]
repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf
repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key
repo1-host-type=tls
repo1-host-user=[USER-1]
spool-path=[TEST_PATH]/db-primary/spool
@ -1596,8 +1641,11 @@ spool-path=[TEST_PATH]/db-primary/spool
-------------------------------------------------------
[db]
pg1-host=db-primary
pg1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt
pg1-host-cmd=[BACKREST-BIN]
pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf
pg1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key
pg1-host-type=tls
pg1-host-user=[USER-1]
pg1-path=[TEST_PATH]/db-primary/db/base-2
@ -1803,8 +1851,11 @@ log-timestamp=n
process-max=2
protocol-timeout=60
repo1-host=backup
repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt
repo1-host-cmd=[BACKREST-BIN]
repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf
repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key
repo1-host-type=tls
repo1-host-user=[USER-1]
spool-path=[TEST_PATH]/db-primary/spool
@ -1812,8 +1863,11 @@ spool-path=[TEST_PATH]/db-primary/spool
-------------------------------------------------------
[db]
pg1-host=db-primary
pg1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt
pg1-host-cmd=[BACKREST-BIN]
pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf
pg1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key
pg1-host-type=tls
pg1-host-user=[USER-1]
pg1-path=[TEST_PATH]/db-primary/db/base-2
@ -2013,8 +2067,11 @@ log-timestamp=n
process-max=2
protocol-timeout=60
repo1-host=backup
repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt
repo1-host-cmd=[BACKREST-BIN]
repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf
repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key
repo1-host-type=tls
repo1-host-user=[USER-1]
spool-path=[TEST_PATH]/db-primary/spool
@ -2022,8 +2079,11 @@ spool-path=[TEST_PATH]/db-primary/spool
-------------------------------------------------------
[db]
pg1-host=db-primary
pg1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt
pg1-host-cmd=[BACKREST-BIN]
pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf
pg1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key
pg1-host-type=tls
pg1-host-user=[USER-1]
pg1-path=[TEST_PATH]/db-primary/db/base-2
@ -2228,8 +2288,11 @@ log-timestamp=n
process-max=2
protocol-timeout=60
repo1-host=backup
repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt
repo1-host-cmd=[BACKREST-BIN]
repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf
repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key
repo1-host-type=tls
repo1-host-user=[USER-1]
spool-path=[TEST_PATH]/db-primary/spool
@ -2237,8 +2300,11 @@ spool-path=[TEST_PATH]/db-primary/spool
-------------------------------------------------------
[db]
pg1-host=db-primary
pg1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt
pg1-host-cmd=[BACKREST-BIN]
pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf
pg1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key
pg1-host-type=tls
pg1-host-user=[USER-1]
pg1-path=[TEST_PATH]/db-primary/db/base-2
@ -2443,7 +2509,7 @@ restore_command = '[BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.con
diff backup - option backup-standby reset - backup performed from primary (backup host)
> [CONTAINER-EXEC] backup [BACKREST-BIN] --config=[TEST_PATH]/backup/pgbackrest.conf --no-online --log-level-console=info --backup-standby --type=diff --stanza=db backup
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: backup command begin [BACKREST-VERSION]: --backup-standby --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=lz4 --config=[TEST_PATH]/backup/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/backup/lock --log-level-console=info --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/backup/log[] --no-log-timestamp --no-online --pg1-host=db-primary --pg1-host-cmd=[BACKREST-BIN] --pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf --pg1-host-user=[USER-1] --pg1-path=[TEST_PATH]/db-primary/db/base-2/base --process-max=2 --protocol-timeout=60 --repo1-cipher-pass=<redacted> --repo1-cipher-type=aes-256-cbc --repo1-path=/ --repo1-s3-bucket=pgbackrest-dev --repo1-s3-endpoint=s3.amazonaws.com --repo1-s3-key=<redacted> --repo1-s3-key-secret=<redacted> --repo1-s3-region=us-east-1 --no-repo1-storage-verify-tls --repo1-type=s3 --stanza=db --start-fast --type=diff
P00 INFO: backup command begin [BACKREST-VERSION]: --backup-standby --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=lz4 --config=[TEST_PATH]/backup/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/backup/lock --log-level-console=info --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/backup/log[] --no-log-timestamp --no-online --pg1-host=db-primary --pg1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --pg1-host-cmd=[BACKREST-BIN] --pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf --pg1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --pg1-host-type=tls --pg1-host-user=[USER-1] --pg1-path=[TEST_PATH]/db-primary/db/base-2/base --process-max=2 --protocol-timeout=60 --repo1-cipher-pass=<redacted> --repo1-cipher-type=aes-256-cbc --repo1-path=/ --repo1-s3-bucket=pgbackrest-dev --repo1-s3-endpoint=s3.amazonaws.com --repo1-s3-key=<redacted> --repo1-s3-key-secret=<redacted> --repo1-s3-region=us-east-1 --no-repo1-storage-verify-tls --repo1-type=s3 --stanza=db --start-fast --type=diff
P00 WARN: option 'repo1-retention-full' is not set for 'repo1-retention-full-type=count', the repository may run out of space
HINT: to retain full backups indefinitely (without warning), set option 'repo1-retention-full' to the maximum.
P00 WARN: option backup-standby is enabled but backup is offline - backups will be performed from the primary
@ -2479,8 +2545,11 @@ log-timestamp=n
process-max=2
protocol-timeout=60
repo1-host=backup
repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt
repo1-host-cmd=[BACKREST-BIN]
repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf
repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key
repo1-host-type=tls
repo1-host-user=[USER-1]
spool-path=[TEST_PATH]/db-primary/spool
@ -2488,8 +2557,11 @@ spool-path=[TEST_PATH]/db-primary/spool
-------------------------------------------------------
[db]
pg1-host=db-primary
pg1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt
pg1-host-cmd=[BACKREST-BIN]
pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf
pg1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key
pg1-host-type=tls
pg1-host-user=[USER-1]
pg1-path=[TEST_PATH]/db-primary/db/base-2/base

View File

@ -1,5 +1,5 @@
run 001 - rmt 0, storage posix, enc 1, cmp lz4
==============================================
run 001 - rmt 0, tls 0, storage posix, enc 1, cmp lz4
=====================================================
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db archive-push [TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000001
------------------------------------------------------------------------------------------------------------------------------------

View File

@ -1,36 +1,38 @@
run 002 - rmt 1, storage s3, enc 0, cmp zst
===========================================
run 002 - rmt 1, tls 1, storage s3, enc 0, cmp zst
==================================================
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db archive-push [TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000001
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: archive-push command begin [BACKREST-VERSION]: [[TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000001] --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=none --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-user=[USER-1] --stanza=db
P00 INFO: archive-push command begin [BACKREST-VERSION]: [[TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000001] --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=none --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --repo1-host-type=tls --repo1-host-user=[USER-1] --stanza=db
P00 ERROR: [103]: unable to find a valid repository:
repo1: [FileMissingError] unable to load info file '/archive/db/archive.info' or '/archive/db/archive.info.copy':
FileMissingError: raised from remote-0 protocol on 'backup': unable to open missing file '/archive/db/archive.info' for read
FileMissingError: raised from remote-0 protocol on 'backup': unable to open missing file '/archive/db/archive.info.copy' for read
FileMissingError: raised from remote-0 tls protocol on 'backup': unable to open missing file '/archive/db/archive.info' for read
FileMissingError: raised from remote-0 tls protocol on 'backup': unable to open missing file '/archive/db/archive.info.copy' for read
HINT: archive.info cannot be opened but is required to push/get WAL segments.
HINT: is archive_command configured correctly in postgresql.conf?
HINT: has a stanza-create been performed?
HINT: use --no-archive-check to disable archive checks during backup if you have an alternate archiving scheme.
P00 DETAIL: statistics: STATISTICS
P00 INFO: archive-push command end: aborted with exception [103]
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db archive-get 000000010000000100000001 [TEST_PATH]/db-primary/db/base/pg_xlog/RECOVERYXLOG
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: archive-get command begin [BACKREST-VERSION]: [000000010000000100000001, [TEST_PATH]/db-primary/db/base/pg_xlog/RECOVERYXLOG] --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-user=[USER-1] --stanza=db
P00 INFO: archive-get command begin [BACKREST-VERSION]: [000000010000000100000001, [TEST_PATH]/db-primary/db/base/pg_xlog/RECOVERYXLOG] --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --repo1-host-type=tls --repo1-host-user=[USER-1] --stanza=db
P00 WARN: repo1: [FileMissingError] unable to load info file '/archive/db/archive.info' or '/archive/db/archive.info.copy':
FileMissingError: raised from remote-0 protocol on 'backup': unable to open missing file '/archive/db/archive.info' for read
FileMissingError: raised from remote-0 protocol on 'backup': unable to open missing file '/archive/db/archive.info.copy' for read
FileMissingError: raised from remote-0 tls protocol on 'backup': unable to open missing file '/archive/db/archive.info' for read
FileMissingError: raised from remote-0 tls protocol on 'backup': unable to open missing file '/archive/db/archive.info.copy' for read
HINT: archive.info cannot be opened but is required to push/get WAL segments.
HINT: is archive_command configured correctly in postgresql.conf?
HINT: has a stanza-create been performed?
HINT: use --no-archive-check to disable archive checks during backup if you have an alternate archiving scheme.
P00 ERROR: [103]: unable to find a valid repository
P00 DETAIL: statistics: STATISTICS
P00 INFO: archive-get command end: aborted with exception [103]
stanza-create db - stanza create (backup host)
> [CONTAINER-EXEC] backup [BACKREST-BIN] --config=[TEST_PATH]/backup/pgbackrest.conf --stanza=db --no-online stanza-create
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: stanza-create command begin [BACKREST-VERSION]: --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/backup/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --lock-path=[TEST_PATH]/backup/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/backup/log[] --no-log-timestamp --no-online --pg1-host=db-primary --pg1-host-cmd=[BACKREST-BIN] --pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf --pg1-host-user=[USER-1] --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-path=/ --repo1-s3-bucket=pgbackrest-dev --repo1-s3-endpoint=s3.amazonaws.com --repo1-s3-key=<redacted> --repo1-s3-key-secret=<redacted> --repo1-s3-region=us-east-1 --no-repo1-storage-verify-tls --repo1-type=s3 --stanza=db
P00 INFO: stanza-create command begin [BACKREST-VERSION]: --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/backup/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --lock-path=[TEST_PATH]/backup/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/backup/log[] --no-log-timestamp --no-online --pg1-host=db-primary --pg1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --pg1-host-cmd=[BACKREST-BIN] --pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf --pg1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --pg1-host-type=tls --pg1-host-user=[USER-1] --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-path=/ --repo1-s3-bucket=pgbackrest-dev --repo1-s3-endpoint=s3.amazonaws.com --repo1-s3-key=<redacted> --repo1-s3-key-secret=<redacted> --repo1-s3-region=us-east-1 --no-repo1-storage-verify-tls --repo1-type=s3 --stanza=db
P00 INFO: stanza-create for stanza 'db' on repo1
P00 DETAIL: statistics: STATISTICS
P00 INFO: stanza-create command end: completed successfully
@ -73,54 +75,60 @@ backrest-checksum="[CHECKSUM]"
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db archive-push --cmd-ssh=/usr/bin/ssh --compress-type=zst [TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000001
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: archive-push command begin [BACKREST-VERSION]: [[TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000001] --buffer-size=[BUFFER-SIZE] --cmd-ssh=/usr/bin/ssh --compress-level=3 --compress-level-network=1 --compress-type=zst --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-user=[USER-1] --stanza=db
P00 INFO: archive-push command begin [BACKREST-VERSION]: [[TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000001] --buffer-size=[BUFFER-SIZE] --cmd-ssh=/usr/bin/ssh --compress-level=3 --compress-level-network=1 --compress-type=zst --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --repo1-host-type=tls --repo1-host-user=[USER-1] --stanza=db
P00 INFO: pushed WAL file '000000010000000100000001' to the archive
P00 DETAIL: statistics: STATISTICS
P00 INFO: archive-push command end: completed successfully
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db archive-get 700000007000000070000000 [TEST_PATH]/db-primary/db/base/pg_xlog/RECOVERYXLOG
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: archive-get command begin [BACKREST-VERSION]: [700000007000000070000000, [TEST_PATH]/db-primary/db/base/pg_xlog/RECOVERYXLOG] --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-user=[USER-1] --stanza=db
P00 INFO: archive-get command begin [BACKREST-VERSION]: [700000007000000070000000, [TEST_PATH]/db-primary/db/base/pg_xlog/RECOVERYXLOG] --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --repo1-host-type=tls --repo1-host-user=[USER-1] --stanza=db
P00 INFO: unable to find 700000007000000070000000 in the archive
P00 DETAIL: statistics: STATISTICS
P00 INFO: archive-get command end: completed successfully
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db archive-get 000000010000000100000001 [TEST_PATH]/db-primary/db/base/pg_xlog/RECOVERYXLOG
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: archive-get command begin [BACKREST-VERSION]: [000000010000000100000001, [TEST_PATH]/db-primary/db/base/pg_xlog/RECOVERYXLOG] --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-user=[USER-1] --stanza=db
P00 INFO: archive-get command begin [BACKREST-VERSION]: [000000010000000100000001, [TEST_PATH]/db-primary/db/base/pg_xlog/RECOVERYXLOG] --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --repo1-host-type=tls --repo1-host-user=[USER-1] --stanza=db
P00 INFO: found 000000010000000100000001 in the repo1: 9.4-1 archive
P00 DETAIL: statistics: STATISTICS
P00 INFO: archive-get command end: completed successfully
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db archive-push --compress-type=zst --archive-async --process-max=2 [TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: archive-push command begin [BACKREST-VERSION]: [[TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002] --archive-async --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=zst --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --process-max=2 --protocol-timeout=60 --repo1-host=backup --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-user=[USER-1] --spool-path=[TEST_PATH]/db-primary/spool --stanza=db
P00 INFO: archive-push command begin [BACKREST-VERSION]: [[TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002] --archive-async --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=zst --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --process-max=2 --protocol-timeout=60 --repo1-host=backup --repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --repo1-host-type=tls --repo1-host-user=[USER-1] --spool-path=[TEST_PATH]/db-primary/spool --stanza=db
P00 INFO: pushed WAL file '000000010000000100000002' to the archive asynchronously
P00 INFO: archive-push command end: completed successfully
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db archive-push --archive-async [TEST_PATH]/db-primary/db/base/pg_xlog/00000002.history
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: archive-push command begin [BACKREST-VERSION]: [[TEST_PATH]/db-primary/db/base/pg_xlog/00000002.history] --archive-async --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=none --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-user=[USER-1] --spool-path=[TEST_PATH]/db-primary/spool --stanza=db
P00 INFO: archive-push command begin [BACKREST-VERSION]: [[TEST_PATH]/db-primary/db/base/pg_xlog/00000002.history] --archive-async --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=none --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --repo1-host-type=tls --repo1-host-user=[USER-1] --spool-path=[TEST_PATH]/db-primary/spool --stanza=db
P00 INFO: pushed WAL file '00000002.history' to the archive asynchronously
P00 INFO: archive-push command end: completed successfully
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db archive-get 000000010000000100000001 [TEST_PATH]/db-primary/db/base/pg_xlog/RECOVERYXLOG
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: archive-get command begin [BACKREST-VERSION]: [000000010000000100000001, [TEST_PATH]/db-primary/db/base/pg_xlog/RECOVERYXLOG] --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-user=[USER-1] --stanza=db
P00 INFO: archive-get command begin [BACKREST-VERSION]: [000000010000000100000001, [TEST_PATH]/db-primary/db/base/pg_xlog/RECOVERYXLOG] --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --repo1-host-type=tls --repo1-host-user=[USER-1] --stanza=db
P00 WARN: repo1: [ArchiveMismatchError] unable to retrieve the archive id for database version '9.4' and system-id '1000000000000000094'
P00 ERROR: [103]: unable to find a valid repository
P00 DETAIL: statistics: STATISTICS
P00 INFO: archive-get command end: aborted with exception [103]
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db archive-push [TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: archive-push command begin [BACKREST-VERSION]: [[TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002] --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=none --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-user=[USER-1] --stanza=db
P00 INFO: archive-push command begin [BACKREST-VERSION]: [[TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002] --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=none --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --repo1-host-type=tls --repo1-host-user=[USER-1] --stanza=db
P00 ERROR: [103]: unable to find a valid repository:
repo1: [ArchiveMismatchError] PostgreSQL version 9.4, system-id 1000000000000000094 do not match repo1 stanza version 9.4, system-id 5000900090001855000
HINT: are you archiving to the correct stanza?
P00 DETAIL: statistics: STATISTICS
P00 INFO: archive-push command end: aborted with exception [103]
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db archive-get 000000010000000100000001 [TEST_PATH]/db-primary/db/base/pg_xlog/RECOVERYXLOG
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: archive-get command begin [BACKREST-VERSION]: [000000010000000100000001, [TEST_PATH]/db-primary/db/base/pg_xlog/RECOVERYXLOG] --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-user=[USER-1] --stanza=db
P00 INFO: archive-get command begin [BACKREST-VERSION]: [000000010000000100000001, [TEST_PATH]/db-primary/db/base/pg_xlog/RECOVERYXLOG] --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --repo1-host-type=tls --repo1-host-user=[USER-1] --stanza=db
P00 WARN: repo1: [ArchiveMismatchError] unable to retrieve the archive id for database version '9.4' and system-id '1000000000000000094'
P00 ERROR: [103]: unable to find a valid repository
P00 DETAIL: statistics: STATISTICS
P00 INFO: archive-get command end: aborted with exception [103]
stop db stanza (db-primary host)
@ -131,14 +139,15 @@ P00 INFO: stop command end: completed successfully
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db archive-push [TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: archive-push command begin [BACKREST-VERSION]: [[TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002] --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=none --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-user=[USER-1] --stanza=db
P00 INFO: archive-push command begin [BACKREST-VERSION]: [[TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002] --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=none --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --repo1-host-type=tls --repo1-host-user=[USER-1] --stanza=db
P00 ERROR: [062]: stop file exists for stanza db
P00 INFO: archive-push command end: aborted with exception [062]
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db archive-get 000000010000000100000001 [TEST_PATH]/db-primary/db/base/pg_xlog/RECOVERYXLOG
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: archive-get command begin [BACKREST-VERSION]: [000000010000000100000001, [TEST_PATH]/db-primary/db/base/pg_xlog/RECOVERYXLOG] --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-user=[USER-1] --stanza=db
P00 INFO: archive-get command begin [BACKREST-VERSION]: [000000010000000100000001, [TEST_PATH]/db-primary/db/base/pg_xlog/RECOVERYXLOG] --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --repo1-host-type=tls --repo1-host-user=[USER-1] --stanza=db
P00 ERROR: [062]: stop file exists for stanza db
P00 DETAIL: statistics: STATISTICS
P00 INFO: archive-get command end: aborted with exception [062]
start db stanza (db-primary host)
@ -149,52 +158,59 @@ P00 INFO: start command end: completed successfully
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db archive-push [TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: archive-push command begin [BACKREST-VERSION]: [[TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002] --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=none --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-user=[USER-1] --stanza=db
P00 INFO: archive-push command begin [BACKREST-VERSION]: [[TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002] --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=none --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --repo1-host-type=tls --repo1-host-user=[USER-1] --stanza=db
P00 WARN: WAL file '000000010000000100000002' already exists in the repo1 archive with the same checksum
HINT: this is valid in some recovery scenarios but may also indicate a problem.
P00 INFO: pushed WAL file '000000010000000100000002' to the archive
P00 DETAIL: statistics: STATISTICS
P00 INFO: archive-push command end: completed successfully
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db archive-push [TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: archive-push command begin [BACKREST-VERSION]: [[TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002] --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=none --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-user=[USER-1] --stanza=db
P00 INFO: archive-push command begin [BACKREST-VERSION]: [[TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002] --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=none --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --repo1-host-type=tls --repo1-host-user=[USER-1] --stanza=db
P00 ERROR: [045]: WAL file '000000010000000100000002' already exists in the repo1 archive with a different checksum
P00 DETAIL: statistics: STATISTICS
P00 INFO: archive-push command end: aborted with exception [045]
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db archive-get --cmd-ssh=/usr/bin/ssh --archive-async 000000010000000100000002 [TEST_PATH]/db-primary/db/base/pg_xlog/RECOVERYXLOG
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: archive-get command begin [BACKREST-VERSION]: [000000010000000100000002, [TEST_PATH]/db-primary/db/base/pg_xlog/RECOVERYXLOG] --archive-async --buffer-size=[BUFFER-SIZE] --cmd-ssh=/usr/bin/ssh --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-user=[USER-1] --spool-path=[TEST_PATH]/db-primary/spool --stanza=db
P00 INFO: archive-get command begin [BACKREST-VERSION]: [000000010000000100000002, [TEST_PATH]/db-primary/db/base/pg_xlog/RECOVERYXLOG] --archive-async --buffer-size=[BUFFER-SIZE] --cmd-ssh=/usr/bin/ssh --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --repo1-host-type=tls --repo1-host-user=[USER-1] --spool-path=[TEST_PATH]/db-primary/spool --stanza=db
P00 INFO: found 000000010000000100000002 in the archive asynchronously
P00 INFO: archive-get command end: completed successfully
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db archive-get --archive-async 00000001.history [TEST_PATH]/db-primary/db/base/pg_xlog/00000001.history
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: archive-get command begin [BACKREST-VERSION]: [00000001.history, [TEST_PATH]/db-primary/db/base/pg_xlog/00000001.history] --archive-async --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-user=[USER-1] --spool-path=[TEST_PATH]/db-primary/spool --stanza=db
P00 INFO: archive-get command begin [BACKREST-VERSION]: [00000001.history, [TEST_PATH]/db-primary/db/base/pg_xlog/00000001.history] --archive-async --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --repo1-host-type=tls --repo1-host-user=[USER-1] --spool-path=[TEST_PATH]/db-primary/spool --stanza=db
P00 INFO: unable to find 00000001.history in the archive
P00 DETAIL: statistics: STATISTICS
P00 INFO: archive-get command end: completed successfully
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db archive-get --archive-async 00000002.history [TEST_PATH]/db-primary/db/base/pg_xlog/00000002.history
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: archive-get command begin [BACKREST-VERSION]: [00000002.history, [TEST_PATH]/db-primary/db/base/pg_xlog/00000002.history] --archive-async --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-user=[USER-1] --spool-path=[TEST_PATH]/db-primary/spool --stanza=db
P00 INFO: archive-get command begin [BACKREST-VERSION]: [00000002.history, [TEST_PATH]/db-primary/db/base/pg_xlog/00000002.history] --archive-async --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --repo1-host-type=tls --repo1-host-user=[USER-1] --spool-path=[TEST_PATH]/db-primary/spool --stanza=db
P00 INFO: found 00000002.history in the repo1: 9.4-1 archive
P00 DETAIL: statistics: STATISTICS
P00 INFO: archive-get command end: completed successfully
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db archive-push [TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002.partial
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: archive-push command begin [BACKREST-VERSION]: [[TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002.partial] --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=none --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-user=[USER-1] --stanza=db
P00 INFO: archive-push command begin [BACKREST-VERSION]: [[TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002.partial] --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=none --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --repo1-host-type=tls --repo1-host-user=[USER-1] --stanza=db
P00 INFO: pushed WAL file '000000010000000100000002.partial' to the archive
P00 DETAIL: statistics: STATISTICS
P00 INFO: archive-push command end: completed successfully
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db archive-push [TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002.partial
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: archive-push command begin [BACKREST-VERSION]: [[TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002.partial] --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=none --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-user=[USER-1] --stanza=db
P00 INFO: archive-push command begin [BACKREST-VERSION]: [[TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002.partial] --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=none --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --repo1-host-type=tls --repo1-host-user=[USER-1] --stanza=db
P00 WARN: WAL file '000000010000000100000002.partial' already exists in the repo1 archive with the same checksum
HINT: this is valid in some recovery scenarios but may also indicate a problem.
P00 INFO: pushed WAL file '000000010000000100000002.partial' to the archive
P00 DETAIL: statistics: STATISTICS
P00 INFO: archive-push command end: completed successfully
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db archive-push [TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002.partial
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: archive-push command begin [BACKREST-VERSION]: [[TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002.partial] --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=none --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-user=[USER-1] --stanza=db
P00 INFO: archive-push command begin [BACKREST-VERSION]: [[TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002.partial] --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=none --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --repo1-host-type=tls --repo1-host-user=[USER-1] --stanza=db
P00 ERROR: [045]: WAL file '000000010000000100000002.partial' already exists in the repo1 archive with a different checksum
P00 DETAIL: statistics: STATISTICS
P00 INFO: archive-push command end: aborted with exception [045]

View File

@ -1,5 +1,5 @@
run 001 - rmt 0, cmp bz2, error version, storage gcs, enc 1
===========================================================
run 001 - rmt 0, tls 0, cmp bz2, error version, storage gcs, enc 1
==================================================================
stanza-create db - create required data for stanza (db-primary host)
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db --no-online stanza-create

View File

@ -1,5 +1,5 @@
run 002 - rmt 1, cmp lz4, error connect, storage azure, enc 0
=============================================================
run 002 - rmt 1, tls 0, cmp lz4, error connect, storage azure, enc 0
====================================================================
stanza-create db - create required data for stanza (backup host)
> [CONTAINER-EXEC] backup [BACKREST-BIN] --config=[TEST_PATH]/backup/pgbackrest.conf --stanza=db --no-online stanza-create

View File

@ -1,5 +1,5 @@
run 001 - remote 0, storage gcs, enc 0, cmp zst
===============================================
run 001 - remote 0, tls 0, storage gcs, enc 0, cmp zst
======================================================
stanza-create db - fail on missing control file (db-primary host)
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db --no-online --log-level-file=[LOG-LEVEL-FILE] stanza-create

View File

@ -1,17 +1,18 @@
run 002 - remote 1, storage azure, enc 1, cmp lz4
=================================================
run 002 - remote 1, tls 1, storage azure, enc 1, cmp lz4
========================================================
stanza-create db - fail on missing control file (backup host)
> [CONTAINER-EXEC] backup [BACKREST-BIN] --config=[TEST_PATH]/backup/pgbackrest.conf --stanza=db --no-online --log-level-file=[LOG-LEVEL-FILE] stanza-create
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: stanza-create command begin [BACKREST-VERSION]: --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/backup/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --lock-path=[TEST_PATH]/backup/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/backup/log[] --no-log-timestamp --no-online --pg1-host=db-primary --pg1-host-cmd=[BACKREST-BIN] --pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf --pg1-host-user=[USER-1] --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-azure-account=<redacted> --repo1-azure-container=azcontainer --repo1-azure-key=<redacted> --repo1-cipher-pass=<redacted> --repo1-cipher-type=aes-256-cbc --repo1-path=/ --repo1-storage-host=azure --no-repo1-storage-verify-tls --repo1-type=azure --stanza=db
P00 ERROR: [055]: raised from remote-0 protocol on 'db-primary': unable to open missing file '[TEST_PATH]/db-primary/db/base/global/pg_control' for read
P00 INFO: stanza-create command begin [BACKREST-VERSION]: --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/backup/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --lock-path=[TEST_PATH]/backup/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/backup/log[] --no-log-timestamp --no-online --pg1-host=db-primary --pg1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --pg1-host-cmd=[BACKREST-BIN] --pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf --pg1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --pg1-host-type=tls --pg1-host-user=[USER-1] --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-azure-account=<redacted> --repo1-azure-container=azcontainer --repo1-azure-key=<redacted> --repo1-cipher-pass=<redacted> --repo1-cipher-type=aes-256-cbc --repo1-path=/ --repo1-storage-host=azure --no-repo1-storage-verify-tls --repo1-type=azure --stanza=db
P00 ERROR: [055]: raised from remote-0 tls protocol on 'db-primary': unable to open missing file '[TEST_PATH]/db-primary/db/base/global/pg_control' for read
P00 DETAIL: statistics: STATISTICS
P00 INFO: stanza-create command end: aborted with exception [055]
stanza-upgrade db - fail on stanza not initialized since archive.info is missing (backup host)
> [CONTAINER-EXEC] backup [BACKREST-BIN] --config=[TEST_PATH]/backup/pgbackrest.conf --stanza=db --no-online stanza-upgrade
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: stanza-upgrade command begin [BACKREST-VERSION]: --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/backup/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --lock-path=[TEST_PATH]/backup/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/backup/log[] --no-log-timestamp --no-online --pg1-host=db-primary --pg1-host-cmd=[BACKREST-BIN] --pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf --pg1-host-user=[USER-1] --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-azure-account=<redacted> --repo1-azure-container=azcontainer --repo1-azure-key=<redacted> --repo1-cipher-pass=<redacted> --repo1-cipher-type=aes-256-cbc --repo1-path=/ --repo1-storage-host=azure --no-repo1-storage-verify-tls --repo1-type=azure --stanza=db
P00 INFO: stanza-upgrade command begin [BACKREST-VERSION]: --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/backup/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --lock-path=[TEST_PATH]/backup/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/backup/log[] --no-log-timestamp --no-online --pg1-host=db-primary --pg1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --pg1-host-cmd=[BACKREST-BIN] --pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf --pg1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --pg1-host-type=tls --pg1-host-user=[USER-1] --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-azure-account=<redacted> --repo1-azure-container=azcontainer --repo1-azure-key=<redacted> --repo1-cipher-pass=<redacted> --repo1-cipher-type=aes-256-cbc --repo1-path=/ --repo1-storage-host=azure --no-repo1-storage-verify-tls --repo1-type=azure --stanza=db
P00 INFO: stanza-upgrade for stanza 'db' on repo1
P00 ERROR: [055]: unable to load info file '/archive/db/archive.info' or '/archive/db/archive.info.copy':
FileMissingError: unable to open missing file '/archive/db/archive.info' for read
@ -26,8 +27,9 @@ P00 INFO: stanza-upgrade command end: aborted with exception [055]
stanza-create db - successfully create the stanza (db-primary host)
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db --no-online stanza-create
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: stanza-create command begin [BACKREST-VERSION]: --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --no-online --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-user=[USER-1] --stanza=db
P00 INFO: stanza-create command begin [BACKREST-VERSION]: --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --no-online --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --repo1-host-type=tls --repo1-host-user=[USER-1] --stanza=db
P00 INFO: stanza-create for stanza 'db' on repo1
P00 DETAIL: statistics: STATISTICS
P00 INFO: stanza-create command end: completed successfully
+ supplemental file: /backup/db/backup.info
@ -75,9 +77,10 @@ backrest-checksum="[CHECKSUM]"
stanza-create db - do not fail on rerun of stanza-create - info files exist and DB section ok (db-primary host)
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db --no-online stanza-create
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: stanza-create command begin [BACKREST-VERSION]: --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --no-online --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-user=[USER-1] --stanza=db
P00 INFO: stanza-create command begin [BACKREST-VERSION]: --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --no-online --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --repo1-host-type=tls --repo1-host-user=[USER-1] --stanza=db
P00 INFO: stanza-create for stanza 'db' on repo1
P00 INFO: stanza 'db' already exists on repo1 and is valid
P00 DETAIL: statistics: STATISTICS
P00 INFO: stanza-create command end: completed successfully
+ supplemental file: /backup/db/backup.info
@ -125,7 +128,7 @@ backrest-checksum="[CHECKSUM]"
stanza-create db - fail on database mismatch and warn force option deprecated (backup host)
> [CONTAINER-EXEC] backup [BACKREST-BIN] --config=[TEST_PATH]/backup/pgbackrest.conf --stanza=db --no-online --force stanza-create
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: stanza-create command begin [BACKREST-VERSION]: --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/backup/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --force --lock-path=[TEST_PATH]/backup/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/backup/log[] --no-log-timestamp --no-online --pg1-host=db-primary --pg1-host-cmd=[BACKREST-BIN] --pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf --pg1-host-user=[USER-1] --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-azure-account=<redacted> --repo1-azure-container=azcontainer --repo1-azure-key=<redacted> --repo1-cipher-pass=<redacted> --repo1-cipher-type=aes-256-cbc --repo1-path=/ --repo1-storage-host=azure --no-repo1-storage-verify-tls --repo1-type=azure --stanza=db
P00 INFO: stanza-create command begin [BACKREST-VERSION]: --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/backup/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --force --lock-path=[TEST_PATH]/backup/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/backup/log[] --no-log-timestamp --no-online --pg1-host=db-primary --pg1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --pg1-host-cmd=[BACKREST-BIN] --pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf --pg1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --pg1-host-type=tls --pg1-host-user=[USER-1] --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-azure-account=<redacted> --repo1-azure-container=azcontainer --repo1-azure-key=<redacted> --repo1-cipher-pass=<redacted> --repo1-cipher-type=aes-256-cbc --repo1-path=/ --repo1-storage-host=azure --no-repo1-storage-verify-tls --repo1-type=azure --stanza=db
P00 WARN: option --force is no longer supported
P00 INFO: stanza-create for stanza 'db' on repo1
P00 ERROR: [028]: backup and archive info files exist but do not match the database
@ -179,7 +182,7 @@ backrest-checksum="[CHECKSUM]"
stanza-upgrade db - already up to date (backup host)
> [CONTAINER-EXEC] backup [BACKREST-BIN] --config=[TEST_PATH]/backup/pgbackrest.conf --stanza=db --no-online stanza-upgrade
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: stanza-upgrade command begin [BACKREST-VERSION]: --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/backup/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --lock-path=[TEST_PATH]/backup/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/backup/log[] --no-log-timestamp --no-online --pg1-host=db-primary --pg1-host-cmd=[BACKREST-BIN] --pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf --pg1-host-user=[USER-1] --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-azure-account=<redacted> --repo1-azure-container=azcontainer --repo1-azure-key=<redacted> --repo1-cipher-pass=<redacted> --repo1-cipher-type=aes-256-cbc --repo1-path=/ --repo1-storage-host=azure --no-repo1-storage-verify-tls --repo1-type=azure --stanza=db
P00 INFO: stanza-upgrade command begin [BACKREST-VERSION]: --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/backup/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --lock-path=[TEST_PATH]/backup/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/backup/log[] --no-log-timestamp --no-online --pg1-host=db-primary --pg1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --pg1-host-cmd=[BACKREST-BIN] --pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf --pg1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --pg1-host-type=tls --pg1-host-user=[USER-1] --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-azure-account=<redacted> --repo1-azure-container=azcontainer --repo1-azure-key=<redacted> --repo1-cipher-pass=<redacted> --repo1-cipher-type=aes-256-cbc --repo1-path=/ --repo1-storage-host=azure --no-repo1-storage-verify-tls --repo1-type=azure --stanza=db
P00 INFO: stanza-upgrade for stanza 'db' on repo1
P00 INFO: stanza 'db' on repo1 is already up to date
P00 DETAIL: statistics: STATISTICS
@ -229,14 +232,16 @@ backrest-checksum="[CHECKSUM]"
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db archive-push [TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000001
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: archive-push command begin [BACKREST-VERSION]: [[TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000001] --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=lz4 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-user=[USER-1] --stanza=db
P00 INFO: archive-push command begin [BACKREST-VERSION]: [[TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000001] --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=lz4 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --repo1-host-type=tls --repo1-host-user=[USER-1] --stanza=db
P00 INFO: pushed WAL file '000000010000000100000001' to the archive
P00 DETAIL: statistics: STATISTICS
P00 INFO: archive-push command end: completed successfully
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db archive-push [TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: archive-push command begin [BACKREST-VERSION]: [[TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002] --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=lz4 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-user=[USER-1] --stanza=db
P00 INFO: archive-push command begin [BACKREST-VERSION]: [[TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002] --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=lz4 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --repo1-host-type=tls --repo1-host-user=[USER-1] --stanza=db
P00 INFO: pushed WAL file '000000010000000100000002' to the archive
P00 DETAIL: statistics: STATISTICS
P00 INFO: archive-push command end: completed successfully
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --log-level-console=warn --archive-push-queue-max=33554432 --stanza=db archive-push [TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000001
@ -248,7 +253,7 @@ P00 ERROR: [103]: unable to find a valid repository:
stanza-upgrade db - successful upgrade creates additional history (backup host)
> [CONTAINER-EXEC] backup [BACKREST-BIN] --config=[TEST_PATH]/backup/pgbackrest.conf --stanza=db --no-online stanza-upgrade
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: stanza-upgrade command begin [BACKREST-VERSION]: --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/backup/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --lock-path=[TEST_PATH]/backup/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/backup/log[] --no-log-timestamp --no-online --pg1-host=db-primary --pg1-host-cmd=[BACKREST-BIN] --pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf --pg1-host-user=[USER-1] --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-azure-account=<redacted> --repo1-azure-container=azcontainer --repo1-azure-key=<redacted> --repo1-cipher-pass=<redacted> --repo1-cipher-type=aes-256-cbc --repo1-path=/ --repo1-storage-host=azure --no-repo1-storage-verify-tls --repo1-type=azure --stanza=db
P00 INFO: stanza-upgrade command begin [BACKREST-VERSION]: --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/backup/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --lock-path=[TEST_PATH]/backup/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/backup/log[] --no-log-timestamp --no-online --pg1-host=db-primary --pg1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --pg1-host-cmd=[BACKREST-BIN] --pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf --pg1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --pg1-host-type=tls --pg1-host-user=[USER-1] --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-azure-account=<redacted> --repo1-azure-container=azcontainer --repo1-azure-key=<redacted> --repo1-cipher-pass=<redacted> --repo1-cipher-type=aes-256-cbc --repo1-path=/ --repo1-storage-host=azure --no-repo1-storage-verify-tls --repo1-type=azure --stanza=db
P00 INFO: stanza-upgrade for stanza 'db' on repo1
P00 DETAIL: statistics: STATISTICS
P00 INFO: stanza-upgrade command end: completed successfully
@ -299,8 +304,9 @@ backrest-checksum="[CHECKSUM]"
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db archive-get 000000010000000100000002 [TEST_PATH]/db-primary/db/base/pg_xlog/RECOVERYXLOG
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: archive-get command begin [BACKREST-VERSION]: [000000010000000100000002, [TEST_PATH]/db-primary/db/base/pg_xlog/RECOVERYXLOG] --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-user=[USER-1] --stanza=db
P00 INFO: archive-get command begin [BACKREST-VERSION]: [000000010000000100000002, [TEST_PATH]/db-primary/db/base/pg_xlog/RECOVERYXLOG] --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --repo1-host-type=tls --repo1-host-user=[USER-1] --stanza=db
P00 INFO: found 000000010000000100000002 in the repo1: 9.3-1 archive
P00 DETAIL: statistics: STATISTICS
P00 INFO: archive-get command end: completed successfully
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --log-level-console=warn --archive-push-queue-max=33554432 --stanza=db archive-push [TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000001
@ -309,7 +315,7 @@ P00 INFO: archive-get command end: completed successfully
full backup - create first full backup (backup host)
> [CONTAINER-EXEC] backup [BACKREST-BIN] --config=[TEST_PATH]/backup/pgbackrest.conf --repo1-retention-full=2 --no-online --type=full --stanza=db backup
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: backup command begin [BACKREST-VERSION]: --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=lz4 --config=[TEST_PATH]/backup/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/backup/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/backup/log[] --no-log-timestamp --no-online --pg1-host=db-primary --pg1-host-cmd=[BACKREST-BIN] --pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf --pg1-host-user=[USER-1] --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-azure-account=<redacted> --repo1-azure-container=azcontainer --repo1-azure-key=<redacted> --repo1-cipher-pass=<redacted> --repo1-cipher-type=aes-256-cbc --repo1-path=/ --repo1-retention-full=2 --repo1-storage-host=azure --no-repo1-storage-verify-tls --repo1-type=azure --stanza=db --start-fast --type=full
P00 INFO: backup command begin [BACKREST-VERSION]: --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=lz4 --config=[TEST_PATH]/backup/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/backup/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/backup/log[] --no-log-timestamp --no-online --pg1-host=db-primary --pg1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --pg1-host-cmd=[BACKREST-BIN] --pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf --pg1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --pg1-host-type=tls --pg1-host-user=[USER-1] --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-azure-account=<redacted> --repo1-azure-container=azcontainer --repo1-azure-key=<redacted> --repo1-cipher-pass=<redacted> --repo1-cipher-type=aes-256-cbc --repo1-path=/ --repo1-retention-full=2 --repo1-storage-host=azure --no-repo1-storage-verify-tls --repo1-type=azure --stanza=db --start-fast --type=full
P01 DETAIL: backup file db-primary:[TEST_PATH]/db-primary/db/base/pg_xlog/RECOVERYXLOG (16MB, 33%) checksum 51a8525d254c01f5edddda30b7fe697c7e44705c
P01 DETAIL: backup file db-primary:[TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002 (16MB, 66%) checksum 51a8525d254c01f5edddda30b7fe697c7e44705c
P01 DETAIL: backup file db-primary:[TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000001 (16MB, 99%) checksum f92539dea1f9482e2946c1138eeeecdea29d7f19
@ -345,8 +351,11 @@ log-subprocess=[LOG-SUBPROCESS]
log-timestamp=n
protocol-timeout=60
repo1-host=backup
repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt
repo1-host-cmd=[BACKREST-BIN]
repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf
repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key
repo1-host-type=tls
repo1-host-user=[USER-1]
spool-path=[TEST_PATH]/db-primary/spool
@ -354,8 +363,11 @@ spool-path=[TEST_PATH]/db-primary/spool
-------------------------------------------------------
[db]
pg1-host=db-primary
pg1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt
pg1-host-cmd=[BACKREST-BIN]
pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf
pg1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key
pg1-host-type=tls
pg1-host-user=[USER-1]
pg1-path=[TEST_PATH]/db-primary/db/base
@ -391,8 +403,9 @@ start-fast=y
stanza-upgrade db - successfully upgrade (db-primary host)
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db --no-online stanza-upgrade
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: stanza-upgrade command begin [BACKREST-VERSION]: --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --no-online --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-user=[USER-1] --stanza=db
P00 INFO: stanza-upgrade command begin [BACKREST-VERSION]: --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --no-online --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-host=backup --repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --repo1-host-type=tls --repo1-host-user=[USER-1] --stanza=db
P00 INFO: stanza-upgrade for stanza 'db' on repo1
P00 DETAIL: statistics: STATISTICS
P00 INFO: stanza-upgrade command end: completed successfully
+ supplemental file: /backup/db/backup.info
@ -447,7 +460,7 @@ backrest-checksum="[CHECKSUM]"
stanza-upgrade db - upgrade fails with mismatched db-ids (backup host)
> [CONTAINER-EXEC] backup [BACKREST-BIN] --config=[TEST_PATH]/backup/pgbackrest.conf --stanza=db --no-online stanza-upgrade
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: stanza-upgrade command begin [BACKREST-VERSION]: --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/backup/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --lock-path=[TEST_PATH]/backup/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/backup/log[] --no-log-timestamp --no-online --pg1-host=db-primary --pg1-host-cmd=[BACKREST-BIN] --pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf --pg1-host-user=[USER-1] --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-azure-account=<redacted> --repo1-azure-container=azcontainer --repo1-azure-key=<redacted> --repo1-cipher-pass=<redacted> --repo1-cipher-type=aes-256-cbc --repo1-path=/ --repo1-storage-host=azure --no-repo1-storage-verify-tls --repo1-type=azure --stanza=db
P00 INFO: stanza-upgrade command begin [BACKREST-VERSION]: --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/backup/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --lock-path=[TEST_PATH]/backup/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/backup/log[] --no-log-timestamp --no-online --pg1-host=db-primary --pg1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --pg1-host-cmd=[BACKREST-BIN] --pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf --pg1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --pg1-host-type=tls --pg1-host-user=[USER-1] --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-azure-account=<redacted> --repo1-azure-container=azcontainer --repo1-azure-key=<redacted> --repo1-cipher-pass=<redacted> --repo1-cipher-type=aes-256-cbc --repo1-path=/ --repo1-storage-host=azure --no-repo1-storage-verify-tls --repo1-type=azure --stanza=db
P00 INFO: stanza-upgrade for stanza 'db' on repo1
P00 ERROR: [028]: backup info file and archive info file do not match
archive: id = 2, version = 9.5, system-id = 1000000000000000095
@ -509,7 +522,7 @@ backrest-checksum="[CHECKSUM]"
diff backup - diff changed to full backup (backup host)
> [CONTAINER-EXEC] backup [BACKREST-BIN] --config=[TEST_PATH]/backup/pgbackrest.conf --repo1-retention-full=2 --no-online --type=diff --stanza=db backup
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: backup command begin [BACKREST-VERSION]: --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=lz4 --config=[TEST_PATH]/backup/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/backup/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/backup/log[] --no-log-timestamp --no-online --pg1-host=db-primary --pg1-host-cmd=[BACKREST-BIN] --pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf --pg1-host-user=[USER-1] --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-azure-account=<redacted> --repo1-azure-container=azcontainer --repo1-azure-key=<redacted> --repo1-cipher-pass=<redacted> --repo1-cipher-type=aes-256-cbc --repo1-path=/ --repo1-retention-full=2 --repo1-storage-host=azure --no-repo1-storage-verify-tls --repo1-type=azure --stanza=db --start-fast --type=diff
P00 INFO: backup command begin [BACKREST-VERSION]: --buffer-size=[BUFFER-SIZE] --compress-level=3 --compress-level-network=1 --compress-type=lz4 --config=[TEST_PATH]/backup/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --job-retry=0 --lock-path=[TEST_PATH]/backup/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/backup/log[] --no-log-timestamp --no-online --pg1-host=db-primary --pg1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --pg1-host-cmd=[BACKREST-BIN] --pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf --pg1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --pg1-host-type=tls --pg1-host-user=[USER-1] --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo1-azure-account=<redacted> --repo1-azure-container=azcontainer --repo1-azure-key=<redacted> --repo1-cipher-pass=<redacted> --repo1-cipher-type=aes-256-cbc --repo1-path=/ --repo1-retention-full=2 --repo1-storage-host=azure --no-repo1-storage-verify-tls --repo1-type=azure --stanza=db --start-fast --type=diff
P00 WARN: no prior backup exists, diff backup has been changed to full
P01 DETAIL: backup file db-primary:[TEST_PATH]/db-primary/db/base/pg_xlog/RECOVERYXLOG (16MB, 33%) checksum 51a8525d254c01f5edddda30b7fe697c7e44705c
P01 DETAIL: backup file db-primary:[TEST_PATH]/db-primary/db/base/pg_xlog/000000010000000100000002 (16MB, 66%) checksum 51a8525d254c01f5edddda30b7fe697c7e44705c
@ -547,8 +560,11 @@ log-subprocess=[LOG-SUBPROCESS]
log-timestamp=n
protocol-timeout=60
repo1-host=backup
repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt
repo1-host-cmd=[BACKREST-BIN]
repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf
repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key
repo1-host-type=tls
repo1-host-user=[USER-1]
spool-path=[TEST_PATH]/db-primary/spool
@ -556,8 +572,11 @@ spool-path=[TEST_PATH]/db-primary/spool
-------------------------------------------------------
[db]
pg1-host=db-primary
pg1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt
pg1-host-cmd=[BACKREST-BIN]
pg1-host-config=[TEST_PATH]/db-primary/pgbackrest.conf
pg1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key
pg1-host-type=tls
pg1-host-user=[USER-1]
pg1-path=[TEST_PATH]/db-primary/db/base
@ -593,9 +612,10 @@ start-fast=y
stanza-delete db - fail on missing stop file (db-primary host)
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --repo=1 --stanza=db stanza-delete
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: stanza-delete command begin [BACKREST-VERSION]: --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo=1 --repo1-host=backup --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-user=[USER-1] --stanza=db
P00 INFO: stanza-delete command begin [BACKREST-VERSION]: --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo=1 --repo1-host=backup --repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --repo1-host-type=tls --repo1-host-user=[USER-1] --stanza=db
P00 ERROR: [055]: stop file does not exist for stanza 'db'
HINT: has the pgbackrest stop command been run on this server for this stanza?
P00 DETAIL: statistics: STATISTICS
P00 INFO: stanza-delete command end: aborted with exception [055]
db must not exist for successful delete
@ -617,7 +637,8 @@ P00 INFO: stop command end: completed successfully
stanza-delete db - successfully delete the stanza (db-primary host)
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --repo=1 --stanza=db stanza-delete
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: stanza-delete command begin [BACKREST-VERSION]: --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo=1 --repo1-host=backup --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-user=[USER-1] --stanza=db
P00 INFO: stanza-delete command begin [BACKREST-VERSION]: --buffer-size=[BUFFER-SIZE] --compress-level-network=1 --config=[TEST_PATH]/db-primary/pgbackrest.conf --db-timeout=45 --exec-id=[EXEC-ID] --lock-path=[TEST_PATH]/db-primary/lock --log-level-console=detail --log-level-file=[LOG-LEVEL-FILE] --log-level-stderr=off --log-path=[TEST_PATH]/db-primary/log[] --no-log-timestamp --pg1-path=[TEST_PATH]/db-primary/db/base --protocol-timeout=60 --repo=1 --repo1-host=backup --repo1-host-cert-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.crt --repo1-host-cmd=[BACKREST-BIN] --repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf --repo1-host-key-file=[REPO_PATH]/test/certificate/pgbackrest-test-client.key --repo1-host-type=tls --repo1-host-user=[USER-1] --stanza=db
P00 DETAIL: statistics: STATISTICS
P00 INFO: stanza-delete command end: completed successfully
db must not exist for successful delete

View File

@ -1,5 +1,5 @@
run 001 - bkp 1, sby 1, dst backup, cmp none, storage posix, enc 0
==================================================================
run 001 - bkp 1, sby 1, tls 0, dst backup, cmp none, storage posix, enc 0
=========================================================================
stanza-create db - main create stanza info files (db-primary host)
> [CONTAINER-EXEC] db-primary [BACKREST-BIN] --config=[TEST_PATH]/db-primary/pgbackrest.conf --stanza=db stanza-create
@ -10,7 +10,7 @@ check db - verify check command runs successfully (db-primary host)
------------------------------------------------------------------------------------------------------------------------------------
check db - verify check command runs successfully (backup host)
> [CONTAINER-EXEC] backup [BACKREST-BIN] --config=[TEST_PATH]/backup/pgbackrest.conf --archive-timeout=5 --reset-pg2-host --reset-pg2-host-cmd --reset-pg2-host-config --reset-pg2-host-user --reset-pg2-path --stanza=db check
> [CONTAINER-EXEC] backup [BACKREST-BIN] --config=[TEST_PATH]/backup/pgbackrest.conf --archive-timeout=5 --reset-pg2-host --reset-pg2-host-type --reset-pg2-host-cmd --reset-pg2-host-config --reset-pg2-host-user --reset-pg2-path --stanza=db check
------------------------------------------------------------------------------------------------------------------------------------
full backup - fail on backup lock exists (backup host)
@ -484,7 +484,7 @@ archive-copy=y
start-fast=y
check db - verify check command on standby (db-standby host)
> [CONTAINER-EXEC] db-standby [BACKREST-BIN] --config=[TEST_PATH]/db-standby/pgbackrest.conf --reset-pg2-host --reset-pg2-host-cmd --reset-pg2-host-config --reset-pg2-host-user --reset-pg2-path --stanza=db check
> [CONTAINER-EXEC] db-standby [BACKREST-BIN] --config=[TEST_PATH]/db-standby/pgbackrest.conf --reset-pg2-host --reset-pg2-host-type --reset-pg2-host-cmd --reset-pg2-host-config --reset-pg2-host-user --reset-pg2-path --stanza=db check
------------------------------------------------------------------------------------------------------------------------------------
diff backup - backup for adhoc expire (backup host)
@ -736,7 +736,7 @@ archive-copy=y
start-fast=y
check db - check command with tablespace (backup host)
> [CONTAINER-EXEC] backup [BACKREST-BIN] --config=[TEST_PATH]/backup/pgbackrest.conf --archive-timeout=5 --reset-pg2-host --reset-pg2-host-cmd --reset-pg2-host-config --reset-pg2-host-user --reset-pg2-path --stanza=db check
> [CONTAINER-EXEC] backup [BACKREST-BIN] --config=[TEST_PATH]/backup/pgbackrest.conf --archive-timeout=5 --reset-pg2-host --reset-pg2-host-type --reset-pg2-host-cmd --reset-pg2-host-config --reset-pg2-host-user --reset-pg2-path --stanza=db check
------------------------------------------------------------------------------------------------------------------------------------
restore, type 'default', expect exit 38 - pg running (db-primary host)

View File

@ -42,6 +42,7 @@ sub new
$self->{strOption},
$self->{strParam},
$self->{bHostUpdate},
$self->{strEntryPoint},
) =
logDebugParam
(
@ -55,6 +56,7 @@ sub new
{name => 'strOption', required => false, trace => true},
{name => 'strParam', required => false, trace => true},
{name => 'bHostUpdate', required => false, trace => true, default => true},
{name => 'strEntryPoint', required => false, trace => true},
);
executeTest("docker rm -f $self->{strContainer}", {bSuppressError => true});
@ -62,6 +64,7 @@ sub new
executeTest("docker run -itd -h $self->{strName} --name=$self->{strContainer}" .
(defined($self->{strOption}) ? ' ' . $self->{strOption} : '') .
(defined($self->{stryMount}) ? ' -v ' . join(' -v ', @{$self->{stryMount}}) : '') .
(defined($self->{strEntryPoint}) ? " --entrypoint=$self->{strEntryPoint} --user=$self->{strUser}" : '') .
" $self->{strImage} " . (defined($self->{strParam}) ? ' ' . $self->{strParam} : ''),
{bSuppressStdErr => true});

View File

@ -407,8 +407,9 @@ sub run
# declaration and the renamed implementation.
if ($strLine =~ /^{/)
{
push(@stryShimModuleSrcRenamed, trim($strFunctionDeclaration) . ";");
push(@stryShimModuleSrcRenamed, $strFunctionShim);
push(
@stryShimModuleSrcRenamed,
trim($strFunctionDeclaration) . "; " . $strFunctionShim);
push(@stryShimModuleSrcRenamed, $strLine);
$strFunctionShim = undef;
@ -416,7 +417,7 @@ sub run
# Else keep constructing the declaration and implementation
else
{
$strFunctionDeclaration .= "${strLine}\n";
$strFunctionDeclaration .= trim($strLine);
$strFunctionShim .= "${strLine}\n";
}
}
@ -436,10 +437,10 @@ sub run
{
my $strLineLast = pop(@stryShimModuleSrcRenamed);
$strFunctionDeclaration = "${strLineLast} ${strLine}\n";
$strFunctionDeclaration = "${strLineLast} ${strLine}";
$strLine =~ s/^${strFunction}\(/${strFunction}_SHIMMED\(/;
$strFunctionShim = "${strLineLast}\n${strLine}\n";
$strFunctionShim = "${strLineLast}\n${strLine}";
$bFound = true;
last;

View File

@ -48,6 +48,7 @@ sub new
$self->{strCommandMain},
$self->{strPgSqlBin},
$self->{strTestPath},
$self->{strRepoPath},
) =
logDebugParam
(
@ -60,6 +61,7 @@ sub new
{name => 'strCommandMain', trace => true},
{name => 'strPgSqlBin', required => false, trace => true},
{name => 'strTestPath', trace => true},
{name => 'strRepoPath', trace => true},
);
# Initialize the test log
@ -366,6 +368,9 @@ sub regExpReplaceAll
# Replace the test path
$strLine =~ s/$self->{strTestPath}/[TEST_PATH]/g;
# Replace the repo path
$strLine =~ s/$self->{strRepoPath}/[REPO_PATH]/g;
# Replace the pgsql path (if exists)
if (defined($self->{strPgSqlBin}))
{

View File

@ -243,7 +243,7 @@ sub begin
{
$self->{oExpect} = new pgBackRestTest::Common::LogTest(
$self->module(), $self->moduleTest(), $self->runCurrent(), $self->doLogForce(), $strDescription,
$self->{strBackRestExe}, $self->pgBinPath(), $self->testPath());
$self->{strBackRestExe}, $self->pgBinPath(), $self->testPath(), $self->basePath());
&log(INFO, ' expect log: ' . $self->{oExpect}->{strFileName});
}

View File

@ -38,6 +38,7 @@ use pgBackRestTest::Common::ContainerTest;
use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::Common::HostGroupTest;
use pgBackRestTest::Common::RunTest;
use pgBackRestTest::Common::VmTest;
####################################################################################################################################
# Error constants
@ -156,7 +157,7 @@ sub new
$strUser = testRunGet()->pgUser();
# Create the host
my $self = $class->SUPER::new($strName, {strImage => $strImage, strUser => $strUser});
my $self = $class->SUPER::new($strName, {strImage => $strImage, strUser => $strUser, bTls => $oParam->{bTls}});
bless $self, $class;
# If repo is on local filesystem then set the repo-path locally
@ -1285,6 +1286,13 @@ sub configCreate
$oParamHash{$strStanza}{'pg1-host-cmd'} = $oHostDb1->backrestExe();
$oParamHash{$strStanza}{'pg1-host-config'} = $oHostDb1->backrestConfig();
if ($oParam->{bTls})
{
$oParamHash{$strStanza}{'pg1-host-type'} = 'tls';
$oParamHash{$strStanza}{'pg1-host-cert-file'} = testRunGet()->basePath() . HOST_CLIENT_CERT;
$oParamHash{$strStanza}{'pg1-host-key-file'} = testRunGet()->basePath() . HOST_CLIENT_KEY;
}
# Port can't be configured for a synthetic host
if (!$self->synthetic())
{
@ -1297,12 +1305,15 @@ sub configCreate
if (defined($oHostDb2))
{
# Add an invalid replica to simulate more than one replica. A warning should be thrown when a stanza is created and a
# valid replica should be chosen.
$oParamHash{$strStanza}{"pg2-host"} = BOGUS;
$oParamHash{$strStanza}{"pg2-host-user"} = $oHostDb2->userGet();
$oParamHash{$strStanza}{"pg2-host-cmd"} = $oHostDb2->backrestExe();
$oParamHash{$strStanza}{"pg2-host-config"} = $oHostDb2->backrestConfig();
$oParamHash{$strStanza}{"pg2-path"} = $oHostDb2->dbBasePath();
# valid replica should be chosen. Only do this for SSH since TLS takes longer to timeout.
if (!$oParam->{bTls})
{
$oParamHash{$strStanza}{"pg2-host"} = BOGUS;
$oParamHash{$strStanza}{"pg2-host-user"} = $oHostDb2->userGet();
$oParamHash{$strStanza}{"pg2-host-cmd"} = $oHostDb2->backrestExe();
$oParamHash{$strStanza}{"pg2-host-config"} = $oHostDb2->backrestConfig();
$oParamHash{$strStanza}{"pg2-path"} = $oHostDb2->dbBasePath();
}
# Set a flag so we know there's a bogus host
$self->{bBogusHost} = true;
@ -1314,6 +1325,13 @@ sub configCreate
$oParamHash{$strStanza}{"pg256-host-config"} = $oHostDb2->backrestConfig();
$oParamHash{$strStanza}{"pg256-path"} = $oHostDb2->dbBasePath();
if ($oParam->{bTls})
{
$oParamHash{$strStanza}{'pg256-host-type'} = 'tls';
$oParamHash{$strStanza}{'pg256-host-cert-file'} = testRunGet()->basePath() . HOST_CLIENT_CERT;
$oParamHash{$strStanza}{'pg256-host-key-file'} = testRunGet()->basePath() . HOST_CLIENT_KEY;
}
# Only test explicit ports on the backup server. This is so locally configured ports are also tested.
if (!$self->synthetic() && $self->nameTest(HOST_BACKUP))
{
@ -1348,12 +1366,27 @@ sub configCreate
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo1-host-cmd'} = $oHostBackup->backrestExe();
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo1-host-config'} = $oHostBackup->backrestConfig();
if ($oParam->{bTls})
{
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo1-host-type'} = 'tls';
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo1-host-cert-file'} = testRunGet()->basePath() . HOST_CLIENT_CERT;
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo1-host-key-file'} = testRunGet()->basePath() . HOST_CLIENT_KEY;
}
if ($iRepoTotal == 2)
{
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo2-host'} = $oHostBackup->nameGet();
$oParam->{bTls} ? $oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo2-host-type'} = 'tls' : undef;
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo2-host-user'} = $oHostBackup->userGet();
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo2-host-cmd'} = $oHostBackup->backrestExe();
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo2-host-config'} = $oHostBackup->backrestConfig();
if ($oParam->{bTls})
{
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo2-host-type'} = 'tls';
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo2-host-cert-file'} = testRunGet()->basePath() . HOST_CLIENT_CERT;
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo2-host-key-file'} = testRunGet()->basePath() . HOST_CLIENT_KEY;
}
}
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'log-path'} = $self->logPath();

View File

@ -44,6 +44,21 @@ use constant HOST_AZURE => 'azure';
use constant HOST_S3 => 's3-server';
push @EXPORT, qw(HOST_S3);
####################################################################################################################################
# CA/cert/key constants
####################################################################################################################################
use constant HOST_CERT_PATH => '/test/certificate/';
use constant HOST_CLIENT_CERT => HOST_CERT_PATH . 'pgbackrest-test-client.crt';
push @EXPORT, qw(HOST_CLIENT_CERT);
use constant HOST_CLIENT_KEY => HOST_CERT_PATH . 'pgbackrest-test-client.key';
push @EXPORT, qw(HOST_CLIENT_KEY);
use constant HOST_SERVER_CA => HOST_CERT_PATH . 'pgbackrest-test-ca.crt';
push @EXPORT, qw(HOST_SERVER_CA);
use constant HOST_SERVER_CERT => HOST_CERT_PATH . 'pgbackrest-test-server.crt';
use constant HOST_SERVER_KEY => HOST_CERT_PATH . 'pgbackrest-test-server.key';
####################################################################################################################################
# new
####################################################################################################################################
@ -68,6 +83,12 @@ sub new
my $strTestPath = testRunGet()->testPath() . ($strName eq HOST_BASE ? '' : "/${strName}");
storageTest()->pathCreate($strTestPath, {strMode => '0770'});
# Make sure keys have the correct permissions
if (chmod(0600, testRunGet()->basePath() . HOST_SERVER_KEY, testRunGet()->basePath() . HOST_CLIENT_KEY) != 2)
{
confess "unable to set mode on keys";
}
# Create the host
my $strProjectPath = dirname(dirname(abs_path($0)));
my $strBinPath = dirname(dirname($strTestPath)) . '/bin/' . testRunGet()->vm() . '/' . PROJECT_EXE;
@ -75,7 +96,13 @@ sub new
my $self = $class->SUPER::new(
$strName, $strContainer, $$oParam{strImage}, $$oParam{strUser}, testRunGet()->vm(),
["${strProjectPath}:${strProjectPath}", "${strTestPath}:${strTestPath}", "${strBinPath}:${strBinPath}:ro"]);
["${strProjectPath}:${strProjectPath}", "${strTestPath}:${strTestPath}", "${strBinPath}:${strBinPath}:ro"], undef,
$oParam->{bTls} ?
'server-start --log-level-console=debug --tls-server-ca-file=' . testRunGet()->basePath() . HOST_SERVER_CA .
' --tls-server-cert-file=' . testRunGet()->basePath() . HOST_SERVER_CERT . ' --tls-server-key-file=' .
testRunGet()->basePath() . HOST_SERVER_KEY . ' --tls-server-auth=pgbackrest-client=* --tls-server-address=0.0.0.0' :
undef,
undef, $oParam->{bTls} ? testRunGet()->backrestExe() : undef);
bless $self, $class;
# Set test path

View File

@ -73,6 +73,7 @@ sub new
{
strName => $bStandby ? HOST_DB_STANDBY : HOST_DB_PRIMARY,
strImage => $$oParam{strImage},
bTls => $oParam->{bTls},
strBackupDestination => $$oParam{strBackupDestination},
oLogTest => $$oParam{oLogTest},
bSynthetic => $$oParam{bSynthetic},

View File

@ -55,6 +55,7 @@ sub new
my $self = $class->SUPER::new(
{
strImage => containerRepo() . ':' . testRunGet()->vm() . "-test",
bTls => $oParam->{bTls},
strBackupDestination => $$oParam{strBackupDestination},
oLogTest => $$oParam{oLogTest},
bSynthetic => true,

View File

@ -62,6 +62,7 @@ sub new
my $self = $class->SUPER::new(
{
strImage => containerRepo() . ':' . testRunGet()->vm() . "-test",
bTls => $oParam->{bTls},
strBackupDestination => $$oParam{strBackupDestination},
oLogTest => $$oParam{oLogTest},
bStandby => $$oParam{bStandby},

View File

@ -88,7 +88,7 @@ sub setup
$oHostBackup = new pgBackRestTest::Env::Host::HostBackupTest(
{strBackupDestination => $strBackupDestination, bSynthetic => $bSynthetic, oLogTest => $oLogTest,
bRepoLocal => $oConfigParam->{strStorage} eq POSIX, bRepoEncrypt => $bRepoEncrypt});
bRepoLocal => $oConfigParam->{strStorage} eq POSIX, bRepoEncrypt => $bRepoEncrypt, bTls => $oConfigParam->{bTls}});
$oHostGroup->hostAdd($oHostBackup);
}
else
@ -104,13 +104,13 @@ sub setup
{
$oHostDbPrimary = new pgBackRestTest::Env::Host::HostDbSyntheticTest(
{strBackupDestination => $strBackupDestination, oLogTest => $oLogTest,
bRepoLocal => $oConfigParam->{strStorage} eq POSIX, bRepoEncrypt => $bRepoEncrypt});
bRepoLocal => $oConfigParam->{strStorage} eq POSIX, bRepoEncrypt => $bRepoEncrypt, bTls => $oConfigParam->{bTls}});
}
else
{
$oHostDbPrimary = new pgBackRestTest::Env::Host::HostDbTest(
{strBackupDestination => $strBackupDestination, oLogTest => $oLogTest, bRepoLocal =>
$oConfigParam->{strStorage} eq POSIX, bRepoEncrypt => $bRepoEncrypt});
$oConfigParam->{strStorage} eq POSIX, bRepoEncrypt => $bRepoEncrypt, bTls => $oConfigParam->{bTls}});
}
$oHostGroup->hostAdd($oHostDbPrimary);
@ -122,7 +122,7 @@ sub setup
{
$oHostDbStandby = new pgBackRestTest::Env::Host::HostDbTest(
{strBackupDestination => $strBackupDestination, bStandby => true, oLogTest => $oLogTest,
bRepoLocal => $oConfigParam->{strStorage} eq POSIX});
bRepoLocal => $oConfigParam->{strStorage} eq POSIX, bTls => $oConfigParam->{bTls}});
$oHostGroup->hostAdd($oHostDbStandby);
}
@ -140,6 +140,7 @@ sub setup
# Create db-primary config
$oHostDbPrimary->configCreate({
bTls => $oConfigParam->{bTls},
strBackupSource => $$oConfigParam{strBackupSource},
strCompressType => $$oConfigParam{strCompressType},
bHardlink => $bHostBackup ? undef : $$oConfigParam{bHardLink},
@ -151,6 +152,7 @@ sub setup
if (defined($oHostBackup))
{
$oHostBackup->configCreate({
bTls => $oConfigParam->{bTls},
strCompressType => $$oConfigParam{strCompressType},
bHardlink => $$oConfigParam{bHardLink},
strStorage => $oConfigParam->{strStorage},
@ -176,6 +178,7 @@ sub setup
if (defined($oHostDbStandby))
{
$oHostDbStandby->configCreate({
bTls => $oConfigParam->{bTls},
strBackupSource => $$oConfigParam{strBackupSource},
strCompressType => $$oConfigParam{strCompressType},
bHardlink => $bHostBackup ? undef : $$oConfigParam{bHardLink},

View File

@ -205,12 +205,12 @@ sub run
foreach my $rhRun
(
{vm => VM2, remote => false, storage => GCS, encrypt => false, delta => true, compress => BZ2},
{vm => VM2, remote => true, storage => AZURE, encrypt => true, delta => false, compress => GZ},
{vm => VM3, remote => false, storage => POSIX, encrypt => false, delta => true, compress => ZST},
{vm => VM3, remote => true, storage => S3, encrypt => true, delta => false, compress => LZ4},
{vm => VM4, remote => false, storage => GCS, encrypt => false, delta => false, compress => GZ},
{vm => VM4, remote => true, storage => S3, encrypt => true, delta => true, compress => ZST},
{vm => VM2, remote => false, tls => false, storage => GCS, encrypt => false, delta => true, compress => BZ2},
{vm => VM2, remote => true, tls => false, storage => AZURE, encrypt => true, delta => false, compress => GZ},
{vm => VM3, remote => false, tls => false, storage => POSIX, encrypt => false, delta => true, compress => ZST},
{vm => VM3, remote => true, tls => true, storage => S3, encrypt => true, delta => false, compress => LZ4},
{vm => VM4, remote => false, tls => false, storage => GCS, encrypt => false, delta => false, compress => GZ},
{vm => VM4, remote => true, tls => true, storage => S3, encrypt => true, delta => true, compress => ZST},
)
{
# Only run tests for this vm
@ -218,18 +218,20 @@ sub run
# Increment the run, log, and decide whether this unit test should be run
my $bRemote = $rhRun->{remote};
my $bTls = $rhRun->{tls};
my $strStorage = $rhRun->{storage};
my $bEncrypt = $rhRun->{encrypt};
my $bDeltaBackup = $rhRun->{delta};
my $strCompressType = $rhRun->{compress};
# Increment the run, log, and decide whether this unit test should be run
if (!$self->begin("rmt ${bRemote}, storage ${strStorage}, enc ${bEncrypt}, delta ${bDeltaBackup}")) {next}
if (!$self->begin("rmt ${bRemote}, tls ${bTls}, storage ${strStorage}, enc ${bEncrypt}, delta ${bDeltaBackup}")) {next}
# Create hosts, file object, and config
my ($oHostDbPrimary, $oHostDbStandby, $oHostBackup) = $self->setup(
true, $self->expect(), {bHostBackup => $bRemote, strStorage => $strStorage, bRepoEncrypt => $bEncrypt,
strCompressType => NONE});
true, $self->expect(),
{bHostBackup => $bRemote, bTls => $bTls, strStorage => $strStorage, bRepoEncrypt => $bEncrypt,
strCompressType => NONE});
# If S3 set process max to 2. This seems like the best place for parallel testing since it will help speed S3 processing
# without slowing down the other tests too much.

View File

@ -43,12 +43,12 @@ sub run
foreach my $rhRun
(
{vm => VM2, remote => false, storage => S3, encrypt => false, compress => NONE, error => 0},
{vm => VM2, remote => true, storage => POSIX, encrypt => true, compress => NONE, error => 0},
{vm => VM3, remote => false, storage => GCS, encrypt => true, compress => BZ2, error => 0},
{vm => VM3, remote => true, storage => AZURE, encrypt => false, compress => LZ4, error => 1},
{vm => VM4, remote => false, storage => S3, encrypt => true, compress => NONE, error => 0},
{vm => VM4, remote => true, storage => GCS, encrypt => false, compress => ZST, error => 0},
{vm => VM2, remote => false, tls => false, storage => S3, encrypt => false, compress => NONE, error => 0},
{vm => VM2, remote => true, tls => true, storage => POSIX, encrypt => true, compress => NONE, error => 0},
{vm => VM3, remote => false, tls => false, storage => GCS, encrypt => true, compress => BZ2, error => 0},
{vm => VM3, remote => true, tls => false, storage => AZURE, encrypt => false, compress => LZ4, error => 1},
{vm => VM4, remote => false, tls => false, storage => S3, encrypt => true, compress => NONE, error => 0},
{vm => VM4, remote => true, tls => true, storage => GCS, encrypt => false, compress => ZST, error => 0},
)
{
# Only run tests for this vm
@ -56,6 +56,7 @@ sub run
# Increment the run, log, and decide whether this unit test should be run
my $bRemote = $rhRun->{remote};
my $bTls = $rhRun->{tls};
my $strStorage = $rhRun->{storage};
my $bEncrypt = $rhRun->{encrypt};
my $strCompressType = $rhRun->{compress};
@ -63,13 +64,14 @@ sub run
# Increment the run, log, and decide whether this unit test should be run
if (!$self->begin(
"rmt ${bRemote}, cmp ${strCompressType}, error " . ($iError ? 'connect' : 'version') .
"rmt ${bRemote}, tls ${bTls}, cmp ${strCompressType}, error " . ($iError ? 'connect' : 'version') .
", storage ${strStorage}, enc ${bEncrypt}")) {next}
# Create hosts, file object, and config
my ($oHostDbPrimary, $oHostDbStandby, $oHostBackup) = $self->setup(
true, $self->expect(), {bHostBackup => $bRemote, strCompressType => $strCompressType, bArchiveAsync => true,
strStorage => $strStorage, bRepoEncrypt => $bEncrypt});
true, $self->expect(),
{bHostBackup => $bRemote, bTls => $bTls, strCompressType => $strCompressType, bArchiveAsync => true,
strStorage => $strStorage, bRepoEncrypt => $bEncrypt});
# Create compression extension
my $strCompressExt = $strCompressType ne NONE ? ".${strCompressType}" : '';

View File

@ -83,12 +83,12 @@ sub run
foreach my $rhRun
(
{vm => VM2, remote => false, storage => S3, encrypt => true, compress => BZ2},
{vm => VM2, remote => true, storage => POSIX, encrypt => true, compress => BZ2},
{vm => VM3, remote => false, storage => POSIX, encrypt => true, compress => LZ4},
{vm => VM3, remote => true, storage => S3, encrypt => false, compress => ZST},
{vm => VM4, remote => false, storage => AZURE, encrypt => true, compress => ZST},
{vm => VM4, remote => true, storage => POSIX, encrypt => false, compress => GZ},
{vm => VM2, remote => false, tls => false, storage => S3, encrypt => true, compress => BZ2},
{vm => VM2, remote => true, tls => false, storage => POSIX, encrypt => true, compress => BZ2},
{vm => VM3, remote => false, tls => false, storage => POSIX, encrypt => true, compress => LZ4},
{vm => VM3, remote => true, tls => true, storage => S3, encrypt => false, compress => ZST},
{vm => VM4, remote => false, tls => false, storage => AZURE, encrypt => true, compress => ZST},
{vm => VM4, remote => true, tls => true, storage => POSIX, encrypt => false, compress => GZ},
)
{
# Only run tests for this vm
@ -96,16 +96,18 @@ sub run
# Increment the run, log, and decide whether this unit test should be run
my $bRemote = $rhRun->{remote};
my $bTls = $rhRun->{tls};
my $strStorage = $rhRun->{storage};
my $bEncrypt = $rhRun->{encrypt};
my $strCompressType = $rhRun->{compress};
if (!$self->begin("rmt ${bRemote}, storage ${strStorage}, enc ${bEncrypt}, cmp ${strCompressType}")) {next}
if (!$self->begin("rmt ${bRemote}, tls ${bTls}, storage ${strStorage}, enc ${bEncrypt}, cmp ${strCompressType}")) {next}
# Create hosts, file object, and config
my ($oHostDbPrimary, $oHostDbStandby, $oHostBackup) = $self->setup(
true, $self->expect(), {bHostBackup => $bRemote, strStorage => $strStorage, bRepoEncrypt => $bEncrypt,
strCompressType => NONE});
true, $self->expect(),
{bHostBackup => $bRemote, bTls => $bTls, strStorage => $strStorage, bRepoEncrypt => $bEncrypt,
strCompressType => NONE});
# Reduce console logging to detail
$oHostDbPrimary->configUpdate({&CFGDEF_SECTION_GLOBAL => {'log-level-console' => lc(DETAIL)}});

View File

@ -41,12 +41,12 @@ sub run
foreach my $rhRun
(
{vm => VM2, remote => false, storage => AZURE, encrypt => true, compress => BZ2},
{vm => VM2, remote => true, storage => POSIX, encrypt => false, compress => GZ},
{vm => VM3, remote => false, storage => GCS, encrypt => false, compress => ZST},
{vm => VM3, remote => true, storage => AZURE, encrypt => true, compress => LZ4},
{vm => VM4, remote => false, storage => S3, encrypt => false, compress => GZ},
{vm => VM4, remote => true, storage => GCS, encrypt => true, compress => ZST},
{vm => VM2, remote => false, tls => false, storage => AZURE, encrypt => true, compress => BZ2},
{vm => VM2, remote => true, tls => true, storage => POSIX, encrypt => false, compress => GZ},
{vm => VM3, remote => false, tls => false, storage => GCS, encrypt => false, compress => ZST},
{vm => VM3, remote => true, tls => true, storage => AZURE, encrypt => true, compress => LZ4},
{vm => VM4, remote => false, tls => false, storage => S3, encrypt => false, compress => GZ},
{vm => VM4, remote => true, tls => false, storage => GCS, encrypt => true, compress => ZST},
)
{
# Only run tests for this vm
@ -54,17 +54,19 @@ sub run
# Increment the run, log, and decide whether this unit test should be run
my $bRemote = $rhRun->{remote};
my $bTls = $rhRun->{tls};
my $strStorage = $rhRun->{storage};
my $bEncrypt = $rhRun->{encrypt};
my $strCompressType = $rhRun->{compress};
# Increment the run, log, and decide whether this unit test should be run
if (!$self->begin("remote ${bRemote}, storage ${strStorage}, enc ${bEncrypt}, cmp ${strCompressType}")) {next}
if (!$self->begin("remote ${bRemote}, tls ${bTls}, storage ${strStorage}, enc ${bEncrypt}, cmp ${strCompressType}")) {next}
# Create hosts, file object, and config
my ($oHostDbPrimary, $oHostDbStandby, $oHostBackup) = $self->setup(
true, $self->expect(), {bHostBackup => $bRemote, strStorage => $strStorage, bRepoEncrypt => $bEncrypt,
strCompressType => $strCompressType});
true, $self->expect(),
{bHostBackup => $bRemote, bTls => $bTls, strStorage => $strStorage, bRepoEncrypt => $bEncrypt,
strCompressType => $strCompressType});
# Archive and backup info file names
my $strArchiveInfoFile = $oHostBackup->repoArchivePath(ARCHIVE_INFO_FILE);

View File

@ -52,20 +52,20 @@ sub run
foreach my $rhRun
(
{pg => PG_VERSION_83, repoDest => HOST_DB_PRIMARY, storage => POSIX, encrypt => false, compress => NONE, repo => 2},
{pg => PG_VERSION_84, repoDest => HOST_BACKUP, storage => AZURE, encrypt => true, compress => GZ, repo => 1},
{pg => PG_VERSION_90, repoDest => HOST_DB_PRIMARY, storage => GCS, encrypt => true, compress => BZ2, repo => 2},
{pg => PG_VERSION_91, repoDest => HOST_DB_STANDBY, storage => GCS, encrypt => false, compress => GZ, repo => 1},
{pg => PG_VERSION_92, repoDest => HOST_DB_STANDBY, storage => POSIX, encrypt => true, compress => NONE, repo => 1},
{pg => PG_VERSION_93, repoDest => HOST_BACKUP, storage => AZURE, encrypt => false, compress => NONE, repo => 2},
{pg => PG_VERSION_94, repoDest => HOST_DB_STANDBY, storage => POSIX, encrypt => true, compress => LZ4, repo => 1},
{pg => PG_VERSION_95, repoDest => HOST_BACKUP, storage => S3, encrypt => false, compress => BZ2, repo => 1},
{pg => PG_VERSION_96, repoDest => HOST_BACKUP, storage => POSIX, encrypt => false, compress => NONE, repo => 2},
{pg => PG_VERSION_10, repoDest => HOST_DB_STANDBY, storage => S3, encrypt => true, compress => GZ, repo => 2},
{pg => PG_VERSION_11, repoDest => HOST_BACKUP, storage => AZURE, encrypt => false, compress => ZST, repo => 2},
{pg => PG_VERSION_12, repoDest => HOST_BACKUP, storage => S3, encrypt => true, compress => LZ4, repo => 1},
{pg => PG_VERSION_13, repoDest => HOST_DB_STANDBY, storage => GCS, encrypt => false, compress => ZST, repo => 1},
{pg => PG_VERSION_14, repoDest => HOST_BACKUP, storage => POSIX, encrypt => true, compress => LZ4, repo => 2},
{pg => PG_VERSION_83, repoDest => HOST_DB_PRIMARY, tls => 0, storage => POSIX, encrypt => 0, compress => NONE, repo => 2},
{pg => PG_VERSION_84, repoDest => HOST_BACKUP, tls => 1, storage => AZURE, encrypt => 1, compress => GZ, repo => 1},
{pg => PG_VERSION_90, repoDest => HOST_DB_PRIMARY, tls => 0, storage => GCS, encrypt => 1, compress => BZ2, repo => 2},
{pg => PG_VERSION_91, repoDest => HOST_DB_STANDBY, tls => 1, storage => GCS, encrypt => 0, compress => GZ, repo => 1},
{pg => PG_VERSION_92, repoDest => HOST_DB_STANDBY, tls => 0, storage => POSIX, encrypt => 1, compress => NONE, repo => 1},
{pg => PG_VERSION_93, repoDest => HOST_BACKUP, tls => 0, storage => AZURE, encrypt => 0, compress => NONE, repo => 2},
{pg => PG_VERSION_94, repoDest => HOST_DB_STANDBY, tls => 0, storage => POSIX, encrypt => 1, compress => LZ4, repo => 1},
{pg => PG_VERSION_95, repoDest => HOST_BACKUP, tls => 1, storage => S3, encrypt => 0, compress => BZ2, repo => 1},
{pg => PG_VERSION_96, repoDest => HOST_BACKUP, tls => 0, storage => POSIX, encrypt => 0, compress => NONE, repo => 2},
{pg => PG_VERSION_10, repoDest => HOST_DB_STANDBY, tls => 1, storage => S3, encrypt => 1, compress => GZ, repo => 2},
{pg => PG_VERSION_11, repoDest => HOST_BACKUP, tls => 1, storage => AZURE, encrypt => 0, compress => ZST, repo => 2},
{pg => PG_VERSION_12, repoDest => HOST_BACKUP, tls => 0, storage => S3, encrypt => 1, compress => LZ4, repo => 1},
{pg => PG_VERSION_13, repoDest => HOST_DB_STANDBY, tls => 1, storage => GCS, encrypt => 0, compress => ZST, repo => 1},
{pg => PG_VERSION_14, repoDest => HOST_BACKUP, tls => 0, storage => POSIX, encrypt => 1, compress => LZ4, repo => 2},
)
{
# Only run tests for this pg version
@ -74,6 +74,7 @@ sub run
# Get run parameters
my $bHostBackup = $rhRun->{repoDest} eq HOST_BACKUP ? true : false;
my $bHostStandby = $self->pgVersion() >= PG_VERSION_HOT_STANDBY ? true : false;
my $bTls = $rhRun->{tls};
my $strBackupDestination = $rhRun->{repoDest};
my $strStorage = $rhRun->{storage};
my $bRepoEncrypt = $rhRun->{encrypt};
@ -86,21 +87,23 @@ sub run
# Increment the run, log, and decide whether this unit test should be run
next if (!$self->begin(
"bkp ${bHostBackup}, sby ${bHostStandby}, dst ${strBackupDestination}, cmp ${strCompressType}" .
"bkp ${bHostBackup}, sby ${bHostStandby}, tls ${bTls}, dst ${strBackupDestination}, cmp ${strCompressType}" .
", storage ${strStorage}, enc ${bRepoEncrypt}",
$bExpectVersion));
# Create hosts, file object, and config
my ($oHostDbPrimary, $oHostDbStandby, $oHostBackup) = $self->setup(
false, $self->expect(),
{bHostBackup => $bHostBackup, bStandby => $bHostStandby, strBackupDestination => $strBackupDestination,
{bHostBackup => $bHostBackup, bStandby => $bHostStandby, bTls => $bTls, strBackupDestination => $strBackupDestination,
strCompressType => $strCompressType, bArchiveAsync => false, strStorage => $strStorage,
bRepoEncrypt => $bRepoEncrypt, iRepoTotal => $iRepoTotal});
# Some commands will fail because of the bogus host created when a standby is present. These options reset the bogus host
# so it won't interfere with commands that won't tolerate a connection failure.
my $strBogusReset = $oHostBackup->bogusHost() ?
' --reset-pg2-host --reset-pg2-host-cmd --reset-pg2-host-config --reset-pg2-host-user --reset-pg2-path' : '';
' --reset-pg2-host --reset-pg2-host-type --reset-pg2-host-cmd --reset-pg2-host-config --reset-pg2-host-user' .
' --reset-pg2-path' :
'';
# If S3 set process max to 2. This seems like the best place for parallel testing since it will help speed S3 processing
# without slowing down the other tests too much.
@ -282,7 +285,7 @@ sub run
# Update message for standby
$oHostDbPrimary->sqlExecute("update test set message = '$strStandbyMessage'");
if ($oHostDbStandby->pgVersion() >= PG_VERSION_BACKUP_STANDBY)
if ($oHostDbStandby->pgVersion() >= PG_VERSION_BACKUP_STANDBY && !$bTls)
{
# If there is only a primary and a replica and the replica is the backup destination, then if pg2-host and
# pg256-host are BOGUS, confirm failure to reach the primary

View File

@ -152,6 +152,9 @@ protocolLocalExec(
ioFdReadNewOpen(strNewFmt(PROTOCOL_SERVICE_LOCAL "-%u shim protocol read", processId), pipeRead[0], 5000),
ioFdWriteNewOpen(strNewFmt(PROTOCOL_SERVICE_LOCAL "-%u shim protocol write", processId), pipeWrite[1], 5000));
// Send one noop to catch any errors that might happen after the greeting
protocolClientNoOp(helper->client);
FUNCTION_LOG_RETURN_VOID();
}
// Else call the base function
@ -248,6 +251,9 @@ protocolRemoteExec(
ioFdReadNewOpen(strNewFmt(PROTOCOL_SERVICE_REMOTE "-%u shim protocol read", processId), pipeRead[0], 5000),
ioFdWriteNewOpen(strNewFmt(PROTOCOL_SERVICE_REMOTE "-%u shim protocol write", processId), pipeWrite[1], 5000));
// Send one noop to catch any errors that might happen after the greeting
protocolClientNoOp(helper->client);
FUNCTION_LOG_RETURN_VOID();
}
// Else call the base function

View File

@ -14,8 +14,8 @@ Server Test Harness
#include "common/crypto/common.h"
#include "common/error.h"
#include "common/io/socket/common.h"
#include "common/io/socket/session.h"
#include "common/io/socket/server.h"
#include "common/io/tls/server.h"
#include "common/io/tls/session.h"
#include "common/log.h"
#include "common/type/buffer.h"
@ -45,6 +45,29 @@ Constants
***********************************************************************************************************************************/
#define HRN_SERVER_HOST "tls.test.pgbackrest.org"
/**********************************************************************************************************************************/
void hrnServerInit(void)
{
FUNCTION_HARNESS_VOID();
// Set correct permissions on private keys
THROW_ON_SYS_ERROR(
chmod(strZ(strNewFmt("%s/" HRN_SERVER_CERT_PREFIX "server.key", hrnPathRepo())), 0600) == -1, FileModeError,
"unable to set mode on server key");
THROW_ON_SYS_ERROR(
chmod(strZ(strNewFmt("%s/" HRN_SERVER_CERT_PREFIX "client.key", hrnPathRepo())), 0600) == -1, FileModeError,
"unable to set mode on client key");
// Add hostname when running in a container
if (testContainer())
{
if (system("echo \"127.0.0.1 " HRN_SERVER_HOST "\" | sudo tee -a /etc/hosts > /dev/null") != 0)
THROW(AssertError, "unable to add test host to /etc/hosts");
}
FUNCTION_HARNESS_RETURN_VOID();
}
/***********************************************************************************************************************************
Send commands to the server
***********************************************************************************************************************************/
@ -220,15 +243,8 @@ void hrnServerRun(IoRead *read, HrnServerProtocol protocol, HrnServerRunParam pa
if (param.port == 0)
param.port = hrnServerPort(0);
// Add test hosts
if (testContainer())
{
if (system("echo \"127.0.0.1 " HRN_SERVER_HOST "\" | sudo tee -a /etc/hosts > /dev/null") != 0)
THROW(AssertError, "unable to add test host to /etc/hosts");
}
// Initialize ssl and create a context
SSL_CTX *serverContext = NULL;
IoServer *tlsServer = NULL;
if (protocol == hrnServerProtocolTls)
{
@ -237,60 +253,14 @@ void hrnServerRun(IoRead *read, HrnServerProtocol protocol, HrnServerRunParam pa
// If certificate and key are not set then use defaults
if (param.certificate == NULL)
{
param.certificate = strNewFmt("%s/" HRN_SERVER_CERT_PREFIX ".crt", hrnPathRepo());
param.key = strNewFmt("%s/" HRN_SERVER_CERT_PREFIX ".key", hrnPathRepo());
param.certificate = strNewFmt("%s/" HRN_SERVER_CERT_PREFIX "server.crt", hrnPathRepo());
param.key = strNewFmt("%s/" HRN_SERVER_CERT_PREFIX "server.key", hrnPathRepo());
}
// Initialize TLS
cryptoInit();
const SSL_METHOD *method = SSLv23_method();
cryptoError(method == NULL, "unable to load TLS method");
serverContext = SSL_CTX_new(method);
cryptoError(serverContext == NULL, "unable to create TLS context");
// Configure the context by setting key and cert
cryptoError(
SSL_CTX_use_certificate_file(serverContext, strZ(param.certificate), SSL_FILETYPE_PEM) <= 0,
"unable to load server certificate");
cryptoError(
SSL_CTX_use_PrivateKey_file(serverContext, strZ(param.key), SSL_FILETYPE_PEM) <= 0,
"unable to load server private key");
tlsServer = tlsServerNew(STRDEF(HRN_SERVER_HOST), param.ca, param.key, param.certificate, 5000);
}
// Create the socket
int serverSocket;
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_port = htons((uint16_t)param.port);
address.sin_addr.s_addr = htonl(INADDR_ANY);
if ((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
THROW_SYS_ERROR(AssertError, "unable to create socket");
// Set the address as reusable so we can bind again in the same process for testing
int reuseAddr = 1;
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr));
// Bind the address. It might take a bit to bind if another process was recently using it so retry a few times.
Wait *wait = waitNew(2000);
int result;
do
{
result = bind(serverSocket, (struct sockaddr *)&address, sizeof(address));
}
while (result < 0 && waitMore(wait));
if (result < 0)
THROW_SYS_ERROR(AssertError, "unable to bind socket");
// Listen for client connections
if (listen(serverSocket, 1) < 0)
THROW_SYS_ERROR(AssertError, "unable to listen on socket");
IoServer *socketServer = sckServerNew(STRDEF("localhost"), param.port, 5000);
// Loop until no more commands
IoSession *serverSession = NULL;
@ -316,25 +286,11 @@ void hrnServerRun(IoRead *read, HrnServerProtocol protocol, HrnServerRunParam pa
case hrnServerCmdAccept:
{
// Accept the socket connection
struct sockaddr_in addr;
unsigned int len = sizeof(addr);
int testClientSocket = accept(serverSocket, (struct sockaddr *)&addr, &len);
if (testClientSocket < 0)
THROW_SYS_ERROR(AssertError, "unable to accept socket");
// Create socket session
sckOptionSet(testClientSocket);
serverSession = sckSessionNew(ioSessionRoleServer, testClientSocket, STRDEF("localhost"), param.port, 5000);
serverSession = ioServerAccept(socketServer, NULL);
// Start TLS if requested
if (protocol == hrnServerProtocolTls)
{
SSL *testClientSSL = SSL_new(serverContext);
serverSession = tlsSessionNew(testClientSSL, serverSession, 5000);
}
serverSession = ioServerAccept(tlsServer, serverSession);
break;
}
@ -402,9 +358,9 @@ void hrnServerRun(IoRead *read, HrnServerProtocol protocol, HrnServerRunParam pa
}
while (!done);
// Free TLS context
if (protocol == hrnServerProtocolTls)
SSL_CTX_free(serverContext);
// Free servers
ioServerFree(tlsServer);
ioServerFree(socketServer);
FUNCTION_HARNESS_RETURN_VOID();
}

View File

@ -7,6 +7,8 @@ client using the hrnServerScript() functions.
#ifndef TEST_COMMON_HARNESS_SERVER_H
#define TEST_COMMON_HARNESS_SERVER_H
#include <sys/stat.h>
#include "common/io/read.h"
#include "common/io/write.h"
@ -27,16 +29,25 @@ Maximum number of ports allowed for each test
/***********************************************************************************************************************************
Path and prefix for test certificates
***********************************************************************************************************************************/
#define HRN_SERVER_CERT_PREFIX "test/certificate/pgbackrest-test"
#define HRN_SERVER_CERT_PREFIX "test/certificate/pgbackrest-test-"
#define HRN_SERVER_CERT HRN_PATH_REPO "/" HRN_SERVER_CERT_PREFIX "server.crt"
#define HRN_SERVER_KEY HRN_PATH_REPO "/" HRN_SERVER_CERT_PREFIX "server.key"
#define HRN_SERVER_CA HRN_PATH_REPO "/" HRN_SERVER_CERT_PREFIX "ca.crt"
#define HRN_SERVER_CLIENT_CERT HRN_PATH_REPO "/" HRN_SERVER_CERT_PREFIX "client.crt"
#define HRN_SERVER_CLIENT_KEY HRN_PATH_REPO "/" HRN_SERVER_CERT_PREFIX "client.key"
/***********************************************************************************************************************************
Functions
***********************************************************************************************************************************/
// Initialize the server
void hrnServerInit(void);
// Run server
typedef struct HrnServerRunParam
{
VAR_PARAM_HEADER;
unsigned int port; // Server port, defaults to hrnServerPort(0)
const String *ca; // TLS CA store when protocol = hrnServerProtocolTls
const String *certificate; // TLS certificate when protocol = hrnServerProtocolTls
const String *key; // TLS key when protocol = hrnServerProtocolTls
} HrnServerRunParam;

View File

@ -128,15 +128,9 @@ testRun(void)
protocolClientNoOp(client), PathCreateError,
"raised from test: unable to create path '/bogus': [13] Permission denied");
// The server is in an error state so ignore any client errors
TRY_BEGIN()
{
protocolClientFree(client);
}
CATCH_ANY()
{
}
TRY_END();
// Do not send the exit command before freeing since the server has already errored
TEST_RESULT_VOID(protocolClientNoExit(client), "client no exit");
TEST_RESULT_VOID(protocolClientFree(client), "client free");
}
HRN_FORK_PARENT_END();
}
@ -215,15 +209,9 @@ testRun(void)
TEST_ERROR(protocolClientNoOp(client), StopError, "raised from test: stop file exists for all stanzas");
// The server is in an error state so ignore any client errors
TRY_BEGIN()
{
protocolClientFree(client);
}
CATCH_ANY()
{
}
TRY_END();
// Do not send the exit command before freeing since the server has already errored
TEST_RESULT_VOID(protocolClientNoExit(client), "client no exit");
TEST_RESULT_VOID(protocolClientFree(client), "client free");
HRN_STORAGE_REMOVE(hrnStorage, "lock/all" STOP_FILE_EXT);
}

View File

@ -0,0 +1,216 @@
/***********************************************************************************************************************************
Test Server Command
***********************************************************************************************************************************/
#include "storage/posix/storage.h"
#include "storage/remote/storage.h"
#include "common/harnessConfig.h"
#include "common/harnessFork.h"
#include "common/harnessServer.h"
#include "common/harnessStorage.h"
/***********************************************************************************************************************************
Test Run
***********************************************************************************************************************************/
void
testRun(void)
{
FUNCTION_HARNESS_VOID();
Storage *storageTest = storagePosixNewP(TEST_PATH_STR, .write = true);
harnessLogLevelSet(logLevelDetail);
// *****************************************************************************************************************************
if (testBegin("cmdServer()"))
{
TEST_TITLE("server");
HRN_FORK_BEGIN(.timeout = 5000)
{
HRN_FORK_CHILD_BEGIN(.prefix = "client repo")
{
StringList *argList = strLstNew();
hrnCfgArgRawZ(argList, cfgOptPgPath, "/BOGUS");
hrnCfgArgRaw(argList, cfgOptRepoHost, hrnServerHost());
hrnCfgArgRawZ(argList, cfgOptRepoHostConfig, TEST_PATH "/pgbackrest.conf");
hrnCfgArgRawZ(argList, cfgOptRepoHostType, "tls");
#if !TEST_IN_CONTAINER
hrnCfgArgRawZ(argList, cfgOptRepoHostCaFile, HRN_SERVER_CA);
#endif
hrnCfgArgRawZ(argList, cfgOptRepoHostCertFile, HRN_SERVER_CLIENT_CERT);
hrnCfgArgRawZ(argList, cfgOptRepoHostKeyFile, HRN_SERVER_CLIENT_KEY);
hrnCfgArgRawFmt(argList, cfgOptRepoHostPort, "%u", hrnServerPort(0));
hrnCfgArgRawZ(argList, cfgOptStanza, "db");
HRN_CFG_LOAD(cfgCmdArchiveGet, argList);
// Client 1
const Storage *storageRemote = NULL;
TEST_ASSIGN(
storageRemote,
storageRemoteNew(
STORAGE_MODE_FILE_DEFAULT, STORAGE_MODE_PATH_DEFAULT, true, NULL,
protocolRemoteGet(protocolStorageTypeRepo, 0), cfgOptionUInt(cfgOptCompressLevelNetwork)),
"new storage 1");
HRN_STORAGE_PUT_Z(storageRemote, "client1.txt", "CLIENT1");
TEST_RESULT_VOID(protocolRemoteFree(0), "free client 1");
// Client 2
TEST_ASSIGN(
storageRemote,
storageRemoteNew(
STORAGE_MODE_FILE_DEFAULT, STORAGE_MODE_PATH_DEFAULT, true, NULL,
protocolRemoteGet(protocolStorageTypeRepo, 0), cfgOptionUInt(cfgOptCompressLevelNetwork)),
"new storage 2");
HRN_STORAGE_PUT_Z(storageRemote, "client2.txt", "CLIENT2");
TEST_RESULT_VOID(protocolRemoteFree(0), "free client 2");
// Notify parent on exit
HRN_FORK_CHILD_NOTIFY_PUT();
}
HRN_FORK_CHILD_END();
HRN_FORK_CHILD_BEGIN(.prefix = "client pg")
{
StringList *argList = strLstNew();
hrnCfgArgRawZ(argList, cfgOptRepoPath, "/BOGUS");
hrnCfgArgRaw(argList, cfgOptPgHost, hrnServerHost());
hrnCfgArgRawZ(argList, cfgOptPgPath, TEST_PATH "/pg");
hrnCfgArgRawZ(argList, cfgOptPgHostType, "tls");
#if !TEST_IN_CONTAINER
hrnCfgArgRawZ(argList, cfgOptPgHostCaFile, HRN_SERVER_CA);
#endif
hrnCfgArgRawZ(argList, cfgOptPgHostCertFile, HRN_SERVER_CLIENT_CERT);
hrnCfgArgRawZ(argList, cfgOptPgHostKeyFile, HRN_SERVER_CLIENT_KEY);
hrnCfgArgRawFmt(argList, cfgOptPgHostPort, "%u", hrnServerPort(0));
hrnCfgArgRawZ(argList, cfgOptStanza, "db");
hrnCfgArgRawZ(argList, cfgOptProcess, "1");
HRN_CFG_LOAD(cfgCmdBackup, argList, .role = cfgCmdRoleLocal);
// Client 3
const Storage *storageRemote = NULL;
TEST_ASSIGN(
storageRemote,
storageRemoteNew(
STORAGE_MODE_FILE_DEFAULT, STORAGE_MODE_PATH_DEFAULT, true, NULL,
protocolRemoteGet(protocolStorageTypePg, 0), cfgOptionUInt(cfgOptCompressLevelNetwork)),
"new storage 3");
HRN_STORAGE_PUT_Z(storageRemote, "client3.txt", "CLIENT3");
TEST_RESULT_VOID(protocolRemoteFree(0), "free client 3");
// Notify parent on exit
HRN_FORK_CHILD_NOTIFY_PUT();
}
HRN_FORK_CHILD_END();
HRN_FORK_PARENT_BEGIN(.prefix = "server")
{
StringList *argList = strLstNew();
hrnCfgArgRawZ(argList, cfgOptTlsServerCaFile, HRN_SERVER_CA);
hrnCfgArgRawZ(argList, cfgOptTlsServerCertFile, HRN_SERVER_CERT);
hrnCfgArgRawZ(argList, cfgOptTlsServerKeyFile, HRN_SERVER_KEY);
hrnCfgArgRawZ(argList, cfgOptTlsServerAuth, "pgbackrest-client=db");
hrnCfgArgRawFmt(argList, cfgOptTlsServerPort, "%u", hrnServerPort(0));
HRN_CFG_LOAD(cfgCmdServerStart, argList);
// Write a config file to demonstrate that settings are loaded
HRN_STORAGE_PUT_Z(storageTest, "pgbackrest.conf", "[global]\nrepo1-path=" TEST_PATH "/repo");
// Get pid of this process to identify child process later
pid_t pid = getpid();
TEST_RESULT_VOID(cmdServer(3), "server");
// If this is a child process then exit immediately
if (pid != getpid())
exit(0);
// Wait for both child processes to exit
HRN_FORK_PARENT_NOTIFY_GET(0);
HRN_FORK_PARENT_NOTIFY_GET(1);
// Check that files written by child processes are present
TEST_STORAGE_GET(storageTest, "repo/client1.txt", "CLIENT1");
TEST_STORAGE_GET(storageTest, "repo/client2.txt", "CLIENT2");
TEST_STORAGE_GET(storageTest, "pg/client3.txt", "CLIENT3");
}
HRN_FORK_PARENT_END();
}
HRN_FORK_END();
}
// *****************************************************************************************************************************
if (testBegin("cmdServerPing()"))
{
TEST_TITLE("error on extra parameters");
StringList *argList = strLstNew();
strLstAddZ(argList, "host");
strLstAddZ(argList, "bogus");
HRN_CFG_LOAD(cfgCmdServerPing, argList);
TEST_ERROR(cmdServerPing(), ParamInvalidError, "extra parameters found");
HRN_FORK_BEGIN(.timeout = 5000)
{
HRN_FORK_CHILD_BEGIN(.prefix = "client")
{
TEST_TITLE("ping localhost");
argList = strLstNew();
hrnCfgArgRawFmt(argList, cfgOptTlsServerPort, "%u", hrnServerPort(0));
HRN_CFG_LOAD(cfgCmdServerPing, argList);
TEST_RESULT_VOID(cmdServerPing(), "ping");
// -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("ping 12.0.0.1");
argList = strLstNew();
hrnCfgArgRawFmt(argList, cfgOptTlsServerPort, "%u", hrnServerPort(0));
strLstAddZ(argList, "127.0.0.1");
HRN_CFG_LOAD(cfgCmdServerPing, argList);
TEST_RESULT_VOID(cmdServerPing(), "ping");
// Notify parent on exit
HRN_FORK_CHILD_NOTIFY_PUT();
}
HRN_FORK_CHILD_END();
HRN_FORK_PARENT_BEGIN(.prefix = "server")
{
StringList *argList = strLstNew();
hrnCfgArgRawZ(argList, cfgOptTlsServerCaFile, HRN_SERVER_CA);
hrnCfgArgRawZ(argList, cfgOptTlsServerCertFile, HRN_SERVER_CERT);
hrnCfgArgRawZ(argList, cfgOptTlsServerKeyFile, HRN_SERVER_KEY);
hrnCfgArgRawZ(argList, cfgOptTlsServerAuth, "bogus=*");
hrnCfgArgRawFmt(argList, cfgOptTlsServerPort, "%u", hrnServerPort(0));
HRN_CFG_LOAD(cfgCmdServerStart, argList);
// Get pid of this process to identify child process later
pid_t pid = getpid();
TEST_RESULT_VOID(cmdServer(2), "server");
// If this is a child process then exit immediately
if (pid != getpid())
exit(0);
// Wait for child process to exit
HRN_FORK_PARENT_NOTIFY_GET(0);
}
HRN_FORK_PARENT_END();
}
HRN_FORK_END();
}
FUNCTION_HARNESS_RETURN_VOID();
}

View File

@ -273,7 +273,7 @@ testRun(void)
{
HttpClient *client = NULL;
TEST_ASSIGN(client, httpClientNew(sckClientNew(STRDEF("localhost"), hrnServerPort(0), 500), 500), "new client");
TEST_ASSIGN(client, httpClientNew(sckClientNew(STRDEF("localhost"), hrnServerPort(0), 500, 500), 500), "new client");
TEST_ERROR_FMT(
httpRequestResponse(httpRequestNewP(client, STRDEF("GET"), STRDEF("/")), false), HostConnectError,
@ -297,7 +297,7 @@ testRun(void)
ioBufferSizeSet(35);
TEST_ASSIGN(client, httpClientNew(sckClientNew(hrnServerHost(), hrnServerPort(0), 5000), 5000), "new client");
TEST_ASSIGN(client, httpClientNew(sckClientNew(hrnServerHost(), hrnServerPort(0), 5000, 5000), 5000), "new client");
// -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("no output from server");

View File

@ -6,9 +6,126 @@ Test Tls Client
#include "common/io/fdRead.h"
#include "common/io/fdWrite.h"
#include "storage/posix/storage.h"
#include "common/harnessFork.h"
#include "common/harnessServer.h"
#include "common/harnessStorage.h"
/***********************************************************************************************************************************
Server cert with only a common name to test absence of alt names
To regenerate, run the following in a temp path:
openssl req -nodes -new -newkey rsa:4096 -sha256 -key ~/pgbackrest/test/certificate/pgbackrest-test-server.key \
-out server-cn-only.csr -subj "/CN=127.0.0.1"
openssl x509 -extensions usr_cert -req -days 99999 -CA ~/pgbackrest/test/certificate/pgbackrest-test-ca.crt \
-CAkey ~/pgbackrest/test/certificate/pgbackrest-test-ca.key -CAcreateserial -in server-cn-only.csr -out server-cn-only.crt
Then copy server-cn-only.crt into the variable below. Use a variable instead of a define so we know when the variable is not used.
***********************************************************************************************************************************/
static const char *const testServerCnOnlyCert =
"-----BEGIN CERTIFICATE-----\n"
"MIIE+TCCAuECFFtID1qGQ+Q6oyFiD9z5YmANCADjMA0GCSqGSIb3DQEBCwUAMFwx\n"
"CzAJBgNVBAYTAlVTMQwwCgYDVQQIDANBbGwxDDAKBgNVBAcMA0FsbDETMBEGA1UE\n"
"CgwKcGdCYWNrUmVzdDEcMBoGA1UEAwwTdGVzdC5wZ2JhY2tyZXN0Lm9yZzAgFw0y\n"
"MTA4MjYxMjIxNTNaGA8yMjk1MDYxMDEyMjE1M1owFDESMBAGA1UEAwwJMTI3LjAu\n"
"MC4xMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwzNZDX/VhTA6lALX\n"
"DZ4AOHv4OQH5wTZipa97XdTrI2TIfMGEffLmv5wzN85pku5HXBuHGJUaUENXt1Ss\n"
"GwdfBx/gZZEA8oONqkrxOoJTrABWIAs5k6TTUd+f3Y39rlsyQj076f1sw6Mw9qoC\n"
"h+JKXDDqw8kGwQHifXdtCrxL9OfV4eq+gYKrqdlyFM08WfKxe0Js8bB5cZ4Bt/GC\n"
"2JhQzQ9bMjYJlxSXIXivP/FFunVT5hZ8gsUVAH+/sm8xlQ4sedW7mIBKkjT3tgL0\n"
"FvchB3XyoZ6Sr0JKVaMOcQjIsTzOqdgawgArO541ZwUWHdJH+DODr/gBWXSnnzhH\n"
"ED5DAvRMPdO/t353qS/ihpacTqQ91B4UKxK1pVNC84ch3spCLnQncl7kn7RhcdCc\n"
"b5g4ZfahRmq79QSoMDvN4+7MtyERLXtSttSWiBzQVVj/jcFNDeGeDjKp6Z55xoso\n"
"tMZ3yVajl4IbuQS1pfTLjp7WdJ58y5hQ+8O/ebjUYIxOo5kZhRZV/jxqoR7Ga9MG\n"
"bAQ7BPcTuItpfGqiWcdYU+ZdyyFwvpXov6qNoCYt58nj7s+FAbed7EzRHa2Z3RVG\n"
"kcqv2iX5EddydHmqKip+QUUR4cPLUXn+kvOHtJEOgAWDURh0DVfhrMD5dX1d+9de\n"
"BUwZ89gYvxkkErPL1o8OPRxyiucCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAlwMZ\n"
"tlqvggfXsJh/AQdl1XxqQKzwC+1OyPozqTUMaEiHLgswJw8eXaZB1/8g9ZODPO3N\n"
"tLh6JfE4gJJ6gs89YmaZLR0oH3RkoFXSi4+t+WdyF0t3QrBuVx4uO3BeEdD1aLXm\n"
"lxS7004mJAEMn9FTBBMwek/DGS1Ic/tHwFCRvvE73mFcPL2Qs03ZzRuYUEI1Ckef\n"
"ONFu6/pydIS5MK0QCP/MfUlKP1D3u2aFEbdNHy4GjzGpfg+1DD/ebSswQG1YpjnN\n"
"5XLEQZ9IKE2ULq9GnnhqPNUTdX6HFHxVvyZUe/iXasOCX7C9PBipj3tulLcPMbLn\n"
"4tToEuLkvsLU2Z6I9mcS88Z30VyYu4BzM6tim7XvsOEObILjs2Qa0dJ2hSF6QJ9L\n"
"NUrbWS591v/PvUdk68kC8UL7o7UVS3lsZoRIZD+X+xdEi+zy4DNIMKUBnWtKamUU\n"
"1VOosL6vDSZYGg0InGfaBm3Bz3elTrWUHCapNQ5Zsxk+Sq+IQ4hauczrOsd6jugR\n"
"m1JzWMUZROfSrcVfighZSencJwJEmyCQwnMUyovPs2v7S+1QQEY210ZZd5Fphoye\n"
"1oA2FndLfr8BOG88+TzwdFilOiZ28lIpMFas38uybJBwlxVYN4/aLyIQGp6AyzGR\n"
"XqmU0pBFqRYS8xENKxk7lPnxFKyEpb3NK3wk3mo=\n"
"-----END CERTIFICATE-----";
/***********************************************************************************************************************************
Client key with a password
To regenerate, run the following in a temp path:
openssl genrsa -aes256 -passout pass:xxxx -out client-pwd.key 1024
Then copy server-pwd.key into the variable below. Use a variable instead of a define so we know when the variable is not used.
***********************************************************************************************************************************/
static const char *const testClientPwdKey =
"-----BEGIN RSA PRIVATE KEY-----\n"
"Proc-Type: 4,ENCRYPTED\n"
"DEK-Info: AES-256-CBC,86BD081993E54559E92BFF24921DCD00\n"
"\n"
"MglGBz4qfnUUs7IuueInkDrn5GRp6V7ZOs/S8rkrOp0i7MTfFJk1cYByu0FCcLqo\n"
"O3UX1dLPOzu74hJGOmOUHEJzrzA09wFIshTZo96z60+gQGcPEWkkkG9eRadJG5EN\n"
"TiAq1xopmAYK+srSBsPD7Xf9KeYpFM4Fa98pWbEhzMBiozgh2fH8+6OK8izDFJ57\n"
"aW8p/9lwZtkO2nd6G4meU+vyVDsL4GpdHcryi+MGGXfhkrr6mFAQ3PLpHTwr6xNI\n"
"wp6IjuLpNwlkNadq8Wgi8qy4YhpdqmSVt/oFJ25HMH+0UT+EAk0f/WMqOgkSmi3y\n"
"HgmG7YYAHel5tVeY59/ovxMvc290KQkthVgYBIT/Sy3O4pTRgu+xBkB5VfxjI9gj\n"
"yVVHsvJHwWdNyUf093Qvroul6Ulob7DOXmPvRWuu6YIBASYIGrmtI2cZ0SQBoySp\n"
"V65yTAwoi7bqsmwCo4sEjKE6FSeVINY/EvwYLbfyGUOmaunWkWkNPsg9fvwTZNOc\n"
"3G1IAypM4++02wVVeLdc0+n9FdE6QX3MWpUeZ5YaOzjBjCisk+jJ46L79bi6Z/Xc\n"
"H1XXBsMnmFhkhd3lraMQ8QYpWus890OmCnimB59SM1W2LgROwv/fXt/8rwgJY1v5\n"
"6VP5KAZkXpCq20gG6C7GW3jL5/prnPoe+uXku4m4iAReUmTewqht8WUyQaiAWy+e\n"
"nH5HoTaB3+9ZLu2RivU9l6y1YwYSAbSWBPbnN4HmjP4rmLG5t1ky9igYfJ3NL3LV\n"
"gJPetwzWuiONyshwMzcg0bE/NjzTXcCaFVKSJ/M++Kd+abDcixUQ3u6htVFHW/L/\n"
"-----END RSA PRIVATE KEY-----";
/***********************************************************************************************************************************
Client cert signed by another CA used to generate invalid client cert error
To regenerate, run the following in a temp path:
openssl genrsa -out bogus-ca.key 4096
openssl req -new -x509 -sha256 -days 99999 -key bogus-ca.key -out bogus-ca.crt -subj "/CN=bogus"
openssl req -nodes -new -newkey rsa:4096 -sha256 -key ~/pgbackrest/test/certificate/pgbackrest-test-client.key \
-out client-bad-ca.csr -subj "/CN=bogus"
openssl x509 -extensions usr_cert -req -days 99999 -CA bogus-ca.crt -CAkey bogus-ca.key -CAcreateserial -in client-bad-ca.csr \
-out client-bad-ca.crt
Then copy client-bad-ca.crt into the variable below. Use a variable instead of a define so we know when the variable is not used.
***********************************************************************************************************************************/
static const char *const testClientBadCa =
"-----BEGIN CERTIFICATE-----\n"
"MIIEqTCCApECFAzHjCL/QJZZRmBhloX298J4V4HbMA0GCSqGSIb3DQEBCwUAMBAx\n"
"DjAMBgNVBAMMBWJvZ3VzMCAXDTIxMDgyNjE0MzYyMloYDzIyOTUwNjEwMTQzNjIy\n"
"WjAQMQ4wDAYDVQQDDAVib2d1czCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC\n"
"ggIBALK5ahiXzFqvha28yqe3SGdezs4IREHUDgbn6Tem9k1GazE/IsdIQ9wj9KHd\n"
"lXwb/2fsdQk1hkPXa4qRBR/AyeBPRL3d0aZzU+pTjV6n9dJ+KIaivuAxoyyY59XM\n"
"36CqTZxe3VqXweRWPn40tzDcUxSVIfipJpFuK2vxpwEHdl/cFQ38/sRoHTjx61nx\n"
"obt4RFiEMAFCxxCk/qDyYISHJmH67dIUEw7ujB4vn/gpk5f1WBY5msZMgT3pJbFv\n"
"xD8dRgUvMpIM5poVFfhHgRq+L2dxQ2jHD5AnlpY6n8XrWy4QQa3AFsWgnD0w0Wn6\n"
"cyU0g5AkIlP+0mMNC9LIPVc9LGKUTrqciBgx8Rysy3mskg8pElEe1ouQOi/Zx8UA\n"
"G3RXqvjxXLkMp3S7PKgrr48uZHAso59+k33EkF/ceLsr3r3VY1WWsiszDfK+vbj6\n"
"Bbxvtv/S2ZYXMA7nM2Ysu20BpHm9LLo4y8HqDeixqw5enOwuDKSeKD1pJgJ5CUYq\n"
"RbA/cUYxHJ36NuPDxec+bhqiJq8RMR4pGGcJ7BvirJxYPJX0LqIfTHL1t2dVm//A\n"
"meMDNiM2quAzpBosjvaWaRUcankYE1dL59eVugoDKCNCPg72LjXBB7bPWZT2Xhkc\n"
"co0etruIYYJmQ3LO0vGe9pOYBu2FHx5FY72b8gpshm+umdL5AgMBAAEwDQYJKoZI\n"
"hvcNAQELBQADggIBAEN3778acjJ46yKzYoM+wiyyiMtmOrf+zvJsF0oK4KcWgec2\n"
"O2grmhxxDdF5c/P6XasDQFl8feQfGeDY76DaLrcmIiOYrtnqg11IZcPOHx5nbpu2\n"
"ZVV5LiMS8nHhQIyxMF/WYYKGzBQ5AY2+t6dozyDo3R4O7CCmsFKc8NaB4maC7Q16\n"
"7MxKXxtAH9I1PigjRMDpi1xQJbXJxFKhZrKBODtreL6cmv6yB4JJezI5ngIdODpI\n"
"MaIS0reRGN4QUpzDaXwYBTaOHaIDShPDOfiA5ai4xK/dEWG2rDu+yk7g5SEKMAxU\n"
"mfUCO1MGY6NwQupLUyfO2VjvfYeB+ipJq6F8tYMGrQJU/PCQT6nxaZdSoZZQF72y\n"
"OuYVfKjnj7MWapGKC3ea1oTUvkwDePe8xg3DBuXImp5mO4MG5K/oVv5SnNVmcUGq\n"
"L9WBrvypJK+3x3vbdyH02DR10TcMRSbDODmW59nx2PQEDUM7ddNZ60dRn8Hdgoz2\n"
"s/Sk3I1gXvZLQ/shS4Aa7XKz/TqhPNrBnMvSnp5/PtjjeBwxIBimuuM1ALFfwz91\n"
"KpzwqfTswuGIO8TWKJZzNTsdwScqmbZTtiVs6GaEZ3FQX5qnrbybX53S2R9fNKm+\n"
"qGj7FtRiSdjkZ7pmNpma6ycPR0RBZyL3aHnig+DDfRRt8TgrZzY3aXBReONb\n"
"-----END CERTIFICATE-----";
/***********************************************************************************************************************************
Test Run
@ -18,6 +135,13 @@ testRun(void)
{
FUNCTION_HARNESS_VOID();
// Create default storage object for testing
Storage *storageTest = storagePosixNewP(TEST_PATH_STR, .write = true);
THROW_ON_SYS_ERROR_FMT(chmod(HRN_SERVER_KEY, 0600) == -1, FileModeError, "unable to set mode on " HRN_SERVER_KEY);
THROW_ON_SYS_ERROR_FMT(
chmod(HRN_SERVER_CLIENT_KEY, 0600) == -1, FileModeError, "unable to set mode on " HRN_SERVER_CLIENT_KEY);
// *****************************************************************************************************************************
if (testBegin("Socket Common"))
{
@ -149,7 +273,7 @@ testRun(void)
// ---------------------------------------------------------------------------------------------------------------------
TEST_TITLE("unable to connect to blocking socket");
IoClient *socketClient = sckClientNew(STR(hostLocal), 7777, 0);
IoClient *socketClient = sckClientNew(STR(hostLocal), 7777, 0, 0);
TEST_RESULT_STR_Z(ioClientName(socketClient), "127.0.0.1:7777", " check name");
socketLocal.block = true;
@ -179,21 +303,21 @@ testRun(void)
{
IoClient *client = NULL;
TEST_ASSIGN(client, sckClientNew(STRDEF("localhost"), hrnServerPort(0), 100), "new client");
TEST_ASSIGN(client, sckClientNew(STRDEF("localhost"), hrnServerPort(0), 100, 100), "new client");
TEST_ERROR_FMT(
ioClientOpen(client), HostConnectError, "unable to connect to 'localhost:%u': [111] Connection refused",
hrnServerPort(0));
// This address should not be in use in a test environment -- if it is the test will fail
TEST_ASSIGN(client, sckClientNew(STRDEF("172.31.255.255"), hrnServerPort(0), 100), "new client");
TEST_ASSIGN(client, sckClientNew(STRDEF("172.31.255.255"), hrnServerPort(0), 100, 100), "new client");
TEST_ERROR_FMT(ioClientOpen(client), HostConnectError, "timeout connecting to '172.31.255.255:%u'", hrnServerPort(0));
}
// Additional coverage not provided by testing with actual certificates
// *****************************************************************************************************************************
if (testBegin("asn1ToStr(), tlsClientHostVerify(), and tlsClientHostVerifyName()"))
if (testBegin("tlsAsn1ToStr(), tlsClientHostVerify(), and tlsClientHostVerifyName()"))
{
TEST_ERROR(asn1ToStr(NULL), CryptoError, "TLS certificate name entry is missing");
TEST_ERROR(tlsAsn1ToStr(NULL), CryptoError, "TLS certificate name entry is missing");
TEST_ERROR(
tlsClientHostVerifyName(
@ -214,33 +338,140 @@ testRun(void)
// Connection errors
// -------------------------------------------------------------------------------------------------------------------------
TEST_ASSIGN(
client, tlsClientNew(sckClientNew(STRDEF("99.99.99.99.99"), 7777, 0), STRDEF("X"), 0, true, NULL, NULL),
client,
tlsClientNew(sckClientNew(STRDEF("99.99.99.99.99"), 7777, 0, 0), STRDEF("X"), 0, 0, true, NULL, NULL, NULL, NULL),
"new client");
TEST_RESULT_STR_Z(ioClientName(client), "99.99.99.99.99:7777", " check name");
TEST_ERROR(
ioClientOpen(client), HostConnectError, "unable to get address for '99.99.99.99.99': [-2] Name or service not known");
TEST_ASSIGN(
client, tlsClientNew(sckClientNew(STRDEF("localhost"), hrnServerPort(0), 100), STRDEF("X"), 100, true, NULL, NULL),
client,
tlsClientNew(
sckClientNew(STRDEF("localhost"), hrnServerPort(0), 100, 100), STRDEF("X"), 100, 100, true, NULL, NULL, NULL, NULL),
"new client");
TEST_ERROR_FMT(
ioClientOpen(client), HostConnectError, "unable to connect to 'localhost:%u': [111] Connection refused",
hrnServerPort(0));
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("bogus client cert/path");
TEST_TITLE("missing ca cert/path");
TEST_ERROR(
ioClientOpen(
tlsClientNew(
sckClientNew(
STRDEF("localhost"), hrnServerPort(0), 5000), STRDEF("X"), 0, true, STRDEF("bogus.crt"), STRDEF("/bogus"))),
sckClientNew(STRDEF("localhost"), hrnServerPort(0), 5000, 5000), STRDEF("X"), 0, 0, true, STRDEF("bogus.crt"),
STRDEF("/bogus"), NULL, NULL)),
CryptoError, "unable to set user-defined CA certificate location: [33558530] No such file or directory");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("missing client cert");
TEST_ERROR(
ioClientOpen(
tlsClientNew(
sckClientNew(STRDEF("localhost"), hrnServerPort(0), 5000, 5000), STRDEF("X"), 0, 0, true, NULL, NULL,
STRDEF("/bogus"), STRDEF("/bogus"))),
CryptoError, "unable to load cert file '/bogus': [33558530] No such file or directory");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("missing client key");
TEST_ERROR(
ioClientOpen(
tlsClientNew(
sckClientNew(STRDEF("localhost"), hrnServerPort(0), 5000, 5000), STRDEF("X"), 0, 0, true, NULL, NULL,
STRDEF(HRN_SERVER_CLIENT_CERT), STRDEF("/bogus"))),
CryptoError, "unable to load key file '/bogus': [33558530] No such file or directory");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("client cert and key do not match");
TEST_ERROR(
ioClientOpen(
tlsClientNew(
sckClientNew(STRDEF("localhost"), hrnServerPort(0), 5000, 5000), STRDEF("X"), 0, 0, true, NULL, NULL,
STRDEF(HRN_SERVER_CLIENT_CERT), STRDEF(HRN_SERVER_KEY))),
CryptoError,
"unable to load key file '" HRN_PATH_REPO "/test/certificate/pgbackrest-test-server.key': [185073780] key values"
" mismatch");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("client cert with passphrase");
storagePutP(storageNewWriteP(storageTest, STRDEF("client-pwd.key"), .modeFile = 0600), BUFSTRZ(testClientPwdKey));
TRY_BEGIN()
{
TEST_ERROR(
tlsClientNew(
sckClientNew(STRDEF("localhost"), hrnServerPort(0), 5000, 5000), STRDEF("X"), 0, 0, true, NULL, NULL,
STRDEF(HRN_SERVER_CLIENT_CERT), STRDEF(TEST_PATH "/client-pwd.key")),
CryptoError, "unable to load key file '" TEST_PATH "/client-pwd.key': [101077092] bad decrypt");
}
CATCH(TestError)
{
TEST_ERROR( // {uncovered - 32-bit error}
tlsClientNew(
sckClientNew(STRDEF("localhost"), hrnServerPort(0), 5000, 5000), STRDEF("X"), 0, 0, true, NULL, NULL,
STRDEF(HRN_SERVER_CLIENT_CERT), STRDEF(TEST_PATH "/client-pwd.key")),
CryptoError, "unable to load key file '" TEST_PATH "/client-pwd.key': [151429224] bad password read");
}
TRY_END();
storageRemoveP(storageTest, STRDEF("client-pwd.key"));
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("key with bad user permissions");
storagePutP(storageNewWriteP(storageTest, STRDEF("client-bad-perm.key"), .modeFile = 0640), BUFSTRDEF("bogus"));
TEST_ERROR(
ioClientOpen(
tlsClientNew(
sckClientNew(STRDEF("localhost"), hrnServerPort(0), 5000, 5000), STRDEF("X"), 0, 0, true, NULL, NULL,
STRDEF(HRN_SERVER_CLIENT_CERT), STRDEF(TEST_PATH "/client-bad-perm.key"))),
FileReadError,
"key file '" TEST_PATH "/client-bad-perm.key' has group or other permissions\n"
"HINT: file must have permissions u=rw (0600) or less if owned by the '" TEST_USER "' user\n"
"HINT: file must have permissions u=rw, g=r (0640) or less if owned by root\n");
storageRemoveP(storageTest, STRDEF("client-bad-perm.key"));
#ifdef TEST_CONTAINER_REQUIRED
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("key with bad user");
storagePutP(storageNewWriteP(storageTest, STRDEF("client-bad-perm.key"), .modeFile = 0660), BUFSTRDEF("bogus"));
HRN_SYSTEM_FMT("sudo chown postgres %s", strZ(storagePathP(storageTest, STRDEF("client-bad-perm.key"))));
TEST_ERROR(
ioClientOpen(
tlsClientNew(
sckClientNew(STRDEF("localhost"), hrnServerPort(0), 5000, 5000), STRDEF("X"), 0, 0, true, NULL, NULL,
STRDEF(HRN_SERVER_CLIENT_CERT), STRDEF(TEST_PATH "/client-bad-perm.key"))),
FileReadError, "key file '" TEST_PATH "/client-bad-perm.key' must be owned by the '" TEST_USER "' user or root");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("key with bad root permissions");
HRN_SYSTEM_FMT("sudo chown root %s", strZ(storagePathP(storageTest, STRDEF("client-bad-perm.key"))));
TEST_ERROR(
ioClientOpen(
tlsClientNew(
sckClientNew(STRDEF("localhost"), hrnServerPort(0), 5000, 5000), STRDEF("X"), 0, 0, true, NULL, NULL,
STRDEF(HRN_SERVER_CLIENT_CERT), STRDEF(TEST_PATH "/client-bad-perm.key"))),
FileReadError,
"key file '" TEST_PATH "/client-bad-perm.key' has group or other permissions\n"
"HINT: file must have permissions u=rw (0600) or less if owned by the '" TEST_USER "' user\n"
"HINT: file must have permissions u=rw, g=r (0640) or less if owned by root\n");
HRN_SYSTEM_FMT("sudo rm %s", strZ(storagePathP(storageTest, STRDEF("client-bad-perm.key"))));
// Certificate location and validation errors
// -------------------------------------------------------------------------------------------------------------------------
// Add test hosts
#ifdef TEST_CONTAINER_REQUIRED
HRN_SYSTEM(
"echo \"127.0.0.1 test.pgbackrest.org host.test2.pgbackrest.org test3.pgbackrest.org\" | sudo tee -a /etc/hosts >"
" /dev/null");
@ -253,8 +484,8 @@ testRun(void)
TEST_RESULT_VOID(
hrnServerRunP(
HRN_FORK_CHILD_READ(), hrnServerProtocolTls,
.certificate = STRDEF(HRN_PATH_REPO "/" HRN_SERVER_CERT_PREFIX "-alt-name.crt"),
.key = STRDEF(HRN_PATH_REPO "/" HRN_SERVER_CERT_PREFIX ".key")),
.certificate = STRDEF(HRN_SERVER_CERT),
.key = STRDEF(HRN_SERVER_KEY)),
"tls alt name server run");
}
HRN_FORK_CHILD_END();
@ -272,8 +503,8 @@ testRun(void)
TEST_ERROR_FMT(
ioClientOpen(
tlsClientNew(
sckClientNew(STRDEF("localhost"), hrnServerPort(0), 5000), STRDEF("X"), 0, true, NULL,
STRDEF("/bogus"))),
sckClientNew(STRDEF("localhost"), hrnServerPort(0), 5000, 5000), STRDEF("X"), 0, 0, true, NULL,
STRDEF("/bogus"), NULL, NULL)),
CryptoError,
"unable to verify certificate presented by 'localhost:%u': [20] unable to get local issuer certificate",
hrnServerPort(0));
@ -287,8 +518,8 @@ testRun(void)
TEST_RESULT_VOID(
ioClientOpen(
tlsClientNew(
sckClientNew(STRDEF("test.pgbackrest.org"), hrnServerPort(0), 5000), STRDEF("test.pgbackrest.org"),
0, true, STRDEF(HRN_PATH_REPO "/" HRN_SERVER_CERT_PREFIX "-ca.crt"), NULL)),
sckClientNew(STRDEF("test.pgbackrest.org"), hrnServerPort(0), 5000, 5000),
STRDEF("test.pgbackrest.org"), 0, 0, true, STRDEF(HRN_SERVER_CA), NULL, NULL, NULL)),
"open connection");
// -----------------------------------------------------------------------------------------------------------------
@ -300,9 +531,8 @@ testRun(void)
TEST_RESULT_VOID(
ioClientOpen(
tlsClientNew(
sckClientNew(STRDEF("host.test2.pgbackrest.org"), hrnServerPort(0), 5000),
STRDEF("host.test2.pgbackrest.org"), 0, true,
STRDEF(HRN_PATH_REPO "/" HRN_SERVER_CERT_PREFIX "-ca.crt"), NULL)),
sckClientNew(STRDEF("host.test2.pgbackrest.org"), hrnServerPort(0), 5000, 5000),
STRDEF("host.test2.pgbackrest.org"), 0, 0, true, STRDEF(HRN_SERVER_CA), NULL, NULL, NULL)),
"open connection");
// -----------------------------------------------------------------------------------------------------------------
@ -314,8 +544,8 @@ testRun(void)
TEST_ERROR(
ioClientOpen(
tlsClientNew(
sckClientNew(STRDEF("test3.pgbackrest.org"), hrnServerPort(0), 5000), STRDEF("test3.pgbackrest.org"),
0, true, STRDEF(HRN_PATH_REPO "/" HRN_SERVER_CERT_PREFIX "-ca.crt"), NULL)),
sckClientNew(STRDEF("test3.pgbackrest.org"), hrnServerPort(0), 5000, 5000),
STRDEF("test3.pgbackrest.org"), 0, 0, true, STRDEF(HRN_SERVER_CA), NULL, NULL, NULL)),
CryptoError,
"unable to find hostname 'test3.pgbackrest.org' in certificate common name or subject alternative names");
@ -328,9 +558,8 @@ testRun(void)
TEST_ERROR_FMT(
ioClientOpen(
tlsClientNew(
sckClientNew(STRDEF("localhost"), hrnServerPort(0), 5000), STRDEF("X"), 0, true,
STRDEF(HRN_PATH_REPO "/" HRN_SERVER_CERT_PREFIX ".crt"),
NULL)),
sckClientNew(STRDEF("localhost"), hrnServerPort(0), 5000, 5000), STRDEF("X"), 0, 0, true,
STRDEF(HRN_SERVER_CERT), NULL, NULL, NULL)),
CryptoError,
"unable to verify certificate presented by 'localhost:%u': [20] unable to get local issuer certificate",
hrnServerPort(0));
@ -344,7 +573,8 @@ testRun(void)
TEST_RESULT_VOID(
ioClientOpen(
tlsClientNew(
sckClientNew(STRDEF("localhost"), hrnServerPort(0), 5000), STRDEF("X"), 0, false, NULL, NULL)),
sckClientNew(STRDEF("localhost"), hrnServerPort(0), 5000, 5000), STRDEF("X"), 0, 0, false, NULL, NULL,
NULL, NULL)),
"open connection");
// -----------------------------------------------------------------------------------------------------------------
@ -353,6 +583,137 @@ testRun(void)
HRN_FORK_PARENT_END();
}
HRN_FORK_END();
// -------------------------------------------------------------------------------------------------------------------------
// Put root-owned server key
storagePutP(
storageNewWriteP(storageTest, STRDEF("server-root-perm.key"), .modeFile = 0640),
storageGetP(storageNewReadP(storagePosixNewP(FSLASH_STR), STRDEF(HRN_SERVER_KEY))));
HRN_SYSTEM_FMT("sudo chown root %s", strZ(storagePathP(storageTest, STRDEF("server-root-perm.key"))));
THROW_ON_SYS_ERROR(
symlink(TEST_PATH "/server-root-perm.key", TEST_PATH "/server-root-perm-link") == -1, FileOpenError,
"unable to create symlink");
// Put CN only server cert
storagePutP(storageNewWriteP(storageTest, STRDEF("server-cn-only.crt")), BUFSTRZ(testServerCnOnlyCert));
// Put bad CA client cert
storagePutP(storageNewWriteP(storageTest, STRDEF("client-bad-ca.crt")), BUFSTRZ(testClientBadCa));
HRN_FORK_BEGIN()
{
HRN_FORK_CHILD_BEGIN(.prefix = "test server", .timeout = 5000)
{
// TLS server to accept connections
IoServer *socketServer = sckServerNew(STRDEF("localhost"), hrnServerPort(0), 5000);
IoServer *tlsServer = tlsServerNew(
STRDEF("localhost"), STRDEF(HRN_SERVER_CA), STRDEF(TEST_PATH "/server-root-perm-link"),
STRDEF(TEST_PATH "/server-cn-only.crt"), 5000);
IoSession *socketSession = NULL;
TEST_RESULT_STR(ioServerName(socketServer), strNewFmt("localhost:%u", hrnServerPort(0)), "socket server name");
TEST_RESULT_STR_Z(ioServerName(tlsServer), "localhost", "tls server name");
// Invalid client cert
if (TEST_64BIT()) // Older 32-bit gives inconsistent results
{
socketSession = ioServerAccept(socketServer, NULL);
TEST_ERROR(
ioServerAccept(tlsServer, socketSession), ServiceError,
"TLS error [1:337100934] certificate verify failed");
}
// Valid client cert
socketSession = ioServerAccept(socketServer, NULL);
IoSession *tlsSession = NULL;
TEST_ASSIGN(tlsSession, ioServerAccept(tlsServer, socketSession), "open server session");
TEST_RESULT_BOOL(ioSessionAuthenticated(tlsSession), true, "server session authenticated");
TEST_RESULT_STR_Z(ioSessionPeerName(tlsSession), "pgbackrest-client", "check peer name");
TEST_RESULT_VOID(ioWrite(ioSessionIoWrite(tlsSession), BUFSTRDEF("message")), "server write");
TEST_RESULT_VOID(ioWriteFlush(ioSessionIoWrite(tlsSession)), "server write flush");
TEST_RESULT_VOID(ioSessionFree(tlsSession), "free server session");
// No client cert
socketSession = ioServerAccept(socketServer, NULL);
TEST_ASSIGN(tlsSession, ioServerAccept(tlsServer, socketSession), "open server session");
TEST_RESULT_BOOL(ioSessionAuthenticated(tlsSession), false, "server session not authenticated");
TEST_RESULT_VOID(ioWrite(ioSessionIoWrite(tlsSession), BUFSTRDEF("message2")), "server write");
TEST_RESULT_VOID(ioWriteFlush(ioSessionIoWrite(tlsSession)), "server write flush");
TEST_RESULT_VOID(ioSessionFree(tlsSession), "free server session");
// Free socket
ioServerFree(socketServer);
}
HRN_FORK_CHILD_END();
HRN_FORK_PARENT_BEGIN(.prefix = "test client")
{
IoSession *clientSession = NULL;
if (TEST_64BIT()) // Older 32-bit gives inconsistent results
{
TEST_TITLE("client cert is invalid (signed by another CA)");
TEST_ASSIGN(
clientSession,
ioClientOpen(
tlsClientNew(
sckClientNew(STRDEF("127.0.0.1"), hrnServerPort(0), 5000, 5000), STRDEF("127.0.0.1"), 5000, 5000,
true, NULL, NULL, STRDEF(TEST_PATH "/client-bad-ca.crt"), STRDEF(HRN_SERVER_CLIENT_KEY))),
"client open");
TEST_ERROR(
ioRead(ioSessionIoRead(clientSession), bufNew(1)), ServiceError,
"TLS error [1:336151576] tlsv1 alert unknown ca");
TEST_RESULT_VOID(ioSessionFree(clientSession), "free client session");
}
// -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("client cert is valid");
TEST_ASSIGN(
clientSession,
ioClientOpen(
tlsClientNew(
sckClientNew(STRDEF("127.0.0.1"), hrnServerPort(0), 5000, 5000), STRDEF("127.0.0.1"), 5000, 5000, true,
NULL, NULL, STRDEF(HRN_SERVER_CLIENT_CERT), STRDEF(HRN_SERVER_CLIENT_KEY))),
"client open");
Buffer *buffer = bufNew(7);
TEST_RESULT_VOID(ioRead(ioSessionIoRead(clientSession), buffer), "client read");
TEST_RESULT_STR_Z(strNewBuf(buffer), "message", "check read");
TEST_RESULT_VOID(ioSessionFree(clientSession), "free client session");
// -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("no client cert");
TEST_ASSIGN(
clientSession,
ioClientOpen(
tlsClientNew(
sckClientNew(STRDEF("127.0.0.1"), hrnServerPort(0), 5000, 5000), STRDEF("127.0.0.1"), 5000, 5000, true,
NULL, NULL, NULL, NULL)),
"client open");
buffer = bufNew(8);
TEST_RESULT_VOID(ioRead(ioSessionIoRead(clientSession), buffer), "client read");
TEST_RESULT_STR_Z(strNewBuf(buffer), "message2", "check read");
TEST_RESULT_VOID(ioSessionFree(clientSession), "free client session");
}
HRN_FORK_PARENT_END();
}
HRN_FORK_END();
storageRemoveP(storageTest, STRDEF("server-root-perm-link"), .errorOnMissing = true);
HRN_SYSTEM_FMT("sudo rm %s", strZ(storagePathP(storageTest, STRDEF("server-root-perm.key"))));
#endif // TEST_CONTAINER_REQUIRED
}
@ -378,8 +739,8 @@ testRun(void)
TEST_ASSIGN(
client,
tlsClientNew(
sckClientNew(hrnServerHost(), hrnServerPort(0), 5000), hrnServerHost(), 0, TEST_IN_CONTAINER, NULL,
NULL),
sckClientNew(hrnServerHost(), hrnServerPort(0), 5000, 5000), hrnServerHost(), 0, 0, TEST_IN_CONTAINER, NULL,
NULL, NULL, NULL),
"new client");
hrnServerScriptAccept(tls);

View File

@ -387,6 +387,47 @@ testRun(void)
TEST_RESULT_BOOL(cfgOptionTest(cfgOptRepoRetentionArchive), false, "repo1-retention-archive not set");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("set default when pg-host-type is tls");
argList = strLstNew();
hrnCfgArgRawZ(argList, cfgOptStanza, "db");
hrnCfgArgRawZ(argList, cfgOptRepoRetentionFull, "1");
hrnCfgArgKeyRawZ(argList, cfgOptPgHost, 1, "host1");
hrnCfgArgKeyRawZ(argList, cfgOptPgPath, 1, "/pg1");
hrnCfgArgKeyRawZ(argList, cfgOptPgHostType, 1, "tls");
hrnCfgArgKeyRawZ(argList, cfgOptPgHostCertFile, 1, "/not-used");
hrnCfgArgKeyRawZ(argList, cfgOptPgHostKeyFile, 1, "/not-used");
hrnCfgArgKeyRawZ(argList, cfgOptPgHost, 2, "host2");
hrnCfgArgKeyRawZ(argList, cfgOptPgPath, 2, "/pg2");
hrnCfgArgKeyRawZ(argList, cfgOptPgHostType, 2, "tls");
hrnCfgArgKeyRawZ(argList, cfgOptPgHostCertFile, 2, "/not-used");
hrnCfgArgKeyRawZ(argList, cfgOptPgHostKeyFile, 2, "/not-used");
hrnCfgArgKeyRawZ(argList, cfgOptPgHostPort, 2, "3333");
HRN_CFG_LOAD(cfgCmdBackup, argList);
TEST_RESULT_UINT(cfgOptionIdxUInt(cfgOptPgHostPort, 0), 8432, "default port");
TEST_RESULT_UINT(cfgOptionIdxUInt(cfgOptPgHostPort, 1), 3333, "set port");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("set default when repo-host-type is tls");
argList = strLstNew();
hrnCfgArgRawZ(argList, cfgOptStanza, "db");
hrnCfgArgKeyRawZ(argList, cfgOptRepoHost, 1, "host1");
hrnCfgArgKeyRawZ(argList, cfgOptRepoHostType, 1, "tls");
hrnCfgArgKeyRawZ(argList, cfgOptRepoHostCertFile, 1, "/not-used");
hrnCfgArgKeyRawZ(argList, cfgOptRepoHostKeyFile, 1, "/not-used");
hrnCfgArgKeyRawZ(argList, cfgOptRepoHost, 2, "host2");
hrnCfgArgKeyRawZ(argList, cfgOptRepoHostType, 2, "tls");
hrnCfgArgKeyRawZ(argList, cfgOptRepoHostCertFile, 2, "/not-used");
hrnCfgArgKeyRawZ(argList, cfgOptRepoHostKeyFile, 2, "/not-used");
hrnCfgArgKeyRawZ(argList, cfgOptRepoHostPort, 2, "4444");
HRN_CFG_LOAD(cfgCmdInfo, argList);
TEST_RESULT_UINT(cfgOptionIdxUInt(cfgOptRepoHostPort, 0), 8432, "default port");
TEST_RESULT_UINT(cfgOptionIdxUInt(cfgOptRepoHostPort, 1), 4444, "set port");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("invalid bucket name with verification enabled fails");

View File

@ -714,7 +714,7 @@ testRun(void)
hrnLogReplaceAdd("(could not connect to server|connection to server on socket).*$", NULL, "PG ERROR", false);
TEST_RESULT_LOG(
"P00 WARN: unable to check pg-4: [DbConnectError] unable to connect to 'dbname='postgres' port=5433': error\n"
"P00 WARN: unable to check pg-5: [DbConnectError] raised from remote-0 protocol on 'localhost':"
"P00 WARN: unable to check pg-5: [DbConnectError] raised from remote-0 ssh protocol on 'localhost':"
" unable to connect to 'dbname='postgres' port=5432': [PG ERROR]");
TEST_RESULT_INT(result.primaryIdx, 3, "check primary idx");

View File

@ -14,6 +14,7 @@ Test Protocol
#include "common/harnessError.h"
#include "common/harnessFork.h"
#include "common/harnessPack.h"
#include "common/harnessServer.h"
/***********************************************************************************************************************************
Test protocol server command handlers
@ -693,6 +694,183 @@ testRun(void)
HRN_FORK_END();
}
// *****************************************************************************************************************************
if (testBegin("protocolRemoteExec() and protocolServer()"))
{
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("invalid allow list");
TEST_ERROR(
protocolServerAuthorize(STRDEF(" "), NULL), OptionInvalidValueError, "'tls-server-auth' option must have a value");
HRN_FORK_BEGIN()
{
HRN_FORK_CHILD_BEGIN()
{
// -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("ping server");
// Connect to server without any verification
IoClient *tlsClient = tlsClientNew(
sckClientNew(hrnServerHost(), hrnServerPort(0), 5000, 5000), hrnServerHost(), 5000, 5000, false, NULL, NULL,
NULL, NULL);
IoSession *tlsSession = ioClientOpen(tlsClient);
// Send ping
ProtocolClient *protocolClient = protocolClientNew(
PROTOCOL_SERVICE_REMOTE_STR, PROTOCOL_SERVICE_REMOTE_STR, ioSessionIoRead(tlsSession),
ioSessionIoWrite(tlsSession));
protocolClientNoExit(protocolClient);
protocolClientNoOp(protocolClient);
protocolClientFree(protocolClient);
// -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("connect to repo server");
StringList *argList = strLstNew();
hrnCfgArgRawZ(argList, cfgOptPgPath, "/pg");
hrnCfgArgRaw(argList, cfgOptRepoHost, hrnServerHost());
hrnCfgArgRawZ(argList, cfgOptRepoHostType, "tls");
hrnCfgArgRawZ(argList, cfgOptRepoHostCertFile, HRN_SERVER_CLIENT_CERT);
hrnCfgArgRawZ(argList, cfgOptRepoHostKeyFile, HRN_SERVER_CLIENT_KEY);
hrnCfgArgRawFmt(argList, cfgOptRepoHostPort, "%u", hrnServerPort(0));
hrnCfgArgRawZ(argList, cfgOptStanza, "db");
HRN_CFG_LOAD(cfgCmdArchiveGet, argList);
ProtocolHelperClient helper = {0};
TEST_RESULT_VOID(protocolRemoteExec(&helper, protocolStorageTypeRepo, 0, 0), "get remote protocol");
TEST_RESULT_VOID(protocolClientFree(helper.client), "free remote protocol");
// -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("access denied connecting to repo server (invalid stanza)");
TEST_ERROR_FMT(
protocolRemoteExec(&helper, protocolStorageTypeRepo, 0, 0), AccessError,
"raised from remote-0 tls protocol on '%s': access denied", strZ(hrnServerHost()));
// -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("access denied connecting to repo server (invalid client)");
TEST_ERROR_FMT(
protocolRemoteExec(&helper, protocolStorageTypeRepo, 0, 0), AccessError,
"raised from remote-0 tls protocol on '%s': access denied", strZ(hrnServerHost()));
// -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("access denied connecting to repo server without a stanza");
argList = strLstNew();
hrnCfgArgRaw(argList, cfgOptRepoHost, hrnServerHost());
hrnCfgArgRawZ(argList, cfgOptRepoHostType, "tls");
hrnCfgArgRawZ(argList, cfgOptRepoHostCertFile, HRN_SERVER_CLIENT_CERT);
hrnCfgArgRawZ(argList, cfgOptRepoHostKeyFile, HRN_SERVER_CLIENT_KEY);
hrnCfgArgRawFmt(argList, cfgOptRepoHostPort, "%u", hrnServerPort(0));
HRN_CFG_LOAD(cfgCmdInfo, argList);
TEST_ERROR_FMT(
protocolRemoteExec(&helper, protocolStorageTypeRepo, 0, 0), AccessError,
"raised from remote-0 tls protocol on '%s': access denied", strZ(hrnServerHost()));
// -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("connect to pg server");
argList = strLstNew();
hrnCfgArgRawZ(argList, cfgOptRepoPath, "/repo");
hrnCfgArgRaw(argList, cfgOptPgHost, hrnServerHost());
hrnCfgArgRawZ(argList, cfgOptPgPath, "/pg");
hrnCfgArgRawZ(argList, cfgOptPgHostType, "tls");
hrnCfgArgRawZ(argList, cfgOptPgHostCertFile, HRN_SERVER_CLIENT_CERT);
hrnCfgArgRawZ(argList, cfgOptPgHostKeyFile, HRN_SERVER_CLIENT_KEY);
hrnCfgArgRawFmt(argList, cfgOptPgHostPort, "%u", hrnServerPort(0));
hrnCfgArgRawZ(argList, cfgOptStanza, "db");
hrnCfgArgRawZ(argList, cfgOptProcess, "1");
HRN_CFG_LOAD(cfgCmdBackup, argList, .role = cfgCmdRoleLocal);
helper = (ProtocolHelperClient){0};
TEST_RESULT_VOID(protocolRemoteExec(&helper, protocolStorageTypePg, 0, 0), "get remote protocol");
TEST_RESULT_VOID(protocolClientFree(helper.client), "free remote protocol");
}
HRN_FORK_CHILD_END();
HRN_FORK_PARENT_BEGIN()
{
IoServer *const tlsServer = tlsServerNew(
STRDEF("localhost"), STRDEF(HRN_SERVER_CA), STRDEF(HRN_SERVER_KEY), STRDEF(HRN_SERVER_CERT), 5000);
IoServer *const socketServer = sckServerNew(STRDEF("localhost"), hrnServerPort(0), 5000);
ProtocolServer *server = NULL;
// Server ping
// -----------------------------------------------------------------------------------------------------------------
IoSession *socketSession = ioServerAccept(socketServer, NULL);
TEST_ASSIGN(server, protocolServer(tlsServer, socketSession), "server start");
TEST_RESULT_PTR(server, NULL, "server is null");
// Repo server (archive-get)
// -----------------------------------------------------------------------------------------------------------------
StringList *const argListBase = strLstNew();
hrnCfgArgRawZ(argListBase, cfgOptTlsServerCaFile, HRN_SERVER_CA);
hrnCfgArgRawZ(argListBase, cfgOptTlsServerCertFile, HRN_SERVER_CERT);
hrnCfgArgRawZ(argListBase, cfgOptTlsServerKeyFile, HRN_SERVER_KEY);
StringList *argList = strLstDup(argListBase);
hrnCfgArgRawZ(argList, cfgOptTlsServerAuth, "pgbackrest-client=db");
HRN_CFG_LOAD(cfgCmdServerStart, argList);
socketSession = ioServerAccept(socketServer, NULL);
TEST_ASSIGN(server, protocolServer(tlsServer, socketSession), "server start");
TEST_RESULT_PTR_NE(server, NULL, "server is not null");
TEST_RESULT_UINT(protocolServerCommandGet(server).id, PROTOCOL_COMMAND_EXIT, "server exit");
// Repo server access denied (archive-get) invalid stanza
// -----------------------------------------------------------------------------------------------------------------
argList = strLstDup(argListBase);
hrnCfgArgRawZ(argList, cfgOptTlsServerAuth, "pgbackrest-client=bogus");
HRN_CFG_LOAD(cfgCmdServerStart, argList);
socketSession = ioServerAccept(socketServer, NULL);
TEST_ERROR(protocolServer(tlsServer, socketSession), AccessError, "access denied");
// Repo server access denied (archive-get) invalid client
// -----------------------------------------------------------------------------------------------------------------
argList = strLstDup(argListBase);
hrnCfgArgRawZ(argList, cfgOptTlsServerAuth, "bogus=*");
HRN_CFG_LOAD(cfgCmdServerStart, argList);
socketSession = ioServerAccept(socketServer, NULL);
TEST_ERROR(protocolServer(tlsServer, socketSession), AccessError, "access denied");
// Repo server access denied (info)
// -----------------------------------------------------------------------------------------------------------------
argList = strLstDup(argListBase);
hrnCfgArgRawZ(argList, cfgOptTlsServerAuth, "pgbackrest-client=db");
HRN_CFG_LOAD(cfgCmdServerStart, argList);
socketSession = ioServerAccept(socketServer, NULL);
TEST_ERROR(protocolServer(tlsServer, socketSession), AccessError, "access denied");
// Pg server (backup)
// -----------------------------------------------------------------------------------------------------------------
argList = strLstDup(argListBase);
hrnCfgArgRawZ(argList, cfgOptTlsServerAuth, "pgbackrest-client=*");
HRN_CFG_LOAD(cfgCmdServerStart, argList);
socketSession = ioServerAccept(socketServer, NULL);
TEST_ASSIGN(server, protocolServer(tlsServer, socketSession), "server start");
TEST_RESULT_PTR_NE(server, NULL, "server is not null");
TEST_RESULT_UINT(protocolServerCommandGet(server).id, PROTOCOL_COMMAND_EXIT, "server exit");
}
HRN_FORK_PARENT_END();
}
HRN_FORK_END();
}
// *****************************************************************************************************************************
if (testBegin("ProtocolParallel and ProtocolParallelJob"))
{

View File

@ -430,7 +430,7 @@ testRun(void)
// Replace the default authClient with one that points locally. The default host and url will still be used so they
// can be verified when testing auth.
((StorageGcs *)storageDriver(storage))->authClient = httpClientNew(
sckClientNew(hrnServerHost(), testPortMeta, 2000), 2000);
sckClientNew(hrnServerHost(), testPortMeta, 2000, 2000), 2000);
// Tests need the chunk size to be 16
((StorageGcs *)storageDriver(storage))->chunkSize = 16;

View File

@ -265,7 +265,8 @@ testRun(void)
httpClientToLog(driver->httpClient),
strNewFmt(
"{ioClient: {type: tls, driver: {ioClient: {type: socket, driver: {host: bucket.s3.amazonaws.com, port: 443"
", timeout: 60000}}, timeout: 60000, verifyPeer: %s}}, reusable: 0, timeout: 60000}",
", timeoutConnect: 60000, timeoutSession: 60000}}, timeoutConnect: 60000, timeoutSession: 60000"
", verifyPeer: %s}}, reusable: 0, timeout: 60000}",
cvtBoolToConstZ(TEST_IN_CONTAINER)),
"check http client");
@ -320,7 +321,7 @@ testRun(void)
argList = strLstDup(commonArgWithoutEndpointList);
hrnCfgArgRawZ(argList, cfgOptRepoS3Endpoint, "custom.endpoint:333");
hrnCfgArgRawZ(argList, cfgOptRepoStorageCaPath, "/path/to/cert");
hrnCfgArgRawZ(argList, cfgOptRepoStorageCaFile, HRN_PATH_REPO "/" HRN_SERVER_CERT_PREFIX ".crt");
hrnCfgArgRawZ(argList, cfgOptRepoStorageCaFile, HRN_SERVER_CA);
hrnCfgEnvRaw(cfgOptRepoS3Token, securityToken);
HRN_CFG_LOAD(cfgCmdArchivePush, argList);
@ -331,7 +332,8 @@ testRun(void)
httpClientToLog(driver->httpClient),
strNewFmt(
"{ioClient: {type: tls, driver: {ioClient: {type: socket, driver: {host: bucket.custom.endpoint, port: 333"
", timeout: 60000}}, timeout: 60000, verifyPeer: %s}}, reusable: 0, timeout: 60000}",
", timeoutConnect: 60000, timeoutSession: 60000}}, timeoutConnect: 60000, timeoutSession: 60000"
", verifyPeer: %s}}, reusable: 0, timeout: 60000}",
cvtBoolToConstZ(TEST_IN_CONTAINER)),
"check http client");
@ -455,7 +457,7 @@ testRun(void)
// Testing requires the auth http client to be redirected
driver->credHost = hrnServerHost();
driver->credHttpClient = httpClientNew(sckClientNew(host, authPort, 5000), 5000);
driver->credHttpClient = httpClientNew(sckClientNew(host, authPort, 5000, 5000), 5000);
// Now that we have checked the role when set explicitly, null it out to make sure it is retrieved automatically
driver->credRole = NULL;

View File

@ -102,6 +102,11 @@ The test code is included directly so it can freely interact with the included C
#include "common/memContext.h"
#endif
#ifdef HRN_FEATURE_LOG
#include "common/harnessLog.h"
void harnessLogLevelDefaultSet(LogLevel logLevel);
#endif
{[C_TEST_INCLUDE]}
/***********************************************************************************************************************************
@ -109,7 +114,10 @@ Includes that are not generally used by tests
***********************************************************************************************************************************/
#include <assert.h>
#include "common/io/socket/common.h"
#if defined(HRN_INTEST_SOCKET) || defined(HRN_FEATURE_SOCKET)
#include "common/io/socket/common.h"
#include "common/harnessServer.h"
#endif
#ifdef HRN_FEATURE_STAT
#include "common/stat.h"
@ -162,11 +170,6 @@ main(int argListSize, const char *argList[])
statInit();
#endif
// Use aggressive keep-alive settings for testing
#if defined(HRN_INTEST_SOCKET) || defined(HRN_FEATURE_SOCKET)
sckInit(false, true, 2, 5, 5);
#endif
// Set neutral umask for testing
umask(0000);
@ -192,6 +195,12 @@ main(int argListSize, const char *argList[])
harnessLogLevelDefaultSet({[C_LOG_LEVEL_TEST]});
#endif
// Use aggressive keep-alive settings for testing
#if defined(HRN_INTEST_SOCKET) || defined(HRN_FEATURE_SOCKET)
sckInit(false, true, 2, 5, 5);
hrnServerInit();
#endif
// Initialize tests
// run, selected
{[C_TEST_LIST]}