From ecf4a87ac687b713d3eb4f191f4c5ead293fa046 Mon Sep 17 00:00:00 2001 From: radhika Date: Sat, 16 Apr 2016 18:16:46 -0400 Subject: [PATCH] 8936: consider blobSigningTtl while generating and verifying signatures. --- sdk/go/keepclient/perms.go | 12 ++++--- sdk/go/keepclient/perms_test.go | 29 ++++++++--------- services/api/app/models/blob.rb | 3 +- services/api/test/unit/blob_test.rb | 21 ++++++++++++- services/keepstore/perms.go | 4 +-- services/keepstore/perms_test.go | 6 +++- tools/keep-block-check/keep-block-check.go | 25 ++++++++++++--- .../keep-block-check/keep-block-check_test.go | 19 +++++++----- tools/keep-rsync/keep-rsync.go | 31 ++++++++++++++----- tools/keep-rsync/keep-rsync_test.go | 17 +++++----- 10 files changed, 114 insertions(+), 53 deletions(-) diff --git a/sdk/go/keepclient/perms.go b/sdk/go/keepclient/perms.go index 12105c6cfc..378fdcdff8 100644 --- a/sdk/go/keepclient/perms.go +++ b/sdk/go/keepclient/perms.go @@ -29,13 +29,15 @@ var ( // makePermSignature generates a SHA-1 HMAC digest for the given blob, // token, expiry, and site secret. -func makePermSignature(blobHash, apiToken, expiry string, permissionSecret []byte) string { +func makePermSignature(blobHash, apiToken, expiry string, blobSigningTTL time.Duration, permissionSecret []byte) string { hmac := hmac.New(sha1.New, permissionSecret) hmac.Write([]byte(blobHash)) hmac.Write([]byte("@")) hmac.Write([]byte(apiToken)) hmac.Write([]byte("@")) hmac.Write([]byte(expiry)) + hmac.Write([]byte("@")) + hmac.Write([]byte(strconv.Itoa(int(blobSigningTTL.Seconds())))) digest := hmac.Sum(nil) return fmt.Sprintf("%x", digest) } @@ -46,7 +48,7 @@ func makePermSignature(blobHash, apiToken, expiry string, permissionSecret []byt // // This function is intended to be used by system components and admin // utilities: userland programs do not know the permissionSecret. -func SignLocator(blobLocator, apiToken string, expiry time.Time, permissionSecret []byte) string { +func SignLocator(blobLocator, apiToken string, expiry time.Time, blobSigningTTL time.Duration, permissionSecret []byte) string { if len(permissionSecret) == 0 || apiToken == "" { return blobLocator } @@ -54,7 +56,7 @@ func SignLocator(blobLocator, apiToken string, expiry time.Time, permissionSecre blobHash := strings.Split(blobLocator, "+")[0] timestampHex := fmt.Sprintf("%08x", expiry.Unix()) return blobLocator + - "+A" + makePermSignature(blobHash, apiToken, timestampHex, permissionSecret) + + "+A" + makePermSignature(blobHash, apiToken, timestampHex, blobSigningTTL, permissionSecret) + "@" + timestampHex } @@ -70,7 +72,7 @@ var signedLocatorRe = regexp.MustCompile(`^([[:xdigit:]]{32}).*\+A([[:xdigit:]]{ // // This function is intended to be used by system components and admin // utilities: userland programs do not know the permissionSecret. -func VerifySignature(signedLocator, apiToken string, permissionSecret []byte) error { +func VerifySignature(signedLocator, apiToken string, blobSigningTTL time.Duration, permissionSecret []byte) error { matches := signedLocatorRe.FindStringSubmatch(signedLocator) if matches == nil { return ErrSignatureMissing @@ -83,7 +85,7 @@ func VerifySignature(signedLocator, apiToken string, permissionSecret []byte) er } else if expiryTime.Before(time.Now()) { return ErrSignatureExpired } - if signatureHex != makePermSignature(blobHash, apiToken, expiryHex, permissionSecret) { + if signatureHex != makePermSignature(blobHash, apiToken, expiryHex, blobSigningTTL, permissionSecret) { return ErrSignatureInvalid } return nil diff --git a/sdk/go/keepclient/perms_test.go b/sdk/go/keepclient/perms_test.go index 1380795287..f0b8f96dc5 100644 --- a/sdk/go/keepclient/perms_test.go +++ b/sdk/go/keepclient/perms_test.go @@ -16,83 +16,84 @@ const ( "gokee3eamvjy8qq1fvy238838enjmy5wzy2md7yvsitp5vztft6j4q866efym7e6" + "vu5wm9fpnwjyxfldw3vbo01mgjs75rgo7qioh8z8ij7jpyp8508okhgbbex3ceei" + "786u5rw2a9gx743dj3fgq2irk" - knownSignature = "257f3f5f5f0a4e4626a18fc74bd42ec34dcb228a" + knownSignature = "44362129a92a48d02b2e0789c597f970f3b1faf3" knownTimestamp = "7fffffff" knownSigHint = "+A" + knownSignature + "@" + knownTimestamp knownSignedLocator = knownLocator + knownSigHint + blobSigningTtl = time.Duration(1) * time.Second ) func TestSignLocator(t *testing.T) { if ts, err := parseHexTimestamp(knownTimestamp); err != nil { t.Errorf("bad knownTimestamp %s", knownTimestamp) } else { - if knownSignedLocator != SignLocator(knownLocator, knownToken, ts, []byte(knownKey)) { + if knownSignedLocator != SignLocator(knownLocator, knownToken, ts, blobSigningTtl, []byte(knownKey)) { t.Fail() } } } func TestVerifySignature(t *testing.T) { - if VerifySignature(knownSignedLocator, knownToken, []byte(knownKey)) != nil { + if VerifySignature(knownSignedLocator, knownToken, blobSigningTtl, []byte(knownKey)) != nil { t.Fail() } } func TestVerifySignatureExtraHints(t *testing.T) { - if VerifySignature(knownLocator+"+K@xyzzy"+knownSigHint, knownToken, []byte(knownKey)) != nil { + if VerifySignature(knownLocator+"+K@xyzzy"+knownSigHint, knownToken, blobSigningTtl, []byte(knownKey)) != nil { t.Fatal("Verify cannot handle hint before permission signature") } - if VerifySignature(knownLocator+knownSigHint+"+Zfoo", knownToken, []byte(knownKey)) != nil { + if VerifySignature(knownLocator+knownSigHint+"+Zfoo", knownToken, blobSigningTtl, []byte(knownKey)) != nil { t.Fatal("Verify cannot handle hint after permission signature") } - if VerifySignature(knownLocator+"+K@xyzzy"+knownSigHint+"+Zfoo", knownToken, []byte(knownKey)) != nil { + if VerifySignature(knownLocator+"+K@xyzzy"+knownSigHint+"+Zfoo", knownToken, blobSigningTtl, []byte(knownKey)) != nil { t.Fatal("Verify cannot handle hints around permission signature") } } // The size hint on the locator string should not affect signature validation. func TestVerifySignatureWrongSize(t *testing.T) { - if VerifySignature(knownHash+"+999999"+knownSigHint, knownToken, []byte(knownKey)) != nil { + if VerifySignature(knownHash+"+999999"+knownSigHint, knownToken, blobSigningTtl, []byte(knownKey)) != nil { t.Fatal("Verify cannot handle incorrect size hint") } - if VerifySignature(knownHash+knownSigHint, knownToken, []byte(knownKey)) != nil { + if VerifySignature(knownHash+knownSigHint, knownToken, blobSigningTtl, []byte(knownKey)) != nil { t.Fatal("Verify cannot handle missing size hint") } } func TestVerifySignatureBadSig(t *testing.T) { badLocator := knownLocator + "+Aaaaaaaaaaaaaaaa@" + knownTimestamp - if VerifySignature(badLocator, knownToken, []byte(knownKey)) != ErrSignatureMissing { + if VerifySignature(badLocator, knownToken, blobSigningTtl, []byte(knownKey)) != ErrSignatureMissing { t.Fail() } } func TestVerifySignatureBadTimestamp(t *testing.T) { badLocator := knownLocator + "+A" + knownSignature + "@OOOOOOOl" - if VerifySignature(badLocator, knownToken, []byte(knownKey)) != ErrSignatureMissing { + if VerifySignature(badLocator, knownToken, blobSigningTtl, []byte(knownKey)) != ErrSignatureMissing { t.Fail() } } func TestVerifySignatureBadSecret(t *testing.T) { - if VerifySignature(knownSignedLocator, knownToken, []byte("00000000000000000000")) != ErrSignatureInvalid { + if VerifySignature(knownSignedLocator, knownToken, blobSigningTtl, []byte("00000000000000000000")) != ErrSignatureInvalid { t.Fail() } } func TestVerifySignatureBadToken(t *testing.T) { - if VerifySignature(knownSignedLocator, "00000000", []byte(knownKey)) != ErrSignatureInvalid { + if VerifySignature(knownSignedLocator, "00000000", blobSigningTtl, []byte(knownKey)) != ErrSignatureInvalid { t.Fail() } } func TestVerifySignatureExpired(t *testing.T) { yesterday := time.Now().AddDate(0, 0, -1) - expiredLocator := SignLocator(knownHash, knownToken, yesterday, []byte(knownKey)) - if VerifySignature(expiredLocator, knownToken, []byte(knownKey)) != ErrSignatureExpired { + expiredLocator := SignLocator(knownHash, knownToken, yesterday, blobSigningTtl, []byte(knownKey)) + if VerifySignature(expiredLocator, knownToken, blobSigningTtl, []byte(knownKey)) != ErrSignatureExpired { t.Fail() } } diff --git a/services/api/app/models/blob.rb b/services/api/app/models/blob.rb index 34600d7a25..91007fcc8a 100644 --- a/services/api/app/models/blob.rb +++ b/services/api/app/models/blob.rb @@ -112,6 +112,7 @@ class Blob OpenSSL::HMAC.hexdigest('sha1', key, [blob_hash, api_token, - timestamp].join('@')) + timestamp, + Rails.configuration.blob_signature_ttl].join('@')) end end diff --git a/services/api/test/unit/blob_test.rb b/services/api/test/unit/blob_test.rb index 0794a751e0..707056d2ad 100644 --- a/services/api/test/unit/blob_test.rb +++ b/services/api/test/unit/blob_test.rb @@ -17,7 +17,7 @@ class BlobTest < ActiveSupport::TestCase 'vu5wm9fpnwjyxfldw3vbo01mgjs75rgo7qioh8z8ij7jpyp8508okhgbbex3ceei' + '786u5rw2a9gx743dj3fgq2irk' @@known_signed_locator = 'acbd18db4cc2f85cedef654fccc4a4d8+3' + - '+A257f3f5f5f0a4e4626a18fc74bd42ec34dcb228a@7fffffff' + '+A44362129a92a48d02b2e0789c597f970f3b1faf3@7fffffff' test 'generate predictable invincible signature' do signed = Blob.sign_locator @@known_locator, { @@ -118,4 +118,23 @@ class BlobTest < ActiveSupport::TestCase Blob.verify_signature!(@@blob_locator, api_token: @@api_token, key: @@key) end end + + test 'signature changes when ttl changes' do + signed = Blob.sign_locator @@known_locator, { + api_token: @@known_token, + key: @@known_key, + expire: 0x7fffffff, + } + assert_equal @@known_signed_locator, signed + + original_ttl = Rails.configuration.blob_signature_ttl + Rails.configuration.blob_signature_ttl = original_ttl*2 + signed2 = Blob.sign_locator @@known_locator, { + api_token: @@known_token, + key: @@known_key, + expire: 0x7fffffff, + } + Rails.configuration.blob_signature_ttl = original_ttl + assert_not_equal signed, signed2 + end end diff --git a/services/keepstore/perms.go b/services/keepstore/perms.go index 6168a321c2..9cd97bd3b7 100644 --- a/services/keepstore/perms.go +++ b/services/keepstore/perms.go @@ -13,7 +13,7 @@ var PermissionSecret []byte // SignLocator takes a blobLocator, an apiToken and an expiry time, and // returns a signed locator string. func SignLocator(blobLocator, apiToken string, expiry time.Time) string { - return keepclient.SignLocator(blobLocator, apiToken, expiry, PermissionSecret) + return keepclient.SignLocator(blobLocator, apiToken, expiry, blobSignatureTTL, PermissionSecret) } // VerifySignature returns nil if the signature on the signedLocator @@ -22,7 +22,7 @@ func SignLocator(blobLocator, apiToken string, expiry time.Time) string { // something the client could have figured out independently) or // PermissionError. func VerifySignature(signedLocator, apiToken string) error { - err := keepclient.VerifySignature(signedLocator, apiToken, PermissionSecret) + err := keepclient.VerifySignature(signedLocator, apiToken, blobSignatureTTL, PermissionSecret) if err == keepclient.ErrSignatureExpired { return ExpiredError } else if err != nil { diff --git a/services/keepstore/perms_test.go b/services/keepstore/perms_test.go index f4443fc7be..bab5ed2453 100644 --- a/services/keepstore/perms_test.go +++ b/services/keepstore/perms_test.go @@ -17,7 +17,7 @@ const ( "gokee3eamvjy8qq1fvy238838enjmy5wzy2md7yvsitp5vztft6j4q866efym7e6" + "vu5wm9fpnwjyxfldw3vbo01mgjs75rgo7qioh8z8ij7jpyp8508okhgbbex3ceei" + "786u5rw2a9gx743dj3fgq2irk" - knownSignature = "257f3f5f5f0a4e4626a18fc74bd42ec34dcb228a" + knownSignature = "44362129a92a48d02b2e0789c597f970f3b1faf3" knownTimestamp = "7fffffff" knownSigHint = "+A" + knownSignature + "@" + knownTimestamp knownSignedLocator = knownLocator + knownSigHint @@ -34,6 +34,8 @@ func TestSignLocator(t *testing.T) { } t0 := time.Unix(tsInt, 0) + blobSignatureTTL = time.Duration(1) * time.Second + PermissionSecret = []byte(knownKey) if x := SignLocator(knownLocator, knownToken, t0); x != knownSignedLocator { t.Fatalf("Got %+q, expected %+q", x, knownSignedLocator) @@ -50,6 +52,8 @@ func TestVerifyLocator(t *testing.T) { PermissionSecret = b }(PermissionSecret) + blobSignatureTTL = time.Duration(1) * time.Second + PermissionSecret = []byte(knownKey) if err := VerifySignature(knownSignedLocator, knownToken); err != nil { t.Fatal(err) diff --git a/tools/keep-block-check/keep-block-check.go b/tools/keep-block-check/keep-block-check.go index ceafa8c7ef..ed546f0104 100644 --- a/tools/keep-block-check/keep-block-check.go +++ b/tools/keep-block-check/keep-block-check.go @@ -48,6 +48,11 @@ func doMain(args []string) error { "", "Block hash prefix. When a prefix is specified, only hashes listed in the file with this prefix will be checked.") + blobSigningTTL := flags.Duration( + "blob-signing-ttl", + 0*time.Second, + "Lifetime of blob permission signatures on the keepservers. If not provided, this will be retrieved from the keepservers.") + verbose := flags.Bool( "v", false, @@ -68,12 +73,12 @@ func doMain(args []string) error { } // setup keepclient - kc, err := setupKeepClient(config, *keepServicesJSON) + kc, err := setupKeepClient(config, *keepServicesJSON, *blobSigningTTL) if err != nil { return fmt.Errorf("Error configuring keepclient: %s", err.Error()) } - return performKeepBlockCheck(kc, blobSigningKey, blockLocators, *verbose) + return performKeepBlockCheck(kc, *blobSigningTTL, blobSigningKey, blockLocators, *verbose) } type apiConfig struct { @@ -138,7 +143,7 @@ func readConfigFromFile(filename string) (config apiConfig, blobSigningKey strin } // setup keepclient using the config provided -func setupKeepClient(config apiConfig, keepServicesJSON string) (kc *keepclient.KeepClient, err error) { +func setupKeepClient(config apiConfig, keepServicesJSON string, blobSigningTTL time.Duration) (kc *keepclient.KeepClient, err error) { arv := arvadosclient.ArvadosClient{ ApiToken: config.APIToken, ApiServer: config.APIHost, @@ -162,6 +167,16 @@ func setupKeepClient(config apiConfig, keepServicesJSON string) (kc *keepclient. } } + // Get if blobSigningTTL is not provided + if blobSigningTTL == 0 { + value, err := arv.Discovery("blobSignatureTtl") + if err == nil { + blobSigningTTL = time.Duration(int(value.(float64))) * time.Second + } else { + return nil, err + } + } + return } @@ -191,7 +206,7 @@ func getBlockLocators(locatorFile, prefix string) (locators []string, err error) } // Get block headers from keep. Log any errors. -func performKeepBlockCheck(kc *keepclient.KeepClient, blobSigningKey string, blockLocators []string, verbose bool) error { +func performKeepBlockCheck(kc *keepclient.KeepClient, blobSigningTTL time.Duration, blobSigningKey string, blockLocators []string, verbose bool) error { totalBlocks := len(blockLocators) notFoundBlocks := 0 current := 0 @@ -203,7 +218,7 @@ func performKeepBlockCheck(kc *keepclient.KeepClient, blobSigningKey string, blo getLocator := locator if blobSigningKey != "" { expiresAt := time.Now().AddDate(0, 0, 1) - getLocator = keepclient.SignLocator(locator, kc.Arvados.ApiToken, expiresAt, []byte(blobSigningKey)) + getLocator = keepclient.SignLocator(locator, kc.Arvados.ApiToken, expiresAt, blobSigningTTL, []byte(blobSigningKey)) } _, _, err := kc.Ask(getLocator) diff --git a/tools/keep-block-check/keep-block-check_test.go b/tools/keep-block-check/keep-block-check_test.go index 65d3e3434b..9b52ded3be 100644 --- a/tools/keep-block-check/keep-block-check_test.go +++ b/tools/keep-block-check/keep-block-check_test.go @@ -10,6 +10,7 @@ import ( "regexp" "strings" "testing" + "time" "git.curoverse.com/arvados.git/sdk/go/arvadostest" "git.curoverse.com/arvados.git/sdk/go/keepclient" @@ -35,6 +36,8 @@ var logBuffer bytes.Buffer var TestHash = "aaaa09c290d0fb1ca068ffaddf22cbd0" var TestHash2 = "aaaac516f788aec4f30932ffb6395c39" +var blobSigningTTL = time.Duration(2*7*24) * time.Hour + func (s *ServerRequiredSuite) SetUpSuite(c *C) { arvadostest.StartAPI() } @@ -79,7 +82,7 @@ func setupKeepBlockCheck(c *C, enforcePermissions bool, keepServicesJSON string) // setup keepclients var err error - kc, err = setupKeepClient(config, keepServicesJSON) + kc, err = setupKeepClient(config, keepServicesJSON, blobSigningTTL) c.Check(err, IsNil) } @@ -151,7 +154,7 @@ func checkNoErrorsLogged(c *C, prefix, suffix string) { func (s *ServerRequiredSuite) TestBlockCheck(c *C) { setupKeepBlockCheck(c, false, "") allLocators := setupTestData(c) - err := performKeepBlockCheck(kc, "", allLocators, true) + err := performKeepBlockCheck(kc, blobSigningTTL, "", allLocators, true) c.Check(err, IsNil) checkNoErrorsLogged(c, "Error verifying block", "Block not found") } @@ -159,7 +162,7 @@ func (s *ServerRequiredSuite) TestBlockCheck(c *C) { func (s *ServerRequiredSuite) TestBlockCheckWithBlobSigning(c *C) { setupKeepBlockCheck(c, true, "") allLocators := setupTestData(c) - err := performKeepBlockCheck(kc, arvadostest.BlobSigningKey, allLocators, true) + err := performKeepBlockCheck(kc, blobSigningTTL, arvadostest.BlobSigningKey, allLocators, true) c.Check(err, IsNil) checkNoErrorsLogged(c, "Error verifying block", "Block not found") } @@ -169,7 +172,7 @@ func (s *ServerRequiredSuite) TestBlockCheck_NoSuchBlock(c *C) { allLocators := setupTestData(c) allLocators = append(allLocators, TestHash) allLocators = append(allLocators, TestHash2) - err := performKeepBlockCheck(kc, "", allLocators, true) + err := performKeepBlockCheck(kc, blobSigningTTL, "", allLocators, true) c.Check(err, NotNil) c.Assert(err.Error(), Equals, "Block verification failed for 2 out of 7 blocks with matching prefix.") checkErrorLog(c, []string{TestHash, TestHash2}, "Error verifying block", "Block not found") @@ -184,7 +187,7 @@ func (s *ServerRequiredSuite) TestBlockCheck_NoSuchBlock_WithMatchingPrefix(c *C defer os.Remove(locatorFile) locators, err := getBlockLocators(locatorFile, "aaa") c.Check(err, IsNil) - err = performKeepBlockCheck(kc, "", locators, true) + err = performKeepBlockCheck(kc, blobSigningTTL, "", locators, true) c.Check(err, NotNil) // Of the 7 blocks in allLocators, only two match the prefix and hence only those are checked c.Assert(err.Error(), Equals, "Block verification failed for 2 out of 2 blocks with matching prefix.") @@ -200,14 +203,14 @@ func (s *ServerRequiredSuite) TestBlockCheck_NoSuchBlock_WithPrefixMismatch(c *C defer os.Remove(locatorFile) locators, err := getBlockLocators(locatorFile, "999") c.Check(err, IsNil) - err = performKeepBlockCheck(kc, "", locators, true) + err = performKeepBlockCheck(kc, blobSigningTTL, "", locators, true) c.Check(err, IsNil) // there were no matching locators in file and hence nothing was checked } func (s *ServerRequiredSuite) TestBlockCheck_BadSignature(c *C) { setupKeepBlockCheck(c, true, "") setupTestData(c) - err := performKeepBlockCheck(kc, "badblobsigningkey", []string{TestHash, TestHash2}, false) + err := performKeepBlockCheck(kc, blobSigningTTL, "badblobsigningkey", []string{TestHash, TestHash2}, false) c.Assert(err.Error(), Equals, "Block verification failed for 2 out of 2 blocks with matching prefix.") checkErrorLog(c, []string{TestHash, TestHash2}, "Error verifying block", "HTTP 403") // verbose logging not requested @@ -243,7 +246,7 @@ var testKeepServicesJSON = `{ // Expect error during performKeepBlockCheck due to unreachable keepservers. func (s *ServerRequiredSuite) TestErrorDuringKeepBlockCheck_FakeKeepservers(c *C) { setupKeepBlockCheck(c, false, testKeepServicesJSON) - err := performKeepBlockCheck(kc, "", []string{TestHash, TestHash2}, true) + err := performKeepBlockCheck(kc, blobSigningTTL, "", []string{TestHash, TestHash2}, true) c.Assert(err.Error(), Equals, "Block verification failed for 2 out of 2 blocks with matching prefix.") checkErrorLog(c, []string{TestHash, TestHash2}, "Error verifying block", "") } diff --git a/tools/keep-rsync/keep-rsync.go b/tools/keep-rsync/keep-rsync.go index 820772eb5b..e46d3684a8 100644 --- a/tools/keep-rsync/keep-rsync.go +++ b/tools/keep-rsync/keep-rsync.go @@ -60,6 +60,11 @@ func doMain() error { "", "Index prefix") + blobSigningTTL := flags.Duration( + "blob-signing-ttl", + 0*time.Second, + "Lifetime of blob permission signatures on source keepservers. If not provided, this will be retrieved from the keepservers.") + // Parse args; omit the first arg which is the command name flags.Parse(os.Args[1:]) @@ -74,18 +79,18 @@ func doMain() error { } // setup src and dst keepclients - kcSrc, err := setupKeepClient(srcConfig, *srcKeepServicesJSON, false, 0) + kcSrc, err := setupKeepClient(srcConfig, *srcKeepServicesJSON, false, 0, *blobSigningTTL) if err != nil { return fmt.Errorf("Error configuring src keepclient: %s", err.Error()) } - kcDst, err := setupKeepClient(dstConfig, *dstKeepServicesJSON, true, *replications) + kcDst, err := setupKeepClient(dstConfig, *dstKeepServicesJSON, true, *replications, *blobSigningTTL) if err != nil { return fmt.Errorf("Error configuring dst keepclient: %s", err.Error()) } // Copy blocks not found in dst from src - err = performKeepRsync(kcSrc, kcDst, srcBlobSigningKey, *prefix) + err = performKeepRsync(kcSrc, kcDst, *blobSigningTTL, srcBlobSigningKey, *prefix) if err != nil { return fmt.Errorf("Error while syncing data: %s", err.Error()) } @@ -155,7 +160,7 @@ func readConfigFromFile(filename string) (config apiConfig, blobSigningKey strin } // setup keepclient using the config provided -func setupKeepClient(config apiConfig, keepServicesJSON string, isDst bool, replications int) (kc *keepclient.KeepClient, err error) { +func setupKeepClient(config apiConfig, keepServicesJSON string, isDst bool, replications int, blobSigningTTL time.Duration) (kc *keepclient.KeepClient, err error) { arv := arvadosclient.ArvadosClient{ ApiToken: config.APIToken, ApiServer: config.APIHost, @@ -193,12 +198,22 @@ func setupKeepClient(config apiConfig, keepServicesJSON string, isDst bool, repl kc.Want_replicas = replications } + // If blobSigningTTL is not provided, get it from source + if !isDst && blobSigningTTL == 0 { + value, err := arv.Discovery("blobSignatureTtl") + if err == nil { + blobSigningTTL = time.Duration(int(value.(float64))) * time.Second + } else { + return nil, err + } + } + return kc, nil } // Get unique block locators from src and dst // Copy any blocks missing in dst -func performKeepRsync(kcSrc, kcDst *keepclient.KeepClient, blobSigningKey, prefix string) error { +func performKeepRsync(kcSrc, kcDst *keepclient.KeepClient, blobSigningTTL time.Duration, blobSigningKey, prefix string) error { // Get unique locators from src srcIndex, err := getUniqueLocators(kcSrc, prefix) if err != nil { @@ -218,7 +233,7 @@ func performKeepRsync(kcSrc, kcDst *keepclient.KeepClient, blobSigningKey, prefi log.Printf("Before keep-rsync, there are %d blocks in src and %d blocks in dst. Start copying %d blocks from src not found in dst.", len(srcIndex), len(dstIndex), len(toBeCopied)) - err = copyBlocksToDst(toBeCopied, kcSrc, kcDst, blobSigningKey) + err = copyBlocksToDst(toBeCopied, kcSrc, kcDst, blobSigningTTL, blobSigningKey) return err } @@ -254,7 +269,7 @@ func getMissingLocators(srcLocators, dstLocators map[string]bool) []string { } // Copy blocks from src to dst; only those that are missing in dst are copied -func copyBlocksToDst(toBeCopied []string, kcSrc, kcDst *keepclient.KeepClient, blobSigningKey string) error { +func copyBlocksToDst(toBeCopied []string, kcSrc, kcDst *keepclient.KeepClient, blobSigningTTL time.Duration, blobSigningKey string) error { total := len(toBeCopied) startedAt := time.Now() @@ -271,7 +286,7 @@ func copyBlocksToDst(toBeCopied []string, kcSrc, kcDst *keepclient.KeepClient, b getLocator := locator expiresAt := time.Now().AddDate(0, 0, 1) if blobSigningKey != "" { - getLocator = keepclient.SignLocator(getLocator, kcSrc.Arvados.ApiToken, expiresAt, []byte(blobSigningKey)) + getLocator = keepclient.SignLocator(getLocator, kcSrc.Arvados.ApiToken, expiresAt, blobSigningTTL, []byte(blobSigningKey)) } reader, len, _, err := kcSrc.Get(getLocator) diff --git a/tools/keep-rsync/keep-rsync_test.go b/tools/keep-rsync/keep-rsync_test.go index afc27f513a..90b16b2421 100644 --- a/tools/keep-rsync/keep-rsync_test.go +++ b/tools/keep-rsync/keep-rsync_test.go @@ -49,6 +49,7 @@ func (s *DoMainTestSuite) SetUpSuite(c *C) { var kcSrc, kcDst *keepclient.KeepClient var srcKeepServicesJSON, dstKeepServicesJSON, blobSigningKey string +var blobSigningTTL = time.Duration(2*7*24) * time.Hour func (s *ServerRequiredSuite) SetUpTest(c *C) { // reset all variables between tests @@ -99,10 +100,10 @@ func setupRsync(c *C, enforcePermissions bool, replications int) { // setup keepclients var err error - kcSrc, err = setupKeepClient(srcConfig, srcKeepServicesJSON, false, 0) + kcSrc, err = setupKeepClient(srcConfig, srcKeepServicesJSON, false, 0, blobSigningTTL) c.Check(err, IsNil) - kcDst, err = setupKeepClient(dstConfig, dstKeepServicesJSON, true, replications) + kcDst, err = setupKeepClient(dstConfig, dstKeepServicesJSON, true, replications, 0) c.Check(err, IsNil) for uuid := range kcSrc.LocalRoots() { @@ -174,7 +175,7 @@ func testNoCrosstalk(c *C, testData string, kc1, kc2 *keepclient.KeepClient) { c.Assert(err, Equals, nil) locator = strings.Split(locator, "+")[0] - _, _, _, err = kc2.Get(keepclient.SignLocator(locator, kc2.Arvados.ApiToken, time.Now().AddDate(0, 0, 1), []byte(blobSigningKey))) + _, _, _, err = kc2.Get(keepclient.SignLocator(locator, kc2.Arvados.ApiToken, time.Now().AddDate(0, 0, 1), blobSigningTTL, []byte(blobSigningKey))) c.Assert(err, NotNil) c.Check(err.Error(), Equals, "Block not found") } @@ -256,7 +257,7 @@ func testKeepRsync(c *C, enforcePermissions bool, prefix string) { // setupTestData setupTestData(c, prefix) - err := performKeepRsync(kcSrc, kcDst, blobSigningKey, prefix) + err := performKeepRsync(kcSrc, kcDst, blobSigningTTL, blobSigningKey, prefix) c.Check(err, IsNil) // Now GetIndex from dst and verify that all 5 from src and the 2 extra blocks are found @@ -327,7 +328,7 @@ func (s *ServerRequiredSuite) TestErrorDuringRsync_FakeSrcKeepservers(c *C) { setupRsync(c, false, 1) - err := performKeepRsync(kcSrc, kcDst, "", "") + err := performKeepRsync(kcSrc, kcDst, blobSigningTTL, "", "") log.Printf("Err = %v", err) c.Check(strings.Contains(err.Error(), "no such host"), Equals, true) } @@ -339,7 +340,7 @@ func (s *ServerRequiredSuite) TestErrorDuringRsync_FakeDstKeepservers(c *C) { setupRsync(c, false, 1) - err := performKeepRsync(kcSrc, kcDst, "", "") + err := performKeepRsync(kcSrc, kcDst, blobSigningTTL, "", "") log.Printf("Err = %v", err) c.Check(strings.Contains(err.Error(), "no such host"), Equals, true) } @@ -354,7 +355,7 @@ func (s *ServerRequiredSuite) TestErrorDuringRsync_ErrorGettingBlockFromSrc(c *C // Change blob signing key to a fake key, so that Get from src fails blobSigningKey = "thisisfakeblobsigningkey" - err := performKeepRsync(kcSrc, kcDst, blobSigningKey, "") + err := performKeepRsync(kcSrc, kcDst, blobSigningTTL, blobSigningKey, "") c.Check(strings.Contains(err.Error(), "HTTP 403 \"Forbidden\""), Equals, true) } @@ -368,7 +369,7 @@ func (s *ServerRequiredSuite) TestErrorDuringRsync_ErrorPuttingBlockInDst(c *C) // Increase Want_replicas on dst to result in insufficient replicas error during Put kcDst.Want_replicas = 2 - err := performKeepRsync(kcSrc, kcDst, blobSigningKey, "") + err := performKeepRsync(kcSrc, kcDst, blobSigningTTL, blobSigningKey, "") c.Check(strings.Contains(err.Error(), "Could not write sufficient replicas"), Equals, true) } -- 2.30.2