2016-04-05 13:30:22 +00:00
|
|
|
#include "command.hh"
|
|
|
|
#include "shared.hh"
|
|
|
|
#include "store-api.hh"
|
|
|
|
#include "thread-pool.hh"
|
|
|
|
|
|
|
|
#include <atomic>
|
|
|
|
|
|
|
|
using namespace nix;
|
|
|
|
|
|
|
|
struct CmdCopySigs : StorePathsCommand
|
|
|
|
{
|
|
|
|
Strings substituterUris;
|
|
|
|
|
|
|
|
CmdCopySigs()
|
|
|
|
{
|
2020-05-04 20:40:19 +00:00
|
|
|
addFlag({
|
|
|
|
.longName = "substituter",
|
|
|
|
.shortName = 's',
|
2021-01-13 13:18:04 +00:00
|
|
|
.description = "Use signatures from specified store.",
|
2020-05-04 20:40:19 +00:00
|
|
|
.labels = {"store-uri"},
|
|
|
|
.handler = {[&](std::string s) { substituterUris.push_back(s); }},
|
|
|
|
});
|
2016-04-05 13:30:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string description() override
|
|
|
|
{
|
|
|
|
return "copy path signatures from substituters (like binary caches)";
|
|
|
|
}
|
|
|
|
|
2019-12-05 18:11:09 +00:00
|
|
|
void run(ref<Store> store, StorePaths storePaths) override
|
2016-04-05 13:30:22 +00:00
|
|
|
{
|
|
|
|
if (substituterUris.empty())
|
2017-07-30 11:27:57 +00:00
|
|
|
throw UsageError("you must specify at least one substituter using '-s'");
|
2016-04-05 13:30:22 +00:00
|
|
|
|
|
|
|
// FIXME: factor out commonality with MixVerify.
|
|
|
|
std::vector<ref<Store>> substituters;
|
|
|
|
for (auto & s : substituterUris)
|
2016-09-02 10:35:48 +00:00
|
|
|
substituters.push_back(openStore(s));
|
2016-04-05 13:30:22 +00:00
|
|
|
|
|
|
|
ThreadPool pool;
|
|
|
|
|
2016-04-25 13:26:07 +00:00
|
|
|
std::string doneLabel = "done";
|
2016-04-05 13:30:22 +00:00
|
|
|
std::atomic<size_t> added{0};
|
|
|
|
|
2017-05-16 14:09:57 +00:00
|
|
|
//logger->setExpected(doneLabel, storePaths.size());
|
2016-04-05 13:30:22 +00:00
|
|
|
|
2019-12-05 18:11:09 +00:00
|
|
|
auto doPath = [&](const Path & storePathS) {
|
2017-07-30 11:27:57 +00:00
|
|
|
//Activity act(*logger, lvlInfo, format("getting signatures for '%s'") % storePath);
|
2016-04-05 13:30:22 +00:00
|
|
|
|
|
|
|
checkInterrupt();
|
|
|
|
|
2019-12-05 18:11:09 +00:00
|
|
|
auto storePath = store->parseStorePath(storePathS);
|
|
|
|
|
2016-04-05 13:30:22 +00:00
|
|
|
auto info = store->queryPathInfo(storePath);
|
|
|
|
|
|
|
|
StringSet newSigs;
|
|
|
|
|
|
|
|
for (auto & store2 : substituters) {
|
2016-04-19 16:50:15 +00:00
|
|
|
try {
|
2019-12-05 18:11:09 +00:00
|
|
|
auto info2 = store2->queryPathInfo(info->path);
|
2016-04-19 16:50:15 +00:00
|
|
|
|
|
|
|
/* Don't import signatures that don't match this
|
|
|
|
binary. */
|
|
|
|
if (info->narHash != info2->narHash ||
|
|
|
|
info->narSize != info2->narSize ||
|
|
|
|
info->references != info2->references)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (auto & sig : info2->sigs)
|
|
|
|
if (!info->sigs.count(sig))
|
|
|
|
newSigs.insert(sig);
|
|
|
|
} catch (InvalidPath &) {
|
|
|
|
}
|
2016-04-05 13:30:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!newSigs.empty()) {
|
|
|
|
store->addSignatures(storePath, newSigs);
|
|
|
|
added += newSigs.size();
|
|
|
|
}
|
|
|
|
|
2017-05-16 14:09:57 +00:00
|
|
|
//logger->incProgress(doneLabel);
|
2016-04-05 13:30:22 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
for (auto & storePath : storePaths)
|
2019-12-05 18:11:09 +00:00
|
|
|
pool.enqueue(std::bind(doPath, store->printStorePath(storePath)));
|
2016-04-05 13:30:22 +00:00
|
|
|
|
|
|
|
pool.process();
|
|
|
|
|
2019-12-05 18:11:09 +00:00
|
|
|
printInfo("imported %d signatures", added);
|
2016-04-05 13:30:22 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-07-24 18:38:56 +00:00
|
|
|
static auto rCmdCopySigs = registerCommand2<CmdCopySigs>({"store", "copy-sigs"});
|
2016-04-05 13:30:22 +00:00
|
|
|
|
2021-01-13 22:31:18 +00:00
|
|
|
struct CmdSign : StorePathsCommand
|
2016-04-05 14:39:29 +00:00
|
|
|
{
|
|
|
|
Path secretKeyFile;
|
|
|
|
|
2021-01-13 22:31:18 +00:00
|
|
|
CmdSign()
|
2016-04-05 14:39:29 +00:00
|
|
|
{
|
2020-05-04 20:40:19 +00:00
|
|
|
addFlag({
|
|
|
|
.longName = "key-file",
|
|
|
|
.shortName = 'k',
|
2021-01-13 13:18:04 +00:00
|
|
|
.description = "File containing the secret signing key.",
|
2020-05-04 20:40:19 +00:00
|
|
|
.labels = {"file"},
|
2020-05-10 19:35:07 +00:00
|
|
|
.handler = {&secretKeyFile},
|
|
|
|
.completer = completePath
|
2020-05-04 20:40:19 +00:00
|
|
|
});
|
2016-04-05 14:39:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string description() override
|
|
|
|
{
|
|
|
|
return "sign the specified paths";
|
|
|
|
}
|
|
|
|
|
2019-12-05 18:11:09 +00:00
|
|
|
void run(ref<Store> store, StorePaths storePaths) override
|
2016-04-05 14:39:29 +00:00
|
|
|
{
|
|
|
|
if (secretKeyFile.empty())
|
2017-07-30 11:27:57 +00:00
|
|
|
throw UsageError("you must specify a secret key file using '-k'");
|
2016-04-05 14:39:29 +00:00
|
|
|
|
|
|
|
SecretKey secretKey(readFile(secretKeyFile));
|
|
|
|
|
|
|
|
size_t added{0};
|
|
|
|
|
|
|
|
for (auto & storePath : storePaths) {
|
|
|
|
auto info = store->queryPathInfo(storePath);
|
|
|
|
|
2016-04-19 16:50:15 +00:00
|
|
|
auto info2(*info);
|
2016-04-05 14:39:29 +00:00
|
|
|
info2.sigs.clear();
|
2019-12-05 18:11:09 +00:00
|
|
|
info2.sign(*store, secretKey);
|
2016-04-05 14:39:29 +00:00
|
|
|
assert(!info2.sigs.empty());
|
|
|
|
|
2016-04-19 16:50:15 +00:00
|
|
|
if (!info->sigs.count(*info2.sigs.begin())) {
|
2016-04-05 14:39:29 +00:00
|
|
|
store->addSignatures(storePath, info2.sigs);
|
|
|
|
added++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-05 18:11:09 +00:00
|
|
|
printInfo("added %d signatures", added);
|
2016-04-05 14:39:29 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-01-13 22:31:18 +00:00
|
|
|
static auto rCmdSign = registerCommand2<CmdSign>({"store", "sign"});
|
2021-01-06 16:41:16 +00:00
|
|
|
|
|
|
|
struct CmdKeyGenerateSecret : Command
|
|
|
|
{
|
|
|
|
std::optional<std::string> keyName;
|
|
|
|
|
|
|
|
CmdKeyGenerateSecret()
|
|
|
|
{
|
|
|
|
addFlag({
|
|
|
|
.longName = "key-name",
|
2021-01-13 13:18:04 +00:00
|
|
|
.description = "Identifier of the key (e.g. `cache.example.org-1`).",
|
2021-01-06 16:41:16 +00:00
|
|
|
.labels = {"name"},
|
|
|
|
.handler = {&keyName},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string description() override
|
|
|
|
{
|
|
|
|
return "generate a secret key for signing store paths";
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string doc() override
|
|
|
|
{
|
|
|
|
return
|
|
|
|
#include "key-generate-secret.md"
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
void run() override
|
|
|
|
{
|
|
|
|
if (!keyName)
|
|
|
|
throw UsageError("required argument '--key-name' is missing");
|
|
|
|
|
|
|
|
std::cout << SecretKey::generate(*keyName).to_string();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct CmdKeyConvertSecretToPublic : Command
|
|
|
|
{
|
|
|
|
std::string description() override
|
|
|
|
{
|
|
|
|
return "generate a public key for verifying store paths from a secret key read from standard input";
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string doc() override
|
|
|
|
{
|
|
|
|
return
|
|
|
|
#include "key-convert-secret-to-public.md"
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
void run() override
|
|
|
|
{
|
|
|
|
SecretKey secretKey(drainFD(STDIN_FILENO));
|
|
|
|
std::cout << secretKey.toPublicKey().to_string();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct CmdKey : NixMultiCommand
|
|
|
|
{
|
|
|
|
CmdKey()
|
|
|
|
: MultiCommand({
|
|
|
|
{"generate-secret", []() { return make_ref<CmdKeyGenerateSecret>(); }},
|
|
|
|
{"convert-secret-to-public", []() { return make_ref<CmdKeyConvertSecretToPublic>(); }},
|
|
|
|
})
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string description() override
|
|
|
|
{
|
|
|
|
return "generate and convert Nix signing keys";
|
|
|
|
}
|
|
|
|
|
|
|
|
Category category() override { return catUtility; }
|
|
|
|
|
|
|
|
void run() override
|
|
|
|
{
|
|
|
|
if (!command)
|
|
|
|
throw UsageError("'nix flake' requires a sub-command.");
|
|
|
|
settings.requireExperimentalFeature("flakes");
|
|
|
|
command->second->prepare();
|
|
|
|
command->second->run();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static auto rCmdKey = registerCommand<CmdKey>("key");
|