[PRIVACY] Add a DNS method to fetch new updates
- Use TXT records in order to determine the latest available version. - This addresses a valid privacy issue, as with HTTP requests the server can keep track(estimated) of how many instances are using Forgejo, with DNS that's basically not possible as the server will never receive any data, as the only ones receiving data are DNS resolvers. (cherry picked from commit0baefb546a
) (cherry picked from commite8ee41880b
) (cherry picked from commit7eca4f3bf1
) (cherry picked from commit6dde3992dc
) (cherry picked from commitfb3a37fbfc
) (cherry picked from commit8304af1e9d
) (cherry picked from commit0543a7d12a
) (cherry picked from commitc3a22933b7
) (cherry picked from commite243707694
) (cherry picked from commit7eb6d1bcf7
) (cherry picked from commit1d7b9535cd
) (cherry picked from commit05920dce67
) (cherry picked from commitf173f27d7c
) (cherry picked from commit90e1c9340e
) (cherry picked from commitde68610ea7
) (cherry picked from commit8d5757ea04
) (cherry picked from commitc7a7fff316
) (cherry picked from commit39ac8b8fc1
) (cherry picked from commit9889203301
) [PRIVACY]: Adjust update checker description - Resolves #323 - Adjust the description of the update check function on the installation page to describe the privacy method instead of the HTTP method by checking gitea.io (cherry picked from commit61eae5b105
) (cherry picked from commit091def20a1
) (cherry picked from commitd5d11bf45a
) (cherry picked from commit71863d4707
)
This commit is contained in:
parent
c1444bae65
commit
11ece4aab1
6 changed files with 77 additions and 11 deletions
|
@ -2166,6 +2166,7 @@ LEVEL = Info
|
||||||
;ENABLE_SUCCESS_NOTICE = false
|
;ENABLE_SUCCESS_NOTICE = false
|
||||||
;SCHEDULE = @every 168h
|
;SCHEDULE = @every 168h
|
||||||
;HTTP_ENDPOINT = https://dl.gitea.com/gitea/version.json
|
;HTTP_ENDPOINT = https://dl.gitea.com/gitea/version.json
|
||||||
|
;DOMAIN_ENDPOINT = release.forgejo.org
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
|
@ -4,8 +4,11 @@
|
||||||
package updatechecker
|
package updatechecker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/json"
|
"code.gitea.io/gitea/modules/json"
|
||||||
"code.gitea.io/gitea/modules/proxy"
|
"code.gitea.io/gitea/modules/proxy"
|
||||||
|
@ -26,7 +29,51 @@ func (r *CheckerState) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GiteaUpdateChecker returns error when new version of Gitea is available
|
// GiteaUpdateChecker returns error when new version of Gitea is available
|
||||||
func GiteaUpdateChecker(httpEndpoint string) error {
|
func GiteaUpdateChecker(httpEndpoint, domainEndpoint string) error {
|
||||||
|
var version string
|
||||||
|
var err error
|
||||||
|
if domainEndpoint != "" {
|
||||||
|
version, err = getVersionDNS(domainEndpoint)
|
||||||
|
} else {
|
||||||
|
version, err = getVersionHTTP(httpEndpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return UpdateRemoteVersion(version)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getVersionDNS will request the TXT records for the domain. If a record starts
|
||||||
|
// with "forgejo_versions=" everything after that will be used as the latest
|
||||||
|
// version available.
|
||||||
|
func getVersionDNS(domainEndpoint string) (version string, err error) {
|
||||||
|
records, err := net.LookupTXT(domainEndpoint)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(records) == 0 {
|
||||||
|
return "", errors.New("no TXT records were found")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, record := range records {
|
||||||
|
if strings.HasPrefix(record, "forgejo_versions=") {
|
||||||
|
// Get all supported versions, separated by a comma.
|
||||||
|
supportedVersions := strings.Split(strings.TrimPrefix(record, "forgejo_versions="), ",")
|
||||||
|
// For now always return the latest supported version.
|
||||||
|
return supportedVersions[len(supportedVersions)-1], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", errors.New("there is no TXT record with a valid value")
|
||||||
|
}
|
||||||
|
|
||||||
|
// getVersionHTTP will make an HTTP request to the endpoint, and the returned
|
||||||
|
// content is JSON. The "latest.version" path's value will be used as the latest
|
||||||
|
// version available.
|
||||||
|
func getVersionHTTP(httpEndpoint string) (version string, err error) {
|
||||||
httpClient := &http.Client{
|
httpClient := &http.Client{
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
Proxy: proxy.Proxy(),
|
Proxy: proxy.Proxy(),
|
||||||
|
@ -35,16 +82,16 @@ func GiteaUpdateChecker(httpEndpoint string) error {
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", httpEndpoint, nil)
|
req, err := http.NewRequest("GET", httpEndpoint, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
resp, err := httpClient.Do(req)
|
resp, err := httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
type respType struct {
|
type respType struct {
|
||||||
|
@ -55,10 +102,9 @@ func GiteaUpdateChecker(httpEndpoint string) error {
|
||||||
respData := respType{}
|
respData := respType{}
|
||||||
err = json.Unmarshal(body, &respData)
|
err = json.Unmarshal(body, &respData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
|
return respData.Latest.Version, nil
|
||||||
return UpdateRemoteVersion(respData.Latest.Version)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateRemoteVersion updates the latest available version of Gitea
|
// UpdateRemoteVersion updates the latest available version of Gitea
|
||||||
|
|
16
modules/updatechecker/update_checker_test.go
Normal file
16
modules/updatechecker/update_checker_test.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright 2023 The Forgejo Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package updatechecker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDNSUpdate(t *testing.T) {
|
||||||
|
version, err := getVersionDNS("release.forgejo.org")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEmpty(t, version)
|
||||||
|
}
|
|
@ -281,6 +281,7 @@ run_user_not_match = The 'run as' username is not the current username: %s -> %s
|
||||||
internal_token_failed = Failed to generate internal token: %v
|
internal_token_failed = Failed to generate internal token: %v
|
||||||
secret_key_failed = Failed to generate secret key: %v
|
secret_key_failed = Failed to generate secret key: %v
|
||||||
save_config_failed = Failed to save configuration: %v
|
save_config_failed = Failed to save configuration: %v
|
||||||
|
enable_update_checker_helper_forgejo = Periodically checks for new Forgejo versions by checking a DNS TXT record at release.forgejo.org.
|
||||||
invalid_admin_setting = Administrator account setting is invalid: %v
|
invalid_admin_setting = Administrator account setting is invalid: %v
|
||||||
invalid_log_root_path = The log path is invalid: %v
|
invalid_log_root_path = The log path is invalid: %v
|
||||||
default_keep_email_private = Hide Email Addresses by Default
|
default_keep_email_private = Hide Email Addresses by Default
|
||||||
|
|
|
@ -143,6 +143,7 @@ func registerUpdateGiteaChecker() {
|
||||||
type UpdateCheckerConfig struct {
|
type UpdateCheckerConfig struct {
|
||||||
BaseConfig
|
BaseConfig
|
||||||
HTTPEndpoint string
|
HTTPEndpoint string
|
||||||
|
DomainEndpoint string
|
||||||
}
|
}
|
||||||
RegisterTaskFatal("update_checker", &UpdateCheckerConfig{
|
RegisterTaskFatal("update_checker", &UpdateCheckerConfig{
|
||||||
BaseConfig: BaseConfig{
|
BaseConfig: BaseConfig{
|
||||||
|
@ -151,9 +152,10 @@ func registerUpdateGiteaChecker() {
|
||||||
Schedule: "@every 168h",
|
Schedule: "@every 168h",
|
||||||
},
|
},
|
||||||
HTTPEndpoint: "https://dl.gitea.com/gitea/version.json",
|
HTTPEndpoint: "https://dl.gitea.com/gitea/version.json",
|
||||||
|
DomainEndpoint: "release.forgejo.org",
|
||||||
}, func(ctx context.Context, _ *user_model.User, config Config) error {
|
}, func(ctx context.Context, _ *user_model.User, config Config) error {
|
||||||
updateCheckerConfig := config.(*UpdateCheckerConfig)
|
updateCheckerConfig := config.(*UpdateCheckerConfig)
|
||||||
return updatechecker.GiteaUpdateChecker(updateCheckerConfig.HTTPEndpoint)
|
return updatechecker.GiteaUpdateChecker(updateCheckerConfig.HTTPEndpoint, updateCheckerConfig.DomainEndpoint)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -152,7 +152,7 @@
|
||||||
<label>{{.locale.Tr "install.enable_update_checker"}}</label>
|
<label>{{.locale.Tr "install.enable_update_checker"}}</label>
|
||||||
<input name="enable_update_checker" type="checkbox">
|
<input name="enable_update_checker" type="checkbox">
|
||||||
</div>
|
</div>
|
||||||
<span class="help">{{.locale.Tr "install.enable_update_checker_helper"}}</span>
|
<span class="help">{{.locale.Tr "install.enable_update_checker_helper_forgejo"}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Optional Settings -->
|
<!-- Optional Settings -->
|
||||||
|
|
Loading…
Reference in a new issue