From d943602d21a09736a6d10d7db4ea3d16f962522d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Garcia?= Date: Wed, 15 Oct 2025 14:33:43 +0000 Subject: [PATCH 01/21] TUN-9919: Make RPM postinstall scriplet idempotent * TUN-9919: Make RPM postinstall scriplet idempotent Before this commit the postinstall scriptlet isn't idempotent, meaning the users see this error in their upgrade logs: `ln: failed to create symbolic link '/usr/local/bin/cloudflared': File exists warning: %post(cloudflared-2025.10.0-1.x86_64) scriptlet failed, exit status 1` This doesn't break the upgrade (which is why we haven't touched this in 5 years), but adding the -f (force) flag to the symlink command prevents this issue from happening Closes TUN-9919 --- postinst.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postinst.sh b/postinst.sh index d460ef3e..8531f3c4 100644 --- a/postinst.sh +++ b/postinst.sh @@ -1,5 +1,5 @@ #!/bin/bash set -eu -ln -s /usr/bin/cloudflared /usr/local/bin/cloudflared +ln -sf /usr/bin/cloudflared /usr/local/bin/cloudflared mkdir -p /usr/local/etc/cloudflared/ touch /usr/local/etc/cloudflared/.installedFromPackageManager || true From 12c2a8e144cdfd365bffa35a759caa93d917466d Mon Sep 17 00:00:00 2001 From: chungthuang Date: Tue, 14 Oct 2025 11:04:00 -0500 Subject: [PATCH 02/21] TUN-9916: Fix the cloudflared binary path used in the component test --- .ci/linux.gitlab-ci.yml | 2 +- .ci/windows.gitlab-ci.yml | 2 +- component-tests/test_management.py | 8 +++++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.ci/linux.gitlab-ci.yml b/.ci/linux.gitlab-ci.yml index d2b3e01b..3f57a42a 100644 --- a/.ci/linux.gitlab-ci.yml +++ b/.ci/linux.gitlab-ci.yml @@ -79,7 +79,7 @@ component-tests-linux: &component-tests-linux - ./.ci/scripts/component-tests.sh variables: &component-tests-variables CI: 1 - COMPONENT_TESTS_CONFIG_CONTENT: Y2xvdWRmbGFyZWRfYmluYXJ5OiBjbG91ZGZsYXJlZApjcmVkZW50aWFsc19maWxlOiBjcmVkLmpzb24Kb3JpZ2luY2VydDogY2VydC5wZW0Kem9uZV9kb21haW46IGFyZ290dW5uZWx0ZXN0LmNvbQp6b25lX3RhZzogNDg3OTZmMWU3MGJiNzY2OWMyOWJiNTFiYTI4MmJmNjU= + COMPONENT_TESTS_CONFIG_CONTENT: Y2xvdWRmbGFyZWRfYmluYXJ5OiAuL2Nsb3VkZmxhcmVkCmNyZWRlbnRpYWxzX2ZpbGU6IGNyZWQuanNvbgpvcmlnaW5jZXJ0OiBjZXJ0LnBlbQp6b25lX2RvbWFpbjogYXJnb3R1bm5lbHRlc3QuY29tCnpvbmVfdGFnOiA0ODc5NmYxZTcwYmI3NjY5YzI5YmI1MWJhMjgyYmY2NQ== tags: - linux-x86-8cpu-16gb artifacts: diff --git a/.ci/windows.gitlab-ci.yml b/.ci/windows.gitlab-ci.yml index fdc5ac0a..9a35edb6 100644 --- a/.ci/windows.gitlab-ci.yml +++ b/.ci/windows.gitlab-ci.yml @@ -36,7 +36,7 @@ load-windows-env-variables: # We have to encode the `COMPONENT_TESTS_ORIGINCERT` secret, because it content is a file, otherwise we can't export it using gitlab - echo "COMPONENT_TESTS_ORIGINCERT=$(echo "$COMPONENT_TESTS_ORIGINCERT" | base64 -w0)" >> windows.env variables: - COMPONENT_TESTS_CONFIG_CONTENT: Y2xvdWRmbGFyZWRfYmluYXJ5OiBjbG91ZGZsYXJlZC5leGUKY3JlZGVudGlhbHNfZmlsZTogY3JlZC5qc29uCm9yaWdpbmNlcnQ6IGNlcnQucGVtCnpvbmVfZG9tYWluOiBhcmdvdHVubmVsdGVzdC5jb20Kem9uZV90YWc6IDQ4Nzk2ZjFlNzBiYjc2NjljMjliYjUxYmEyODJiZjY1 + COMPONENT_TESTS_CONFIG_CONTENT: Y2xvdWRmbGFyZWRfYmluYXJ5OiAuL2Nsb3VkZmxhcmVkLmV4ZQpjcmVkZW50aWFsc19maWxlOiBjcmVkLmpzb24Kb3JpZ2luY2VydDogY2VydC5wZW0Kem9uZV9kb21haW46IGFyZ290dW5uZWx0ZXN0LmNvbQp6b25lX3RhZzogNDg3OTZmMWU3MGJiNzY2OWMyOWJiNTFiYTI4MmJmNjU= artifacts: access: 'none' reports: diff --git a/component-tests/test_management.py b/component-tests/test_management.py index cc6701fd..c9ae3d7e 100644 --- a/component-tests/test_management.py +++ b/component-tests/test_management.py @@ -107,7 +107,13 @@ class TestManagement: assert resp.status_code == 404, "Expected cloudflared to return 404 for /metrics" + + @retry(stop_max_attempt_number=MAX_RETRIES, wait_fixed=BACKOFF_SECS * 1000) def send_request(url, headers={}): with requests.Session() as s: - return s.get(url, timeout=BACKOFF_SECS, headers=headers) + resp = s.get(url, timeout=BACKOFF_SECS, headers=headers) + if resp.status_code == 530: + LOGGER.debug(f"Received 530 status, retrying request to {url}") + raise Exception(f"Received 530 status code from {url}") + return resp From 691550a6f2a0e8fff4957759246e49c24cdb5a89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Garcia?= Date: Tue, 21 Oct 2025 09:57:22 +0000 Subject: [PATCH 03/21] TUN-9941: Use new GPG key for RPM builds * TUN-9941: Use new GPG key for RPM builds Closes TUN-9941 --- release_pkgs.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/release_pkgs.py b/release_pkgs.py index 6214815a..ae3d59ff 100644 --- a/release_pkgs.py +++ b/release_pkgs.py @@ -430,11 +430,17 @@ if __name__ == "__main__": secondary_gpg_key_name = None if len(key_results) > 1: secondary_gpg_key_id, secondary_gpg_key_name = key_results[1] - # Import RPM public keys (one or two) - pkg_creator.import_rpm_key(args.gpg_public_key) + + if args.args.gpg_private_key_2: + print(f"signing RPM with secondary gpg_key: {secondary_gpg_key_id}") + pkg_creator.import_rpm_key(args.gpg_public_key_2) + else: + print(f"signing RPM with primary gpg_key: {primary_gpg_key_name}") + pkg_creator.import_rpm_key(args.gpg_public_key) + pkg_uploader = PkgUploader(args.account, args.bucket, args.id, args.secret) - print(f"signing with primary gpg_key: {primary_gpg_key_id} and secondary gpg_key: {secondary_gpg_key_id}") + print(f"signing deb with primary gpg_key: {primary_gpg_key_id} and secondary gpg_key: {secondary_gpg_key_id}") create_deb_packaging( pkg_creator, pkg_uploader, From 95642486c6568379abaddddfd8f03e8e3bcde94f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Garcia?= Date: Tue, 21 Oct 2025 13:11:33 +0000 Subject: [PATCH 04/21] TUN-9941: Fix typo causing r2-release-next deployment to fail * TUN-9941: Fix typo causing r2-release-next deployment to fail Closes TUN-9941 --- release_pkgs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release_pkgs.py b/release_pkgs.py index ae3d59ff..9f98c549 100644 --- a/release_pkgs.py +++ b/release_pkgs.py @@ -431,7 +431,7 @@ if __name__ == "__main__": if len(key_results) > 1: secondary_gpg_key_id, secondary_gpg_key_name = key_results[1] - if args.args.gpg_private_key_2: + if args.gpg_private_key_2: print(f"signing RPM with secondary gpg_key: {secondary_gpg_key_id}") pkg_creator.import_rpm_key(args.gpg_public_key_2) else: From 3a71c1bcd86593d6cc8ea6c3aebb1e5b63a59bcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Garcia?= Date: Tue, 21 Oct 2025 15:51:57 +0000 Subject: [PATCH 05/21] TUN-9941: Lookup correct key for RPM signature * TUN-9941: Lookup correct key for RPM signature Closes TUN-9941 --- release_pkgs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release_pkgs.py b/release_pkgs.py index 9f98c549..173e842b 100644 --- a/release_pkgs.py +++ b/release_pkgs.py @@ -459,7 +459,7 @@ if __name__ == "__main__": "./built_artifacts", args.release_tag, args.binary, - primary_gpg_key_name, + secondary_gpg_key_name, args.pkg_upload_url, args.gpg_public_key_url, args.upload_repo_file, From 2b456b9a792eac54466aab2a9b2321a94c4d59c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20=22Pisco=22=20Fernandes?= Date: Thu, 23 Oct 2025 10:54:18 +0100 Subject: [PATCH 06/21] TUN-9954: Update from go1.24.6 to go1.24.9 --- .ci/image/Dockerfile | 2 +- .gitlab-ci.yml | 2 +- Dockerfile | 2 +- Dockerfile.amd64 | 2 +- Dockerfile.arm64 | 2 +- cfsetup.yaml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.ci/image/Dockerfile b/.ci/image/Dockerfile index 4e92860e..d2766e09 100644 --- a/.ci/image/Dockerfile +++ b/.ci/image/Dockerfile @@ -7,7 +7,7 @@ RUN apt-get update && \ apt-get install --no-install-recommends --allow-downgrades -y \ build-essential \ git \ - go-boring=1.24.6-1 \ + go-boring=1.24.9-1 \ libffi-dev \ procps \ python3-dev \ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c9673553..5a0ab560 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,5 @@ variables: - GO_VERSION: "go1.24.6" + GO_VERSION: "go1.24.9" GIT_DEPTH: "0" default: diff --git a/Dockerfile b/Dockerfile index fd1676e2..d861f82c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # use a builder image for building cloudflare ARG TARGET_GOOS ARG TARGET_GOARCH -FROM golang:1.24.4 AS builder +FROM golang:1.24.9 AS builder ENV GO111MODULE=on \ CGO_ENABLED=0 \ TARGET_GOOS=${TARGET_GOOS} \ diff --git a/Dockerfile.amd64 b/Dockerfile.amd64 index b00ed3cb..c28b5679 100644 --- a/Dockerfile.amd64 +++ b/Dockerfile.amd64 @@ -1,5 +1,5 @@ # use a builder image for building cloudflare -FROM golang:1.24.4 AS builder +FROM golang:1.24.9 AS builder ENV GO111MODULE=on \ CGO_ENABLED=0 \ # the CONTAINER_BUILD envvar is used set github.com/cloudflare/cloudflared/metrics.Runtime=virtual diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 index 3bf0ebbf..79b3147f 100644 --- a/Dockerfile.arm64 +++ b/Dockerfile.arm64 @@ -1,5 +1,5 @@ # use a builder image for building cloudflare -FROM golang:1.24.4 AS builder +FROM golang:1.24.9 AS builder ENV GO111MODULE=on \ CGO_ENABLED=0 \ # the CONTAINER_BUILD envvar is used set github.com/cloudflare/cloudflared/metrics.Runtime=virtual diff --git a/cfsetup.yaml b/cfsetup.yaml index f58ee5ed..b007312c 100644 --- a/cfsetup.yaml +++ b/cfsetup.yaml @@ -1,4 +1,4 @@ -pinned_go: &pinned_go go-boring=1.24.4-1 +pinned_go: &pinned_go go-boring=1.24.9-1 build_dir: &build_dir /cfsetup_build default-flavor: bookworm From 114683f49e022247031700597397d1f69aa2196c Mon Sep 17 00:00:00 2001 From: Christopher Meng Date: Thu, 23 Oct 2025 14:38:09 +0000 Subject: [PATCH 07/21] Fix systemd service installation hanging * Fix systemd service installation hanging --- This kills the hanging when there is a network issue (port blocking or no Internet) and the installation cannot be completed with no error sent to the output. Before (killed manually since it hangs forever): ![499987567-de9003f9-4aaa-4667-9495-1d4b01069bed](/uploads/01063e6c2cf81fdd91ac8fbcd7f04a1b/499987567-de9003f9-4aaa-4667-9495-1d4b01069bed.png){width=817 height=69} After: ![499986549-f031035f-1633-46c0-a896-d9fd37054e83](/uploads/00c273f37d415617104b44736921b3d7/499986549-f031035f-1633-46c0-a896-d9fd37054e83.png){width=825 height=78} --- --- cmd/cloudflared/linux_service.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/cloudflared/linux_service.go b/cmd/cloudflared/linux_service.go index a950bb9f..c544465a 100644 --- a/cmd/cloudflared/linux_service.go +++ b/cmd/cloudflared/linux_service.go @@ -60,7 +60,7 @@ After=network-online.target Wants=network-online.target [Service] -TimeoutStartSec=0 +TimeoutStartSec=15 Type=notify ExecStart={{ .Path }} --no-autoupdate{{ range .ExtraArgs }} {{ . }}{{ end }} Restart=on-failure From 1367b967b3a2af17df28f4062b29d7210e5f6003 Mon Sep 17 00:00:00 2001 From: chungthuang Date: Tue, 28 Oct 2025 08:41:42 -0500 Subject: [PATCH 08/21] TUN-9849: Add cf-proxy-* to control response headers These headers will not be returned to the eyeball --- connection/header.go | 3 ++- connection/header_test.go | 30 +++++++++++++++--------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/connection/header.go b/connection/header.go index 269e56a7..0b48e822 100644 --- a/connection/header.go +++ b/connection/header.go @@ -53,7 +53,8 @@ var headerEncoding = base64.RawStdEncoding func IsControlResponseHeader(headerName string) bool { return strings.HasPrefix(headerName, ":") || strings.HasPrefix(headerName, "cf-int-") || - strings.HasPrefix(headerName, "cf-cloudflared-") + strings.HasPrefix(headerName, "cf-cloudflared-") || + strings.HasPrefix(headerName, "cf-proxy-") } // isWebsocketClientHeader returns true if the header name is required by the client to upgrade properly diff --git a/connection/header_test.go b/connection/header_test.go index 1ca4b31b..4aa259c4 100644 --- a/connection/header_test.go +++ b/connection/header_test.go @@ -1,18 +1,17 @@ package connection import ( - "fmt" "net/http" "reflect" "sort" "testing" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestSerializeHeaders(t *testing.T) { request, err := http.NewRequest(http.MethodGet, "http://example.com", nil) - assert.NoError(t, err) + require.NoError(t, err) mockHeaders := http.Header{ "Mock-Header-One": {"Mock header one value", "three"}, @@ -39,22 +38,22 @@ func TestSerializeHeaders(t *testing.T) { serializedHeaders := SerializeHeaders(request.Header) // Sanity check: the headers serialized to something that's not an empty string - assert.NotEqual(t, "", serializedHeaders) + require.NotEqual(t, "", serializedHeaders) // Deserialize back, and ensure we get the same set of headers deserializedHeaders, err := DeserializeHeaders(serializedHeaders) - assert.NoError(t, err) + require.NoError(t, err) - assert.Equal(t, 13, len(deserializedHeaders)) + require.Len(t, deserializedHeaders, 13) expectedHeaders := headerToReqHeader(mockHeaders) sort.Sort(ByName(deserializedHeaders)) sort.Sort(ByName(expectedHeaders)) - assert.True( + require.True( t, reflect.DeepEqual(expectedHeaders, deserializedHeaders), - fmt.Sprintf("got = %#v, want = %#v\n", deserializedHeaders, expectedHeaders), + "got = %#v, want = %#v\n", deserializedHeaders, expectedHeaders, ) } @@ -82,12 +81,12 @@ func headerToReqHeader(headers http.Header) (reqHeaders []HTTPHeader) { func TestSerializeNoHeaders(t *testing.T) { request, err := http.NewRequest(http.MethodGet, "http://example.com", nil) - assert.NoError(t, err) + require.NoError(t, err) serializedHeaders := SerializeHeaders(request.Header) deserializedHeaders, err := DeserializeHeaders(serializedHeaders) - assert.NoError(t, err) - assert.Equal(t, 0, len(deserializedHeaders)) + require.NoError(t, err) + require.Empty(t, deserializedHeaders) } func TestDeserializeMalformed(t *testing.T) { @@ -102,21 +101,22 @@ func TestDeserializeMalformed(t *testing.T) { for _, malformedValue := range malformedData { _, err = DeserializeHeaders(malformedValue) - assert.Error(t, err) + require.Error(t, err) } } func TestIsControlResponseHeader(t *testing.T) { controlResponseHeaders := []string{ - // Anything that begins with cf-int- or cf-cloudflared- + // Anything that begins with cf-int-, cf-cloudflared- or cf-proxy- "cf-int-sample-header", "cf-cloudflared-sample-header", + "cf-proxy-sample-header", // Any http2 pseudoheader ":sample-pseudo-header", } for _, header := range controlResponseHeaders { - assert.True(t, IsControlResponseHeader(header)) + require.True(t, IsControlResponseHeader(header)) } } @@ -130,6 +130,6 @@ func TestIsNotControlResponseHeader(t *testing.T) { } for _, header := range notControlResponseHeaders { - assert.False(t, IsControlResponseHeader(header)) + require.False(t, IsControlResponseHeader(header)) } } From 58519d1268896b92407559dc4d733450eff16501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20=22Pisco=22=20Fernandes?= Date: Thu, 30 Oct 2025 14:47:29 +0000 Subject: [PATCH 09/21] chore: Update ci image to use goboring 1.24.9 --- .ci/linux.gitlab-ci.yml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.ci/linux.gitlab-ci.yml b/.ci/linux.gitlab-ci.yml index 3f57a42a..96181af7 100644 --- a/.ci/linux.gitlab-ci.yml +++ b/.ci/linux.gitlab-ci.yml @@ -5,6 +5,7 @@ runner: linux-x86-8cpu-16gb stage: build golangVersion: "boring-1.24" + imageVersion: "3371-f5539bd6f83d@sha256:a2a68f580070f9411d0d3155959ed63b700ef319b5fcc62db340e92227bbc628" CGO_ENABLED: 1 include: @@ -16,7 +17,6 @@ include: <<: *golang_inputs jobPrefix: linux-build GOLANG_MAKE_TARGET: ci-build - imageVersion: "3308-283bdf9@sha256:fcd83570c91565a72eab132c38e0f589a481e2f3d4f3779f9f9a93eb555fee4a" ######################## ### Linux FIPS Build ### @@ -26,8 +26,6 @@ include: <<: *golang_inputs jobPrefix: linux-fips-build GOLANG_MAKE_TARGET: ci-fips-build - imageVersion: "3308-283bdf9@sha256:fcd83570c91565a72eab132c38e0f589a481e2f3d4f3779f9f9a93eb555fee4a" - ################# ### Unit Tests ## @@ -38,8 +36,6 @@ include: stage: test jobPrefix: test GOLANG_MAKE_TARGET: ci-test - imageVersion: "3308-283bdf9@sha256:fcd83570c91565a72eab132c38e0f589a481e2f3d4f3779f9f9a93eb555fee4a" - ###################### ### Unit Tests FIPS ## @@ -50,8 +46,6 @@ include: stage: test jobPrefix: test-fips GOLANG_MAKE_TARGET: ci-fips-test - imageVersion: "3308-283bdf9@sha256:fcd83570c91565a72eab132c38e0f589a481e2f3d4f3779f9f9a93eb555fee4a" - ################# ### Vuln Check ## @@ -63,8 +57,6 @@ include: stage: validate jobPrefix: vulncheck GOLANG_MAKE_TARGET: vulncheck - imageVersion: "3308-283bdf9@sha256:fcd83570c91565a72eab132c38e0f589a481e2f3d4f3779f9f9a93eb555fee4a" - ################################# ### Run Linux Component Tests ### From 4faa03dfed38b9220465461b5a3ae706aeb0a728 Mon Sep 17 00:00:00 2001 From: GoncaloGarcia Date: Fri, 24 Oct 2025 14:42:50 +0100 Subject: [PATCH 10/21] TUN-9961: Add pkg.cloudflared.com index.html to git repo This makes it easier to track changes and allows us to update it in a gitlab pipeline if we choose to in the future --- release/index.html | 212 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 release/index.html diff --git a/release/index.html b/release/index.html new file mode 100644 index 00000000..e2723851 --- /dev/null +++ b/release/index.html @@ -0,0 +1,212 @@ + + + + +

Cloudflare packages

+ + +
+

Cloudflared

+ + + +

Warning: Public Key Rollover (30 October 2025)

+

+ We have rolled our public key for package signing. If you are using RPM-based distributions (RHEL, + CentOS, Amazon Linux, etc.) or Debian Trixie and have the old key installed, RPM/Deb packages will no longer work with the old key. + Please update your repository configuration using the instructions below to ensure you can continue receiving + package updates. The previous keys will still work for other distributions for the time being, but it is now DEPRECATED and will be removed on 30 April 2026 +

+ +

Any Debian Based Distribution (Recommended)

+
+# Add cloudflare gpg key
+sudo mkdir -p --mode=0755 /usr/share/keyrings
+curl -fsSL https://pkg.cloudflare.com/cloudflare-public-v2.gpg | sudo tee /usr/share/keyrings/cloudflare-public-v2.gpg >/dev/null
+
+# Add this repo to your apt repositories
+# Stable
+echo 'deb [signed-by=/usr/share/keyrings/cloudflare-public-v2.gpg] https://pkg.cloudflare.com/cloudflared any main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
+# Nightly
+echo 'deb [signed-by=/usr/share/keyrings/cloudflare-public-v2.gpg] https://next.pkg.cloudflare.com/cloudflared any main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
+
+# install cloudflared
+sudo apt-get update && sudo apt-get install cloudflared
+
+ +

Debian Bookworm

+
+# Add cloudflare gpg key
+sudo mkdir -p --mode=0755 /usr/share/keyrings
+curl -fsSL https://pkg.cloudflare.com/cloudflare-public-v2.gpg | sudo tee /usr/share/keyrings/cloudflare-public-v2.gpg >/dev/null
+
+# Add this repo to your apt repositories
+# Stable
+echo 'deb [signed-by=/usr/share/keyrings/cloudflare-public-v2.gpg] https://pkg.cloudflare.com/cloudflared bookworm main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
+# Nightly
+echo 'deb [signed-by=/usr/share/keyrings/cloudflare-public-v2.gpg] https://next.pkg.cloudflare.com/cloudflared bookworm main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
+
+# install cloudflared
+sudo apt-get update && sudo apt-get install cloudflared
+
+ +

Ubuntu 20.04 (Focal Fossa)

+
+# Add cloudflare gpg key
+sudo mkdir -p --mode=0755 /usr/share/keyrings
+curl -fsSL https://pkg.cloudflare.com/cloudflare-public-v2.gpg | sudo tee /usr/share/keyrings/cloudflare-public-v2.gpg >/dev/null
+
+# Add this repo to your apt repositories
+# Stable
+echo 'deb [signed-by=/usr/share/keyrings/cloudflare-public-v2.gpg] https://pkg.cloudflare.com/cloudflared focal main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
+# Nightly
+echo 'deb [signed-by=/usr/share/keyrings/cloudflare-public-v2.gpg] https://next.pkg.cloudflare.com/cloudflared focal main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
+
+# install cloudflared
+sudo apt-get update && sudo apt-get install cloudflared
+
+ +

Ubuntu 22.04 (Jammy Jellyfish)

+
+# Add cloudflare gpg key
+sudo mkdir -p --mode=0755 /usr/share/keyrings
+curl -fsSL https://pkg.cloudflare.com/cloudflare-public-v2.gpg | sudo tee /usr/share/keyrings/cloudflare-public-v2.gpg >/dev/null
+
+# Add this repo to your apt repositories
+# Stable
+echo 'deb [signed-by=/usr/share/keyrings/cloudflare-public-v2.gpg] https://pkg.cloudflare.com/cloudflared jammy main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
+# Nightly
+echo 'deb [signed-by=/usr/share/keyrings/cloudflare-public-v2.gpg] https://next.pkg.cloudflare.com/cloudflared jammy main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
+
+# install cloudflared
+sudo apt-get update && sudo apt-get install cloudflared
+
+ +

Ubuntu 24.04 (Noble Numbat)

+
+# Add cloudflare gpg key
+sudo mkdir -p --mode=0755 /usr/share/keyrings
+curl -fsSL https://pkg.cloudflare.com/cloudflare-public-v2.gpg | sudo tee /usr/share/keyrings/cloudflare-public-v2.gpg >/dev/null
+
+# Add this repo to your apt repositories
+# Stable
+echo 'deb [signed-by=/usr/share/keyrings/cloudflare-public-v2.gpg] https://pkg.cloudflare.com/cloudflared noble main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
+# Nightly
+echo 'deb [signed-by=/usr/share/keyrings/cloudflare-public-v2.gpg] https://next.pkg.cloudflare.com/cloudflared noble main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
+
+# install cloudflared
+sudo apt-get update && sudo apt-get install cloudflared
+
+ +

Amazon Linux

+
+# Add cloudflared.repo to /etc/yum.repos.d/
+# Stable
+curl -fsSl https://pkg.cloudflare.com/cloudflared.repo | sudo tee /etc/yum.repos.d/cloudflared.repo
+# Nightly
+curl -fsSl https://next.pkg.cloudflare.com/cloudflared.repo | sudo tee /etc/yum.repos.d/cloudflared.repo
+
+#update repo
+sudo yum update
+
+# install cloudflared
+sudo yum install cloudflared
+
+ + +

RHEL Generic

+
+# Add cloudflared.repo to /etc/yum.repos.d/
+# Stable
+curl -fsSl https://pkg.cloudflare.com/cloudflared.repo | sudo tee /etc/yum.repos.d/cloudflared.repo
+# Nightly
+curl -fsSl https://next.pkg.cloudflare.com/cloudflared.repo | sudo tee /etc/yum.repos.d/cloudflared.repo
+
+#update repo
+sudo yum update
+
+# install cloudflared
+sudo yum install cloudflared
+
+ + +

Centos 7

+
+# This requires yum config-manager
+sudo yum install yum-utils
+
+# Add cloudflared.repo to config-manager
+# Stable
+sudo yum-config-manager --add-repo https://pkg.cloudflare.com/cloudflared.repo
+# Nightly
+sudo yum-config-manager --add-repo https://next.pkg.cloudflare.com/cloudflared.repo
+
+# install cloudflared
+yum install cloudflared
+
+ +

Centos 8

+
+# This requires dnf config-manager
+# Add cloudflared.repo to config-manager
+# Stable
+sudo dnf config-manager --add-repo https://pkg.cloudflare.com/cloudflared.repo
+# Nightly
+sudo dnf config-manager --add-repo https://next.pkg.cloudflare.com/cloudflared.repo
+
+# install cloudflared
+sudo dnf install cloudflared
+
+ +

Centos Stream

+
+# This requires dnf config-manager
+# Add cloudflared.repo to config-manager
+# Stable
+sudo dnf config-manager --add-repo https://pkg.cloudflare.com/cloudflared.repo
+# Nightly
+sudo dnf config-manager --add-repo https://next.pkg.cloudflare.com/cloudflared.repo
+
+# install cloudflared
+sudo dnf install cloudflared
+
+ + +

Gokeyless

+

Debian

+
+sudo mkdir -p --mode=0755 /usr/share/keyrings
+curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
+
+# Add this repo to your apt repositories
+echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/gokeyless buster main' | sudo tee /etc/apt/sources.list.d/cloudflare.list
+
+# install gokeyless
+sudo apt-get update && sudo apt-get install gokeyless
+
+ +

Centos 8

+
+# This requires dnf config-manager
+# Add gokeyless.repo to config-manager
+sudo dnf config-manager --add-repo https://pkg.cloudflare.com/gokeyless.repo
+
+# install gokeyless
+sudo dnf install gokeyless
+
+ + From 0caf31c543112b834c958a2f456daf11990f82c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20=22Pisco=22=20Fernandes?= Date: Thu, 30 Oct 2025 14:56:29 +0000 Subject: [PATCH 11/21] Release 2025.10.1 --- RELEASE_NOTES | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index fc193df3..3ac451a4 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,3 +1,15 @@ +2025.10.1 +- 2025-10-30 chore: Update ci image to use goboring 1.24.9 +- 2025-10-28 TUN-9849: Add cf-proxy-* to control response headers +- 2025-10-24 TUN-9961: Add pkg.cloudflared.com index.html to git repo +- 2025-10-23 TUN-9954: Update from go1.24.6 to go1.24.9 +- 2025-10-23 Fix systemd service installation hanging +- 2025-10-21 TUN-9941: Use new GPG key for RPM builds +- 2025-10-21 TUN-9941: Fix typo causing r2-release-next deployment to fail +- 2025-10-21 TUN-9941: Lookup correct key for RPM signature +- 2025-10-15 TUN-9919: Make RPM postinstall scriplet idempotent +- 2025-10-14 TUN-9916: Fix the cloudflared binary path used in the component test + 2025.10.0 - 2025-10-14 chore: Fix upload of RPM repo file during double signing - 2025-10-13 TUN-9882: Bump datagram v3 write channel capacity From e9f0628555396c17981e7790bfc427e5e16833e6 Mon Sep 17 00:00:00 2001 From: Luis Neto Date: Fri, 31 Oct 2025 14:03:45 +0000 Subject: [PATCH 12/21] chore: add claude review * chore: add claude review --- .gitlab-ci.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5a0ab560..02db7216 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,7 +7,7 @@ default: VAULT_ID_TOKEN: aud: https://vault.cfdata.org -stages: [sync, pre-build, build, validate, test, package, release] +stages: [sync, pre-build, build, validate, test, package, release, review] include: ##################################################### @@ -44,3 +44,10 @@ include: ################# Release Packages ################## ##################################################### - local: .ci/release.gitlab-ci.yml + + ##################################################### + ############## Manual Claude Review ################# + ##################################################### + - component: $CI_SERVER_FQDN/cloudflare/ci/ai/review@~latest + inputs: + whenToRun: "manual" From 334300bae77d41bb093056ffd649acb571df7c8d Mon Sep 17 00:00:00 2001 From: Chung-Ting Date: Fri, 31 Oct 2025 19:31:58 +0000 Subject: [PATCH 13/21] Chore: Update documentation links in README --- README.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 14281ac0..4edda958 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,14 @@ Contains the command-line client for Cloudflare Tunnel, a tunneling daemon that proxies traffic from the Cloudflare network to your origins. This daemon sits between Cloudflare network and your origin (e.g. a webserver). Cloudflare attracts client requests and sends them to you via this daemon, without requiring you to poke holes on your firewall --- your origin can remain as closed as possible. -Extensive documentation can be found in the [Cloudflare Tunnel section](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps) of the Cloudflare Docs. +Extensive documentation can be found in the [Cloudflare Tunnel section](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel) of the Cloudflare Docs. All usages related with proxying to your origins are available under `cloudflared tunnel help`. You can also use `cloudflared` to access Tunnel origins (that are protected with `cloudflared tunnel`) for TCP traffic at Layer 4 (i.e., not HTTP/websocket), which is relevant for use cases such as SSH, RDP, etc. Such usages are available under `cloudflared access help`. -You can instead use [WARP client](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/private-networks) +You can instead use [WARP client](https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/warp/) to access private origins behind Tunnels for Layer 4 traffic without requiring `cloudflared access` commands on the client side. @@ -19,41 +19,41 @@ to access private origins behind Tunnels for Layer 4 traffic without requiring ` Before you use Cloudflare Tunnel, you'll need to complete a few steps in the Cloudflare dashboard: you need to add a website to your Cloudflare account. Note that today it is possible to use Tunnel without a website (e.g. for private routing), but for legacy reasons this requirement is still necessary: -1. [Add a website to Cloudflare](https://support.cloudflare.com/hc/en-us/articles/201720164-Creating-a-Cloudflare-account-and-adding-a-website) -2. [Change your domain nameservers to Cloudflare](https://support.cloudflare.com/hc/en-us/articles/205195708) +1. [Add a website to Cloudflare](https://developers.cloudflare.com/fundamentals/manage-domains/add-site/) +2. [Change your domain nameservers to Cloudflare](https://developers.cloudflare.com/dns/zone-setups/full-setup/setup/) ## Installing `cloudflared` Downloads are available as standalone binaries, a Docker image, and Debian, RPM, and Homebrew packages. You can also find releases [here](https://github.com/cloudflare/cloudflared/releases) on the `cloudflared` GitHub repository. -* You can [install on macOS](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation#macos) via Homebrew or by downloading the [latest Darwin amd64 release](https://github.com/cloudflare/cloudflared/releases) -* Binaries, Debian, and RPM packages for Linux [can be found here](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation#linux) +* You can [install on macOS](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/#macos) via Homebrew or by downloading the [latest Darwin amd64 release](https://github.com/cloudflare/cloudflared/releases) +* Binaries, Debian, and RPM packages for Linux [can be found here](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/#linux) * A Docker image of `cloudflared` is [available on DockerHub](https://hub.docker.com/r/cloudflare/cloudflared) -* You can install on Windows machines with the [steps here](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation#windows) +* You can install on Windows machines with the [steps here](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/#windows) * To build from source, install the required version of go, mentioned in the [Development](#development) section below. Then you can run `make cloudflared`. -User documentation for Cloudflare Tunnel can be found at https://developers.cloudflare.com/cloudflare-one/connections/connect-apps +User documentation for Cloudflare Tunnel can be found at https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/ ## Creating Tunnels and routing traffic Once installed, you can authenticate `cloudflared` into your Cloudflare account and begin creating Tunnels to serve traffic to your origins. -* Create a Tunnel with [these instructions](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/) +* Create a Tunnel with [these instructions](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/) * Route traffic to that Tunnel: - * Via public [DNS records in Cloudflare](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/routing-to-tunnel/dns) - * Or via a public hostname guided by a [Cloudflare Load Balancer](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/routing-to-tunnel/lb) - * Or from [WARP client private traffic](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/private-net/) + * Via public [DNS records in Cloudflare](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/routing-to-tunnel/dns/) + * Or via a public hostname guided by a [Cloudflare Load Balancer](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/routing-to-tunnel/public-load-balancers/) + * Or from [WARP client private traffic](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/private-net/) ## TryCloudflare -Want to test Cloudflare Tunnel before adding a website to Cloudflare? You can do so with TryCloudflare using the documentation [available here](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/do-more-with-tunnels/trycloudflare/). +Want to test Cloudflare Tunnel before adding a website to Cloudflare? You can do so with TryCloudflare using the documentation [available here](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/trycloudflare/). ## Deprecated versions -Cloudflare currently supports versions of cloudflared that are **within one year** of the most recent release. Breaking changes unrelated to feature availability may be introduced that will impact versions released more than one year ago. You can read more about upgrading cloudflared in our [developer documentation](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/#updating-cloudflared). +Cloudflare currently supports versions of cloudflared that are **within one year** of the most recent release. Breaking changes unrelated to feature availability may be introduced that will impact versions released more than one year ago. You can read more about upgrading cloudflared in our [developer documentation](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/downloads/update-cloudflared/). For example, as of January 2023 Cloudflare will support cloudflared version 2023.1.1 to cloudflared 2022.1.1. From 70658b863bccff5e78f7704760af02fffe82bceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Garcia?= Date: Tue, 4 Nov 2025 16:59:30 +0000 Subject: [PATCH 14/21] chore: Update cloudflared signing key name in index.html * chore: Update cloudflared signing key name in index.html We want to preserve the old key name so that we don't have to update the dev docs. We will have the same key under this name and the v2 name to account for everyone who has already updated. --- release/index.html | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/release/index.html b/release/index.html index e2723851..97c05e2a 100644 --- a/release/index.html +++ b/release/index.html @@ -36,13 +36,13 @@
 # Add cloudflare gpg key
 sudo mkdir -p --mode=0755 /usr/share/keyrings
-curl -fsSL https://pkg.cloudflare.com/cloudflare-public-v2.gpg | sudo tee /usr/share/keyrings/cloudflare-public-v2.gpg >/dev/null
+curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
 
 # Add this repo to your apt repositories
 # Stable
-echo 'deb [signed-by=/usr/share/keyrings/cloudflare-public-v2.gpg] https://pkg.cloudflare.com/cloudflared any main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
+echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared any main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
 # Nightly
-echo 'deb [signed-by=/usr/share/keyrings/cloudflare-public-v2.gpg] https://next.pkg.cloudflare.com/cloudflared any main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
+echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://next.pkg.cloudflare.com/cloudflared any main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
 
 # install cloudflared
 sudo apt-get update && sudo apt-get install cloudflared
@@ -52,13 +52,13 @@ sudo apt-get update && sudo apt-get install cloudflared
 
 # Add cloudflare gpg key
 sudo mkdir -p --mode=0755 /usr/share/keyrings
-curl -fsSL https://pkg.cloudflare.com/cloudflare-public-v2.gpg | sudo tee /usr/share/keyrings/cloudflare-public-v2.gpg >/dev/null
+curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
 
 # Add this repo to your apt repositories
 # Stable
-echo 'deb [signed-by=/usr/share/keyrings/cloudflare-public-v2.gpg] https://pkg.cloudflare.com/cloudflared bookworm main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
+echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared bookworm main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
 # Nightly
-echo 'deb [signed-by=/usr/share/keyrings/cloudflare-public-v2.gpg] https://next.pkg.cloudflare.com/cloudflared bookworm main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
+echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://next.pkg.cloudflare.com/cloudflared bookworm main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
 
 # install cloudflared
 sudo apt-get update && sudo apt-get install cloudflared
@@ -68,13 +68,13 @@ sudo apt-get update && sudo apt-get install cloudflared
 
 # Add cloudflare gpg key
 sudo mkdir -p --mode=0755 /usr/share/keyrings
-curl -fsSL https://pkg.cloudflare.com/cloudflare-public-v2.gpg | sudo tee /usr/share/keyrings/cloudflare-public-v2.gpg >/dev/null
+curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
 
 # Add this repo to your apt repositories
 # Stable
-echo 'deb [signed-by=/usr/share/keyrings/cloudflare-public-v2.gpg] https://pkg.cloudflare.com/cloudflared focal main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
+echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared focal main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
 # Nightly
-echo 'deb [signed-by=/usr/share/keyrings/cloudflare-public-v2.gpg] https://next.pkg.cloudflare.com/cloudflared focal main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
+echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://next.pkg.cloudflare.com/cloudflared focal main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
 
 # install cloudflared
 sudo apt-get update && sudo apt-get install cloudflared
@@ -84,13 +84,13 @@ sudo apt-get update && sudo apt-get install cloudflared
 
 # Add cloudflare gpg key
 sudo mkdir -p --mode=0755 /usr/share/keyrings
-curl -fsSL https://pkg.cloudflare.com/cloudflare-public-v2.gpg | sudo tee /usr/share/keyrings/cloudflare-public-v2.gpg >/dev/null
+curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
 
 # Add this repo to your apt repositories
 # Stable
-echo 'deb [signed-by=/usr/share/keyrings/cloudflare-public-v2.gpg] https://pkg.cloudflare.com/cloudflared jammy main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
+echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared jammy main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
 # Nightly
-echo 'deb [signed-by=/usr/share/keyrings/cloudflare-public-v2.gpg] https://next.pkg.cloudflare.com/cloudflared jammy main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
+echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://next.pkg.cloudflare.com/cloudflared jammy main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
 
 # install cloudflared
 sudo apt-get update && sudo apt-get install cloudflared
@@ -100,13 +100,13 @@ sudo apt-get update && sudo apt-get install cloudflared
 
 # Add cloudflare gpg key
 sudo mkdir -p --mode=0755 /usr/share/keyrings
-curl -fsSL https://pkg.cloudflare.com/cloudflare-public-v2.gpg | sudo tee /usr/share/keyrings/cloudflare-public-v2.gpg >/dev/null
+curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
 
 # Add this repo to your apt repositories
 # Stable
-echo 'deb [signed-by=/usr/share/keyrings/cloudflare-public-v2.gpg] https://pkg.cloudflare.com/cloudflared noble main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
+echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared noble main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
 # Nightly
-echo 'deb [signed-by=/usr/share/keyrings/cloudflare-public-v2.gpg] https://next.pkg.cloudflare.com/cloudflared noble main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
+echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://next.pkg.cloudflare.com/cloudflared noble main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
 
 # install cloudflared
 sudo apt-get update && sudo apt-get install cloudflared

From a8fdbb83d03c182efe7042241bbe99974a257a32 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20=22Pisco=22=20Fernandes?=
 
Date: Fri, 31 Oct 2025 14:43:50 +0000
Subject: [PATCH 15/21] TUN-9800: Add pipelines for linux packaging

---
 .ci/commons.gitlab-ci.yml                     |   6 +-
 .ci/image/Dockerfile                          |   8 +-
 .ci/linux.gitlab-ci.yml                       |  31 +++
 .ci/release.gitlab-ci.yml                     | 120 +++++++++--
 .../scripts/linux/build-packages-fips.sh      |   0
 .ci/scripts/linux/build-packages.sh           |  59 +++++
 .ci/scripts/release-target.sh                 |  18 ++
 Makefile                                      |  10 +-
 build-packages.sh                             |  48 -----
 cfsetup.yaml                                  | 201 +-----------------
 release_pkgs.py                               |  10 +-
 11 files changed, 229 insertions(+), 282 deletions(-)
 rename build-packages-fips.sh => .ci/scripts/linux/build-packages-fips.sh (100%)
 create mode 100755 .ci/scripts/linux/build-packages.sh
 create mode 100755 .ci/scripts/release-target.sh
 delete mode 100755 build-packages.sh

diff --git a/.ci/commons.gitlab-ci.yml b/.ci/commons.gitlab-ci.yml
index 990c5709..43b43f22 100644
--- a/.ci/commons.gitlab-ci.yml
+++ b/.ci/commons.gitlab-ci.yml
@@ -3,14 +3,14 @@
   # Rules to run the job only on the master branch
   run-on-master:
     - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
-      when: always
+      when: on_success
     - when: never
   # Rules to run the job only on merge requests
   run-on-mr:
     - if: $CI_COMMIT_TAG
       when: never
     - if: $CI_PIPELINE_SOURCE == "merge_request_event"
-      when: always
+      when: on_success
     - when: never
   # Rules to run the job on merge_requests and master branch
   run-always:
@@ -18,7 +18,7 @@
       when: never
     - if: $CI_PIPELINE_SOURCE == "merge_request_event"
     - if: $CI_COMMIT_BRANCH != null && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
-      when: always
+      when: on_success
     - when: never
 
 # This before_script is injected into every job that runs on master meaning that if there is no tag the step
diff --git a/.ci/image/Dockerfile b/.ci/image/Dockerfile
index d2766e09..9d700fff 100644
--- a/.ci/image/Dockerfile
+++ b/.ci/image/Dockerfile
@@ -16,7 +16,13 @@ RUN apt-get update && \
         python3-venv \
         # libmsi and libgcab are libraries the wixl binary depends on.
         libmsi-dev \
-        libgcab-dev && \
+        libgcab-dev \
+        # deb and rpm build tools
+        rubygem-fpm \
+        rpm \
+        # create deb and rpm repository files
+        reprepro \
+        createrepo-c && \
     rm -rf /var/lib/apt/lists/* && \
     # Install wixl
     curl -o /usr/local/bin/wixl -L https://pkg.cloudflare.com/binaries/wixl && \
diff --git a/.ci/linux.gitlab-ci.yml b/.ci/linux.gitlab-ci.yml
index 96181af7..a751a24e 100644
--- a/.ci/linux.gitlab-ci.yml
+++ b/.ci/linux.gitlab-ci.yml
@@ -8,6 +8,18 @@
   imageVersion: "3371-f5539bd6f83d@sha256:a2a68f580070f9411d0d3155959ed63b700ef319b5fcc62db340e92227bbc628"
   CGO_ENABLED: 1
 
+.default-packaging-job: &packaging-job-defaults
+  stage: build
+  needs:
+    - ci-image-get-image-ref
+  rules:
+    - !reference [.default-rules, run-on-master]
+  image: $BUILD_IMAGE
+  cache: {}
+  artifacts:
+    paths:
+      - artifacts/*
+
 include:
   ###################
   ### Linux Build ###
@@ -89,3 +101,22 @@ component-tests-linux-fips:
   variables:
     <<: *component-tests-variables
     COMPONENT_TESTS_FIPS: 1
+
+################################
+####### Linux Packaging ########
+################################
+linux-packaging:
+  <<: *packaging-job-defaults
+  parallel:
+    matrix:
+      - ARCH: ["386", "amd64", "arm", "armhf", "arm64"]
+  script:
+    - ./.ci/scripts/linux/build-packages.sh ${ARCH}
+
+################################
+##### Linux FIPS Packaging #####
+################################
+linux-packaging-fips:
+  <<: *packaging-job-defaults
+  script:
+    - ./.ci/scripts/linux/build-packages-fips.sh
diff --git a/.ci/release.gitlab-ci.yml b/.ci/release.gitlab-ci.yml
index a41247cb..5c7c53b2 100644
--- a/.ci/release.gitlab-ci.yml
+++ b/.ci/release.gitlab-ci.yml
@@ -1,17 +1,28 @@
 include:
   - local: .ci/commons.gitlab-ci.yml
 
-###########################################
-### Push Cloudflared Binaries to Github ###
-###########################################
-release-cloudflared-to-github:
+  ######################################
+  ### Build and Push DockerHub Image ###
+  ######################################
+  - component: $CI_SERVER_FQDN/cloudflare/ci/docker-image/build-push-image@~latest
+    inputs:
+      stage: release
+      jobPrefix: docker-hub
+      runOnMR: false
+      runOnBranches: '^master$'
+      runOnChangesTo: ['RELEASE_NOTES']
+      needs:
+        - generate-version-file
+        - release-cloudflared-to-r2
+      commentImageRefs: false
+      runner: vm-linux-x86-4cpu-8gb
+      DOCKER_USER_BRANCH: svcgithubdockerhubcloudflar045
+      DOCKER_PASSWORD_BRANCH: gitlab/cloudflare/tun/cloudflared/_dev/dockerhub/svc_password/data
+      EXTRA_DIB_ARGS: --overwrite
+
+.default-release-job: &release-job-defaults
   stage: release
   image: $BUILD_IMAGE
-  extends: .check-tag
-  needs:
-    - ci-image-get-image-ref
-    - package-windows
-    - build-and-sign-cloudflared-macos
   rules:
     - !reference [.default-rules, run-on-master]
   cache:
@@ -19,8 +30,16 @@ release-cloudflared-to-github:
       - .cache/pip
   variables:
     PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
+    # KV Vars
     KV_NAMESPACE: 380e19aa04314648949b6ad841417ebe
-    KV_ACCOUNT: 5ab4e9dfbd435d24068829fda0077963
+    KV_ACCOUNT: &cf-account 5ab4e9dfbd435d24068829fda0077963
+    # R2 Vars
+    R2_BUCKET: cloudflared-pkgs
+    R2_ACCOUNT_ID: *cf-account
+    # APT and RPM Repository Vars
+    GPG_PUBLIC_KEY_URL: "https://pkg.cloudflare.com/cloudflare-ascii-pubkey.gpg"
+    PKG_URL: "https://pkg.cloudflare.com/cloudflared"
+    BINARY_NAME: cloudflared
   secrets:
     KV_API_TOKEN:
       vault: gitlab/cloudflare/tun/cloudflared/_dev/cfd_kv_api_token/data@kv
@@ -28,12 +47,77 @@ release-cloudflared-to-github:
     API_KEY:
       vault: gitlab/cloudflare/tun/cloudflared/_dev/cfd_github_api_key/data@kv
       file: false
+    R2_CLIENT_ID:
+      vault: gitlab/cloudflare/tun/cloudflared/_dev/_terraform_atlantis/r2_api_token/client_id@kv
+      file: false
+    R2_CLIENT_SECRET:
+      vault: gitlab/cloudflare/tun/cloudflared/_dev/_terraform_atlantis/r2_api_token/client_secret@kv
+      file: false
+    LINUX_SIGNING_PUBLIC_KEY:
+      vault: gitlab/cloudflare/tun/cloudflared/_dev/gpg_v1/public_key@kv
+      file: false
+    LINUX_SIGNING_PRIVATE_KEY:
+      vault: gitlab/cloudflare/tun/cloudflared/_dev/gpg_v1/private_key@kv
+      file: false
+    LINUX_SIGNING_PUBLIC_KEY_2:
+      vault: gitlab/cloudflare/tun/cloudflared/_dev/gpg_v2/public_key@kv
+      file: false
+    LINUX_SIGNING_PRIVATE_KEY_2:
+      vault: gitlab/cloudflare/tun/cloudflared/_dev/gpg_v2/private_key@kv
+      file: false
+
+###########################################
+### Push Cloudflared Binaries to Github ###
+###########################################
+release-cloudflared-to-github:
+  <<: *release-job-defaults
+  extends: .check-tag
+  needs:
+    - build-and-sign-cloudflared-macos
+    - ci-image-get-image-ref
+    - linux-packaging
+    - linux-packaging-fips
+    - package-windows
   script:
-    - python3 --version ; pip --version  # For debugging
-    - python3 -m venv venv
-    - source venv/bin/activate
-    - pip install pynacl==1.4.0 pygithub==1.55
-    - echo $VERSION
-    - echo $TAG_EXISTS
-    - echo "Running release because tag exists."
-    - make gitlab-release
+    - ./.ci/scripts/release-target.sh github-release
+
+#########################################
+### Upload Cloudflared Binaries to R2 ###
+#########################################
+release-cloudflared-to-r2:
+  <<: *release-job-defaults
+  extends: .check-tag
+  needs:
+    - ci-image-get-image-ref
+    - linux-packaging # We only release non-FIPS binaries to R2
+    - release-cloudflared-to-github
+  script:
+    - ./.ci/scripts/release-target.sh r2-linux-release
+
+#################################################
+### Upload Cloudflared Nightly Binaries to R2 ###
+#################################################
+release-cloudflared-nightly-to-r2:
+  <<: *release-job-defaults
+  variables:
+    R2_BUCKET: cloudflared-pkgs-next
+    GPG_PUBLIC_KEY_URL: "https://next.pkg.cloudflare.com/cloudflare-ascii-pubkey.gpg"
+    PKG_URL: "https://next.pkg.cloudflare.com/cloudflared"
+  needs:
+    - ci-image-get-image-ref
+    - linux-packaging # We only release non-FIPS binaries to R2
+  script:
+    - ./.ci/scripts/release-target.sh r2-linux-release
+
+#############################
+### Generate Version File ###
+#############################
+generate-version-file:
+  <<: *release-job-defaults
+  needs:
+    - ci-image-get-image-ref
+  script:
+    - make generate-docker-version
+  artifacts:
+    paths:
+      - versions
diff --git a/build-packages-fips.sh b/.ci/scripts/linux/build-packages-fips.sh
similarity index 100%
rename from build-packages-fips.sh
rename to .ci/scripts/linux/build-packages-fips.sh
diff --git a/.ci/scripts/linux/build-packages.sh b/.ci/scripts/linux/build-packages.sh
new file mode 100755
index 00000000..a6ca2037
--- /dev/null
+++ b/.ci/scripts/linux/build-packages.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+
+# Check if architecture argument is provided
+if [ $# -eq 0 ]; then
+    echo "Error: Architecture argument is required"
+    echo "Usage: $0 "
+    exit 1
+fi
+
+# Parameters
+arch=$1
+
+# Get Version
+VERSION=$(git describe --tags --always --match "[0-9][0-9][0-9][0-9].*.*")
+echo $VERSION
+
+# Disable FIPS module in go-boring
+export GOEXPERIMENT=noboringcrypto
+export CGO_ENABLED=0
+
+# This controls the directory the built artifacts go into
+export ARTIFACT_DIR=artifacts/
+mkdir -p $ARTIFACT_DIR
+
+export TARGET_OS=linux
+
+unset TARGET_ARM
+export TARGET_ARCH=$arch
+
+## Support for arm platforms without hardware FPU enabled
+if [[ $arch == arm ]] ; then
+    export TARGET_ARCH=arm
+    export TARGET_ARM=5
+fi
+
+## Support for armhf builds
+if [[ $arch == armhf ]] ; then
+    export TARGET_ARCH=arm
+    export TARGET_ARM=7
+fi
+
+make cloudflared-deb
+mv cloudflared\_$VERSION\_$arch.deb $ARTIFACT_DIR/cloudflared-linux-$arch.deb
+
+# rpm packages invert the - and _ and use x86_64 instead of amd64.
+RPMVERSION=$(echo $VERSION|sed -r 's/-/_/g')
+RPMARCH=$arch
+if [ $arch == "amd64" ];then
+    RPMARCH="x86_64"
+fi
+if [ $arch == "arm64" ]; then
+    RPMARCH="aarch64"
+fi
+make cloudflared-rpm
+mv cloudflared-$RPMVERSION-1.$RPMARCH.rpm $ARTIFACT_DIR/cloudflared-linux-$RPMARCH.rpm
+
+# finally move the linux binary as well.
+mv ./cloudflared $ARTIFACT_DIR/cloudflared-linux-$arch
+
diff --git a/.ci/scripts/release-target.sh b/.ci/scripts/release-target.sh
new file mode 100755
index 00000000..8c998310
--- /dev/null
+++ b/.ci/scripts/release-target.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+set -e -o pipefail
+
+# Check if a make target is provided as an argument
+if [ $# -eq 0 ]; then
+    echo "Error: Make target argument is required"
+    echo "Usage: $0 "
+    exit 1
+fi
+
+MAKE_TARGET=$1
+
+python3 -m venv venv
+source venv/bin/activate
+
+# Our release scripts are written in python, so we should install their dependecies here.
+pip install pynacl==1.4.0 pygithub==1.55 boto3==1.22.9 python-gnupg==0.4.9
+make $MAKE_TARGET
diff --git a/Makefile b/Makefile
index 7eaccec7..8490480e 100644
--- a/Makefile
+++ b/Makefile
@@ -221,10 +221,6 @@ cloudflared-deb: cloudflared cloudflared.1
 cloudflared-rpm: cloudflared cloudflared.1
 	$(call build_package,rpm)
 
-.PHONY: cloudflared-pkg
-cloudflared-pkg: cloudflared cloudflared.1
-	$(call build_package,osxpkg)
-
 .PHONY: cloudflared-msi
 cloudflared-msi:
 	wixl --define Version=$(VERSION) --define Path=$(EXECUTABLE_PATH) --output cloudflared-$(VERSION)-$(TARGET_ARCH).msi cloudflared.wxs
@@ -235,12 +231,8 @@ github-release-dryrun:
 
 .PHONY: github-release
 github-release:
-	python3 github_release.py --path $(PWD)/built_artifacts --release-version $(VERSION)
-	python3 github_message.py --release-version $(VERSION)
-
-.PHONY: gitlab-release
-gitlab-release:
 	python3 github_release.py --path $(PWD)/artifacts/ --release-version $(VERSION)
+	python3 github_message.py --release-version $(VERSION)
 
 .PHONY: r2-linux-release
 r2-linux-release:
diff --git a/build-packages.sh b/build-packages.sh
deleted file mode 100755
index df5dc7bb..00000000
--- a/build-packages.sh
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/bin/bash
-VERSION=$(git describe --tags --always --match "[0-9][0-9][0-9][0-9].*.*")
-echo $VERSION
-
-# Disable FIPS module in go-boring
-export GOEXPERIMENT=noboringcrypto
-export CGO_ENABLED=0
-
-# This controls the directory the built artifacts go into
-export ARTIFACT_DIR=artifacts/
-mkdir -p $ARTIFACT_DIR
-
-linuxArchs=("386" "amd64" "arm" "armhf" "arm64")
-export TARGET_OS=linux
-for arch in ${linuxArchs[@]}; do
-    unset TARGET_ARM
-    export TARGET_ARCH=$arch
-
-    ## Support for arm platforms without hardware FPU enabled
-    if [[ $arch == arm ]] ; then
-        export TARGET_ARCH=arm
-        export TARGET_ARM=5
-    fi
-    
-    ## Support for armhf builds 
-    if [[ $arch == armhf ]] ; then
-        export TARGET_ARCH=arm
-        export TARGET_ARM=7 
-    fi
-    
-    make cloudflared-deb
-    mv cloudflared\_$VERSION\_$arch.deb $ARTIFACT_DIR/cloudflared-linux-$arch.deb
-
-    # rpm packages invert the - and _ and use x86_64 instead of amd64.
-    RPMVERSION=$(echo $VERSION|sed -r 's/-/_/g')
-    RPMARCH=$arch
-    if [ $arch == "amd64" ];then
-        RPMARCH="x86_64"
-    fi
-    if [ $arch == "arm64" ]; then
-        RPMARCH="aarch64"
-    fi
-    make cloudflared-rpm
-    mv cloudflared-$RPMVERSION-1.$RPMARCH.rpm $ARTIFACT_DIR/cloudflared-linux-$RPMARCH.rpm
-
-    # finally move the linux binary as well.
-    mv ./cloudflared $ARTIFACT_DIR/cloudflared-linux-$arch
-done
diff --git a/cfsetup.yaml b/cfsetup.yaml
index b007312c..a9be5d11 100644
--- a/cfsetup.yaml
+++ b/cfsetup.yaml
@@ -4,85 +4,6 @@ build_dir: &build_dir /cfsetup_build
 default-flavor: bookworm
 
 bookworm: &bookworm
-  build-linux:
-    build_dir: *build_dir
-    builddeps: &build_deps
-      - *pinned_go
-      - build-essential
-      - fakeroot
-      - rubygem-fpm
-      - rpm
-      - libffi-dev
-      - golangci-lint=1.64.8-2
-    pre-cache: &build_pre_cache
-      - export GOCACHE=/cfsetup_build/.cache/go-build
-      - go install golang.org/x/tools/cmd/goimports@v0.30.0
-    post-cache:
-      # Linting
-      - make lint
-      - make fmt-check
-      # Build binary for component test
-      - GOOS=linux GOARCH=amd64 make cloudflared
-  build-linux-fips:
-    build_dir: *build_dir
-    builddeps: *build_deps
-    pre-cache: *build_pre_cache
-    post-cache:
-      - export FIPS=true
-      # Build binary for component test
-      - GOOS=linux GOARCH=amd64 make cloudflared
-  cover:
-    build_dir: *build_dir
-    builddeps: *build_deps
-    pre-cache: *build_pre_cache
-    post-cache:
-      - make cover
-  # except FIPS and macos
-  build-linux-release:
-    build_dir: *build_dir
-    builddeps: &build_deps_release
-      - *pinned_go
-      - build-essential
-      - fakeroot
-      - rubygem-fpm
-      - rpm
-      - libffi-dev
-      - python3-dev
-      - python3-pip
-      - python3-setuptools
-      - wget
-      - python3-venv
-    post-cache:
-      - python3 -m venv env
-      - . /cfsetup_build/env/bin/activate
-      - pip install pynacl==1.4.0 pygithub==1.55 boto3==1.22.9 python-gnupg==0.4.9
-      # build all packages (except macos and FIPS) and move them to /cfsetup/built_artifacts
-      - ./build-packages.sh
-  # handle FIPS separately so that we built with gofips compiler
-  build-linux-fips-release:
-    build_dir: *build_dir
-    builddeps: *build_deps_release
-    post-cache:
-      # same logic as above, but for FIPS packages only
-      - ./build-packages-fips.sh
-  generate-versions-file:
-    build_dir: *build_dir
-    builddeps:
-      - *pinned_go
-      - build-essential
-    post-cache:
-      - make generate-docker-version
-  build-deb:
-    build_dir: *build_dir
-    builddeps: &build_deb_deps
-      - *pinned_go
-      - build-essential
-      - fakeroot
-      - rubygem-fpm
-    post-cache:
-      - export GOOS=linux
-      - export GOARCH=amd64
-      - make cloudflared-deb
   build-fips-internal-deb:
     build_dir: *build_dir
     builddeps: &build_fips_deb_deps
@@ -118,126 +39,14 @@ bookworm: &bookworm
       - make cloudflared-deb
   build-deb-arm64:
     build_dir: *build_dir
-    builddeps: *build_deb_deps
+    builddeps:
+      - *pinned_go
+      - build-essential
+      - fakeroot
+      - rubygem-fpm
     post-cache:
       - export GOOS=linux
       - export GOARCH=arm64
       - make cloudflared-deb
-  test:
-    build_dir: *build_dir
-    builddeps: &build_deps_tests
-      - *pinned_go
-      - build-essential
-      - fakeroot
-      - rubygem-fpm
-      - rpm
-      - libffi-dev
-      - gotest-to-teamcity
-    pre-cache: *build_pre_cache
-    post-cache:
-      - export GOOS=linux
-      - export GOARCH=amd64
-      - export PATH="$HOME/go/bin:$PATH"
-      - make test | gotest-to-teamcity
-  test-fips:
-    build_dir: *build_dir
-    builddeps: *build_deps_tests
-    pre-cache: *build_pre_cache
-    post-cache:
-      - export GOOS=linux
-      - export GOARCH=amd64
-      - export FIPS=true
-      - export PATH="$HOME/go/bin:$PATH"
-      - make test | gotest-to-teamcity
-  component-test:
-    build_dir: *build_dir
-    builddeps: &build_deps_component_test
-      - *pinned_go
-      - python3
-      - python3-pip
-      - python3-setuptools
-      # procps installs the ps command which is needed in test_sysv_service
-      # because the init script uses ps pid to determine if the agent is
-      # running
-      - procps
-      - python3-venv
-    pre-cache-copy-paths:
-      - component-tests/requirements.txt
-    post-cache: &component_test_post_cache
-      - python3 -m venv env
-      - . env/bin/activate
-      - pip install --upgrade -r component-tests/requirements.txt
-      # Creates and routes a Named Tunnel for this build. Also constructs
-      # config file from env vars.
-      - python3 component-tests/setup.py --type create
-      - pytest component-tests -o log_cli=true --log-cli-level=INFO
-      # The Named Tunnel is deleted and its route unprovisioned here.
-      - python3 component-tests/setup.py --type cleanup
-  component-test-fips:
-    build_dir: *build_dir
-    builddeps: *build_deps_component_test
-    pre-cache-copy-paths:
-      - component-tests/requirements.txt
-    post-cache: *component_test_post_cache
-  github-release-dryrun:
-    build_dir: *build_dir
-    builddeps:
-      - *pinned_go
-      - build-essential
-      - python3-dev
-      - libffi-dev
-      - python3-setuptools
-      - python3-pip
-      - python3-venv
-    post-cache:
-      - python3 -m venv env
-      - . env/bin/activate
-      - pip install pynacl==1.4.0 pygithub==1.55
-      - make github-release-dryrun
-  github-release:
-    build_dir: *build_dir
-    builddeps:
-      - *pinned_go
-      - build-essential
-      - python3-dev
-      - libffi-dev
-      - python3-setuptools
-      - python3-pip
-      - python3-venv
-    post-cache:
-      - python3 -m venv env
-      - . env/bin/activate
-      - pip install pynacl==1.4.0 pygithub==1.55
-      - make github-release
-  r2-linux-release:
-    build_dir: *build_dir
-    builddeps: &r2-linux-release-deps
-      - *pinned_go
-      - build-essential
-      - fakeroot
-      - rubygem-fpm
-      - rpm
-      - wget
-      - python3-dev
-      - libffi-dev
-      - python3-setuptools
-      - python3-pip
-      - reprepro
-      - createrepo-c
-      - python3-venv
-    post-cache:
-      - python3 -m venv env
-      - . env/bin/activate
-      - pip install pynacl==1.4.0 pygithub==1.55 boto3==1.22.9 python-gnupg==0.4.9
-      - make r2-linux-release
-
-  r2-next-linux-release:
-    build_dir: *build_dir
-    builddeps: *r2-linux-release-deps
-    post-cache:
-      - python3 -m venv env
-      - . env/bin/activate
-      - pip install pynacl==1.4.0 pygithub==1.55 boto3==1.22.9 python-gnupg==0.4.9
-      - make r2-next-linux-release
 
 trixie: *bookworm
diff --git a/release_pkgs.py b/release_pkgs.py
index 173e842b..df16e092 100644
--- a/release_pkgs.py
+++ b/release_pkgs.py
@@ -260,7 +260,7 @@ def upload_from_directories(pkg_uploader, directory, release, binary):
 
 
 """ 
-    1. looks into a built_artifacts folder for cloudflared debs
+    1. looks into a artifacts folder for cloudflared debs
     2. creates Packages.gz, InRelease (signed) files
     3. uploads them to Cloudflare R2 
 
@@ -294,7 +294,7 @@ def create_deb_packaging(pkg_creator, pkg_uploader, releases, primary_gpg_key_id
     for release in releases:
         for arch in archs:
             print(f"creating deb pkgs for {release} and {arch}...")
-            pkg_creator.create_deb_pkgs(release, f"./built_artifacts/cloudflared-linux-{arch}.deb")
+            pkg_creator.create_deb_pkgs(release, f"./artifacts/cloudflared-linux-{arch}.deb")
 
     print("uploading latest to r2...")
     upload_from_directories(pkg_uploader, "dists", None, binary_name)
@@ -381,10 +381,6 @@ def parse_args():
             downloaders can use to verify signing"
     )
 
-    parser.add_argument(
-        "--gpg-public-key-url-2", default=os.environ.get("GPG_PUBLIC_KEY_URL_2"), help="Secondary GPG public key url for rollover"
-    )
-
     parser.add_argument(
         "--pkg-upload-url", default=os.environ.get("PKG_URL"), help="URL to be used by downloaders"
     )
@@ -456,7 +452,7 @@ if __name__ == "__main__":
     create_rpm_packaging(
         pkg_creator,
         pkg_uploader,
-        "./built_artifacts",
+        "./artifacts",
         args.release_tag,
         args.binary,
         secondary_gpg_key_name,

From eedbcf46d462b25c71c5837b219b1d220f3c8e0c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gon=C3=A7alo=20Garcia?= 
Date: Thu, 6 Nov 2025 11:41:21 +0000
Subject: [PATCH 16/21] TUN-9863: Introduce Code Signing for Windows Builds

* TUN-9863: Introduce Code Signing for Windows Builds

This commit adds a signing step to the build script for windows binaries.
Since we package the MSI on Linux, this commit adds another CI step that depends on package-windows and signs all of the windows packages.

To do so, we use azuresigntool which relies on a certificate stored in Azure Vault.

Closes TUN-9863
---
 .ci/release.gitlab-ci.yml        |  2 +-
 .ci/scripts/windows/builds.ps1   |  5 +++
 .ci/scripts/windows/sign-msi.ps1 | 26 ++++++++++++++++
 .ci/windows.gitlab-ci.yml        | 52 +++++++++++++++++++++++++++-----
 4 files changed, 77 insertions(+), 8 deletions(-)
 create mode 100644 .ci/scripts/windows/sign-msi.ps1

diff --git a/.ci/release.gitlab-ci.yml b/.ci/release.gitlab-ci.yml
index 5c7c53b2..8bafffe1 100644
--- a/.ci/release.gitlab-ci.yml
+++ b/.ci/release.gitlab-ci.yml
@@ -77,7 +77,7 @@ release-cloudflared-to-github:
     - ci-image-get-image-ref
     - linux-packaging
     - linux-packaging-fips
-    - package-windows
+    - windows-package-sign
   script:
     - ./.ci/scripts/release-target.sh github-release
 
diff --git a/.ci/scripts/windows/builds.ps1 b/.ci/scripts/windows/builds.ps1
index e4a42ea2..3abae290 100644
--- a/.ci/scripts/windows/builds.ps1
+++ b/.ci/scripts/windows/builds.ps1
@@ -4,6 +4,7 @@ $ProgressPreference = "SilentlyContinue"
 
 $env:TARGET_OS = "windows"
 $env:LOCAL_OS = "windows"
+$TIMESTAMP_RFC3161 = "http://timestamp.digicert.com"
 
 New-Item -Path ".\artifacts" -ItemType Directory
 
@@ -13,6 +14,8 @@ $env:LOCAL_ARCH = "amd64"
 $env:CGO_ENABLED = 1
 & make cloudflared
 if ($LASTEXITCODE -ne 0) { throw "Failed to build cloudflared for amd64" }
+# Sign build
+azuresigntool.exe sign -kvu $env:KEY_VAULT_URL -kvi "$env:KEY_VAULT_CLIENT_ID" -kvs "$env:KEY_VAULT_SECRET" -kvc "$env:KEY_VAULT_CERTIFICATE" -kvt "$env:KEY_VAULT_TENANT_ID" -tr "$TIMESTAMP_RFC3161" -d "Cloudflare Tunnel Daemon" .\cloudflared.exe
 copy .\cloudflared.exe .\artifacts\cloudflared-windows-amd64.exe
 
 Write-Output "Building for 386"
@@ -21,4 +24,6 @@ $env:LOCAL_ARCH = "386"
 $env:CGO_ENABLED = 0
 & make cloudflared
 if ($LASTEXITCODE -ne 0) { throw "Failed to build cloudflared for 386" }
+## Sign build
+azuresigntool.exe sign -kvu $env:KEY_VAULT_URL -kvi "$env:KEY_VAULT_CLIENT_ID" -kvs "$env:KEY_VAULT_SECRET" -kvc "$env:KEY_VAULT_CERTIFICATE" -kvt "$env:KEY_VAULT_TENANT_ID" -tr "$TIMESTAMP_RFC3161" -d "Cloudflare Tunnel Daemon" .\cloudflared.exe
 copy .\cloudflared.exe .\artifacts\cloudflared-windows-386.exe
diff --git a/.ci/scripts/windows/sign-msi.ps1 b/.ci/scripts/windows/sign-msi.ps1
new file mode 100644
index 00000000..9f29cd79
--- /dev/null
+++ b/.ci/scripts/windows/sign-msi.ps1
@@ -0,0 +1,26 @@
+# Sign Windows artifacts using azuretool
+# This script processes MSI files from the artifacts directory
+
+$ErrorActionPreference = "Stop"
+
+# Define paths
+$ARTIFACT_DIR = "artifacts"
+$TIMESTAMP_RFC3161 = "http://timestamp.digicert.com"
+
+Write-Host "Looking for Windows artifacts to sign in $ARTIFACT_DIR..."
+
+# Find all Windows MSI files
+$msiFiles = Get-ChildItem -Path $ARTIFACT_DIR -Filter "cloudflared-windows-*.msi" -ErrorAction SilentlyContinue
+
+if ($msiFiles.Count -eq 0) {
+    Write-Host "No Windows MSI files found in $ARTIFACT_DIR"
+    exit 1
+}
+
+Write-Host "Found $($msiFiles.Count) file(s) to sign:"
+foreach ($file in $msiFiles) {
+    Write-Host "Running azuretool sign for $($file.Name)"
+    azuresigntool.exe sign -kvu $env:KEY_VAULT_URL -kvi "$env:KEY_VAULT_CLIENT_ID" -kvs "$env:KEY_VAULT_SECRET" -kvc "$env:KEY_VAULT_CERTIFICATE" -kvt "$env:KEY_VAULT_TENANT_ID" -tr "$TIMESTAMP_RFC3161" -d "Cloudflare Tunnel Daemon" .\\$ARTIFACT_DIR\\$($file.Name)
+}
+
+Write-Host "Signing process completed"
diff --git a/.ci/windows.gitlab-ci.yml b/.ci/windows.gitlab-ci.yml
index 9a35edb6..4a1bb35a 100644
--- a/.ci/windows.gitlab-ci.yml
+++ b/.ci/windows.gitlab-ci.yml
@@ -14,7 +14,7 @@ include:
 ##########################################
 ### Build Cloudflared Windows Binaries ###
 ##########################################
-build-cloudflared-windows:
+windows-build-cloudflared:
   <<: *windows-build-defaults
   stage: build
   script:
@@ -26,7 +26,7 @@ build-cloudflared-windows:
 ######################################################
 ### Load Environment Variables for Component Tests ###
 ######################################################
-load-windows-env-variables:
+windows-load-env-variables:
   stage: pre-build
   extends: .component-tests
   script:
@@ -35,8 +35,29 @@ load-windows-env-variables:
     - echo "DNS_API_TOKEN=$DNS_API_TOKEN" >> windows.env
     # We have to encode the `COMPONENT_TESTS_ORIGINCERT` secret, because it content is a file, otherwise we can't export it using gitlab
     - echo "COMPONENT_TESTS_ORIGINCERT=$(echo "$COMPONENT_TESTS_ORIGINCERT" | base64 -w0)" >> windows.env
+    - echo "KEY_VAULT_URL=$KEY_VAULT_URL" >> windows.env
+    - echo "KEY_VAULT_CLIENT_ID=$KEY_VAULT_CLIENT_ID" >> windows.env
+    - echo "KEY_VAULT_TENANT_ID=$KEY_VAULT_TENANT_ID" >> windows.env
+    - echo "KEY_VAULT_SECRET=$KEY_VAULT_SECRET" >> windows.env
+    - echo "KEY_VAULT_CERTIFICATE=$KEY_VAULT_CERTIFICATE" >> windows.env
   variables:
     COMPONENT_TESTS_CONFIG_CONTENT: Y2xvdWRmbGFyZWRfYmluYXJ5OiAuL2Nsb3VkZmxhcmVkLmV4ZQpjcmVkZW50aWFsc19maWxlOiBjcmVkLmpzb24Kb3JpZ2luY2VydDogY2VydC5wZW0Kem9uZV9kb21haW46IGFyZ290dW5uZWx0ZXN0LmNvbQp6b25lX3RhZzogNDg3OTZmMWU3MGJiNzY2OWMyOWJiNTFiYTI4MmJmNjU=
+  secrets:
+    KEY_VAULT_URL:
+      vault: gitlab/cloudflare/tun/cloudflared/_dev/azure_vault/app_info/key_vault_url@kv
+      file: false
+    KEY_VAULT_CLIENT_ID:
+      vault: gitlab/cloudflare/tun/cloudflared/_dev/azure_vault/app_info/key_vault_client_id@kv
+      file: false
+    KEY_VAULT_TENANT_ID:
+      vault: gitlab/cloudflare/tun/cloudflared/_dev/azure_vault/app_info/key_vault_tenant_id@kv
+      file: false
+    KEY_VAULT_SECRET:
+      vault: gitlab/cloudflare/tun/cloudflared/_dev/azure_vault/secret/key_vault_secret@kv
+      file: false
+    KEY_VAULT_CERTIFICATE:
+      vault: gitlab/cloudflare/tun/cloudflared/_dev/azure_vault/certificate/key_vault_certificate@kv
+      file: false
   artifacts:
     access: 'none'
     reports:
@@ -45,12 +66,12 @@ load-windows-env-variables:
 ###################################
 ### Run Windows Component Tests ###
 ###################################
-component-tests-cloudflared-windows:
+windows-component-tests-cloudflared:
   <<: *windows-build-defaults
   stage: test
-  needs: ["load-windows-env-variables"]
+  needs: ["windows-load-env-variables"]
   script:
-    # We have to decode the secret we encoded on the `load-windows-env-variables` job
+    # We have to decode the secret we encoded on the `windows-load-env-variables` job
     - $env:COMPONENT_TESTS_ORIGINCERT = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($env:COMPONENT_TESTS_ORIGINCERT))
     - powershell -ExecutionPolicy Bypass -File ".\.ci\scripts\windows\go-wrapper.ps1" "${GO_VERSION}" ".\.ci\scripts\windows\component-test.ps1"
   artifacts:
@@ -60,13 +81,13 @@ component-tests-cloudflared-windows:
 ################################
 ### Package Windows Binaries ###
 ################################
-package-windows:
+windows-package:
   rules:
     - !reference [.default-rules, run-on-master]
   stage: package
   needs:
     - ci-image-get-image-ref
-    - build-cloudflared-windows
+    - windows-build-cloudflared
   image: $BUILD_IMAGE
   script:
     - .ci/scripts/package-windows.sh
@@ -74,3 +95,20 @@ package-windows:
   artifacts:
     paths:
       - artifacts/*
+
+#############################
+### Sign Windows Binaries ###
+#############################
+windows-package-sign:
+  <<: *windows-build-defaults
+  rules:
+    - !reference [.default-rules, run-on-master]
+  stage: package
+  needs:
+    - windows-package
+    - windows-load-env-variables
+  script:
+    - powershell -ExecutionPolicy Bypass -File ".\.ci\scripts\windows\sign-msi.ps1"
+  artifacts:
+    paths:
+      - artifacts/*

From 4cfebb83195646dc2d0c6174db350abbbf3a02bf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20=22Pisco=22=20Fernandes?=
 
Date: Thu, 6 Nov 2025 10:50:33 +0000
Subject: [PATCH 17/21] TUN-9800: Prefix gitlab steps with operating system

---
 .ci/linux.gitlab-ci.yml   | 8 ++++----
 .ci/mac.gitlab-ci.yml     | 6 +++---
 .ci/release.gitlab-ci.yml | 5 +++--
 3 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/.ci/linux.gitlab-ci.yml b/.ci/linux.gitlab-ci.yml
index a751a24e..92cfdc79 100644
--- a/.ci/linux.gitlab-ci.yml
+++ b/.ci/linux.gitlab-ci.yml
@@ -9,7 +9,7 @@
   CGO_ENABLED: 1
 
 .default-packaging-job: &packaging-job-defaults
-  stage: build
+  stage: package
   needs:
     - ci-image-get-image-ref
   rules:
@@ -73,7 +73,7 @@ include:
 #################################
 ### Run Linux Component Tests ###
 #################################
-component-tests-linux: &component-tests-linux
+linux-component-tests: &linux-component-tests
   stage: test
   extends: .component-tests
   needs:
@@ -93,8 +93,8 @@ component-tests-linux: &component-tests-linux
 ######################################
 ### Run Linux FIPS Component Tests ###
 ######################################
-component-tests-linux-fips:
-  <<: *component-tests-linux
+linux-component-tests-fips:
+  <<: *linux-component-tests
   needs:
     - ci-image-get-image-ref
     - linux-fips-build-boring-make
diff --git a/.ci/mac.gitlab-ci.yml b/.ci/mac.gitlab-ci.yml
index 94e16fbd..321adf5d 100644
--- a/.ci/mac.gitlab-ci.yml
+++ b/.ci/mac.gitlab-ci.yml
@@ -17,7 +17,7 @@ include:
 ######################################
 ### Build Cloudflared Mac Binaries ###
 ######################################
-build-cloudflared-macos: &build-mac
+macos-build-cloudflared: &mac-build
   <<: *mac-build-defaults
   stage: build
   artifacts:
@@ -38,8 +38,8 @@ build-cloudflared-macos: &build-mac
 ###############################################
 ### Build and Sign Cloudflared Mac Binaries ###
 ###############################################
-build-and-sign-cloudflared-macos:
-  <<: *build-mac
+macos-build-and-sign-cloudflared:
+  <<: *mac-build
   rules:
     - !reference [.default-rules, run-on-master]
   secrets:
diff --git a/.ci/release.gitlab-ci.yml b/.ci/release.gitlab-ci.yml
index 8bafffe1..ab417812 100644
--- a/.ci/release.gitlab-ci.yml
+++ b/.ci/release.gitlab-ci.yml
@@ -28,7 +28,7 @@ include:
   cache:
     paths:
       - .cache/pip
-  variables:
+  variables: &release-job-variables
     PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
     # KV Vars
     KV_NAMESPACE: 380e19aa04314648949b6ad841417ebe
@@ -73,10 +73,10 @@ release-cloudflared-to-github:
   <<: *release-job-defaults
   extends: .check-tag
   needs:
-    - build-and-sign-cloudflared-macos
     - ci-image-get-image-ref
     - linux-packaging
     - linux-packaging-fips
+    - macos-build-and-sign-cloudflared
     - windows-package-sign
   script:
     - ./.ci/scripts/release-target.sh github-release
@@ -100,6 +100,7 @@ release-cloudflared-to-r2:
 release-cloudflared-nightly-to-r2:
   <<: *release-job-defaults
   variables:
+    <<: *release-job-variables
     R2_BUCKET: cloudflared-pkgs-next
     GPG_PUBLIC_KEY_URL: "https://next.pkg.cloudflare.com/cloudflare-ascii-pubkey.gpg"
     PKG_URL: "https://next.pkg.cloudflare.com/cloudflared"

From 29e8d936f2b1e202af016dadc97b15a36a8d95a2 Mon Sep 17 00:00:00 2001
From: GoncaloGarcia 
Date: Fri, 7 Nov 2025 08:15:20 +0000
Subject: [PATCH 18/21] Release 2025.11.0

---
 RELEASE_NOTES | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/RELEASE_NOTES b/RELEASE_NOTES
index 3ac451a4..5bb839fc 100644
--- a/RELEASE_NOTES
+++ b/RELEASE_NOTES
@@ -1,3 +1,11 @@
+2025.11.0
+- 2025-11-06 TUN-9863: Introduce Code Signing for Windows Builds
+- 2025-11-06 TUN-9800: Prefix gitlab steps with operating system
+- 2025-11-04 chore: Update cloudflared signing key name in index.html
+- 2025-10-31 chore: add claude review
+- 2025-10-31 Chore: Update documentation links in README
+- 2025-10-31 TUN-9800: Add pipelines for linux packaging
+
 2025.10.1
 - 2025-10-30 chore: Update ci image to use goboring 1.24.9
 - 2025-10-28 TUN-9849: Add cf-proxy-* to control response headers

From 9ce16c5aac119dee542ff512ac35095b1a31d160 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20=22Pisco=22=20Fernandes?=
 
Date: Fri, 7 Nov 2025 11:13:47 +0000
Subject: [PATCH 19/21] TUN-9800: Fix docker hub push step

---
 .ci/release.gitlab-ci.yml      |  9 +++++++--
 component-tests/test_tunnel.py | 21 ++++++++++++++-------
 2 files changed, 21 insertions(+), 9 deletions(-)

diff --git a/.ci/release.gitlab-ci.yml b/.ci/release.gitlab-ci.yml
index ab417812..644e20a2 100644
--- a/.ci/release.gitlab-ci.yml
+++ b/.ci/release.gitlab-ci.yml
@@ -16,8 +16,13 @@ include:
         - release-cloudflared-to-r2
       commentImageRefs: false
       runner: vm-linux-x86-4cpu-8gb
-      DOCKER_USER_BRANCH: svcgithubdockerhubcloudflar045
-      DOCKER_PASSWORD_BRANCH: gitlab/cloudflare/tun/cloudflared/_dev/dockerhub/svc_password/data
+      # Based on if the CI reference is protected or not the CI component will
+      # either use _BRANCH or _PROD, therefore, to prevent the pipelines from failing
+      # we simply set both to the same value.
+      DOCKER_USER_BRANCH: &docker-hub-user svcgithubdockerhubcloudflar045
+      DOCKER_PASSWORD_BRANCH: &docker-hub-password gitlab/cloudflare/tun/cloudflared/_dev/dockerhub/svc_password/data
+      DOCKER_USER_PROD: *docker-hub-user
+      DOCKER_PASSWORD_PROD: *docker-hub-password
       EXTRA_DIB_ARGS: --overwrite
 
 .default-release-job: &release-job-defaults
diff --git a/component-tests/test_tunnel.py b/component-tests/test_tunnel.py
index 93a39c51..05b46dc0 100644
--- a/component-tests/test_tunnel.py
+++ b/component-tests/test_tunnel.py
@@ -33,13 +33,20 @@ class TestTunnel:
         LOGGER.debug(config)
         with start_cloudflared(tmp_path, config, cfd_pre_args=["tunnel", "--ha-connections", "1"],  cfd_args=["run"], new_process=True):
             wait_tunnel_ready(require_min_connections=1)
-            resp = send_request(config.get_url()+"/")
-            assert resp.status_code == 503, "Expected cloudflared to return 503 for all requests with no ingress defined"
-            resp = send_request(config.get_url()+"/test")
-            assert resp.status_code == 503, "Expected cloudflared to return 503 for all requests with no ingress defined"
+            expected_status_code = 503
+            resp = send_request(config.get_url()+"/", expected_status_code)
+            assert resp.status_code == expected_status_code, "Expected cloudflared to return 503 for all requests with no ingress defined"
+            resp = send_request(config.get_url()+"/test", expected_status_code)
+            assert resp.status_code == expected_status_code, "Expected cloudflared to return 503 for all requests with no ingress defined"
 
+def retry_if_result_none(result):
+    '''
+    Returns True if the result is None, indicating that the function should be retried.
+    '''
+    return result is None
 
-@retry(stop_max_attempt_number=MAX_RETRIES, wait_fixed=BACKOFF_SECS * 1000)
-def send_request(url, headers={}):
+@retry(retry_on_result=retry_if_result_none, stop_max_attempt_number=MAX_RETRIES, wait_fixed=BACKOFF_SECS * 1000)
+def send_request(url, expected_status_code=200):
     with requests.Session() as s:
-        return s.get(url, timeout=BACKOFF_SECS, headers=headers)
+        resp = s.get(url, timeout=BACKOFF_SECS)
+        return resp if resp.status_code == expected_status_code else None

From 17533b124c225c9090ef1de5cd2ee201e8c398e8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20=22Pisco=22=20Fernandes?=
 
Date: Fri, 7 Nov 2025 16:30:58 +0000
Subject: [PATCH 20/21] Release 2025.11.1

---
 RELEASE_NOTES | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/RELEASE_NOTES b/RELEASE_NOTES
index 5bb839fc..a891a998 100644
--- a/RELEASE_NOTES
+++ b/RELEASE_NOTES
@@ -1,3 +1,6 @@
+2025.11.1
+- 2025-11-07 TUN-9800: Fix docker hub push step
+
 2025.11.0
 - 2025-11-06 TUN-9863: Introduce Code Signing for Windows Builds
 - 2025-11-06 TUN-9800: Prefix gitlab steps with operating system

From 31f45fb5056bef48efae27fdc5b74bbd26fdf8a3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20=22Pisco=22=20Fernandes?=
 
Date: Fri, 7 Nov 2025 18:38:31 +0000
Subject: [PATCH 21/21] TUN-9800: Migrate apt internal builds to Gitlab

---
 .ci/apt-internal.gitlab-ci.yml | 151 +++++++++++++++++++++++++++++++++
 .ci/commons.gitlab-ci.yml      |  22 ++---
 .ci/image/Dockerfile           |   5 +-
 .ci/release.gitlab-ci.yml      |  12 ++-
 .gitlab-ci.yml                 |   7 +-
 cfsetup.yaml                   |  54 +-----------
 6 files changed, 178 insertions(+), 73 deletions(-)
 create mode 100644 .ci/apt-internal.gitlab-ci.yml

diff --git a/.ci/apt-internal.gitlab-ci.yml b/.ci/apt-internal.gitlab-ci.yml
new file mode 100644
index 00000000..a1df8e27
--- /dev/null
+++ b/.ci/apt-internal.gitlab-ci.yml
@@ -0,0 +1,151 @@
+.register_inputs: ®ister_inputs
+  stage: release-internal
+  runOnBranches: "^master$"
+  COMPONENT: "common"
+
+.register_inputs_stable_bookworm: ®ister_inputs_stable_bookworm
+  <<: *register_inputs
+  runOnChangesTo: ['RELEASE_NOTES']
+  FLAVOR: "bookworm"
+  SERIES: "stable"
+
+.register_inputs_stable_trixie: ®ister_inputs_stable_trixie
+  <<: *register_inputs
+  runOnChangesTo: ['RELEASE_NOTES']
+  FLAVOR: "trixie"
+  SERIES: "stable"
+
+.register_inputs_next_bookworm: ®ister_inputs_next_bookworm
+  <<: *register_inputs
+  FLAVOR: "bookworm"
+  SERIES: next
+
+.register_inputs_next_trixie: ®ister_inputs_next_trixie
+  <<: *register_inputs
+  FLAVOR: "trixie"
+  SERIES: next
+
+################################################
+### Generate Debian Package for Internal APT ###
+################################################
+.cloudflared-apt-build: &cloudflared_apt_build
+  stage: package
+  needs:
+    - ci-image-get-image-ref
+    - linux-packaging # For consistency, we only run this job after we knew we could build the packages for external delivery
+  image: $BUILD_IMAGE
+  cache: {}
+  script:
+    - make cloudflared-deb
+  artifacts:
+    paths:
+      - cloudflared*.deb
+
+##############
+### Stable ###
+##############
+cloudflared-amd64-stable:
+  <<: *cloudflared_apt_build
+  rules:
+    - !reference [.default-rules, run-on-release]
+  variables: &amd64-stable-vars
+    GOOS: linux
+    GOARCH: amd64
+    FIPS: true
+    ORIGINAL_NAME: true
+    CGO_ENABLED: 1
+
+cloudflared-arm64-stable:
+  <<: *cloudflared_apt_build
+  rules:
+    - !reference [.default-rules, run-on-release]
+  variables: &arm64-stable-vars
+    GOOS: linux
+    GOARCH: arm64
+    FIPS: false # TUN-7595
+    ORIGINAL_NAME: true
+    CGO_ENABLED: 1
+
+############
+### Next ###
+############
+cloudflared-amd64-next:
+  <<: *cloudflared_apt_build
+  rules:
+    - !reference [.default-rules, run-on-master]
+  variables:
+    <<: *amd64-stable-vars
+    NIGHTLY: true
+
+cloudflared-arm64-next:
+  <<: *cloudflared_apt_build
+  rules:
+    - !reference [.default-rules, run-on-master]
+  variables:
+    <<: *arm64-stable-vars
+    NIGHTLY: true
+
+include:
+  - local: .ci/commons.gitlab-ci.yml
+
+  ##########################################
+  ### Publish Packages to Internal Repos ###
+  ##########################################
+  # Bookworm AMD64
+  - component: $CI_SERVER_FQDN/cloudflare/ci/apt-register/register@~latest
+    inputs:
+      <<: *register_inputs_stable_bookworm
+      jobPrefix: cloudflared-bookworm-amd64
+      needs: &amd64-stable ["cloudflared-amd64-stable"]
+
+  # Bookworm ARM64
+  - component: $CI_SERVER_FQDN/cloudflare/ci/apt-register/register@~latest
+    inputs:
+      <<: *register_inputs_stable_bookworm
+      jobPrefix: cloudflared-bookworm-arm64
+      needs: &arm64-stable ["cloudflared-arm64-stable"]
+
+  # Trixie AMD64
+  - component: $CI_SERVER_FQDN/cloudflare/ci/apt-register/register@~latest
+    inputs:
+      <<: *register_inputs_stable_trixie
+      jobPrefix: cloudflared-trixie-amd64
+      needs: *amd64-stable
+
+  # Trixie ARM64
+  - component: $CI_SERVER_FQDN/cloudflare/ci/apt-register/register@~latest
+    inputs:
+      <<: *register_inputs_stable_trixie
+      jobPrefix: cloudflared-trixie-arm64
+      needs: *arm64-stable
+
+  ##################################################
+  ### Publish Nightly Packages to Internal Repos ###
+  ##################################################
+  # Bookworm AMD64
+  - component: $CI_SERVER_FQDN/cloudflare/ci/apt-register/register@~latest
+    inputs:
+      <<: *register_inputs_next_bookworm
+      jobPrefix: cloudflared-nightly-bookworm-amd64
+      needs: &amd64-next ['cloudflared-amd64-next']
+
+  # Bookworm ARM64
+  - component: $CI_SERVER_FQDN/cloudflare/ci/apt-register/register@~latest
+    inputs:
+      <<: *register_inputs_next_bookworm
+      jobPrefix: cloudflared-nightly-bookworm-arm64
+      needs: &arm64-next ['cloudflared-arm64-next']
+
+  # Trixie AMD64
+  - component: $CI_SERVER_FQDN/cloudflare/ci/apt-register/register@~latest
+    inputs:
+      <<: *register_inputs_next_trixie
+      jobPrefix: cloudflared-nightly-trixie-amd64
+      needs: *amd64-next
+
+  # Trixie ARM64
+  - component: $CI_SERVER_FQDN/cloudflare/ci/apt-register/register@~latest
+    inputs:
+      <<: *register_inputs_next_trixie
+      jobPrefix: cloudflared-nightly-trixie-arm64
+      needs: *arm64-next
diff --git a/.ci/commons.gitlab-ci.yml b/.ci/commons.gitlab-ci.yml
index 43b43f22..28a839af 100644
--- a/.ci/commons.gitlab-ci.yml
+++ b/.ci/commons.gitlab-ci.yml
@@ -20,21 +20,13 @@
     - if: $CI_COMMIT_BRANCH != null && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
       when: on_success
     - when: never
-
-# This before_script is injected into every job that runs on master meaning that if there is no tag the step
-# will succeed but only write "No tag present - Skipping" to the console.
-.check-tag:
-  before_script:
-    - |
-      # Check if there is a Git tag pointing to HEAD
-      echo "Tag found: $(git tag --points-at HEAD | grep .)"
-      if git tag --points-at HEAD | grep .; then
-        echo "Tag found: $(git tag --points-at HEAD | grep .)"
-        export "VERSION=$(git tag --points-at HEAD | grep .)"
-      else
-        echo "No tag present — skipping."
-        exit 0
-      fi
+  # Rules to run the job only when a release happens
+  run-on-release:
+    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+      changes:
+          - 'RELEASE_NOTES'
+      when: on_success
+    - when: never
 
 .component-tests:
   image: $BUILD_IMAGE
diff --git a/.ci/image/Dockerfile b/.ci/image/Dockerfile
index 9d700fff..05536bfb 100644
--- a/.ci/image/Dockerfile
+++ b/.ci/image/Dockerfile
@@ -22,7 +22,10 @@ RUN apt-get update && \
         rpm \
         # create deb and rpm repository files
         reprepro \
-        createrepo-c && \
+        createrepo-c \
+        # gcc for cross architecture compilation in arm
+        gcc-aarch64-linux-gnu \
+        libc6-dev-arm64-cross && \
     rm -rf /var/lib/apt/lists/* && \
     # Install wixl
     curl -o /usr/local/bin/wixl -L https://pkg.cloudflare.com/binaries/wixl && \
diff --git a/.ci/release.gitlab-ci.yml b/.ci/release.gitlab-ci.yml
index 644e20a2..89d68743 100644
--- a/.ci/release.gitlab-ci.yml
+++ b/.ci/release.gitlab-ci.yml
@@ -28,8 +28,6 @@ include:
 .default-release-job: &release-job-defaults
   stage: release
   image: $BUILD_IMAGE
-  rules:
-    - !reference [.default-rules, run-on-master]
   cache:
     paths:
       - .cache/pip
@@ -76,7 +74,8 @@ include:
 ###########################################
 release-cloudflared-to-github:
   <<: *release-job-defaults
-  extends: .check-tag
+  rules:
+    - !reference [.default-rules, run-on-release]
   needs:
     - ci-image-get-image-ref
     - linux-packaging
@@ -91,7 +90,8 @@ release-cloudflared-to-github:
 #########################################
 release-cloudflared-to-r2:
   <<: *release-job-defaults
-  extends: .check-tag
+  rules:
+    - !reference [.default-rules, run-on-release]
   needs:
     - ci-image-get-image-ref
     - linux-packaging # We only release non-FIPS binaries to R2
@@ -104,6 +104,8 @@ release-cloudflared-to-r2:
 #################################################
 release-cloudflared-nightly-to-r2:
   <<: *release-job-defaults
+  rules:
+    - !reference [.default-rules, run-on-master]
   variables:
     <<: *release-job-variables
     R2_BUCKET: cloudflared-pkgs-next
@@ -120,6 +122,8 @@ release-cloudflared-nightly-to-r2:
 #############################
 generate-version-file:
   <<: *release-job-defaults
+  rules:
+    - !reference [.default-rules, run-on-release]
   needs:
     - ci-image-get-image-ref
   script:
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 02db7216..bfc88f37 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -7,7 +7,7 @@ default:
     VAULT_ID_TOKEN:
       aud: https://vault.cfdata.org
 
-stages: [sync, pre-build, build, validate, test, package, release, review]
+stages: [sync, pre-build, build, validate, test, package, release, release-internal, review]
 
 include:
   #####################################################
@@ -45,6 +45,11 @@ include:
   #####################################################
   - local: .ci/release.gitlab-ci.yml
 
+  #####################################################
+  ########## Release Packages Internally ##############
+  #####################################################
+  - local: .ci/apt-internal.gitlab-ci.yml
+
   #####################################################
   ############## Manual Claude Review #################
   #####################################################
diff --git a/cfsetup.yaml b/cfsetup.yaml
index a9be5d11..05b05c3a 100644
--- a/cfsetup.yaml
+++ b/cfsetup.yaml
@@ -1,52 +1,2 @@
-pinned_go: &pinned_go go-boring=1.24.9-1
-
-build_dir: &build_dir /cfsetup_build
-default-flavor: bookworm
-
-bookworm: &bookworm
-  build-fips-internal-deb:
-    build_dir: *build_dir
-    builddeps: &build_fips_deb_deps
-      - *pinned_go
-      - build-essential
-      - fakeroot
-      - rubygem-fpm
-    post-cache:
-      - export GOOS=linux
-      - export GOARCH=amd64
-      - export FIPS=true
-      - export ORIGINAL_NAME=true
-      - make cloudflared-deb
-  build-internal-deb-nightly-amd64:
-    build_dir: *build_dir
-    builddeps: *build_fips_deb_deps
-    post-cache:
-      - export GOOS=linux
-      - export GOARCH=amd64
-      - export NIGHTLY=true
-      - export FIPS=true
-      - export ORIGINAL_NAME=true
-      - make cloudflared-deb
-  build-internal-deb-nightly-arm64:
-    build_dir: *build_dir
-    builddeps: *build_fips_deb_deps
-    post-cache:
-      - export GOOS=linux
-      - export GOARCH=arm64
-      - export NIGHTLY=true
-      # - export FIPS=true # TUN-7595
-      - export ORIGINAL_NAME=true
-      - make cloudflared-deb
-  build-deb-arm64:
-    build_dir: *build_dir
-    builddeps:
-      - *pinned_go
-      - build-essential
-      - fakeroot
-      - rubygem-fpm
-    post-cache:
-      - export GOOS=linux
-      - export GOARCH=arm64
-      - make cloudflared-deb
-
-trixie: *bookworm
+# A valid cfsetup.yaml is required but we dont have any real config to specify
+dummy_key: true