Skip to content

HTTP 301 redirect loop with Istio httpsRedirect feature enabled #1852

@ghaskins

Description

@ghaskins

Describe the bug

Istio supports TLS within Ingress Gateways. One feature of this support is an optional automatic redirect from HTTP to HTTPS. However, this feature appears incompatible with OrbStack's integration with ingress controllers, as enabling causes an infinite 301 redirect cycle on OrbStack but works in other Kubernetes environments.

To Reproduce

  1. Create a fresh Kubernetes cluster in OrbStack
  2. Install Istio per the OrbStack instructions
$ istioctl install --set values.cni.cniBinDir=/var/lib/rancher/k3s/data/current/bin/ --set values.cni.cniConfDir=/var/lib/rancher/k3s/agent/etc/cni/net.d
  1. Enable automatic side-car injection in the default namespace
$ kubectl label namespace default istio-injection=enabled --overwrite
  1. Deploy an example application with TLS and httpsRedirect enabled
$ cat <<EOF | kubectl apply -f -
---
apiVersion: v1
kind: Secret
metadata:
  name: httpbin-credential
  namespace: istio-system
type: kubernetes.io/tls
data:
  tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURLVENDQWhHZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREF0TVJVd0V3WURWUVFLREF4bGVHRnQKY0d4bElFbHVZeTR4RkRBU0JnTlZCQU1NQzJWNFlXMXdiR1V1WTI5dE1CNFhEVEkxTURNeU16RXlNek16T1ZvWApEVEkyTURNeU16RXlNek16T1Zvd1B6RWVNQndHQTFVRUF3d1ZhSFIwY0dKcGJpNXJPSE11YjNKaUxteHZZMkZzCk1SMHdHd1lEVlFRS0RCUm9kSFJ3WW1sdUlHOXlaMkZ1YVhwaGRHbHZiakNDQVNJd0RRWUpLb1pJaHZjTkFRRUIKQlFBRGdnRVBBRENDQVFvQ2dnRUJBSklpUGdtbUk1cXc4ZVBqMUkxait2NjF0QjEwVi8wTnZ3UjRKRlNTSGhPTgpOTTZXVTJCQjY0UlBqdUFoV1ZCdlVyMXE0QkVGSFFLZnNzVHlvMFN0SkErUDEwM0JjTEs4Z1pmQnMyNTlMQ1JFCldFQStrK1h3VjBTZko1eEFHZUZ3ckJFeUJtT0p1aStjQWxDUXhtNm5YRk1rTzBmeUtKZ25qTW1hajlQOUNMSlUKMFQ4ODFhYzZWL2p3NFdaeVR1QTdjeFczc2NOVUFBQjNDakhPWWRVeTZBelVOUmc5UEFHSm1pUkxLa0JKc25XTApzSW5lNnBBT0liK1FvaUcyUXV6djl6cnBCMTdMc2xRbXFrNHpUM2dqQlVpUEkzMGNJODM0YnFmTEZRaUt2SENmCnlpUzZCMFRyM1M1cldhdlpRajdLTWVmSGxUT05MdlFiTFlBUjRmY0loRE1DQXdFQUFhTkNNRUF3SFFZRFZSME8KQkJZRUZCZm8wakNwOXl0TWQ0WkUvb1BxdkxpUGNtR2tNQjhHQTFVZEl3UVlNQmFBRkZPT1JTU05La1hLdHB2TgpsK1VNOGMxZFNmRFNNQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUJBUUE5anNxSzBSUHlTQWtCYXJkVWhZM1JibENkCkNqTVNPVGlKamxEUTF4K2JSUHBRRDFwYWptQXA4SVZDT1l1QWltRFVLTDdxR2xKOVZwS1FLWW81VWFEWTFxRTYKeGxGMzNjUTY5Vm81bWxqMHp5Wi9rLzJOcUozMy80OEpzZlFjZWM3REZsczNTbEhpMEtUV0d1VnRKMHhlbUpWSwpYdWkvVEVkazJEYm5waDdGZjViZWFIdlNqV3h6M1lPK3E0dDdoTzAyTklDb3ZCZ1ZFdFpXTmRRS1ozUjhVUjluCjRSZWNQbXdKYmdoQThlMElCc2NhN1FabHl0TlRCYXpMUlpPZ0Q4YTBBeERNNE11ZEtPbGo3djRBVXcvbzVITDQKa0F4K2krT1NCNDVsSmxkU3RQRnJCbGY3MDVsL0NvNUpBZWZvYkdydkFWcDdqYTNSQjlJbnZzaUZiVlNuCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
  tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2Z0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktnd2dnU2tBZ0VBQW9JQkFRQ1NJajRKcGlPYXNQSGoKNDlTTlkvcit0YlFkZEZmOURiOEVlQ1JVa2g0VGpUVE9sbE5nUWV1RVQ0N2dJVmxRYjFLOWF1QVJCUjBDbjdMRQo4cU5FclNRUGo5ZE53WEN5dklHWHdiTnVmU3drUkZoQVBwUGw4RmRFbnllY1FCbmhjS3dSTWdaamlib3ZuQUpRCmtNWnVwMXhUSkR0SDhpaVlKNHpKbW8vVC9RaXlWTkUvUE5Xbk9sZjQ4T0ZtY2s3Z08zTVZ0N0hEVkFBQWR3b3gKem1IVk11Z00xRFVZUFR3Qmlab2tTeXBBU2JKMWk3Q0ozdXFRRGlHL2tLSWh0a0xzNy9jNjZRZGV5N0pVSnFwTwpNMDk0SXdWSWp5TjlIQ1BOK0c2bnl4VUlpcnh3bjhva3VnZEU2OTB1YTFtcjJVSSt5akhueDVVempTNzBHeTJBCkVlSDNDSVF6QWdNQkFBRUNnZ0VBSm9Bc0RSbS9CcW40eU54RkswMDhteS9mT09WbXF5NmNkWDU3ditaUlUwQmcKT2MrRVB2ZVBKK2hZRWJmbmtwZ0oyZ3JXNXRVTWpCenNHaFI5TElKYlhsZzgxdWtRbXA2c25BRjJGeldXVkNYSApoSUdZTUx3T0dlKytuVjJNRkFlOTViZmkxQVc4em45UllzMko0aXhvMkNUVEU0VDY2Zjk5aWI0Vmg3b0NPUTFXCm5RNTFiZnZxaDZaNjRiRWh0aWFKOFRobHF1Q1RuM0ROckxveGNMbTFlaURaeHJXMVlKTkx4VnRTa2VzbUV3WjYKNTUzSm96SWN4eGVTWkdPOHVNaUx5eHl2RkoydktONWNxbmZOdEx1SGVzZ2wveWFMZDY5eit4WU4rM2xuN0JtegpRUWNrNXVmcXFFaXJpekFud1hHaEFCVzA0LzVBWE1aSnpZaC9icVNKSFFLQmdRREs0eGJ0ZEdCRE4wRlhQUUtoCkFYTXVpRVFHN0U4NnltY1FxYlpZdzZmcGMyT1BGdUpyVUtxd1hqZHRRd2N1MjRILzl2VFJIV1o2NDNEYXdlVDIKdFVvOVZCV283SG42Q3B4dGZBL0RhalJmdVZpMUFvRHhCNXZIZ3FXUmJqSlZpb3YrNlM4NlFSZWNoTnJOVU1RYwphRmdWSmZxckhOR3BrTHNjeTcvRXo4Zm9SUUtCZ1FDNFk3VmVFY0VWWktSNmpmV0Mxd0NNSTFIR250cEZQb2xxCmdpRFRNckZMcDVKNENQOTd1VmxRbGNzbU4xRVdxWVFMcmtCZ3U4S1pZeFZxVnRhWXROb3lGdXBZMUkvNnI3N1YKYm5ZZ3BzWW8rOEU1aVc2WlNkK05mcTdBN0tuRGFsV2FYbXBXNkRtY3dPbFBIaEdyL2l5L2M0c3NyYmp4Z21IbgpEMGE0WmdGdUZ3S0JnUUNpbDdVakhSV2NLR2NENjgvMGlmM3R0NlNVeThKL0FmL3cyYUZacDRza1NxWHA0VlFZCjNxKzc0ZldYYVpVVEZ6VXFsWllERkFXeHpCNEFJazFFN1dxT2xaazZKREZva2lKa2wzem1oWEZ0bVZNZTFOL1MKeWQ4czEyV3FrOTVnc1VTYVIvUGlRcElTakdIeEVwM25pak8wMXhmNUU0SnUycDhwWjV2S3RSMm01UUtCZ1FDaApkd2JDS2svbldPdERPbjZNSlpRZ0kybWZ3VWtucTd5UG5RWDdpMzkvM1d6QVdqakc4aWJKRDNNR3N4ZjUvVlZaCkdTemN5RzUrL1NvcXBZSmoxTExoZS9tWThnSm1wbkU0MnZMc1lyNVI4eXRnSS8zS2s4c0VUN1BkMHlCUnRCTGsKQzl1WTRPQmFhUCtsUFpLQ0J5RE9RZkxVbkFmQXpNakltakwybExTWjN3S0JnQkxHalNPNFRBL2NxK3JnRTZhVApuQ1k1UWpLeXZBVXVsbmlrRUxWYy80aWRYWjlmLytVV0VMRm5scTlnbXFSUGptVTErc0NMWTdHK2xpa2xuZTQyCjV2MnRqT1Axd0JmZnpYL2VZVlhSQ3ZLSldRdkNkbERENk5aS2JpNGMvbTRyN3BmNm9zSmd5N2NyWWNMNGNZR2MKazRYOFFMKy84M1dTS3BWMG1IMHVraXpiCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K
---
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: httpbin-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 80
        name: http
        protocol: HTTP
      hosts:
        - httpbin.k8s.orb.local
      tls:
        httpsRedirect: true # sends 301 redirect for http requests
    - port:
        number: 443
        name: https
        protocol: HTTPS
      tls:
        mode: SIMPLE
        credentialName: httpbin-credential # must be the same as secret
      hosts:
        - httpbin.k8s.orb.local
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: httpbin
spec:
  hosts:
    - httpbin.k8s.orb.local
  gateways:
    - httpbin-gateway
  http:
    - route:
        - destination:
            host: httpbin
            port:
              number: 8000
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: httpbin
---
apiVersion: v1
kind: Service
metadata:
  name: httpbin
  labels:
    app: httpbin
    service: httpbin
spec:
  ports:
  - name: http
    port: 8000
    targetPort: 8080
  selector:
    app: httpbin
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpbin
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpbin
      version: v1
  template:
    metadata:
      labels:
        app: httpbin
        version: v1
    spec:
      serviceAccountName: httpbin
      containers:
      - image: docker.io/mccutchen/go-httpbin:v2.15.0
        imagePullPolicy: IfNotPresent
        name: httpbin
        ports:
        - containerPort: 8080
EOF
  1. Try to access https://httpbin.k8s.orb.local and note that we get a 301 redirect even though we are already on HTTPS.

Expected behavior

Accessing http://httpbin.k8s.orb.local should redirect to https://httpbin.k8s.orb.local, and accessing https://httpbin.k8s.orb.local should serve the httpbin application.

Instead, both http and https return a 301 redirect to https://httpbin.k8s.orb.local/ (note the added trailing slash), resulting in an infinite redirect cycle.

Diagnostic report (REQUIRED)

OrbStack info:
Version: 1.10.3
Commit: 2b5dd5f580d80a3d2494b7b40dde2ef46813cfc5 (v1.10.3)

System info:
macOS: 15.3.2 (24D81)
CPU: arm64, 16 cores
CPU model: Apple M4 Max
Model: Mac16,5
Memory: 128 GiB

Full report: https://orbstack.dev/_admin/diag/orbstack-diagreport_2025-03-23T13-31-41.426135Z.zip

Screenshots and additional context (optional)

Removing

      tls:
        httpsRedirect: true # sends 301 redirect for http requests

allows the https endpoint to be reached as expected, though we now lack the http->https promotion redirect feature.

Metadata

Metadata

Assignees

No one assigned

    Labels

    t/bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions