mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2025-12-17 23:56:56 +00:00
GODT-1673: TLS certs generation for gRPC service
Wait for Bridge certificate and use it for gRPC connection Other: add README file for Bridge-GUI prerequisites GODT-1673: Configure Client/Server to make use of the bridge cert Other : comments + todo on known issue Other: fix go import alias [skip-ci]
This commit is contained in:
@ -30,37 +30,94 @@ using namespace grpc;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
|
||||
/// \todo GODT-1673 Decide how to generate/store/share this certificate.
|
||||
|
||||
std::string const cert = R"(-----BEGIN CERTIFICATE-----
|
||||
MIIC5TCCAc2gAwIBAgIJAMUQK0VGexMsMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNV
|
||||
BAMMCWxvY2FsaG9zdDAeFw0yMjA2MTQxNjUyNTVaFw0yMjA3MTQxNjUyNTVaMBQx
|
||||
EjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
|
||||
ggEBAL6T1JQ0jptq512PBLASpCLFB0px7KIzEml0oMUCkVgUF+2cayrvdBXJZnaO
|
||||
SG+/JPnHDcQ/ecgqkh2Ii6a2x2kWA5KqWiV+bSHp0drXyUGJfM85muLsnrhYwJ83
|
||||
HHtweoUVebRZvHn66KjaH8nBJ+YVWyYbSUhJezcg6nBSEtkW+I/XUHu4S2C7FUc5
|
||||
DXPO3yWWZuZ22OZz70DY3uYE/9COuilotuKdj7XgeKDyKIvRXjPFyqGxwnnp6bXC
|
||||
vWvrQdcxy0wM+vZxew3QtA/Ag9uKJU9owP6noauXw95l49lEVIA5KXVNtdaldVht
|
||||
MO/QoelLZC7h79PK22zbii3x930CAwEAAaM6MDgwFAYDVR0RBA0wC4IJbG9jYWxo
|
||||
b3N0MAsGA1UdDwQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0B
|
||||
AQsFAAOCAQEAW/9PE8dcAN+0C3K96Xd6Y3qOOtQhRw+WlZXhtiqMtlJfTjvuGKs9
|
||||
58xuKcTvU5oobxLv+i5+4gpqLjUZZ9FBnYXZIACNVzq4PEXf+YdzcA+y6RS/rqT4
|
||||
dUjsuYrScAmdXK03Duw3HWYrTp8gsJzIaYGTltUrOn0E4k/TsZb/tZ6z+oH7Fi+p
|
||||
wdsI6Ut6Zwm3Z7WLn5DDk8KvFjHjZkdsCb82SFSAUVrzWo5EtbLIY/7y3A5rGp9D
|
||||
t0AVpuGPo5Vn+MW1WA9HT8lhjz0v5wKGMOBi3VYW+Yx8FWHDpacvbZwVM0MjMSAd
|
||||
M7SXYbNDiLF4LwPLsunoLsW133Ky7s99MA==
|
||||
-----END CERTIFICATE-----)";
|
||||
|
||||
|
||||
Empty empty; // re-used across client calls.
|
||||
|
||||
QString const configFolder = "protonmail/bridge";
|
||||
QString const certFile = "cert.pem";
|
||||
|
||||
int const maxConnectionTimeSecs = 60; ///< Amount of time after which we consider connection attemps to the server have failed.
|
||||
|
||||
int const maxCertificateWaitMsecs = 60 * 1000; ///< Ammount of time we wait for he server to generate the certificate.
|
||||
}
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \return user configuration directory used by bridge (based on Golang OS/File::UserConfigDir).
|
||||
//****************************************************************************************************************************************************
|
||||
static const QString _userConfigDir(){
|
||||
QString dir;
|
||||
#ifdef Q_OS_WIN
|
||||
dir = qgetenv ("AppData");
|
||||
if (dir.isEmpty())
|
||||
throw Exception("%AppData% is not defined.");
|
||||
#elif defined(Q_OS_IOS) || defined(Q_OS_DARWIN)
|
||||
dir = qgetenv ("HOME");
|
||||
if (dir.isEmpty())
|
||||
throw Exception("$HOME is not defined.");
|
||||
dir += "/Library/Application Support";
|
||||
#else
|
||||
dir = qgetenv ("XDG_CONFIG_HOME");
|
||||
if (dir.isEmpty())
|
||||
dir = qgetenv ("HOME");
|
||||
if (dir.isEmpty())
|
||||
throw Exception("neither $XDG_CONFIG_HOME nor $HOME are defined");
|
||||
dir += "/.config";
|
||||
#endif
|
||||
QString folder = dir + "/" + configFolder;
|
||||
QDir().mkpath(folder);
|
||||
|
||||
return folder;
|
||||
}
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \brief wait for certificate generation by Bridge
|
||||
/// \return server certificate generated by Bridge
|
||||
//****************************************************************************************************************************************************
|
||||
std::string GRPCClient::getServerCertificate()
|
||||
{
|
||||
const QString filename = _userConfigDir() + "/" + certFile;
|
||||
QFile file(filename);
|
||||
// TODO : the certificate can exist but still be invalid.
|
||||
// If the certificate is close to its limit, the bridge will generate a new one.
|
||||
// If we read the certificate before the bridge rewrites it the certificate will be invalid.
|
||||
if (!file.exists())
|
||||
{
|
||||
// wait for file creation
|
||||
QFileSystemWatcher watcher(this);
|
||||
if (!watcher.addPath(_userConfigDir()))
|
||||
throw Exception("Failed to watch User Config Directory");
|
||||
connect(&watcher, &QFileSystemWatcher::directoryChanged, this, &GRPCClient::configFolderChanged);
|
||||
|
||||
// set up an eventLoop to wait for the certIsReady signal or timeout.
|
||||
QTimer timer;
|
||||
timer.setSingleShot(true);
|
||||
QEventLoop loop;
|
||||
connect(this, &GRPCClient::certIsReady, &loop, &QEventLoop::quit);
|
||||
connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
|
||||
timer.start(maxCertificateWaitMsecs);
|
||||
loop.exec();
|
||||
|
||||
// timeout case.
|
||||
if(!timer.isActive())
|
||||
throw Exception("Server failed to generate certificate on time");
|
||||
//else certIsReadySignal.
|
||||
}
|
||||
|
||||
if (!file.open(QFile::ReadOnly))
|
||||
throw Exception("Failed to read the server certificate");
|
||||
QByteArray qbaCert = file.readAll();
|
||||
std::string cert(qbaCert.constData(), qbaCert.length());
|
||||
file.close();
|
||||
return cert;
|
||||
}
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \brief Action on UserConfig directory changes, looking for the certificate creation
|
||||
//****************************************************************************************************************************************************
|
||||
void GRPCClient::configFolderChanged()
|
||||
{
|
||||
QFile cert(_userConfigDir() + "/" + certFile);
|
||||
if (cert.exists())
|
||||
emit certIsReady();
|
||||
}
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[out] outError If the function returns false, this variable contains a description of the error.
|
||||
@ -71,9 +128,9 @@ bool GRPCClient::connectToServer(QString &outError)
|
||||
try
|
||||
{
|
||||
SslCredentialsOptions opts;
|
||||
opts.pem_root_certs += cert;
|
||||
opts.pem_root_certs += this->getServerCertificate();
|
||||
|
||||
channel_ = CreateChannel("localhost:9292", grpc::SslCredentials(opts));
|
||||
channel_ = CreateChannel("127.0.0.1:9292", grpc::SslCredentials(opts));
|
||||
if (!channel_)
|
||||
throw Exception("Channel creation failed.");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user