mirror of
https://github.com/cloudflare/cloudflared
synced 2026-06-08 13:33:07 +00:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c1d8c5e960 | |||
| 7ae1d4668e | |||
| adb7d40084 | |||
| 541c63d737 | |||
| f1d6f0c0be | |||
| 958b6f1d24 | |||
| 6d1d91d9f9 | |||
| fc0ecf4185 | |||
| 349586007c |
@@ -1,3 +1,6 @@
|
||||
## 2023.9.0
|
||||
### Notices
|
||||
- The `warp-routing` `enabled: boolean` flag is no longer supported in the configuration file. Warp Routing traffic (eg TCP, UDP, ICMP) traffic is proxied to cloudflared if routes to the target tunnel are configured. This change does not affect remotely managed tunnels, but for locally managed tunnels, users that might be relying on this feature flag to block traffic should instead guarantee that tunnel has no Private Routes configured for the tunnel.
|
||||
## 2023.7.0
|
||||
### New Features
|
||||
- You can now enable additional diagnostics over the management.argotunnel.com service for your active cloudflared connectors via a new runtime flag `--management-diagnostics` (or env `TUNNEL_MANAGEMENT_DIAGNOSTICS`). This feature is provided as opt-in and requires the flag to enable. Endpoints such as /metrics provides your prometheus metrics endpoint another mechanism to be reached. Additionally /debug/pprof/(goroutine|heap) are also introduced to allow for remotely retrieving active pprof information from a running cloudflared connector.
|
||||
|
||||
@@ -53,9 +53,6 @@ Want to test Cloudflare Tunnel before adding a website to Cloudflare? You can do
|
||||
|
||||
## Deprecated versions
|
||||
|
||||
Cloudflare currently supports versions of `cloudflared` 2020.5.1 and later. Breaking changes unrelated to feature availability may be introduced that will impact versions released prior to 2020.5.1. You can read more about upgrading `cloudflared` in our [developer documentation](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation#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/connections/connect-networks/downloads/#updating-cloudflared).
|
||||
|
||||
| Version(s) | Deprecation status |
|
||||
|---|---|
|
||||
| 2020.5.1 and later | Supported |
|
||||
| Versions prior to 2020.5.1 | No longer supported |
|
||||
For example, as of January 2023 Cloudflare will support cloudflared version 2023.1.1 to cloudflared 2022.1.1.
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
2023.10.0
|
||||
- 2023-10-06 TUN-7864: Document cloudflared versions support
|
||||
- 2023-10-03 CUSTESC-33731: Make rule match test report rule in 0-index base
|
||||
- 2023-09-22 TUN-7824: Fix usage of systemctl status to detect which services are installed
|
||||
- 2023-09-20 TUN-7813: Improve tunnel delete command to use cascade delete
|
||||
- 2023-09-20 TUN-7787: cloudflared only list ip routes targeted for cfd_tunnel
|
||||
- 2023-09-15 TUN-7787: Refactor cloudflared to use new route endpoints based on route IDs
|
||||
- 2023-09-08 TUN-7776: Remove warp-routing flag from cloudflared
|
||||
- 2023-09-05 TUN-7756: Clarify that QUIC is mandatory to support ICMP proxying
|
||||
|
||||
2023.8.2
|
||||
- 2023-08-25 TUN-7700: Implement feature selector to determine if connections will prefer post quantum cryptography
|
||||
- 2023-08-22 TUN-7707: Use X25519Kyber768Draft00 curve when post-quantum feature is enabled
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@ type TunnelClient interface {
|
||||
GetTunnel(tunnelID uuid.UUID) (*Tunnel, error)
|
||||
GetTunnelToken(tunnelID uuid.UUID) (string, error)
|
||||
GetManagementToken(tunnelID uuid.UUID) (string, error)
|
||||
DeleteTunnel(tunnelID uuid.UUID) error
|
||||
DeleteTunnel(tunnelID uuid.UUID, cascade bool) error
|
||||
ListTunnels(filter *TunnelFilter) ([]*Tunnel, error)
|
||||
ListActiveClients(tunnelID uuid.UUID) ([]*ActiveClient, error)
|
||||
CleanupConnections(tunnelID uuid.UUID, params *CleanupParams) error
|
||||
@@ -22,7 +22,7 @@ type HostnameClient interface {
|
||||
type IPRouteClient interface {
|
||||
ListRoutes(filter *IpRouteFilter) ([]*DetailedRoute, error)
|
||||
AddRoute(newRoute NewRoute) (Route, error)
|
||||
DeleteRoute(params DeleteRouteParams) error
|
||||
DeleteRoute(id uuid.UUID) error
|
||||
GetByIP(params GetRouteByIpParams) (DetailedRoute, error)
|
||||
}
|
||||
|
||||
|
||||
+8
-11
@@ -75,10 +75,12 @@ type NewRoute struct {
|
||||
// MarshalJSON handles fields with non-JSON types (e.g. net.IPNet).
|
||||
func (r NewRoute) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&struct {
|
||||
Network string `json:"network"`
|
||||
TunnelID uuid.UUID `json:"tunnel_id"`
|
||||
Comment string `json:"comment"`
|
||||
VNetID *uuid.UUID `json:"virtual_network_id,omitempty"`
|
||||
}{
|
||||
Network: r.Network.String(),
|
||||
TunnelID: r.TunnelID,
|
||||
Comment: r.Comment,
|
||||
VNetID: r.VNetID,
|
||||
@@ -87,6 +89,7 @@ func (r NewRoute) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// DetailedRoute is just a Route with some extra fields, e.g. TunnelName.
|
||||
type DetailedRoute struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
Network CIDR `json:"network"`
|
||||
TunnelID uuid.UUID `json:"tunnel_id"`
|
||||
// Optional field. When unset, it means the DetailedRoute belongs to the default virtual network.
|
||||
@@ -115,7 +118,8 @@ func (r DetailedRoute) TableString() string {
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"%s\t%s\t%s\t%s\t%s\t%s\t%s\t",
|
||||
"%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t",
|
||||
r.ID,
|
||||
r.Network.String(),
|
||||
vnetColumn,
|
||||
r.Comment,
|
||||
@@ -126,12 +130,6 @@ func (r DetailedRoute) TableString() string {
|
||||
)
|
||||
}
|
||||
|
||||
type DeleteRouteParams struct {
|
||||
Network net.IPNet
|
||||
// Optional field. If unset, backend will assume the default vnet for the account.
|
||||
VNetID *uuid.UUID
|
||||
}
|
||||
|
||||
type GetRouteByIpParams struct {
|
||||
Ip net.IP
|
||||
// Optional field. If unset, backend will assume the default vnet for the account.
|
||||
@@ -158,7 +156,7 @@ func (r *RESTClient) ListRoutes(filter *IpRouteFilter) ([]*DetailedRoute, error)
|
||||
// AddRoute calls the Tunnelstore POST endpoint for a given route.
|
||||
func (r *RESTClient) AddRoute(newRoute NewRoute) (Route, error) {
|
||||
endpoint := r.baseEndpoints.accountRoutes
|
||||
endpoint.Path = path.Join(endpoint.Path, "network", url.PathEscape(newRoute.Network.String()))
|
||||
endpoint.Path = path.Join(endpoint.Path)
|
||||
resp, err := r.sendRequest("POST", endpoint, newRoute)
|
||||
if err != nil {
|
||||
return Route{}, errors.Wrap(err, "REST request failed")
|
||||
@@ -173,10 +171,9 @@ func (r *RESTClient) AddRoute(newRoute NewRoute) (Route, error) {
|
||||
}
|
||||
|
||||
// DeleteRoute calls the Tunnelstore DELETE endpoint for a given route.
|
||||
func (r *RESTClient) DeleteRoute(params DeleteRouteParams) error {
|
||||
func (r *RESTClient) DeleteRoute(id uuid.UUID) error {
|
||||
endpoint := r.baseEndpoints.accountRoutes
|
||||
endpoint.Path = path.Join(endpoint.Path, "network", url.PathEscape(params.Network.String()))
|
||||
setVnetParam(&endpoint, params.VNetID)
|
||||
endpoint.Path = path.Join(endpoint.Path, url.PathEscape(id.String()))
|
||||
|
||||
resp, err := r.sendRequest("DELETE", endpoint, nil)
|
||||
if err != nil {
|
||||
|
||||
+25
-18
@@ -58,31 +58,29 @@ type IpRouteFilter struct {
|
||||
|
||||
// NewIpRouteFilterFromCLI parses CLI flags to discover which filters should get applied.
|
||||
func NewIpRouteFilterFromCLI(c *cli.Context) (*IpRouteFilter, error) {
|
||||
f := &IpRouteFilter{
|
||||
queryParams: url.Values{},
|
||||
}
|
||||
f := NewIPRouteFilter()
|
||||
|
||||
// Set deletion filter
|
||||
if flag := filterIpRouteDeleted.Name; c.IsSet(flag) && c.Bool(flag) {
|
||||
f.deleted()
|
||||
f.Deleted()
|
||||
} else {
|
||||
f.notDeleted()
|
||||
f.NotDeleted()
|
||||
}
|
||||
|
||||
if subset, err := cidrFromFlag(c, filterSubsetIpRoute); err != nil {
|
||||
return nil, err
|
||||
} else if subset != nil {
|
||||
f.networkIsSupersetOf(*subset)
|
||||
f.NetworkIsSupersetOf(*subset)
|
||||
}
|
||||
|
||||
if superset, err := cidrFromFlag(c, filterSupersetIpRoute); err != nil {
|
||||
return nil, err
|
||||
} else if superset != nil {
|
||||
f.networkIsSupersetOf(*superset)
|
||||
f.NetworkIsSupersetOf(*superset)
|
||||
}
|
||||
|
||||
if comment := c.String(filterIpRouteComment.Name); comment != "" {
|
||||
f.commentIs(comment)
|
||||
f.CommentIs(comment)
|
||||
}
|
||||
|
||||
if tunnelID := c.String(filterIpRouteTunnelID.Name); tunnelID != "" {
|
||||
@@ -90,7 +88,7 @@ func NewIpRouteFilterFromCLI(c *cli.Context) (*IpRouteFilter, error) {
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Couldn't parse UUID from %s", filterIpRouteTunnelID.Name)
|
||||
}
|
||||
f.tunnelID(u)
|
||||
f.TunnelID(u)
|
||||
}
|
||||
|
||||
if vnetId := c.String(filterIpRouteByVnet.Name); vnetId != "" {
|
||||
@@ -98,7 +96,7 @@ func NewIpRouteFilterFromCLI(c *cli.Context) (*IpRouteFilter, error) {
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Couldn't parse UUID from %s", filterIpRouteByVnet.Name)
|
||||
}
|
||||
f.vnetID(u)
|
||||
f.VNetID(u)
|
||||
}
|
||||
|
||||
if maxFetch := c.Int("max-fetch-size"); maxFetch > 0 {
|
||||
@@ -124,35 +122,44 @@ func cidrFromFlag(c *cli.Context, flag cli.StringFlag) (*net.IPNet, error) {
|
||||
return subset, nil
|
||||
}
|
||||
|
||||
func (f *IpRouteFilter) commentIs(comment string) {
|
||||
func NewIPRouteFilter() *IpRouteFilter {
|
||||
values := &IpRouteFilter{queryParams: url.Values{}}
|
||||
|
||||
// always list cfd_tunnel routes only
|
||||
values.queryParams.Set("tun_types", "cfd_tunnel")
|
||||
|
||||
return values
|
||||
}
|
||||
|
||||
func (f *IpRouteFilter) CommentIs(comment string) {
|
||||
f.queryParams.Set("comment", comment)
|
||||
}
|
||||
|
||||
func (f *IpRouteFilter) notDeleted() {
|
||||
func (f *IpRouteFilter) NotDeleted() {
|
||||
f.queryParams.Set("is_deleted", "false")
|
||||
}
|
||||
|
||||
func (f *IpRouteFilter) deleted() {
|
||||
func (f *IpRouteFilter) Deleted() {
|
||||
f.queryParams.Set("is_deleted", "true")
|
||||
}
|
||||
|
||||
func (f *IpRouteFilter) networkIsSubsetOf(superset net.IPNet) {
|
||||
func (f *IpRouteFilter) NetworkIsSubsetOf(superset net.IPNet) {
|
||||
f.queryParams.Set("network_subset", superset.String())
|
||||
}
|
||||
|
||||
func (f *IpRouteFilter) networkIsSupersetOf(subset net.IPNet) {
|
||||
func (f *IpRouteFilter) NetworkIsSupersetOf(subset net.IPNet) {
|
||||
f.queryParams.Set("network_superset", subset.String())
|
||||
}
|
||||
|
||||
func (f *IpRouteFilter) existedAt(existedAt time.Time) {
|
||||
func (f *IpRouteFilter) ExistedAt(existedAt time.Time) {
|
||||
f.queryParams.Set("existed_at", existedAt.Format(time.RFC3339))
|
||||
}
|
||||
|
||||
func (f *IpRouteFilter) tunnelID(id uuid.UUID) {
|
||||
func (f *IpRouteFilter) TunnelID(id uuid.UUID) {
|
||||
f.queryParams.Set("tunnel_id", id.String())
|
||||
}
|
||||
|
||||
func (f *IpRouteFilter) vnetID(id uuid.UUID) {
|
||||
func (f *IpRouteFilter) VNetID(id uuid.UUID) {
|
||||
f.queryParams.Set("virtual_network_id", id.String())
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ func TestDetailedRouteJsonRoundtrip(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
`{
|
||||
"id":"91ebc578-cc99-4641-9937-0fb630505fa0",
|
||||
"network":"10.1.2.40/29",
|
||||
"tunnel_id":"fba6ffea-807f-4e7a-a740-4184ee1b82c8",
|
||||
"comment":"test",
|
||||
@@ -80,6 +81,7 @@ func TestDetailedRouteJsonRoundtrip(t *testing.T) {
|
||||
},
|
||||
{
|
||||
`{
|
||||
"id":"91ebc578-cc99-4641-9937-0fb630505fa0",
|
||||
"network":"10.1.2.40/29",
|
||||
"tunnel_id":"fba6ffea-807f-4e7a-a740-4184ee1b82c8",
|
||||
"virtual_network_id":"38c95083-8191-4110-8339-3f438d44fdb9",
|
||||
@@ -167,9 +169,10 @@ func TestRouteTableString(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, network)
|
||||
r := DetailedRoute{
|
||||
ID: uuid.Nil,
|
||||
Network: CIDR(*network),
|
||||
}
|
||||
row := r.TableString()
|
||||
fmt.Println(row)
|
||||
require.True(t, strings.HasPrefix(row, "1.2.3.4/32"))
|
||||
require.True(t, strings.HasPrefix(row, "00000000-0000-0000-0000-000000000000\t1.2.3.4/32"))
|
||||
}
|
||||
|
||||
+6
-1
@@ -159,9 +159,14 @@ func (r *RESTClient) GetManagementToken(tunnelID uuid.UUID) (token string, err e
|
||||
return "", r.statusCodeToError("get tunnel token", resp)
|
||||
}
|
||||
|
||||
func (r *RESTClient) DeleteTunnel(tunnelID uuid.UUID) error {
|
||||
func (r *RESTClient) DeleteTunnel(tunnelID uuid.UUID, cascade bool) error {
|
||||
endpoint := r.baseEndpoints.accountLevel
|
||||
endpoint.Path = path.Join(endpoint.Path, fmt.Sprintf("%v", tunnelID))
|
||||
// Cascade will delete all tunnel dependencies (connections, routes, etc.) that
|
||||
// are linked to the deleted tunnel.
|
||||
if cascade {
|
||||
endpoint.RawQuery = "cascade=true"
|
||||
}
|
||||
resp, err := r.sendRequest("DELETE", endpoint, nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "REST request failed")
|
||||
|
||||
@@ -369,7 +369,7 @@ func uninstallSystemd(log *zerolog.Logger) error {
|
||||
// Get only the installed services
|
||||
installedServices := make(map[string]ServiceTemplate)
|
||||
for serviceName, serviceTemplate := range systemdAllTemplates {
|
||||
if err := runCommand("systemctl", "status", serviceName); err == nil {
|
||||
if err := runCommand("systemctl", "list-units", "--all", "|", "grep", serviceName); err == nil {
|
||||
installedServices[serviceName] = serviceTemplate
|
||||
} else {
|
||||
log.Info().Msgf("Service '%s' not installed, skipping its uninstall", serviceName)
|
||||
|
||||
@@ -139,7 +139,7 @@ func testURLCommand(c *cli.Context) error {
|
||||
}
|
||||
|
||||
_, i := ing.FindMatchingRule(requestURL.Hostname(), requestURL.Path)
|
||||
fmt.Printf("Matched rule #%d\n", i+1)
|
||||
fmt.Printf("Matched rule #%d\n", i)
|
||||
fmt.Println(ing.Rules[i].MultiLineString())
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ func (sc *subcommandContext) create(name string, credentialsFilePath string, sec
|
||||
var errorLines []string
|
||||
errorLines = append(errorLines, fmt.Sprintf("Your tunnel '%v' was created with ID %v. However, cloudflared couldn't write tunnel credentials to %s.", tunnel.Name, tunnel.ID, credentialsFilePath))
|
||||
errorLines = append(errorLines, fmt.Sprintf("The file-writing error is: %v", writeFileErr))
|
||||
if deleteErr := client.DeleteTunnel(tunnel.ID); deleteErr != nil {
|
||||
if deleteErr := client.DeleteTunnel(tunnel.ID, true); deleteErr != nil {
|
||||
errorLines = append(errorLines, fmt.Sprintf("Cloudflared tried to delete the tunnel for you, but encountered an error. You should use `cloudflared tunnel delete %v` to delete the tunnel yourself, because the tunnel can't be run without the tunnelfile.", tunnel.ID))
|
||||
errorLines = append(errorLines, fmt.Sprintf("The delete tunnel error is: %v", deleteErr))
|
||||
} else {
|
||||
@@ -206,13 +206,8 @@ func (sc *subcommandContext) delete(tunnelIDs []uuid.UUID) error {
|
||||
if !tunnel.DeletedAt.IsZero() {
|
||||
return fmt.Errorf("Tunnel %s has already been deleted", tunnel.ID)
|
||||
}
|
||||
if forceFlagSet {
|
||||
if err := client.CleanupConnections(tunnel.ID, cfapi.NewCleanupParams()); err != nil {
|
||||
return errors.Wrapf(err, "Error cleaning up connections for tunnel %s", tunnel.ID)
|
||||
}
|
||||
}
|
||||
|
||||
if err := client.DeleteTunnel(tunnel.ID); err != nil {
|
||||
if err := client.DeleteTunnel(tunnel.ID, forceFlagSet); err != nil {
|
||||
return errors.Wrapf(err, "Error deleting tunnel %s", tunnel.ID)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package tunnel
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/cloudflare/cloudflared/cfapi"
|
||||
@@ -24,12 +27,12 @@ func (sc *subcommandContext) addRoute(newRoute cfapi.NewRoute) (cfapi.Route, err
|
||||
return client.AddRoute(newRoute)
|
||||
}
|
||||
|
||||
func (sc *subcommandContext) deleteRoute(params cfapi.DeleteRouteParams) error {
|
||||
func (sc *subcommandContext) deleteRoute(id uuid.UUID) error {
|
||||
client, err := sc.client()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, noClientMsg)
|
||||
}
|
||||
return client.DeleteRoute(params)
|
||||
return client.DeleteRoute(id)
|
||||
}
|
||||
|
||||
func (sc *subcommandContext) getRouteByIP(params cfapi.GetRouteByIpParams) (cfapi.DetailedRoute, error) {
|
||||
@@ -39,3 +42,25 @@ func (sc *subcommandContext) getRouteByIP(params cfapi.GetRouteByIpParams) (cfap
|
||||
}
|
||||
return client.GetByIP(params)
|
||||
}
|
||||
|
||||
func (sc *subcommandContext) getRouteId(network net.IPNet, vnetId *uuid.UUID) (uuid.UUID, error) {
|
||||
filters := cfapi.NewIPRouteFilter()
|
||||
filters.NotDeleted()
|
||||
filters.NetworkIsSubsetOf(network)
|
||||
filters.NetworkIsSupersetOf(network)
|
||||
|
||||
if vnetId != nil {
|
||||
filters.VNetID(*vnetId)
|
||||
}
|
||||
|
||||
result, err := sc.listRoutes(filters)
|
||||
if err != nil {
|
||||
return uuid.Nil, err
|
||||
}
|
||||
|
||||
if len(result) != 1 {
|
||||
return uuid.Nil, errors.New("unable to find route for provided network and vnet")
|
||||
}
|
||||
|
||||
return result[0].ID, nil
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ func (d *deleteMockTunnelStore) GetTunnelToken(tunnelID uuid.UUID) (string, erro
|
||||
return "token", nil
|
||||
}
|
||||
|
||||
func (d *deleteMockTunnelStore) DeleteTunnel(tunnelID uuid.UUID) error {
|
||||
func (d *deleteMockTunnelStore) DeleteTunnel(tunnelID uuid.UUID, cascade bool) error {
|
||||
tunnel, ok := d.mockTunnels[tunnelID]
|
||||
if !ok {
|
||||
return fmt.Errorf("Couldn't find tunnel: %v", tunnelID)
|
||||
|
||||
@@ -119,8 +119,8 @@ var (
|
||||
forceDeleteFlag = &cli.BoolFlag{
|
||||
Name: "force",
|
||||
Aliases: []string{"f"},
|
||||
Usage: "Cleans up any stale connections before the tunnel is deleted. cloudflared will not " +
|
||||
"delete a tunnel with connections without this flag.",
|
||||
Usage: "Deletes a tunnel even if tunnel is connected and it has dependencies associated to it. (eg. IP routes)." +
|
||||
" It is not possible to delete tunnels that have connections or non-deleted dependencies, without this flag.",
|
||||
EnvVars: []string{"TUNNEL_RUN_FORCE_OVERWRITE"},
|
||||
}
|
||||
selectProtocolFlag = altsrc.NewStringFlag(&cli.StringFlag{
|
||||
|
||||
@@ -21,6 +21,8 @@ var (
|
||||
Aliases: []string{"vn"},
|
||||
Usage: "The ID or name of the virtual network to which the route is associated to.",
|
||||
}
|
||||
|
||||
routeAddError = errors.New("You must supply exactly one argument, the ID or CIDR of the route you want to delete")
|
||||
)
|
||||
|
||||
func buildRouteIPSubcommand() *cli.Command {
|
||||
@@ -68,11 +70,9 @@ which virtual network's routing table you want to add the route to with:
|
||||
Name: "delete",
|
||||
Action: cliutil.ConfiguredAction(deleteRouteCommand),
|
||||
Usage: "Delete a row from your organization's private routing table",
|
||||
UsageText: "cloudflared tunnel [--config FILEPATH] route ip delete [flags] [CIDR]",
|
||||
Description: `Deletes the row for a given CIDR from your routing table. That portion of your network
|
||||
will no longer be reachable by the WARP clients. Note that if you use virtual
|
||||
networks, then you have to tell which virtual network whose routing table you
|
||||
have a row deleted from.`,
|
||||
UsageText: "cloudflared tunnel [--config FILEPATH] route ip delete [flags] [Route ID or CIDR]",
|
||||
Description: `Deletes the row for the given route ID from your routing table. That portion of your network
|
||||
will no longer be reachable.`,
|
||||
Flags: []cli.Flag{vnetFlag},
|
||||
},
|
||||
{
|
||||
@@ -187,33 +187,36 @@ func deleteRouteCommand(c *cli.Context) error {
|
||||
}
|
||||
|
||||
if c.NArg() != 1 {
|
||||
return errors.New("You must supply exactly one argument, the network whose route you want to delete (in CIDR form e.g. 1.2.3.4/32)")
|
||||
return routeAddError
|
||||
}
|
||||
|
||||
_, network, err := net.ParseCIDR(c.Args().First())
|
||||
var routeId uuid.UUID
|
||||
routeId, err = uuid.Parse(c.Args().First())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Invalid network CIDR")
|
||||
}
|
||||
if network == nil {
|
||||
return errors.New("Invalid network CIDR")
|
||||
}
|
||||
_, network, err := net.ParseCIDR(c.Args().First())
|
||||
if err != nil || network == nil {
|
||||
return routeAddError
|
||||
}
|
||||
|
||||
params := cfapi.DeleteRouteParams{
|
||||
Network: *network,
|
||||
}
|
||||
var vnetId *uuid.UUID
|
||||
if c.IsSet(vnetFlag.Name) {
|
||||
id, err := getVnetId(sc, c.String(vnetFlag.Name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
vnetId = &id
|
||||
}
|
||||
|
||||
if c.IsSet(vnetFlag.Name) {
|
||||
vnetId, err := getVnetId(sc, c.String(vnetFlag.Name))
|
||||
routeId, err = sc.getRouteId(*network, vnetId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
params.VNetID = &vnetId
|
||||
}
|
||||
|
||||
if err := sc.deleteRoute(params); err != nil {
|
||||
if err := sc.deleteRoute(routeId); err != nil {
|
||||
return errors.Wrap(err, "API error")
|
||||
}
|
||||
fmt.Printf("Successfully deleted route for %s\n", network)
|
||||
fmt.Printf("Successfully deleted route with ID %s\n", routeId)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -269,7 +272,7 @@ func formatAndPrintRouteList(routes []*cfapi.DetailedRoute) {
|
||||
defer writer.Flush()
|
||||
|
||||
// Print column headers with tabbed columns
|
||||
_, _ = fmt.Fprintln(writer, "NETWORK\tVIRTUAL NET ID\tCOMMENT\tTUNNEL ID\tTUNNEL NAME\tCREATED\tDELETED\t")
|
||||
_, _ = fmt.Fprintln(writer, "ID\tNETWORK\tVIRTUAL NET ID\tCOMMENT\tTUNNEL ID\tTUNNEL NAME\tCREATED\tDELETED\t")
|
||||
|
||||
// Loop through routes, create formatted string for each, and print using tabwriter
|
||||
for _, route := range routes {
|
||||
|
||||
@@ -36,17 +36,17 @@ class TestConfig:
|
||||
_ = start_cloudflared(tmp_path, config, validate_args)
|
||||
|
||||
self.match_rule(tmp_path, config,
|
||||
"http://example.com/index.html", 1)
|
||||
"http://example.com/index.html", 0)
|
||||
self.match_rule(tmp_path, config,
|
||||
"https://example.com/index.html", 1)
|
||||
"https://example.com/index.html", 0)
|
||||
self.match_rule(tmp_path, config,
|
||||
"https://api.example.com/login", 2)
|
||||
"https://api.example.com/login", 1)
|
||||
self.match_rule(tmp_path, config,
|
||||
"https://wss.example.com", 3)
|
||||
"https://wss.example.com", 2)
|
||||
self.match_rule(tmp_path, config,
|
||||
"https://ssh.example.com", 4)
|
||||
"https://ssh.example.com", 3)
|
||||
self.match_rule(tmp_path, config,
|
||||
"https://api.example.com", 5)
|
||||
"https://api.example.com", 4)
|
||||
|
||||
# This is used to check that the command tunnel ingress url <url> matches rule number <rule_num>. Note that rule number uses 1-based indexing
|
||||
|
||||
|
||||
@@ -257,7 +257,6 @@ type Configuration struct {
|
||||
}
|
||||
|
||||
type WarpRoutingConfig struct {
|
||||
Enabled bool `yaml:"enabled" json:"enabled"`
|
||||
ConnectTimeout *CustomDuration `yaml:"connectTimeout" json:"connectTimeout,omitempty"`
|
||||
TCPKeepAlive *CustomDuration `yaml:"tcpKeepAlive" json:"tcpKeepAlive,omitempty"`
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ func TestConfigFileSettings(t *testing.T) {
|
||||
Service: "https://localhost:8001",
|
||||
}
|
||||
warpRouting = WarpRoutingConfig{
|
||||
Enabled: true,
|
||||
ConnectTimeout: &CustomDuration{Duration: 2 * time.Second},
|
||||
TCPKeepAlive: &CustomDuration{Duration: 10 * time.Second},
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@ type Orchestrator interface {
|
||||
UpdateConfig(version int32, config []byte) *pogs.UpdateConfigurationResponse
|
||||
GetConfigJSON() ([]byte, error)
|
||||
GetOriginProxy() (OriginProxy, error)
|
||||
WarpRoutingEnabled() (enabled bool)
|
||||
}
|
||||
|
||||
type NamedTunnelProperties struct {
|
||||
|
||||
+1
-1
@@ -103,7 +103,7 @@ func NewQUICConnection(
|
||||
sessionDemuxChan := make(chan *packet.Session, demuxChanCapacity)
|
||||
datagramMuxer := quicpogs.NewDatagramMuxerV2(session, logger, sessionDemuxChan)
|
||||
sessionManager := datagramsession.NewManager(logger, datagramMuxer.SendToSession, sessionDemuxChan)
|
||||
packetRouter := ingress.NewPacketRouter(packetRouterConfig, datagramMuxer, logger, orchestrator.WarpRoutingEnabled)
|
||||
packetRouter := ingress.NewPacketRouter(packetRouterConfig, datagramMuxer, logger)
|
||||
|
||||
return &QUICConnection{
|
||||
session: session,
|
||||
|
||||
+1
-5
@@ -44,14 +44,12 @@ const (
|
||||
)
|
||||
|
||||
type WarpRoutingConfig struct {
|
||||
Enabled bool `yaml:"enabled" json:"enabled"`
|
||||
ConnectTimeout config.CustomDuration `yaml:"connectTimeout" json:"connectTimeout,omitempty"`
|
||||
TCPKeepAlive config.CustomDuration `yaml:"tcpKeepAlive" json:"tcpKeepAlive,omitempty"`
|
||||
}
|
||||
|
||||
func NewWarpRoutingConfig(raw *config.WarpRoutingConfig) WarpRoutingConfig {
|
||||
cfg := WarpRoutingConfig{
|
||||
Enabled: raw.Enabled,
|
||||
ConnectTimeout: defaultWarpRoutingConnectTimeout,
|
||||
TCPKeepAlive: defaultTCPKeepAlive,
|
||||
}
|
||||
@@ -65,9 +63,7 @@ func NewWarpRoutingConfig(raw *config.WarpRoutingConfig) WarpRoutingConfig {
|
||||
}
|
||||
|
||||
func (c *WarpRoutingConfig) RawConfig() config.WarpRoutingConfig {
|
||||
raw := config.WarpRoutingConfig{
|
||||
Enabled: c.Enabled,
|
||||
}
|
||||
raw := config.WarpRoutingConfig{}
|
||||
if c.ConnectTimeout.Duration != defaultWarpRoutingConnectTimeout.Duration {
|
||||
raw.ConnectTimeout = &c.ConnectTimeout
|
||||
}
|
||||
|
||||
+11
-17
@@ -23,12 +23,11 @@ type muxer interface {
|
||||
|
||||
// PacketRouter routes packets between Upstream and ICMPRouter. Currently it rejects all other type of ICMP packets
|
||||
type PacketRouter struct {
|
||||
globalConfig *GlobalRouterConfig
|
||||
muxer muxer
|
||||
logger *zerolog.Logger
|
||||
checkRouterEnabledFunc func() bool
|
||||
icmpDecoder *packet.ICMPDecoder
|
||||
encoder *packet.Encoder
|
||||
globalConfig *GlobalRouterConfig
|
||||
muxer muxer
|
||||
logger *zerolog.Logger
|
||||
icmpDecoder *packet.ICMPDecoder
|
||||
encoder *packet.Encoder
|
||||
}
|
||||
|
||||
// GlobalRouterConfig is the configuration shared by all instance of Router.
|
||||
@@ -40,14 +39,13 @@ type GlobalRouterConfig struct {
|
||||
}
|
||||
|
||||
// NewPacketRouter creates a PacketRouter that handles ICMP packets. Packets are read from muxer but dropped if globalConfig is nil.
|
||||
func NewPacketRouter(globalConfig *GlobalRouterConfig, muxer muxer, logger *zerolog.Logger, checkRouterEnabledFunc func() bool) *PacketRouter {
|
||||
func NewPacketRouter(globalConfig *GlobalRouterConfig, muxer muxer, logger *zerolog.Logger) *PacketRouter {
|
||||
return &PacketRouter{
|
||||
globalConfig: globalConfig,
|
||||
muxer: muxer,
|
||||
logger: logger,
|
||||
checkRouterEnabledFunc: checkRouterEnabledFunc,
|
||||
icmpDecoder: packet.NewICMPDecoder(),
|
||||
encoder: packet.NewEncoder(),
|
||||
globalConfig: globalConfig,
|
||||
muxer: muxer,
|
||||
logger: logger,
|
||||
icmpDecoder: packet.NewICMPDecoder(),
|
||||
encoder: packet.NewEncoder(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,10 +90,6 @@ func (r *PacketRouter) handlePacket(ctx context.Context, rawPacket packet.RawPac
|
||||
return
|
||||
}
|
||||
|
||||
if enabled := r.checkRouterEnabledFunc(); !enabled {
|
||||
return
|
||||
}
|
||||
|
||||
icmpPacket, err := r.icmpDecoder.Decode(rawPacket)
|
||||
if err != nil {
|
||||
r.logger.Err(err).Msg("Failed to decode ICMP packet from quic datagram")
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"net/netip"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/gopacket/layers"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -29,9 +28,7 @@ var (
|
||||
|
||||
func TestRouterReturnTTLExceed(t *testing.T) {
|
||||
muxer := newMockMuxer(0)
|
||||
routerEnabled := &routerEnabledChecker{}
|
||||
routerEnabled.set(true)
|
||||
router := NewPacketRouter(packetConfig, muxer, &noopLogger, routerEnabled.isEnabled)
|
||||
router := NewPacketRouter(packetConfig, muxer, &noopLogger)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
routerStopped := make(chan struct{})
|
||||
go func() {
|
||||
@@ -80,65 +77,6 @@ func TestRouterReturnTTLExceed(t *testing.T) {
|
||||
<-routerStopped
|
||||
}
|
||||
|
||||
func TestRouterCheckEnabled(t *testing.T) {
|
||||
muxer := newMockMuxer(0)
|
||||
routerEnabled := &routerEnabledChecker{}
|
||||
router := NewPacketRouter(packetConfig, muxer, &noopLogger, routerEnabled.isEnabled)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
routerStopped := make(chan struct{})
|
||||
go func() {
|
||||
router.Serve(ctx)
|
||||
close(routerStopped)
|
||||
}()
|
||||
|
||||
pk := packet.ICMP{
|
||||
IP: &packet.IP{
|
||||
Src: netip.MustParseAddr("192.168.1.1"),
|
||||
Dst: netip.MustParseAddr("10.0.0.1"),
|
||||
Protocol: layers.IPProtocolICMPv4,
|
||||
TTL: 1,
|
||||
},
|
||||
Message: &icmp.Message{
|
||||
Type: ipv4.ICMPTypeEcho,
|
||||
Code: 0,
|
||||
Body: &icmp.Echo{
|
||||
ID: 12481,
|
||||
Seq: 8036,
|
||||
Data: []byte(t.Name()),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// router is disabled
|
||||
encoder := packet.NewEncoder()
|
||||
encodedPacket, err := encoder.Encode(&pk)
|
||||
require.NoError(t, err)
|
||||
sendPacket := quicpogs.RawPacket(encodedPacket)
|
||||
|
||||
muxer.edgeToCfd <- sendPacket
|
||||
select {
|
||||
case <-time.After(time.Millisecond * 10):
|
||||
case <-muxer.cfdToEdge:
|
||||
t.Error("Unexpected reply when router is disabled")
|
||||
}
|
||||
routerEnabled.set(true)
|
||||
// router is enabled, expects reply
|
||||
muxer.edgeToCfd <- sendPacket
|
||||
<-muxer.cfdToEdge
|
||||
|
||||
routerEnabled.set(false)
|
||||
// router is disabled
|
||||
muxer.edgeToCfd <- sendPacket
|
||||
select {
|
||||
case <-time.After(time.Millisecond * 10):
|
||||
case <-muxer.cfdToEdge:
|
||||
t.Error("Unexpected reply when router is disabled")
|
||||
}
|
||||
|
||||
cancel()
|
||||
<-routerStopped
|
||||
}
|
||||
|
||||
func assertTTLExceed(t *testing.T, originalPacket *packet.ICMP, expectedSrc netip.Addr, muxer *mockMuxer) {
|
||||
encoder := packet.NewEncoder()
|
||||
rawPacket, err := encoder.Encode(originalPacket)
|
||||
|
||||
@@ -60,7 +60,6 @@ func TestNewLocalConfig_MarshalJSON(t *testing.T) {
|
||||
}
|
||||
],
|
||||
"warp-routing": {
|
||||
"enabled": true,
|
||||
"connectTimeout": 1
|
||||
}
|
||||
}
|
||||
@@ -83,7 +82,6 @@ func TestNewLocalConfig_MarshalJSON(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, remoteConfig.WarpRouting, ingress.WarpRoutingConfig{
|
||||
Enabled: true,
|
||||
ConnectTimeout: config.CustomDuration{
|
||||
Duration: time.Second,
|
||||
},
|
||||
|
||||
@@ -29,11 +29,10 @@ type Orchestrator struct {
|
||||
// Underlying value is proxy.Proxy, can be read without the lock, but still needs the lock to update
|
||||
proxy atomic.Value
|
||||
// Set of internal ingress rules defined at cloudflared startup (separate from user-defined ingress rules)
|
||||
internalRules []ingress.Rule
|
||||
warpRoutingEnabled atomic.Bool
|
||||
config *Config
|
||||
tags []tunnelpogs.Tag
|
||||
log *zerolog.Logger
|
||||
internalRules []ingress.Rule
|
||||
config *Config
|
||||
tags []tunnelpogs.Tag
|
||||
log *zerolog.Logger
|
||||
|
||||
// orchestrator must not handle any more updates after shutdownC is closed
|
||||
shutdownC <-chan struct{}
|
||||
@@ -136,11 +135,6 @@ func (o *Orchestrator) updateIngress(ingressRules ingress.Ingress, warpRouting i
|
||||
o.proxy.Store(proxy)
|
||||
o.config.Ingress = &ingressRules
|
||||
o.config.WarpRouting = warpRouting
|
||||
if warpRouting.Enabled {
|
||||
o.warpRoutingEnabled.Store(true)
|
||||
} else {
|
||||
o.warpRoutingEnabled.Store(false)
|
||||
}
|
||||
|
||||
// If proxyShutdownC is nil, there is no previous running proxy
|
||||
if o.proxyShutdownC != nil {
|
||||
@@ -209,10 +203,6 @@ func (o *Orchestrator) GetOriginProxy() (connection.OriginProxy, error) {
|
||||
return proxy, nil
|
||||
}
|
||||
|
||||
func (o *Orchestrator) WarpRoutingEnabled() bool {
|
||||
return o.warpRoutingEnabled.Load()
|
||||
}
|
||||
|
||||
func (o *Orchestrator) waitToCloseLastProxy() {
|
||||
<-o.shutdownC
|
||||
o.lock.Lock()
|
||||
|
||||
@@ -55,7 +55,6 @@ func TestUpdateConfiguration(t *testing.T) {
|
||||
initOriginProxy, err := orchestrator.GetOriginProxy()
|
||||
require.NoError(t, err)
|
||||
require.Implements(t, (*connection.OriginProxy)(nil), initOriginProxy)
|
||||
require.False(t, orchestrator.WarpRoutingEnabled())
|
||||
|
||||
configJSONV2 := []byte(`
|
||||
{
|
||||
@@ -87,7 +86,6 @@ func TestUpdateConfiguration(t *testing.T) {
|
||||
}
|
||||
],
|
||||
"warp-routing": {
|
||||
"enabled": true,
|
||||
"connectTimeout": 10
|
||||
}
|
||||
}
|
||||
@@ -126,8 +124,6 @@ func TestUpdateConfiguration(t *testing.T) {
|
||||
require.Equal(t, config.CustomDuration{Duration: time.Second * 90}, configV2.Ingress.Rules[2].Config.ConnectTimeout)
|
||||
require.Equal(t, false, configV2.Ingress.Rules[2].Config.NoTLSVerify)
|
||||
require.Equal(t, true, configV2.Ingress.Rules[2].Config.NoHappyEyeballs)
|
||||
require.True(t, configV2.WarpRouting.Enabled)
|
||||
require.Equal(t, configV2.WarpRouting.Enabled, orchestrator.WarpRoutingEnabled())
|
||||
require.Equal(t, configV2.WarpRouting.ConnectTimeout.Duration, 10*time.Second)
|
||||
|
||||
originProxyV2, err := orchestrator.GetOriginProxy()
|
||||
@@ -162,7 +158,6 @@ func TestUpdateConfiguration(t *testing.T) {
|
||||
}
|
||||
],
|
||||
"warp-routing": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
`)
|
||||
@@ -171,8 +166,6 @@ func TestUpdateConfiguration(t *testing.T) {
|
||||
require.Len(t, configV10.Ingress.Rules, 1)
|
||||
require.True(t, configV10.Ingress.Rules[0].Matches("blogs.tunnel.io", "/2022/02/10"))
|
||||
require.Equal(t, ingress.HelloWorldService, configV10.Ingress.Rules[0].Service.String())
|
||||
require.False(t, configV10.WarpRouting.Enabled)
|
||||
require.Equal(t, configV10.WarpRouting.Enabled, orchestrator.WarpRoutingEnabled())
|
||||
|
||||
originProxyV10, err := orchestrator.GetOriginProxy()
|
||||
require.NoError(t, err)
|
||||
@@ -191,7 +184,6 @@ func TestUpdateConfiguration_FromMigration(t *testing.T) {
|
||||
initOriginProxy, err := orchestrator.GetOriginProxy()
|
||||
require.NoError(t, err)
|
||||
require.Implements(t, (*connection.OriginProxy)(nil), initOriginProxy)
|
||||
require.False(t, orchestrator.WarpRoutingEnabled())
|
||||
|
||||
configJSONV2 := []byte(`
|
||||
{
|
||||
@@ -201,7 +193,6 @@ func TestUpdateConfiguration_FromMigration(t *testing.T) {
|
||||
}
|
||||
],
|
||||
"warp-routing": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
`)
|
||||
@@ -271,7 +262,6 @@ func TestConcurrentUpdateAndRead(t *testing.T) {
|
||||
}
|
||||
],
|
||||
"warp-routing": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
`, hostname, httpOrigin.URL, expectedHost))
|
||||
@@ -283,7 +273,6 @@ func TestConcurrentUpdateAndRead(t *testing.T) {
|
||||
}
|
||||
],
|
||||
"warp-routing": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
`)
|
||||
@@ -296,7 +285,6 @@ func TestConcurrentUpdateAndRead(t *testing.T) {
|
||||
}
|
||||
],
|
||||
"warp-routing": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
`)
|
||||
@@ -516,7 +504,6 @@ func TestClosePreviousProxies(t *testing.T) {
|
||||
}
|
||||
],
|
||||
"warp-routing": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
`, hostname))
|
||||
@@ -529,7 +516,6 @@ func TestClosePreviousProxies(t *testing.T) {
|
||||
}
|
||||
],
|
||||
"warp-routing": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
`)
|
||||
@@ -612,7 +598,6 @@ func TestPersistentConnection(t *testing.T) {
|
||||
}
|
||||
],
|
||||
"warp-routing": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
`, wsOrigin.URL))
|
||||
@@ -679,7 +664,6 @@ func TestPersistentConnection(t *testing.T) {
|
||||
}
|
||||
],
|
||||
"warp-routing": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
`)
|
||||
|
||||
+2
-4
@@ -57,10 +57,8 @@ func NewOriginProxy(
|
||||
tags: tags,
|
||||
log: log,
|
||||
}
|
||||
if warpRouting.Enabled {
|
||||
proxy.warpRouting = ingress.NewWarpRoutingService(warpRouting)
|
||||
log.Info().Msgf("Warp-routing is enabled")
|
||||
}
|
||||
|
||||
proxy.warpRouting = ingress.NewWarpRoutingService(warpRouting)
|
||||
|
||||
return proxy
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ var (
|
||||
testTags = []tunnelpogs.Tag{{Name: "Name", Value: "value"}}
|
||||
noWarpRouting = ingress.WarpRoutingConfig{}
|
||||
testWarpRouting = ingress.WarpRoutingConfig{
|
||||
Enabled: true,
|
||||
ConnectTimeout: config.CustomDuration{Duration: time.Second},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -353,7 +353,7 @@ func selectNextProtocol(
|
||||
"Cloudflare Network with `quic` protocol, then most likely your machine/network is getting its egress " +
|
||||
"UDP to port 7844 (or others) blocked or dropped. Make sure to allow egress connectivity as per " +
|
||||
"https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/ports-and-ips/\n" +
|
||||
"If you are using private routing to this Tunnel, then UDP (and Private DNS Resolution) will not work " +
|
||||
"If you are using private routing to this Tunnel, then ICMP, UDP (and Private DNS Resolution) will not work " +
|
||||
"unless your cloudflared can connect with Cloudflare Network with `quic`.")
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user