Merge branch 'cloudflare:master' into tunnel-health
This commit is contained in:
commit
4ce0e1bd38
|
@ -0,0 +1,25 @@
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request: {}
|
||||||
|
workflow_dispatch: {}
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- master
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * *'
|
||||||
|
name: Semgrep config
|
||||||
|
jobs:
|
||||||
|
semgrep:
|
||||||
|
name: semgrep/ci
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
env:
|
||||||
|
SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
|
||||||
|
SEMGREP_URL: https://cloudflare.semgrep.dev
|
||||||
|
SEMGREP_APP_URL: https://cloudflare.semgrep.dev
|
||||||
|
SEMGREP_VERSION_CHECK_URL: https://cloudflare.semgrep.dev/api/check-version
|
||||||
|
container:
|
||||||
|
image: returntocorp/semgrep
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- run: semgrep ci
|
|
@ -1,3 +1,18 @@
|
||||||
|
2024.9.1
|
||||||
|
- 2024-09-10 Revert Release 2024.9.0
|
||||||
|
|
||||||
|
2024.9.0
|
||||||
|
- 2024-09-10 TUN-8621: Fix cloudflared version in change notes.
|
||||||
|
- 2024-09-06 PPIP-2310: Update quick tunnel disclaimer
|
||||||
|
- 2024-08-30 TUN-8621: Prevent QUIC connection from closing before grace period after unregistering
|
||||||
|
- 2024-08-09 TUN-8592: Use metadata from the edge to determine if request body is empty for QUIC transport
|
||||||
|
- 2024-06-26 TUN-8484: Print response when QuickTunnel can't be unmarshalled
|
||||||
|
|
||||||
|
2024.8.3
|
||||||
|
- 2024-08-15 TUN-8591 login command without extra text
|
||||||
|
- 2024-03-25 remove code that will not be executed
|
||||||
|
- 2024-03-25 remove code that will not be executed
|
||||||
|
|
||||||
2024.8.2
|
2024.8.2
|
||||||
- 2024-08-05 TUN-8583: change final directory of artifacts
|
- 2024-08-05 TUN-8583: change final directory of artifacts
|
||||||
- 2024-08-05 TUN-8585: Avoid creating GH client when dry-run is true
|
- 2024-08-05 TUN-8585: Avoid creating GH client when dry-run is true
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
package cliutil
|
package cliutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
|
@ -13,6 +16,7 @@ type BuildInfo struct {
|
||||||
GoArch string `json:"go_arch"`
|
GoArch string `json:"go_arch"`
|
||||||
BuildType string `json:"build_type"`
|
BuildType string `json:"build_type"`
|
||||||
CloudflaredVersion string `json:"cloudflared_version"`
|
CloudflaredVersion string `json:"cloudflared_version"`
|
||||||
|
Checksum string `json:"checksum"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetBuildInfo(buildType, version string) *BuildInfo {
|
func GetBuildInfo(buildType, version string) *BuildInfo {
|
||||||
|
@ -22,11 +26,12 @@ func GetBuildInfo(buildType, version string) *BuildInfo {
|
||||||
GoArch: runtime.GOARCH,
|
GoArch: runtime.GOARCH,
|
||||||
BuildType: buildType,
|
BuildType: buildType,
|
||||||
CloudflaredVersion: version,
|
CloudflaredVersion: version,
|
||||||
|
Checksum: currentBinaryChecksum(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bi *BuildInfo) Log(log *zerolog.Logger) {
|
func (bi *BuildInfo) Log(log *zerolog.Logger) {
|
||||||
log.Info().Msgf("Version %s", bi.CloudflaredVersion)
|
log.Info().Msgf("Version %s (Checksum %s)", bi.CloudflaredVersion, bi.Checksum)
|
||||||
if bi.BuildType != "" {
|
if bi.BuildType != "" {
|
||||||
log.Info().Msgf("Built%s", bi.GetBuildTypeMsg())
|
log.Info().Msgf("Built%s", bi.GetBuildTypeMsg())
|
||||||
}
|
}
|
||||||
|
@ -51,3 +56,28 @@ func (bi *BuildInfo) GetBuildTypeMsg() string {
|
||||||
func (bi *BuildInfo) UserAgent() string {
|
func (bi *BuildInfo) UserAgent() string {
|
||||||
return fmt.Sprintf("cloudflared/%s", bi.CloudflaredVersion)
|
return fmt.Sprintf("cloudflared/%s", bi.CloudflaredVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FileChecksum opens a file and returns the SHA256 checksum.
|
||||||
|
func FileChecksum(filePath string) (string, error) {
|
||||||
|
f, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
h := sha256.New()
|
||||||
|
if _, err := io.Copy(h, f); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%x", h.Sum(nil)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func currentBinaryChecksum() string {
|
||||||
|
currentPath, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
sum, _ := FileChecksum(currentPath)
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
|
@ -91,7 +91,7 @@ func main() {
|
||||||
|
|
||||||
tunnel.Init(bInfo, graceShutdownC) // we need this to support the tunnel sub command...
|
tunnel.Init(bInfo, graceShutdownC) // we need this to support the tunnel sub command...
|
||||||
access.Init(graceShutdownC, Version)
|
access.Init(graceShutdownC, Version)
|
||||||
updater.Init(Version)
|
updater.Init(bInfo)
|
||||||
tracing.Init(Version)
|
tracing.Init(Version)
|
||||||
token.Init(Version)
|
token.Init(Version)
|
||||||
tail.Init(bInfo)
|
tail.Init(bInfo)
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
"golang.org/x/term"
|
"golang.org/x/term"
|
||||||
|
|
||||||
|
"github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil"
|
||||||
"github.com/cloudflare/cloudflared/config"
|
"github.com/cloudflare/cloudflared/config"
|
||||||
"github.com/cloudflare/cloudflared/logger"
|
"github.com/cloudflare/cloudflared/logger"
|
||||||
)
|
)
|
||||||
|
@ -31,7 +32,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
version string
|
buildInfo *cliutil.BuildInfo
|
||||||
BuiltForPackageManager = ""
|
BuiltForPackageManager = ""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -81,8 +82,8 @@ func (uo *UpdateOutcome) noUpdate() bool {
|
||||||
return uo.Error == nil && uo.Updated == false
|
return uo.Error == nil && uo.Updated == false
|
||||||
}
|
}
|
||||||
|
|
||||||
func Init(v string) {
|
func Init(info *cliutil.BuildInfo) {
|
||||||
version = v
|
buildInfo = info
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckForUpdate(options updateOptions) (CheckResult, error) {
|
func CheckForUpdate(options updateOptions) (CheckResult, error) {
|
||||||
|
@ -100,11 +101,12 @@ func CheckForUpdate(options updateOptions) (CheckResult, error) {
|
||||||
cfdPath = encodeWindowsPath(cfdPath)
|
cfdPath = encodeWindowsPath(cfdPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
s := NewWorkersService(version, url, cfdPath, Options{IsBeta: options.isBeta,
|
s := NewWorkersService(buildInfo.CloudflaredVersion, url, cfdPath, Options{IsBeta: options.isBeta,
|
||||||
IsForced: options.isForced, RequestedVersion: options.intendedVersion})
|
IsForced: options.isForced, RequestedVersion: options.intendedVersion})
|
||||||
|
|
||||||
return s.Check()
|
return s.Check()
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeWindowsPath(path string) string {
|
func encodeWindowsPath(path string) string {
|
||||||
// We do this because Windows allows spaces in directories such as
|
// We do this because Windows allows spaces in directories such as
|
||||||
// Program Files but does not allow these directories to be spaced in batch files.
|
// Program Files but does not allow these directories to be spaced in batch files.
|
||||||
|
@ -196,10 +198,9 @@ func loggedUpdate(log *zerolog.Logger, options updateOptions) UpdateOutcome {
|
||||||
|
|
||||||
// AutoUpdater periodically checks for new version of cloudflared.
|
// AutoUpdater periodically checks for new version of cloudflared.
|
||||||
type AutoUpdater struct {
|
type AutoUpdater struct {
|
||||||
configurable *configurable
|
configurable *configurable
|
||||||
listeners *gracenet.Net
|
listeners *gracenet.Net
|
||||||
updateConfigChan chan *configurable
|
log *zerolog.Logger
|
||||||
log *zerolog.Logger
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AutoUpdaterConfigurable is the attributes of AutoUpdater that can be reconfigured during runtime
|
// AutoUpdaterConfigurable is the attributes of AutoUpdater that can be reconfigured during runtime
|
||||||
|
@ -210,10 +211,9 @@ type configurable struct {
|
||||||
|
|
||||||
func NewAutoUpdater(updateDisabled bool, freq time.Duration, listeners *gracenet.Net, log *zerolog.Logger) *AutoUpdater {
|
func NewAutoUpdater(updateDisabled bool, freq time.Duration, listeners *gracenet.Net, log *zerolog.Logger) *AutoUpdater {
|
||||||
return &AutoUpdater{
|
return &AutoUpdater{
|
||||||
configurable: createUpdateConfig(updateDisabled, freq, log),
|
configurable: createUpdateConfig(updateDisabled, freq, log),
|
||||||
listeners: listeners,
|
listeners: listeners,
|
||||||
updateConfigChan: make(chan *configurable),
|
log: log,
|
||||||
log: log,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,12 +232,20 @@ func createUpdateConfig(updateDisabled bool, freq time.Duration, log *zerolog.Lo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run will perodically check for cloudflared updates, download them, and then restart the current cloudflared process
|
||||||
|
// to use the new version. It delays the first update check by the configured frequency as to not attempt a
|
||||||
|
// download immediately and restart after starting (in the case that there is an upgrade available).
|
||||||
func (a *AutoUpdater) Run(ctx context.Context) error {
|
func (a *AutoUpdater) Run(ctx context.Context) error {
|
||||||
ticker := time.NewTicker(a.configurable.freq)
|
ticker := time.NewTicker(a.configurable.freq)
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
case <-ticker.C:
|
||||||
|
}
|
||||||
updateOutcome := loggedUpdate(a.log, updateOptions{updateDisabled: !a.configurable.enabled})
|
updateOutcome := loggedUpdate(a.log, updateOptions{updateDisabled: !a.configurable.enabled})
|
||||||
if updateOutcome.Updated {
|
if updateOutcome.Updated {
|
||||||
Init(updateOutcome.Version)
|
buildInfo.CloudflaredVersion = updateOutcome.Version
|
||||||
if IsSysV() {
|
if IsSysV() {
|
||||||
// SysV doesn't have a mechanism to keep service alive, we have to restart the process
|
// SysV doesn't have a mechanism to keep service alive, we have to restart the process
|
||||||
a.log.Info().Msg("Restarting service managed by SysV...")
|
a.log.Info().Msg("Restarting service managed by SysV...")
|
||||||
|
@ -254,25 +262,9 @@ func (a *AutoUpdater) Run(ctx context.Context) error {
|
||||||
} else if updateOutcome.UserMessage != "" {
|
} else if updateOutcome.UserMessage != "" {
|
||||||
a.log.Warn().Msg(updateOutcome.UserMessage)
|
a.log.Warn().Msg(updateOutcome.UserMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
case newConfigurable := <-a.updateConfigChan:
|
|
||||||
ticker.Stop()
|
|
||||||
a.configurable = newConfigurable
|
|
||||||
ticker = time.NewTicker(a.configurable.freq)
|
|
||||||
// Check if there is new version of cloudflared after receiving new AutoUpdaterConfigurable
|
|
||||||
case <-ticker.C:
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update is the method to pass new AutoUpdaterConfigurable to a running AutoUpdater. It is safe to be called concurrently
|
|
||||||
func (a *AutoUpdater) Update(updateDisabled bool, newFreq time.Duration) {
|
|
||||||
a.updateConfigChan <- createUpdateConfig(updateDisabled, newFreq, a.log)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isAutoupdateEnabled(log *zerolog.Logger, updateDisabled bool, updateFreq time.Duration) bool {
|
func isAutoupdateEnabled(log *zerolog.Logger, updateDisabled bool, updateFreq time.Duration) bool {
|
||||||
if !supportAutoUpdate(log) {
|
if !supportAutoUpdate(log) {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -9,8 +9,14 @@ import (
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
|
"github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Init(cliutil.GetBuildInfo("TEST", "TEST"))
|
||||||
|
}
|
||||||
|
|
||||||
func TestDisabledAutoUpdater(t *testing.T) {
|
func TestDisabledAutoUpdater(t *testing.T) {
|
||||||
listeners := &gracenet.Net{}
|
listeners := &gracenet.Net{}
|
||||||
log := zerolog.Nop()
|
log := zerolog.Nop()
|
||||||
|
|
|
@ -3,6 +3,7 @@ package updater
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime"
|
"runtime"
|
||||||
)
|
)
|
||||||
|
@ -79,6 +80,10 @@ func (s *WorkersService) Check() (CheckResult, error) {
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != 200 {
|
||||||
|
return nil, fmt.Errorf("unable to check for update: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
var v VersionResponse
|
var v VersionResponse
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&v); err != nil {
|
if err := json.NewDecoder(resp.Body).Decode(&v); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -3,7 +3,6 @@ package updater
|
||||||
import (
|
import (
|
||||||
"archive/tar"
|
"archive/tar"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"crypto/sha256"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -16,6 +15,10 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/getsentry/sentry-go"
|
||||||
|
|
||||||
|
"github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -27,9 +30,9 @@ const (
|
||||||
// start the service
|
// start the service
|
||||||
// exit with code 0 if we've reached this point indicating success.
|
// exit with code 0 if we've reached this point indicating success.
|
||||||
windowsUpdateCommandTemplate = `sc stop cloudflared >nul 2>&1
|
windowsUpdateCommandTemplate = `sc stop cloudflared >nul 2>&1
|
||||||
|
del "{{.OldPath}}"
|
||||||
rename "{{.TargetPath}}" {{.OldName}}
|
rename "{{.TargetPath}}" {{.OldName}}
|
||||||
rename "{{.NewPath}}" {{.BinaryName}}
|
rename "{{.NewPath}}" {{.BinaryName}}
|
||||||
del "{{.OldPath}}"
|
|
||||||
sc start cloudflared >nul 2>&1
|
sc start cloudflared >nul 2>&1
|
||||||
exit /b 0`
|
exit /b 0`
|
||||||
batchFileName = "cfd_update.bat"
|
batchFileName = "cfd_update.bat"
|
||||||
|
@ -86,8 +89,25 @@ func (v *WorkersVersion) Apply() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// check that the file is what is expected
|
downloadSum, err := cliutil.FileChecksum(newFilePath)
|
||||||
if err := isValidChecksum(v.checksum, newFilePath); err != nil {
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the file downloaded matches what is expected.
|
||||||
|
if v.checksum != downloadSum {
|
||||||
|
return errors.New("checksum validation failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the currently running version has the same checksum
|
||||||
|
if downloadSum == buildInfo.Checksum {
|
||||||
|
// Currently running binary matches the downloaded binary so we have no reason to update. This is
|
||||||
|
// typically unexpected, as such we emit a sentry event.
|
||||||
|
localHub := sentry.CurrentHub().Clone()
|
||||||
|
err := errors.New("checksum validation matches currently running process")
|
||||||
|
localHub.CaptureException(err)
|
||||||
|
// Make sure to cleanup the new downloaded file since we aren't upgrading versions.
|
||||||
|
os.Remove(newFilePath)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,27 +209,6 @@ func isCompressedFile(urlstring string) bool {
|
||||||
return strings.HasSuffix(u.Path, ".tgz")
|
return strings.HasSuffix(u.Path, ".tgz")
|
||||||
}
|
}
|
||||||
|
|
||||||
// checks if the checksum in the json response matches the checksum of the file download
|
|
||||||
func isValidChecksum(checksum, filePath string) error {
|
|
||||||
f, err := os.Open(filePath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
h := sha256.New()
|
|
||||||
if _, err := io.Copy(h, f); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
hash := fmt.Sprintf("%x", h.Sum(nil))
|
|
||||||
|
|
||||||
if checksum != hash {
|
|
||||||
return errors.New("checksum validation failed")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeBatchFile writes a batch file out to disk
|
// writeBatchFile writes a batch file out to disk
|
||||||
// see the dicussion on why it has to be done this way
|
// see the dicussion on why it has to be done this way
|
||||||
func writeBatchFile(targetPath string, newPath string, oldPath string) error {
|
func writeBatchFile(targetPath string, newPath string, oldPath string) error {
|
||||||
|
|
|
@ -8,6 +8,7 @@ const (
|
||||||
FeaturePostQuantum = "postquantum"
|
FeaturePostQuantum = "postquantum"
|
||||||
FeatureQUICSupportEOF = "support_quic_eof"
|
FeatureQUICSupportEOF = "support_quic_eof"
|
||||||
FeatureManagementLogs = "management_logs"
|
FeatureManagementLogs = "management_logs"
|
||||||
|
FeatureDatagramV3 = "support_datagram_v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
Loading…
Reference in New Issue