Compare commits

...

14 commits

Author SHA1 Message Date
Armaël Guéneau
16e17375c5 doc: fix typo in connect/backup.md 2024-03-03 13:02:56 +01:00
95ab36aae7 Merge pull request 'Bump version to v0.9.2' (#747) from rel-0.9.2 into main
Reviewed-on: Deuxfleurs/garage#747
2024-03-01 16:20:28 +00:00
6a7623e90d
[rel-0.9.2] Bump version to v0.9.2 2024-03-01 16:54:39 +01:00
70b9904e91 Merge pull request 'AWS signatures v4: don't actually check Content-Type is signed' (#745) from fix-signed-headers into main
Reviewed-on: Deuxfleurs/garage#745
2024-03-01 12:50:15 +00:00
a36248a169
[fix-signed-headers] aws signatures v4: don't actually check Content-Type is signed
This page of the AWS docs indicate that Content-Type should be part of
the CanonicalHeaders (and therefore SignedHeaders) strings in signature
calculation:

https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html

However, testing with Minio Client revealed that it did not sign the
Content-Type header, and therefore we broke CI by expecting it to be
signed. With this commit, we don't mandate Content-Type to be signed
anymore, for better compatibility with the ecosystem. Testing against
the official behavior of S3 on AWS has not been done.
2024-03-01 13:12:18 +01:00
b8c7a560ef Merge pull request 'Fix potential timing side-channels in authentication mechanisms' (#737) from fix-auth-ct-eq into main
Reviewed-on: Deuxfleurs/garage#737
2024-02-29 14:04:38 +00:00
d3cf560e5c Merge pull request 'Docs: add default metrics_token in quick start + uniformize use of base64' (#739) from doc-default-token into main
Reviewed-on: Deuxfleurs/garage#739
2024-02-29 12:20:24 +00:00
73b11eb17c
[doc-default-token] add default metrics_token in quick start + uniformize use of base64 2024-02-29 13:17:36 +01:00
6d33e721c4
[fix-auth-ct-eq] use consant time comparison for awsv4 signature verification 2024-02-29 13:07:18 +01:00
eaac4924ef
[fix-auth-ct-eq] use argon2 hashing and verification for admin/metrics token checking 2024-02-29 13:07:15 +01:00
02005055ae Merge pull request 'Mention deduplication and compression in features page' (#736) from doc-dedup into main
Reviewed-on: Deuxfleurs/garage#736
2024-02-28 12:49:26 +00:00
a294dd9473
[doc-dedup] reorder features, move no-RAFT down 2024-02-28 13:48:45 +01:00
947973982d
[doc-dedup] fix #rpc_bind_outgoing anchor in config page 2024-02-28 13:45:30 +01:00
dc995059aa
[doc-dedup] mention deduplication and compression in features page 2024-02-28 13:43:30 +01:00
22 changed files with 246 additions and 154 deletions

44
Cargo.lock generated
View file

@ -120,6 +120,18 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6"
[[package]]
name = "argon2"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
dependencies = [
"base64ct",
"blake2",
"cpufeatures",
"password-hash",
]
[[package]] [[package]]
name = "arrayvec" name = "arrayvec"
version = "0.5.2" version = "0.5.2"
@ -1265,7 +1277,7 @@ dependencies = [
[[package]] [[package]]
name = "garage" name = "garage"
version = "0.9.1" version = "0.9.2"
dependencies = [ dependencies = [
"assert-json-diff", "assert-json-diff",
"async-trait", "async-trait",
@ -1319,8 +1331,9 @@ dependencies = [
[[package]] [[package]]
name = "garage_api" name = "garage_api"
version = "0.9.1" version = "0.9.2"
dependencies = [ dependencies = [
"argon2",
"async-trait", "async-trait",
"base64 0.21.7", "base64 0.21.7",
"bytes", "bytes",
@ -1367,7 +1380,7 @@ dependencies = [
[[package]] [[package]]
name = "garage_block" name = "garage_block"
version = "0.9.1" version = "0.9.2"
dependencies = [ dependencies = [
"arc-swap", "arc-swap",
"async-compression", "async-compression",
@ -1394,7 +1407,7 @@ dependencies = [
[[package]] [[package]]
name = "garage_db" name = "garage_db"
version = "0.9.1" version = "0.9.2"
dependencies = [ dependencies = [
"err-derive", "err-derive",
"heed", "heed",
@ -1407,7 +1420,7 @@ dependencies = [
[[package]] [[package]]
name = "garage_model" name = "garage_model"
version = "0.9.1" version = "0.9.2"
dependencies = [ dependencies = [
"arc-swap", "arc-swap",
"async-trait", "async-trait",
@ -1435,7 +1448,7 @@ dependencies = [
[[package]] [[package]]
name = "garage_net" name = "garage_net"
version = "0.9.1" version = "0.9.2"
dependencies = [ dependencies = [
"arc-swap", "arc-swap",
"async-trait", "async-trait",
@ -1461,7 +1474,7 @@ dependencies = [
[[package]] [[package]]
name = "garage_rpc" name = "garage_rpc"
version = "0.9.1" version = "0.9.2"
dependencies = [ dependencies = [
"arc-swap", "arc-swap",
"async-trait", "async-trait",
@ -1496,7 +1509,7 @@ dependencies = [
[[package]] [[package]]
name = "garage_table" name = "garage_table"
version = "0.9.1" version = "0.9.2"
dependencies = [ dependencies = [
"arc-swap", "arc-swap",
"async-trait", "async-trait",
@ -1518,7 +1531,7 @@ dependencies = [
[[package]] [[package]]
name = "garage_util" name = "garage_util"
version = "0.9.1" version = "0.9.2"
dependencies = [ dependencies = [
"arc-swap", "arc-swap",
"async-trait", "async-trait",
@ -1552,7 +1565,7 @@ dependencies = [
[[package]] [[package]]
name = "garage_web" name = "garage_web"
version = "0.9.1" version = "0.9.2"
dependencies = [ dependencies = [
"err-derive", "err-derive",
"futures", "futures",
@ -2799,6 +2812,17 @@ dependencies = [
"regex", "regex",
] ]
[[package]]
name = "password-hash"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
dependencies = [
"base64ct",
"rand_core",
"subtle",
]
[[package]] [[package]]
name = "paste" name = "paste"
version = "1.0.14" version = "1.0.14"

175
Cargo.nix
View file

@ -34,7 +34,7 @@ args@{
ignoreLockHash, ignoreLockHash,
}: }:
let let
nixifiedLockHash = "9377d18da3b48658f9d8b2070db135db2d9ac6d9c692d6656948b765348498cc"; nixifiedLockHash = "3e1e0730302ee7d1f4185a13ad0086392562eb7cb962b1212801847887beaa47";
workspaceSrc = if args.workspaceSrc == null then ./. else args.workspaceSrc; workspaceSrc = if args.workspaceSrc == null then ./. else args.workspaceSrc;
currentLockHash = builtins.hashFile "sha256" (workspaceSrc + /Cargo.lock); currentLockHash = builtins.hashFile "sha256" (workspaceSrc + /Cargo.lock);
lockHashIgnored = if ignoreLockHash lockHashIgnored = if ignoreLockHash
@ -58,17 +58,17 @@ in
{ {
cargo2nixVersion = "0.11.0"; cargo2nixVersion = "0.11.0";
workspace = { workspace = {
garage_db = rustPackages.unknown.garage_db."0.9.1"; garage_db = rustPackages.unknown.garage_db."0.9.2";
garage_util = rustPackages.unknown.garage_util."0.9.1"; garage_util = rustPackages.unknown.garage_util."0.9.2";
garage_net = rustPackages.unknown.garage_net."0.9.1"; garage_net = rustPackages.unknown.garage_net."0.9.2";
garage_rpc = rustPackages.unknown.garage_rpc."0.9.1"; garage_rpc = rustPackages.unknown.garage_rpc."0.9.2";
format_table = rustPackages.unknown.format_table."0.1.1"; format_table = rustPackages.unknown.format_table."0.1.1";
garage_table = rustPackages.unknown.garage_table."0.9.1"; garage_table = rustPackages.unknown.garage_table."0.9.2";
garage_block = rustPackages.unknown.garage_block."0.9.1"; garage_block = rustPackages.unknown.garage_block."0.9.2";
garage_model = rustPackages.unknown.garage_model."0.9.1"; garage_model = rustPackages.unknown.garage_model."0.9.2";
garage_api = rustPackages.unknown.garage_api."0.9.1"; garage_api = rustPackages.unknown.garage_api."0.9.2";
garage_web = rustPackages.unknown.garage_web."0.9.1"; garage_web = rustPackages.unknown.garage_web."0.9.2";
garage = rustPackages.unknown.garage."0.9.1"; garage = rustPackages.unknown.garage."0.9.2";
k2v-client = rustPackages.unknown.k2v-client."0.0.4"; k2v-client = rustPackages.unknown.k2v-client."0.0.4";
}; };
"registry+https://github.com/rust-lang/crates.io-index".addr2line."0.21.0" = overridableMkRustCrate (profileName: rec { "registry+https://github.com/rust-lang/crates.io-index".addr2line."0.21.0" = overridableMkRustCrate (profileName: rec {
@ -235,6 +235,25 @@ in
src = fetchCratesIo { inherit name version; sha256 = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6"; }; src = fetchCratesIo { inherit name version; sha256 = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6"; };
}); });
"registry+https://github.com/rust-lang/crates.io-index".argon2."0.5.3" = overridableMkRustCrate (profileName: rec {
name = "argon2";
version = "0.5.3";
registry = "registry+https://github.com/rust-lang/crates.io-index";
src = fetchCratesIo { inherit name version; sha256 = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"; };
features = builtins.concatLists [
[ "alloc" ]
[ "default" ]
[ "password-hash" ]
[ "rand" ]
];
dependencies = {
base64ct = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".base64ct."1.6.0" { inherit profileName; }).out;
blake2 = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".blake2."0.10.6" { inherit profileName; }).out;
${ if hostPlatform.parsed.cpu.name == "i686" || hostPlatform.parsed.cpu.name == "x86_64" then "cpufeatures" else null } = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".cpufeatures."0.2.12" { inherit profileName; }).out;
password_hash = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".password-hash."0.5.0" { inherit profileName; }).out;
};
});
"registry+https://github.com/rust-lang/crates.io-index".arrayvec."0.5.2" = overridableMkRustCrate (profileName: rec { "registry+https://github.com/rust-lang/crates.io-index".arrayvec."0.5.2" = overridableMkRustCrate (profileName: rec {
name = "arrayvec"; name = "arrayvec";
version = "0.5.2"; version = "0.5.2";
@ -1852,9 +1871,9 @@ in
}; };
}); });
"unknown".garage."0.9.1" = overridableMkRustCrate (profileName: rec { "unknown".garage."0.9.2" = overridableMkRustCrate (profileName: rec {
name = "garage"; name = "garage";
version = "0.9.1"; version = "0.9.2";
registry = "unknown"; registry = "unknown";
src = fetchCrateLocal (workspaceSrc + "/src/garage"); src = fetchCrateLocal (workspaceSrc + "/src/garage");
features = builtins.concatLists [ features = builtins.concatLists [
@ -1881,15 +1900,15 @@ in
format_table = (rustPackages."unknown".format_table."0.1.1" { inherit profileName; }).out; format_table = (rustPackages."unknown".format_table."0.1.1" { inherit profileName; }).out;
futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.30" { inherit profileName; }).out; futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.30" { inherit profileName; }).out;
futures_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-util."0.3.30" { inherit profileName; }).out; futures_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-util."0.3.30" { inherit profileName; }).out;
garage_api = (rustPackages."unknown".garage_api."0.9.1" { inherit profileName; }).out; garage_api = (rustPackages."unknown".garage_api."0.9.2" { inherit profileName; }).out;
garage_block = (rustPackages."unknown".garage_block."0.9.1" { inherit profileName; }).out; garage_block = (rustPackages."unknown".garage_block."0.9.2" { inherit profileName; }).out;
garage_db = (rustPackages."unknown".garage_db."0.9.1" { inherit profileName; }).out; garage_db = (rustPackages."unknown".garage_db."0.9.2" { inherit profileName; }).out;
garage_model = (rustPackages."unknown".garage_model."0.9.1" { inherit profileName; }).out; garage_model = (rustPackages."unknown".garage_model."0.9.2" { inherit profileName; }).out;
garage_net = (rustPackages."unknown".garage_net."0.9.1" { inherit profileName; }).out; garage_net = (rustPackages."unknown".garage_net."0.9.2" { inherit profileName; }).out;
garage_rpc = (rustPackages."unknown".garage_rpc."0.9.1" { inherit profileName; }).out; garage_rpc = (rustPackages."unknown".garage_rpc."0.9.2" { inherit profileName; }).out;
garage_table = (rustPackages."unknown".garage_table."0.9.1" { inherit profileName; }).out; garage_table = (rustPackages."unknown".garage_table."0.9.2" { inherit profileName; }).out;
garage_util = (rustPackages."unknown".garage_util."0.9.1" { inherit profileName; }).out; garage_util = (rustPackages."unknown".garage_util."0.9.2" { inherit profileName; }).out;
garage_web = (rustPackages."unknown".garage_web."0.9.1" { inherit profileName; }).out; garage_web = (rustPackages."unknown".garage_web."0.9.2" { inherit profileName; }).out;
git_version = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".git-version."0.3.9" { inherit profileName; }).out; git_version = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".git-version."0.3.9" { inherit profileName; }).out;
hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out; hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out;
sodiumoxide = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".kuska-sodiumoxide."0.2.5-0" { inherit profileName; }).out; sodiumoxide = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".kuska-sodiumoxide."0.2.5-0" { inherit profileName; }).out;
@ -1927,9 +1946,9 @@ in
}; };
}); });
"unknown".garage_api."0.9.1" = overridableMkRustCrate (profileName: rec { "unknown".garage_api."0.9.2" = overridableMkRustCrate (profileName: rec {
name = "garage_api"; name = "garage_api";
version = "0.9.1"; version = "0.9.2";
registry = "unknown"; registry = "unknown";
src = fetchCrateLocal (workspaceSrc + "/src/api"); src = fetchCrateLocal (workspaceSrc + "/src/api");
features = builtins.concatLists [ features = builtins.concatLists [
@ -1939,6 +1958,7 @@ in
(lib.optional (rootFeatures' ? "garage/default" || rootFeatures' ? "garage/metrics" || rootFeatures' ? "garage_api/metrics" || rootFeatures' ? "garage_api/prometheus") "prometheus") (lib.optional (rootFeatures' ? "garage/default" || rootFeatures' ? "garage/metrics" || rootFeatures' ? "garage_api/metrics" || rootFeatures' ? "garage_api/prometheus") "prometheus")
]; ];
dependencies = { dependencies = {
argon2 = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".argon2."0.5.3" { inherit profileName; }).out;
async_trait = (buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".async-trait."0.1.77" { profileName = "__noProfile"; }).out; async_trait = (buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".async-trait."0.1.77" { profileName = "__noProfile"; }).out;
base64 = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".base64."0.21.7" { inherit profileName; }).out; base64 = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".base64."0.21.7" { inherit profileName; }).out;
bytes = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".bytes."1.5.0" { inherit profileName; }).out; bytes = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".bytes."1.5.0" { inherit profileName; }).out;
@ -1948,12 +1968,12 @@ in
form_urlencoded = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".form_urlencoded."1.2.1" { inherit profileName; }).out; form_urlencoded = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".form_urlencoded."1.2.1" { inherit profileName; }).out;
futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.30" { inherit profileName; }).out; futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.30" { inherit profileName; }).out;
futures_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-util."0.3.30" { inherit profileName; }).out; futures_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-util."0.3.30" { inherit profileName; }).out;
garage_block = (rustPackages."unknown".garage_block."0.9.1" { inherit profileName; }).out; garage_block = (rustPackages."unknown".garage_block."0.9.2" { inherit profileName; }).out;
garage_model = (rustPackages."unknown".garage_model."0.9.1" { inherit profileName; }).out; garage_model = (rustPackages."unknown".garage_model."0.9.2" { inherit profileName; }).out;
garage_net = (rustPackages."unknown".garage_net."0.9.1" { inherit profileName; }).out; garage_net = (rustPackages."unknown".garage_net."0.9.2" { inherit profileName; }).out;
garage_rpc = (rustPackages."unknown".garage_rpc."0.9.1" { inherit profileName; }).out; garage_rpc = (rustPackages."unknown".garage_rpc."0.9.2" { inherit profileName; }).out;
garage_table = (rustPackages."unknown".garage_table."0.9.1" { inherit profileName; }).out; garage_table = (rustPackages."unknown".garage_table."0.9.2" { inherit profileName; }).out;
garage_util = (rustPackages."unknown".garage_util."0.9.1" { inherit profileName; }).out; garage_util = (rustPackages."unknown".garage_util."0.9.2" { inherit profileName; }).out;
hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out; hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out;
hmac = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hmac."0.12.1" { inherit profileName; }).out; hmac = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hmac."0.12.1" { inherit profileName; }).out;
http = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".http."1.0.0" { inherit profileName; }).out; http = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".http."1.0.0" { inherit profileName; }).out;
@ -1984,9 +2004,9 @@ in
}; };
}); });
"unknown".garage_block."0.9.1" = overridableMkRustCrate (profileName: rec { "unknown".garage_block."0.9.2" = overridableMkRustCrate (profileName: rec {
name = "garage_block"; name = "garage_block";
version = "0.9.1"; version = "0.9.2";
registry = "unknown"; registry = "unknown";
src = fetchCrateLocal (workspaceSrc + "/src/block"); src = fetchCrateLocal (workspaceSrc + "/src/block");
features = builtins.concatLists [ features = builtins.concatLists [
@ -2000,11 +2020,11 @@ in
bytesize = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".bytesize."1.3.0" { inherit profileName; }).out; bytesize = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".bytesize."1.3.0" { inherit profileName; }).out;
futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.30" { inherit profileName; }).out; futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.30" { inherit profileName; }).out;
futures_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-util."0.3.30" { inherit profileName; }).out; futures_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-util."0.3.30" { inherit profileName; }).out;
garage_db = (rustPackages."unknown".garage_db."0.9.1" { inherit profileName; }).out; garage_db = (rustPackages."unknown".garage_db."0.9.2" { inherit profileName; }).out;
garage_net = (rustPackages."unknown".garage_net."0.9.1" { inherit profileName; }).out; garage_net = (rustPackages."unknown".garage_net."0.9.2" { inherit profileName; }).out;
garage_rpc = (rustPackages."unknown".garage_rpc."0.9.1" { inherit profileName; }).out; garage_rpc = (rustPackages."unknown".garage_rpc."0.9.2" { inherit profileName; }).out;
garage_table = (rustPackages."unknown".garage_table."0.9.1" { inherit profileName; }).out; garage_table = (rustPackages."unknown".garage_table."0.9.2" { inherit profileName; }).out;
garage_util = (rustPackages."unknown".garage_util."0.9.1" { inherit profileName; }).out; garage_util = (rustPackages."unknown".garage_util."0.9.2" { inherit profileName; }).out;
hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out; hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out;
opentelemetry = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".opentelemetry."0.17.0" { inherit profileName; }).out; opentelemetry = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".opentelemetry."0.17.0" { inherit profileName; }).out;
rand = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".rand."0.8.5" { inherit profileName; }).out; rand = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".rand."0.8.5" { inherit profileName; }).out;
@ -2017,9 +2037,9 @@ in
}; };
}); });
"unknown".garage_db."0.9.1" = overridableMkRustCrate (profileName: rec { "unknown".garage_db."0.9.2" = overridableMkRustCrate (profileName: rec {
name = "garage_db"; name = "garage_db";
version = "0.9.1"; version = "0.9.2";
registry = "unknown"; registry = "unknown";
src = fetchCrateLocal (workspaceSrc + "/src/db"); src = fetchCrateLocal (workspaceSrc + "/src/db");
features = builtins.concatLists [ features = builtins.concatLists [
@ -2044,9 +2064,9 @@ in
}; };
}); });
"unknown".garage_model."0.9.1" = overridableMkRustCrate (profileName: rec { "unknown".garage_model."0.9.2" = overridableMkRustCrate (profileName: rec {
name = "garage_model"; name = "garage_model";
version = "0.9.1"; version = "0.9.2";
registry = "unknown"; registry = "unknown";
src = fetchCrateLocal (workspaceSrc + "/src/model"); src = fetchCrateLocal (workspaceSrc + "/src/model");
features = builtins.concatLists [ features = builtins.concatLists [
@ -2065,12 +2085,12 @@ in
err_derive = (buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".err-derive."0.3.1" { profileName = "__noProfile"; }).out; err_derive = (buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".err-derive."0.3.1" { profileName = "__noProfile"; }).out;
futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.30" { inherit profileName; }).out; futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.30" { inherit profileName; }).out;
futures_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-util."0.3.30" { inherit profileName; }).out; futures_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-util."0.3.30" { inherit profileName; }).out;
garage_block = (rustPackages."unknown".garage_block."0.9.1" { inherit profileName; }).out; garage_block = (rustPackages."unknown".garage_block."0.9.2" { inherit profileName; }).out;
garage_db = (rustPackages."unknown".garage_db."0.9.1" { inherit profileName; }).out; garage_db = (rustPackages."unknown".garage_db."0.9.2" { inherit profileName; }).out;
garage_net = (rustPackages."unknown".garage_net."0.9.1" { inherit profileName; }).out; garage_net = (rustPackages."unknown".garage_net."0.9.2" { inherit profileName; }).out;
garage_rpc = (rustPackages."unknown".garage_rpc."0.9.1" { inherit profileName; }).out; garage_rpc = (rustPackages."unknown".garage_rpc."0.9.2" { inherit profileName; }).out;
garage_table = (rustPackages."unknown".garage_table."0.9.1" { inherit profileName; }).out; garage_table = (rustPackages."unknown".garage_table."0.9.2" { inherit profileName; }).out;
garage_util = (rustPackages."unknown".garage_util."0.9.1" { inherit profileName; }).out; garage_util = (rustPackages."unknown".garage_util."0.9.2" { inherit profileName; }).out;
hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out; hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out;
opentelemetry = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".opentelemetry."0.17.0" { inherit profileName; }).out; opentelemetry = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".opentelemetry."0.17.0" { inherit profileName; }).out;
rand = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".rand."0.8.5" { inherit profileName; }).out; rand = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".rand."0.8.5" { inherit profileName; }).out;
@ -2082,9 +2102,9 @@ in
}; };
}); });
"unknown".garage_net."0.9.1" = overridableMkRustCrate (profileName: rec { "unknown".garage_net."0.9.2" = overridableMkRustCrate (profileName: rec {
name = "garage_net"; name = "garage_net";
version = "0.9.1"; version = "0.9.2";
registry = "unknown"; registry = "unknown";
src = fetchCrateLocal (workspaceSrc + "/src/net"); src = fetchCrateLocal (workspaceSrc + "/src/net");
features = builtins.concatLists [ features = builtins.concatLists [
@ -2119,9 +2139,9 @@ in
}; };
}); });
"unknown".garage_rpc."0.9.1" = overridableMkRustCrate (profileName: rec { "unknown".garage_rpc."0.9.2" = overridableMkRustCrate (profileName: rec {
name = "garage_rpc"; name = "garage_rpc";
version = "0.9.1"; version = "0.9.2";
registry = "unknown"; registry = "unknown";
src = fetchCrateLocal (workspaceSrc + "/src/rpc"); src = fetchCrateLocal (workspaceSrc + "/src/rpc");
features = builtins.concatLists [ features = builtins.concatLists [
@ -2143,9 +2163,9 @@ in
format_table = (rustPackages."unknown".format_table."0.1.1" { inherit profileName; }).out; format_table = (rustPackages."unknown".format_table."0.1.1" { inherit profileName; }).out;
futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.30" { inherit profileName; }).out; futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.30" { inherit profileName; }).out;
futures_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-util."0.3.30" { inherit profileName; }).out; futures_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-util."0.3.30" { inherit profileName; }).out;
garage_db = (rustPackages."unknown".garage_db."0.9.1" { inherit profileName; }).out; garage_db = (rustPackages."unknown".garage_db."0.9.2" { inherit profileName; }).out;
garage_net = (rustPackages."unknown".garage_net."0.9.1" { inherit profileName; }).out; garage_net = (rustPackages."unknown".garage_net."0.9.2" { inherit profileName; }).out;
garage_util = (rustPackages."unknown".garage_util."0.9.1" { inherit profileName; }).out; garage_util = (rustPackages."unknown".garage_util."0.9.2" { inherit profileName; }).out;
gethostname = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".gethostname."0.4.3" { inherit profileName; }).out; gethostname = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".gethostname."0.4.3" { inherit profileName; }).out;
hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out; hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out;
itertools = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".itertools."0.12.1" { inherit profileName; }).out; itertools = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".itertools."0.12.1" { inherit profileName; }).out;
@ -2167,9 +2187,9 @@ in
}; };
}); });
"unknown".garage_table."0.9.1" = overridableMkRustCrate (profileName: rec { "unknown".garage_table."0.9.2" = overridableMkRustCrate (profileName: rec {
name = "garage_table"; name = "garage_table";
version = "0.9.1"; version = "0.9.2";
registry = "unknown"; registry = "unknown";
src = fetchCrateLocal (workspaceSrc + "/src/table"); src = fetchCrateLocal (workspaceSrc + "/src/table");
dependencies = { dependencies = {
@ -2178,9 +2198,9 @@ in
bytes = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".bytes."1.5.0" { inherit profileName; }).out; bytes = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".bytes."1.5.0" { inherit profileName; }).out;
futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.30" { inherit profileName; }).out; futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.30" { inherit profileName; }).out;
futures_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-util."0.3.30" { inherit profileName; }).out; futures_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures-util."0.3.30" { inherit profileName; }).out;
garage_db = (rustPackages."unknown".garage_db."0.9.1" { inherit profileName; }).out; garage_db = (rustPackages."unknown".garage_db."0.9.2" { inherit profileName; }).out;
garage_rpc = (rustPackages."unknown".garage_rpc."0.9.1" { inherit profileName; }).out; garage_rpc = (rustPackages."unknown".garage_rpc."0.9.2" { inherit profileName; }).out;
garage_util = (rustPackages."unknown".garage_util."0.9.1" { inherit profileName; }).out; garage_util = (rustPackages."unknown".garage_util."0.9.2" { inherit profileName; }).out;
hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out; hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out;
hexdump = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hexdump."0.1.1" { inherit profileName; }).out; hexdump = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hexdump."0.1.1" { inherit profileName; }).out;
opentelemetry = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".opentelemetry."0.17.0" { inherit profileName; }).out; opentelemetry = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".opentelemetry."0.17.0" { inherit profileName; }).out;
@ -2192,9 +2212,9 @@ in
}; };
}); });
"unknown".garage_util."0.9.1" = overridableMkRustCrate (profileName: rec { "unknown".garage_util."0.9.2" = overridableMkRustCrate (profileName: rec {
name = "garage_util"; name = "garage_util";
version = "0.9.1"; version = "0.9.2";
registry = "unknown"; registry = "unknown";
src = fetchCrateLocal (workspaceSrc + "/src/util"); src = fetchCrateLocal (workspaceSrc + "/src/util");
features = builtins.concatLists [ features = builtins.concatLists [
@ -2210,8 +2230,8 @@ in
digest = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".digest."0.10.7" { inherit profileName; }).out; digest = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".digest."0.10.7" { inherit profileName; }).out;
err_derive = (buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".err-derive."0.3.1" { profileName = "__noProfile"; }).out; err_derive = (buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".err-derive."0.3.1" { profileName = "__noProfile"; }).out;
futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.30" { inherit profileName; }).out; futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.30" { inherit profileName; }).out;
garage_db = (rustPackages."unknown".garage_db."0.9.1" { inherit profileName; }).out; garage_db = (rustPackages."unknown".garage_db."0.9.2" { inherit profileName; }).out;
garage_net = (rustPackages."unknown".garage_net."0.9.1" { inherit profileName; }).out; garage_net = (rustPackages."unknown".garage_net."0.9.2" { inherit profileName; }).out;
hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out; hex = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hex."0.4.3" { inherit profileName; }).out;
hexdump = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hexdump."0.1.1" { inherit profileName; }).out; hexdump = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hexdump."0.1.1" { inherit profileName; }).out;
http = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".http."1.0.0" { inherit profileName; }).out; http = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".http."1.0.0" { inherit profileName; }).out;
@ -2236,18 +2256,18 @@ in
}; };
}); });
"unknown".garage_web."0.9.1" = overridableMkRustCrate (profileName: rec { "unknown".garage_web."0.9.2" = overridableMkRustCrate (profileName: rec {
name = "garage_web"; name = "garage_web";
version = "0.9.1"; version = "0.9.2";
registry = "unknown"; registry = "unknown";
src = fetchCrateLocal (workspaceSrc + "/src/web"); src = fetchCrateLocal (workspaceSrc + "/src/web");
dependencies = { dependencies = {
err_derive = (buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".err-derive."0.3.1" { profileName = "__noProfile"; }).out; err_derive = (buildRustPackages."registry+https://github.com/rust-lang/crates.io-index".err-derive."0.3.1" { profileName = "__noProfile"; }).out;
futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.30" { inherit profileName; }).out; futures = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".futures."0.3.30" { inherit profileName; }).out;
garage_api = (rustPackages."unknown".garage_api."0.9.1" { inherit profileName; }).out; garage_api = (rustPackages."unknown".garage_api."0.9.2" { inherit profileName; }).out;
garage_model = (rustPackages."unknown".garage_model."0.9.1" { inherit profileName; }).out; garage_model = (rustPackages."unknown".garage_model."0.9.2" { inherit profileName; }).out;
garage_table = (rustPackages."unknown".garage_table."0.9.1" { inherit profileName; }).out; garage_table = (rustPackages."unknown".garage_table."0.9.2" { inherit profileName; }).out;
garage_util = (rustPackages."unknown".garage_util."0.9.1" { inherit profileName; }).out; garage_util = (rustPackages."unknown".garage_util."0.9.2" { inherit profileName; }).out;
http = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".http."1.0.0" { inherit profileName; }).out; http = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".http."1.0.0" { inherit profileName; }).out;
http_body_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".http-body-util."0.1.0" { inherit profileName; }).out; http_body_util = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".http-body-util."0.1.0" { inherit profileName; }).out;
hyper = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hyper."1.1.0" { inherit profileName; }).out; hyper = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".hyper."1.1.0" { inherit profileName; }).out;
@ -3989,6 +4009,23 @@ in
}; };
}); });
"registry+https://github.com/rust-lang/crates.io-index".password-hash."0.5.0" = overridableMkRustCrate (profileName: rec {
name = "password-hash";
version = "0.5.0";
registry = "registry+https://github.com/rust-lang/crates.io-index";
src = fetchCratesIo { inherit name version; sha256 = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"; };
features = builtins.concatLists [
[ "alloc" ]
[ "default" ]
[ "rand_core" ]
];
dependencies = {
base64ct = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".base64ct."1.6.0" { inherit profileName; }).out;
rand_core = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".rand_core."0.6.4" { inherit profileName; }).out;
subtle = (rustPackages."registry+https://github.com/rust-lang/crates.io-index".subtle."2.5.0" { inherit profileName; }).out;
};
});
"registry+https://github.com/rust-lang/crates.io-index".paste."1.0.14" = overridableMkRustCrate (profileName: rec { "registry+https://github.com/rust-lang/crates.io-index".paste."1.0.14" = overridableMkRustCrate (profileName: rec {
name = "paste"; name = "paste";
version = "1.0.14"; version = "1.0.14";

View file

@ -21,19 +21,20 @@ default-members = ["src/garage"]
# Internal Garage crates # Internal Garage crates
format_table = { version = "0.1.1", path = "src/format-table" } format_table = { version = "0.1.1", path = "src/format-table" }
garage_api = { version = "0.9.1", path = "src/api" } garage_api = { version = "0.9.2", path = "src/api" }
garage_block = { version = "0.9.1", path = "src/block" } garage_block = { version = "0.9.2", path = "src/block" }
garage_db = { version = "0.9.1", path = "src/db", default-features = false } garage_db = { version = "0.9.2", path = "src/db", default-features = false }
garage_model = { version = "0.9.1", path = "src/model", default-features = false } garage_model = { version = "0.9.2", path = "src/model", default-features = false }
garage_net = { version = "0.9.1", path = "src/net" } garage_net = { version = "0.9.2", path = "src/net" }
garage_rpc = { version = "0.9.1", path = "src/rpc" } garage_rpc = { version = "0.9.2", path = "src/rpc" }
garage_table = { version = "0.9.1", path = "src/table" } garage_table = { version = "0.9.2", path = "src/table" }
garage_util = { version = "0.9.1", path = "src/util" } garage_util = { version = "0.9.2", path = "src/util" }
garage_web = { version = "0.9.1", path = "src/web" } garage_web = { version = "0.9.2", path = "src/web" }
k2v-client = { version = "0.0.4", path = "src/k2v-client" } k2v-client = { version = "0.0.4", path = "src/k2v-client" }
# External crates from crates.io # External crates from crates.io
arc-swap = "1.0" arc-swap = "1.0"
argon2 = "0.5"
async-trait = "0.1.7" async-trait = "0.1.7"
backtrace = "0.3" backtrace = "0.3"
base64 = "0.21" base64 = "0.21"

View file

@ -55,8 +55,8 @@ Create your key and bucket:
```bash ```bash
garage key create my-key garage key create my-key
garage bucket create backup garage bucket create backups
garage bucket allow backup --read --write --key my-key garage bucket allow backups --read --write --key my-key
``` ```
Then register your Key ID and Secret key in your environment: Then register your Key ID and Secret key in your environment:

View file

@ -85,14 +85,14 @@ to store 2 TB of data in total.
## Get a Docker image ## Get a Docker image
Our docker image is currently named `dxflrs/garage` and is stored on the [Docker Hub](https://hub.docker.com/r/dxflrs/garage/tags?page=1&ordering=last_updated). Our docker image is currently named `dxflrs/garage` and is stored on the [Docker Hub](https://hub.docker.com/r/dxflrs/garage/tags?page=1&ordering=last_updated).
We encourage you to use a fixed tag (eg. `v0.9.1`) and not the `latest` tag. We encourage you to use a fixed tag (eg. `v0.9.2`) and not the `latest` tag.
For this example, we will use the latest published version at the time of the writing which is `v0.9.1` but it's up to you For this example, we will use the latest published version at the time of the writing which is `v0.9.2` but it's up to you
to check [the most recent versions on the Docker Hub](https://hub.docker.com/r/dxflrs/garage/tags?page=1&ordering=last_updated). to check [the most recent versions on the Docker Hub](https://hub.docker.com/r/dxflrs/garage/tags?page=1&ordering=last_updated).
For example: For example:
``` ```
sudo docker pull dxflrs/garage:v0.9.1 sudo docker pull dxflrs/garage:v0.9.2
``` ```
## Deploying and configuring Garage ## Deploying and configuring Garage
@ -157,7 +157,7 @@ docker run \
-v /etc/garage.toml:/etc/garage.toml \ -v /etc/garage.toml:/etc/garage.toml \
-v /var/lib/garage/meta:/var/lib/garage/meta \ -v /var/lib/garage/meta:/var/lib/garage/meta \
-v /var/lib/garage/data:/var/lib/garage/data \ -v /var/lib/garage/data:/var/lib/garage/data \
dxflrs/garage:v0.9.1 dxflrs/garage:v0.9.2
``` ```
With this command line, Garage should be started automatically at each boot. With this command line, Garage should be started automatically at each boot.
@ -171,7 +171,7 @@ If you want to use `docker-compose`, you may use the following `docker-compose.y
version: "3" version: "3"
services: services:
garage: garage:
image: dxflrs/garage:v0.9.1 image: dxflrs/garage:v0.9.2
network_mode: "host" network_mode: "host"
restart: unless-stopped restart: unless-stopped
volumes: volumes:

View file

@ -79,8 +79,9 @@ index = "index.html"
api_bind_addr = "[::]:3904" api_bind_addr = "[::]:3904"
[admin] [admin]
api_bind_addr = "0.0.0.0:3903" api_bind_addr = "[::]:3903"
admin_token = "$(openssl rand -base64 32)" admin_token = "$(openssl rand -base64 32)"
metrics_token = "$(openssl rand -base64 32)"
EOF EOF
``` ```

View file

@ -69,8 +69,8 @@ root_domain = ".web.garage"
[admin] [admin]
api_bind_addr = "0.0.0.0:3903" api_bind_addr = "0.0.0.0:3903"
metrics_token = "cacce0b2de4bc2d9f5b5fdff551e01ac1496055aed248202d415398987e35f81" metrics_token = "BCAdFjoa9G0KJR0WXnHHm7fs1ZAbfpI8iIZ+Z/a2NgI="
admin_token = "ae8cb40ea7368bbdbb6430af11cca7da833d3458a5f52086f4e805a570fb5c2a" admin_token = "UkLeGWEvHnXBqnueR3ISEMWpOnm40jH2tM2HnnL/0F4="
trace_sink = "http://localhost:4317" trace_sink = "http://localhost:4317"
``` ```
@ -417,7 +417,7 @@ the node, even in the case of a NAT: the NAT should be configured to forward the
port number to the same internal port nubmer. This means that if you have several nodes running port number to the same internal port nubmer. This means that if you have several nodes running
behind a NAT, they should each use a different RPC port number. behind a NAT, they should each use a different RPC port number.
#### `rpc_bind_outgoing` {#rpc_bind_outgoing} (since v0.9.2) #### `rpc_bind_outgoing`(since v0.9.2) {#rpc_bind_outgoing}
If enabled, pre-bind all sockets for outgoing connections to the same IP address If enabled, pre-bind all sockets for outgoing connections to the same IP address
used for listening (the IP address specified in `rpc_bind_addr`) before used for listening (the IP address specified in `rpc_bind_addr`) before
@ -612,7 +612,7 @@ the socket will have 0220 mode. Make sure to set user and group permissions acco
The token for accessing the Metrics endpoint. If this token is not set, the The token for accessing the Metrics endpoint. If this token is not set, the
Metrics endpoint can be accessed without access control. Metrics endpoint can be accessed without access control.
You can use any random string for this value. We recommend generating a random token with `openssl rand -hex 32`. You can use any random string for this value. We recommend generating a random token with `openssl rand -base64 32`.
`metrics_token` was introduced in Garage `v0.7.2`. `metrics_token` was introduced in Garage `v0.7.2`.
`metrics_token_file` and the `GARAGE_METRICS_TOKEN` environment variable are supported since Garage `v0.8.2`. `metrics_token_file` and the `GARAGE_METRICS_TOKEN` environment variable are supported since Garage `v0.8.2`.
@ -624,7 +624,7 @@ You can use any random string for this value. We recommend generating a random t
The token for accessing all of the other administration endpoints. If this The token for accessing all of the other administration endpoints. If this
token is not set, access to these endpoints is disabled entirely. token is not set, access to these endpoints is disabled entirely.
You can use any random string for this value. We recommend generating a random token with `openssl rand -hex 32`. You can use any random string for this value. We recommend generating a random token with `openssl rand -base64 32`.
`admin_token` was introduced in Garage `v0.7.2`. `admin_token` was introduced in Garage `v0.7.2`.
`admin_token_file` and the `GARAGE_ADMIN_TOKEN` environment variable are supported since Garage `v0.8.2`. `admin_token_file` and the `GARAGE_ADMIN_TOKEN` environment variable are supported since Garage `v0.8.2`.

View file

@ -37,6 +37,21 @@ A Garage cluster can very easily evolve over time, as storage nodes are added or
Garage will automatically rebalance data between nodes as needed to ensure the desired number of copies. Garage will automatically rebalance data between nodes as needed to ensure the desired number of copies.
Read about cluster layout management [here](@/documentation/operations/layout.md). Read about cluster layout management [here](@/documentation/operations/layout.md).
### Several replication modes
Garage supports a variety of replication modes, with 1 copy, 2 copies or 3 copies of your data,
and with various levels of consistency, in order to adapt to a variety of usage scenarios.
Read our reference page on [supported replication modes](@/documentation/reference-manual/configuration.md#replication_mode)
to select the replication mode best suited to your use case (hint: in most cases, `replication_mode = "3"` is what you want).
### Compression and deduplication
All data stored in Garage is deduplicated, and optionnally compressed using
Zstd. Objects uploaded to Garage are chunked in blocks of constant sizes (see
[`block_size`](@/documentation/reference-manual/configuration.md#block_size)),
and the hashes of individual blocks are used to dispatch them to storage nodes
and to deduplicate them.
### No RAFT slowing you down ### No RAFT slowing you down
It might seem strange to tout the absence of something as a desirable feature, It might seem strange to tout the absence of something as a desirable feature,
@ -48,13 +63,6 @@ As a consequence, requests can be handled much faster, even in cases where laten
between cluster nodes is important (see our [benchmarks](@/documentation/design/benchmarks/index.md) for data on this). between cluster nodes is important (see our [benchmarks](@/documentation/design/benchmarks/index.md) for data on this).
This is particularly usefull when nodes are far from one another and talk to one other through standard Internet connections. This is particularly usefull when nodes are far from one another and talk to one other through standard Internet connections.
### Several replication modes
Garage supports a variety of replication modes, with 1 copy, 2 copies or 3 copies of your data,
and with various levels of consistency, in order to adapt to a variety of usage scenarios.
Read our reference page on [supported replication modes](@/documentation/reference-manual/configuration.md#replication_mode)
to select the replication mode best suited to your use case (hint: in most cases, `replication_mode = "3"` is what you want).
### Web server for static websites ### Web server for static websites
A storage bucket can easily be configured to be served directly by Garage as a static web site. A storage bucket can easily be configured to be served directly by Garage as a static web site.

View file

@ -21,4 +21,4 @@ version: 0.4.1
# incremented each time you make changes to the application. Versions are not expected to # incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using. # follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes. # It is recommended to use it with quotes.
appVersion: "v0.9.1" appVersion: "v0.9.2"

View file

@ -81,11 +81,9 @@ if [ -z "$SKIP_AWS" ]; then
echo "Invalid multipart upload" echo "Invalid multipart upload"
exit 1 exit 1
fi fi
aws s3api delete-object --bucket eprouvette --key upload
fi fi
echo "OK!!"
exit 0
# S3CMD # S3CMD
if [ -z "$SKIP_S3CMD" ]; then if [ -z "$SKIP_S3CMD" ]; then
echo "🛠️ Testing with s3cmd" echo "🛠️ Testing with s3cmd"

View file

@ -1,6 +1,6 @@
[package] [package]
name = "garage_api" name = "garage_api"
version = "0.9.1" version = "0.9.2"
authors = ["Alex Auvolat <alex@adnab.me>"] authors = ["Alex Auvolat <alex@adnab.me>"]
edition = "2018" edition = "2018"
license = "AGPL-3.0" license = "AGPL-3.0"
@ -21,6 +21,7 @@ garage_net.workspace = true
garage_util.workspace = true garage_util.workspace = true
garage_rpc.workspace = true garage_rpc.workspace = true
argon2.workspace = true
async-trait.workspace = true async-trait.workspace = true
base64.workspace = true base64.workspace = true
bytes.workspace = true bytes.workspace = true

View file

@ -1,6 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use argon2::password_hash::PasswordHash;
use async_trait::async_trait; use async_trait::async_trait;
use http::header::{ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_ORIGIN, ALLOW}; use http::header::{ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_ORIGIN, ALLOW};
@ -45,14 +46,8 @@ impl AdminApiServer {
#[cfg(feature = "metrics")] exporter: PrometheusExporter, #[cfg(feature = "metrics")] exporter: PrometheusExporter,
) -> Self { ) -> Self {
let cfg = &garage.config.admin; let cfg = &garage.config.admin;
let metrics_token = cfg let metrics_token = cfg.metrics_token.as_deref().map(hash_bearer_token);
.metrics_token let admin_token = cfg.admin_token.as_deref().map(hash_bearer_token);
.as_ref()
.map(|tok| format!("Bearer {}", tok));
let admin_token = cfg
.admin_token
.as_ref()
.map(|tok| format!("Bearer {}", tok));
Self { Self {
garage, garage,
#[cfg(feature = "metrics")] #[cfg(feature = "metrics")]
@ -248,11 +243,11 @@ impl ApiHandler for AdminApiServer {
req: Request<IncomingBody>, req: Request<IncomingBody>,
endpoint: Endpoint, endpoint: Endpoint,
) -> Result<Response<ResBody>, Error> { ) -> Result<Response<ResBody>, Error> {
let expected_auth_header = let required_auth_hash =
match endpoint.authorization_type() { match endpoint.authorization_type() {
Authorization::None => None, Authorization::None => None,
Authorization::MetricsToken => self.metrics_token.as_ref(), Authorization::MetricsToken => self.metrics_token.as_deref(),
Authorization::AdminToken => match &self.admin_token { Authorization::AdminToken => match self.admin_token.as_deref() {
None => return Err(Error::forbidden( None => return Err(Error::forbidden(
"Admin token isn't configured, admin API access is disabled for security.", "Admin token isn't configured, admin API access is disabled for security.",
)), )),
@ -260,14 +255,11 @@ impl ApiHandler for AdminApiServer {
}, },
}; };
if let Some(h) = expected_auth_header { if let Some(password_hash) = required_auth_hash {
match req.headers().get("Authorization") { match req.headers().get("Authorization") {
None => return Err(Error::forbidden("Authorization token must be provided")), None => return Err(Error::forbidden("Authorization token must be provided")),
Some(v) => { Some(authorization) => {
let authorized = v.to_str().map(|hv| hv.trim() == h).unwrap_or(false); verify_bearer_token(&authorization, password_hash)?;
if !authorized {
return Err(Error::forbidden("Invalid authorization token provided"));
}
} }
} }
} }
@ -342,3 +334,35 @@ impl ApiEndpoint for Endpoint {
fn add_span_attributes(&self, _span: SpanRef<'_>) {} fn add_span_attributes(&self, _span: SpanRef<'_>) {}
} }
fn hash_bearer_token(token: &str) -> String {
use argon2::{
password_hash::{rand_core::OsRng, PasswordHasher, SaltString},
Argon2,
};
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();
argon2
.hash_password(token.trim().as_bytes(), &salt)
.expect("could not hash API token")
.to_string()
}
fn verify_bearer_token(token: &hyper::http::HeaderValue, password_hash: &str) -> Result<(), Error> {
use argon2::{password_hash::PasswordVerifier, Argon2};
let parsed_hash = PasswordHash::new(&password_hash).unwrap();
token
.to_str()?
.strip_prefix("Bearer ")
.and_then(|token| {
Argon2::default()
.verify_password(token.trim().as_bytes(), &parsed_hash)
.ok()
})
.ok_or_else(|| Error::forbidden("Invalid authorization token"))?;
Ok(())
}

View file

@ -3,7 +3,7 @@ use std::convert::TryFrom;
use chrono::{DateTime, Duration, NaiveDateTime, TimeZone, Utc}; use chrono::{DateTime, Duration, NaiveDateTime, TimeZone, Utc};
use hmac::Mac; use hmac::Mac;
use hyper::header::{HeaderMap, HeaderName, HeaderValue, AUTHORIZATION, CONTENT_TYPE, HOST}; use hyper::header::{HeaderMap, HeaderName, HeaderValue, AUTHORIZATION, HOST};
use hyper::{body::Incoming as IncomingBody, Method, Request}; use hyper::{body::Incoming as IncomingBody, Method, Request};
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
@ -74,12 +74,13 @@ async fn check_standard_signature(
let authorization = Authorization::parse_header(request.headers())?; let authorization = Authorization::parse_header(request.headers())?;
// Verify that all necessary request headers are included in signed_headers // Verify that all necessary request headers are included in signed_headers
// For standard AWSv4 signatures, the following must be included: // The following must be included for all signatures:
// - the Host header (mandatory) // - the Host header (mandatory)
// - the Content-Type header, if it is used in the request
// - all x-amz-* headers used in the request // - all x-amz-* headers used in the request
// AWS also indicates that the Content-Type header should be signed if
// it is used, but Minio client doesn't sign it so we don't check it for compatibility.
let signed_headers = split_signed_headers(&authorization)?; let signed_headers = split_signed_headers(&authorization)?;
verify_signed_headers(request.headers(), &signed_headers, &[CONTENT_TYPE])?; verify_signed_headers(request.headers(), &signed_headers)?;
let canonical_request = canonical_request( let canonical_request = canonical_request(
service, service,
@ -129,7 +130,7 @@ async fn check_presigned_signature(
// - the Host header (mandatory) // - the Host header (mandatory)
// - all x-amz-* headers used in the request // - all x-amz-* headers used in the request
let signed_headers = split_signed_headers(&authorization)?; let signed_headers = split_signed_headers(&authorization)?;
verify_signed_headers(request.headers(), &signed_headers, &[])?; verify_signed_headers(request.headers(), &signed_headers)?;
// The X-Amz-Signature value is passed as a query parameter, // The X-Amz-Signature value is passed as a query parameter,
// but the signature cannot be computed from a string that contains itself. // but the signature cannot be computed from a string that contains itself.
@ -229,16 +230,12 @@ fn split_signed_headers(authorization: &Authorization) -> Result<Vec<HeaderName>
Ok(signed_headers) Ok(signed_headers)
} }
fn verify_signed_headers( fn verify_signed_headers(headers: &HeaderMap, signed_headers: &[HeaderName]) -> Result<(), Error> {
headers: &HeaderMap,
signed_headers: &[HeaderName],
extra_headers: &[HeaderName],
) -> Result<(), Error> {
if !signed_headers.contains(&HOST) { if !signed_headers.contains(&HOST) {
return Err(Error::bad_request("Header `Host` should be signed")); return Err(Error::bad_request("Header `Host` should be signed"));
} }
for (name, _) in headers.iter() { for (name, _) in headers.iter() {
if name.as_str().starts_with("x-amz-") || extra_headers.contains(name) { if name.as_str().starts_with("x-amz-") {
if !signed_headers.contains(name) { if !signed_headers.contains(name) {
return Err(Error::bad_request(format!( return Err(Error::bad_request(format!(
"Header `{}` should be signed", "Header `{}` should be signed",
@ -375,9 +372,10 @@ pub async fn verify_v4(
) )
.ok_or_internal_error("Unable to build signing HMAC")?; .ok_or_internal_error("Unable to build signing HMAC")?;
hmac.update(payload); hmac.update(payload);
let our_signature = hex::encode(hmac.finalize().into_bytes()); let signature =
if auth.signature != our_signature { hex::decode(&auth.signature).map_err(|_| Error::forbidden("Invalid signature"))?;
return Err(Error::forbidden("Invalid signature".to_string())); if hmac.verify_slice(&signature).is_err() {
return Err(Error::forbidden("Invalid signature"));
} }
Ok(key) Ok(key)

View file

@ -1,6 +1,6 @@
[package] [package]
name = "garage_block" name = "garage_block"
version = "0.9.1" version = "0.9.2"
authors = ["Alex Auvolat <alex@adnab.me>"] authors = ["Alex Auvolat <alex@adnab.me>"]
edition = "2018" edition = "2018"
license = "AGPL-3.0" license = "AGPL-3.0"

View file

@ -1,6 +1,6 @@
[package] [package]
name = "garage_db" name = "garage_db"
version = "0.9.1" version = "0.9.2"
authors = ["Alex Auvolat <alex@adnab.me>"] authors = ["Alex Auvolat <alex@adnab.me>"]
edition = "2018" edition = "2018"
license = "AGPL-3.0" license = "AGPL-3.0"

View file

@ -1,6 +1,6 @@
[package] [package]
name = "garage" name = "garage"
version = "0.9.1" version = "0.9.2"
authors = ["Alex Auvolat <alex@adnab.me>"] authors = ["Alex Auvolat <alex@adnab.me>"]
edition = "2018" edition = "2018"
license = "AGPL-3.0" license = "AGPL-3.0"

View file

@ -1,6 +1,6 @@
[package] [package]
name = "garage_model" name = "garage_model"
version = "0.9.1" version = "0.9.2"
authors = ["Alex Auvolat <alex@adnab.me>"] authors = ["Alex Auvolat <alex@adnab.me>"]
edition = "2018" edition = "2018"
license = "AGPL-3.0" license = "AGPL-3.0"

View file

@ -1,9 +1,9 @@
[package] [package]
name = "garage_net" name = "garage_net"
version = "0.9.1" version = "0.9.2"
authors = ["Alex Auvolat <alex@adnab.me>"] authors = ["Alex Auvolat <alex@adnab.me>"]
edition = "2018" edition = "2018"
license-file = "AGPL-3.0" license = "AGPL-3.0"
description = "Networking library for Garage RPC communication, forked from Netapp" description = "Networking library for Garage RPC communication, forked from Netapp"
repository = "https://git.deuxfleurs.fr/Deuxfleurs/garage" repository = "https://git.deuxfleurs.fr/Deuxfleurs/garage"
readme = "../../README.md" readme = "../../README.md"

View file

@ -1,6 +1,6 @@
[package] [package]
name = "garage_rpc" name = "garage_rpc"
version = "0.9.1" version = "0.9.2"
authors = ["Alex Auvolat <alex@adnab.me>"] authors = ["Alex Auvolat <alex@adnab.me>"]
edition = "2018" edition = "2018"
license = "AGPL-3.0" license = "AGPL-3.0"

View file

@ -1,6 +1,6 @@
[package] [package]
name = "garage_table" name = "garage_table"
version = "0.9.1" version = "0.9.2"
authors = ["Alex Auvolat <alex@adnab.me>"] authors = ["Alex Auvolat <alex@adnab.me>"]
edition = "2018" edition = "2018"
license = "AGPL-3.0" license = "AGPL-3.0"

View file

@ -1,6 +1,6 @@
[package] [package]
name = "garage_util" name = "garage_util"
version = "0.9.1" version = "0.9.2"
authors = ["Alex Auvolat <alex@adnab.me>"] authors = ["Alex Auvolat <alex@adnab.me>"]
edition = "2018" edition = "2018"
license = "AGPL-3.0" license = "AGPL-3.0"

View file

@ -1,6 +1,6 @@
[package] [package]
name = "garage_web" name = "garage_web"
version = "0.9.1" version = "0.9.2"
authors = ["Alex Auvolat <alex@adnab.me>", "Quentin Dufour <quentin@dufour.io>"] authors = ["Alex Auvolat <alex@adnab.me>", "Quentin Dufour <quentin@dufour.io>"]
edition = "2018" edition = "2018"
license = "AGPL-3.0" license = "AGPL-3.0"