mirror of
https://github.com/safing/portbase
synced 2025-09-01 10:09:50 +00:00
Download missing sigs
This commit is contained in:
parent
44dc8df5d6
commit
cded2438f6
2 changed files with 128 additions and 15 deletions
101
updater/fetch.go
101
updater/fetch.go
|
@ -42,17 +42,17 @@ func (reg *ResourceRegistry) fetchFile(ctx context.Context, client *http.Client,
|
|||
var (
|
||||
verifiedHash *lhash.LabeledHash
|
||||
sigFileData []byte
|
||||
verifOpts = reg.GetVerificationOptions(rv.resource.Identifier)
|
||||
)
|
||||
if verifOpts != nil {
|
||||
if rv.resource.VerificationOptions != nil {
|
||||
verifiedHash, sigFileData, err = reg.fetchAndVerifySigFile(
|
||||
ctx, client,
|
||||
verifOpts, rv.versionedPath()+filesig.Extension, rv.SigningMetadata(),
|
||||
rv.resource.VerificationOptions,
|
||||
rv.versionedSigPath(), rv.SigningMetadata(),
|
||||
tries,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
switch verifOpts.DownloadPolicy {
|
||||
switch rv.resource.VerificationOptions.DownloadPolicy {
|
||||
case SignaturePolicyRequire:
|
||||
return fmt.Errorf("signature verification failed: %w", err)
|
||||
case SignaturePolicyWarn:
|
||||
|
@ -82,7 +82,7 @@ func (reg *ResourceRegistry) fetchFile(ctx context.Context, client *http.Client,
|
|||
// Write to the hasher at the same time, if needed.
|
||||
var hasher hash.Hash
|
||||
var writeDst io.Writer = atomicFile
|
||||
if verifiedHash != nil && verifOpts.DownloadPolicy != SignaturePolicyDisable {
|
||||
if verifiedHash != nil {
|
||||
hasher = verifiedHash.Algorithm().RawHasher()
|
||||
writeDst = io.MultiWriter(hasher, atomicFile)
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ func (reg *ResourceRegistry) fetchFile(ctx context.Context, client *http.Client,
|
|||
if verifiedHash.EqualRaw(downloadDigest) {
|
||||
log.Infof("%s: verified signature of %s", reg.Name, downloadURL)
|
||||
} else {
|
||||
switch verifOpts.DownloadPolicy {
|
||||
switch rv.resource.VerificationOptions.DownloadPolicy {
|
||||
case SignaturePolicyRequire:
|
||||
return errors.New("file does not match signed checksum")
|
||||
case SignaturePolicyWarn:
|
||||
|
@ -110,15 +110,18 @@ func (reg *ResourceRegistry) fetchFile(ctx context.Context, client *http.Client,
|
|||
case SignaturePolicyDisable:
|
||||
log.Debugf("%s: checksum does not match file from %s", reg.Name, downloadURL)
|
||||
}
|
||||
|
||||
// Reset hasher to signal that the sig should not be written.
|
||||
hasher = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Write signature file, if we have one.
|
||||
if len(sigFileData) > 0 {
|
||||
// Write signature file, if we have one and if verification succeeded.
|
||||
if len(sigFileData) > 0 && hasher != nil {
|
||||
sigFilePath := rv.storagePath() + filesig.Extension
|
||||
err := ioutil.WriteFile(sigFilePath, sigFileData, 0o0644) //nolint:gosec
|
||||
if err != nil {
|
||||
switch verifOpts.DownloadPolicy {
|
||||
switch rv.resource.VerificationOptions.DownloadPolicy {
|
||||
case SignaturePolicyRequire:
|
||||
return fmt.Errorf("failed to write signature file %s: %w", sigFilePath, err)
|
||||
case SignaturePolicyWarn:
|
||||
|
@ -147,6 +150,84 @@ func (reg *ResourceRegistry) fetchFile(ctx context.Context, client *http.Client,
|
|||
return nil
|
||||
}
|
||||
|
||||
func (reg *ResourceRegistry) fetchMissingSig(ctx context.Context, client *http.Client, rv *ResourceVersion, tries int) error {
|
||||
// backoff when retrying
|
||||
if tries > 0 {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil // module is shutting down
|
||||
case <-time.After(time.Duration(tries*tries) * time.Second):
|
||||
}
|
||||
}
|
||||
|
||||
// Check destination dir.
|
||||
dirPath := filepath.Dir(rv.storagePath())
|
||||
err := reg.storageDir.EnsureAbsPath(dirPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not create updates folder: %s", dirPath)
|
||||
}
|
||||
|
||||
// Download and verify the missing signature.
|
||||
verifiedHash, sigFileData, err := reg.fetchAndVerifySigFile(
|
||||
ctx, client,
|
||||
rv.resource.VerificationOptions,
|
||||
rv.versionedSigPath(), rv.SigningMetadata(),
|
||||
tries,
|
||||
)
|
||||
if err != nil {
|
||||
switch rv.resource.VerificationOptions.DownloadPolicy {
|
||||
case SignaturePolicyRequire:
|
||||
return fmt.Errorf("signature verification failed: %w", err)
|
||||
case SignaturePolicyWarn:
|
||||
log.Warningf("%s: failed to verify downloaded signature of %s: %s", reg.Name, rv.versionedPath(), err)
|
||||
case SignaturePolicyDisable:
|
||||
log.Debugf("%s: failed to verify downloaded signature of %s: %s", reg.Name, rv.versionedPath(), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check if the signature matches the resource file.
|
||||
ok, err := verifiedHash.MatchesFile(rv.storagePath())
|
||||
if err != nil {
|
||||
switch rv.resource.VerificationOptions.DownloadPolicy {
|
||||
case SignaturePolicyRequire:
|
||||
return fmt.Errorf("error while verifying resource file: %w", err)
|
||||
case SignaturePolicyWarn:
|
||||
log.Warningf("%s: error while verifying resource file %s", reg.Name, rv.storagePath())
|
||||
case SignaturePolicyDisable:
|
||||
log.Debugf("%s: error while verifying resource file %s", reg.Name, rv.storagePath())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if !ok {
|
||||
switch rv.resource.VerificationOptions.DownloadPolicy {
|
||||
case SignaturePolicyRequire:
|
||||
return errors.New("resource file does not match signed checksum")
|
||||
case SignaturePolicyWarn:
|
||||
log.Warningf("%s: checksum does not match resource file from %s", reg.Name, rv.storagePath())
|
||||
case SignaturePolicyDisable:
|
||||
log.Debugf("%s: checksum does not match resource file from %s", reg.Name, rv.storagePath())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write signature file.
|
||||
err = ioutil.WriteFile(rv.storageSigPath(), sigFileData, 0o0644) //nolint:gosec
|
||||
if err != nil {
|
||||
switch rv.resource.VerificationOptions.DownloadPolicy {
|
||||
case SignaturePolicyRequire:
|
||||
return fmt.Errorf("failed to write signature file %s: %w", rv.storageSigPath(), err)
|
||||
case SignaturePolicyWarn:
|
||||
log.Warningf("%s: failed to write signature file %s: %s", reg.Name, rv.storageSigPath(), err)
|
||||
case SignaturePolicyDisable:
|
||||
log.Debugf("%s: failed to write signature file %s: %s", reg.Name, rv.storageSigPath(), err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Infof("%s: fetched %s (stored to %s)", reg.Name, rv.versionedSigPath(), rv.storageSigPath())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (reg *ResourceRegistry) fetchAndVerifySigFile(ctx context.Context, client *http.Client, verifOpts *VerificationOptions, sigFilePath string, requiredMetadata map[string]string, tries int) (*lhash.LabeledHash, []byte, error) {
|
||||
// Download signature file.
|
||||
resp, _, err := reg.makeRequest(ctx, client, sigFilePath, tries)
|
||||
|
@ -242,7 +323,7 @@ func (reg *ResourceRegistry) makeRequest(ctx context.Context, client *http.Clien
|
|||
downloadURL = u.String()
|
||||
|
||||
// create request
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", downloadURL, http.NoBody)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, downloadURL, http.NoBody)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("failed to create request for %q: %w", downloadURL, err)
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ func (reg *ResourceRegistry) downloadIndex(ctx context.Context, client *http.Cli
|
|||
}
|
||||
|
||||
// Check if the index matches the verified hash.
|
||||
if verifiedHash.MatchesData(indexData) {
|
||||
if verifiedHash.Matches(indexData) {
|
||||
log.Infof("%s: verified signature of %s", reg.Name, downloadURL)
|
||||
} else {
|
||||
sigErr = ErrIndexChecksumMismatch
|
||||
|
@ -170,6 +170,7 @@ func (reg *ResourceRegistry) downloadIndex(ctx context.Context, client *http.Cli
|
|||
func (reg *ResourceRegistry) DownloadUpdates(ctx context.Context) error {
|
||||
// create list of downloads
|
||||
var toUpdate []*ResourceVersion
|
||||
var missingSigs []*ResourceVersion
|
||||
reg.RLock()
|
||||
for _, res := range reg.resources {
|
||||
res.Lock()
|
||||
|
@ -181,8 +182,15 @@ func (reg *ResourceRegistry) DownloadUpdates(ctx context.Context) error {
|
|||
|
||||
// add all non-available and eligible versions to update queue
|
||||
for _, rv := range res.Versions {
|
||||
if !rv.Available && rv.CurrentRelease {
|
||||
switch {
|
||||
case !rv.CurrentRelease:
|
||||
// We are not interested in older releases.
|
||||
case !rv.Available:
|
||||
// File is not available.
|
||||
toUpdate = append(toUpdate, rv)
|
||||
case !rv.SigAvailable && res.VerificationOptions != nil:
|
||||
// File signature is not available and verification is enabled.
|
||||
missingSigs = append(missingSigs, rv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -192,7 +200,7 @@ func (reg *ResourceRegistry) DownloadUpdates(ctx context.Context) error {
|
|||
reg.RUnlock()
|
||||
|
||||
// nothing to update
|
||||
if len(toUpdate) == 0 {
|
||||
if len(toUpdate) == 0 && len(missingSigs) == 0 {
|
||||
log.Infof("%s: everything up to date", reg.Name)
|
||||
return nil
|
||||
}
|
||||
|
@ -203,10 +211,10 @@ func (reg *ResourceRegistry) DownloadUpdates(ctx context.Context) error {
|
|||
}
|
||||
|
||||
// download updates
|
||||
log.Infof("%s: starting to download %d updates parallel", reg.Name, len(toUpdate))
|
||||
log.Infof("%s: starting to download %d updates in parallel", reg.Name, len(toUpdate)+len(missingSigs))
|
||||
var wg sync.WaitGroup
|
||||
|
||||
wg.Add(len(toUpdate))
|
||||
wg.Add(len(toUpdate) + len(missingSigs))
|
||||
client := &http.Client{}
|
||||
|
||||
for idx := range toUpdate {
|
||||
|
@ -233,6 +241,30 @@ func (reg *ResourceRegistry) DownloadUpdates(ctx context.Context) error {
|
|||
}(toUpdate[idx])
|
||||
}
|
||||
|
||||
for idx := range missingSigs {
|
||||
go func(rv *ResourceVersion) {
|
||||
var err error
|
||||
|
||||
defer wg.Done()
|
||||
defer func() {
|
||||
if x := recover(); x != nil {
|
||||
log.Errorf("%s: %s: captured panic: %s", reg.Name, rv.resource.Identifier, x)
|
||||
}
|
||||
}()
|
||||
|
||||
for tries := 0; tries < 3; tries++ {
|
||||
err = reg.fetchMissingSig(ctx, client, rv, tries)
|
||||
if err == nil {
|
||||
rv.SigAvailable = true
|
||||
return
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
log.Warningf("%s: failed to download missing sig of %s version %s: %s", reg.Name, rv.resource.Identifier, rv.VersionNumber, err)
|
||||
}
|
||||
}(missingSigs[idx])
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
log.Infof("%s: finished downloading updates", reg.Name)
|
||||
|
|
Loading…
Add table
Reference in a new issue