├── .codespellignore ├── .dockerignore ├── .github ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml ├── fabricbot.json └── workflows │ ├── build-and-pusblish-mcr.yml │ ├── build.yml │ ├── codeql-analysis.yml │ ├── codespell.yml │ ├── dependency-review.yml │ ├── golangci-lint.yml │ ├── scorecard.yml │ └── trivy.yml ├── .gitignore ├── .pipeline ├── component-governance-detection.yaml ├── daily-gc.yaml ├── e2e.yaml └── scripts │ ├── deploy-testenv.sh │ └── gc.sh ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── NOTICE.txt ├── PROJECT ├── README.md ├── SECURITY.md ├── SUPPORT.md ├── api └── v1alpha1 │ ├── gatewaylbconfiguration_types.go │ ├── gatewaystatus_types.go │ ├── gatewayvmconfiguration_types.go │ ├── groupversion_info.go │ ├── podendpoint_types.go │ ├── staticgatewayconfiguration_types.go │ └── zz_generated.deepcopy.go ├── buf.gen.yaml ├── buf.lock ├── buf.yaml ├── cmd ├── add-netns │ ├── main.go │ └── main_test.go ├── copy │ ├── main.go │ └── main_test.go ├── kube-egress-cni-ipam │ ├── main.go │ ├── main_suite_test.go │ └── main_test.go ├── kube-egress-cni │ ├── main.go │ ├── main_suite_test.go │ ├── main_test.go │ └── testdata │ │ └── static ├── kube-egress-gateway-cnimanager │ ├── cmd │ │ ├── root.go │ │ └── serve.go │ └── main.go ├── kube-egress-gateway-controller │ ├── cmd │ │ └── root.go │ └── main.go └── kube-egress-gateway-daemon │ ├── cmd │ └── root.go │ └── main.go ├── config ├── azureconfig │ └── kustomization.yaml ├── cnimanager │ ├── daemon │ │ ├── cnimanager.yaml │ │ └── kustomization.yaml │ ├── kustomization.yaml │ └── rbac │ │ ├── kustomization.yaml │ │ ├── role.yaml │ │ ├── role_binding.yaml │ │ └── service_account.yaml ├── crd │ ├── bases │ │ ├── egressgateway.kubernetes.azure.com_gatewaylbconfigurations.yaml │ │ ├── egressgateway.kubernetes.azure.com_gatewaystatuses.yaml │ │ ├── egressgateway.kubernetes.azure.com_gatewayvmconfigurations.yaml │ │ ├── egressgateway.kubernetes.azure.com_podendpoints.yaml │ │ └── egressgateway.kubernetes.azure.com_staticgatewayconfigurations.yaml │ ├── kustomization.yaml │ └── kustomizeconfig.yaml ├── daemon │ ├── kustomization.yaml │ ├── manager │ │ ├── kustomization.yaml │ │ └── manager.yaml │ └── rbac │ │ ├── kustomization.yaml │ │ ├── role.yaml │ │ ├── role_binding.yaml │ │ └── service_account.yaml ├── default │ └── kustomization.yaml ├── environment_variables │ ├── environment.env │ └── kustomization.yaml ├── manager │ ├── certmanager │ │ ├── certificate.yaml │ │ ├── kustomization.yaml │ │ └── kustomizeconfig.yaml │ ├── kustomization.yaml │ ├── manager │ │ ├── kustomization.yaml │ │ ├── manager.yaml │ │ └── manager_config_patch.yaml │ ├── manager_webhook_patch.yaml │ ├── prometheus │ │ ├── kustomization.yaml │ │ └── monitor.yaml │ ├── rbac │ │ ├── auth_proxy_role_binding.yaml │ │ ├── auth_proxy_service.yaml │ │ ├── kustomization.yaml │ │ ├── leader_election_role.yaml │ │ ├── leader_election_role_binding.yaml │ │ ├── role.yaml │ │ ├── role_binding.yaml │ │ └── service_account.yaml │ ├── webhook │ │ ├── kustomization.yaml │ │ ├── kustomizeconfig.yaml │ │ ├── manifests.yaml │ │ └── service.yaml │ └── webhookcainjection_patch.yaml ├── rbac │ ├── auth_proxy_client_clusterrole.yaml │ ├── auth_proxy_role.yaml │ ├── gatewaystatus_editor_role.yaml │ ├── gatewaystatus_viewer_role.yaml │ └── kustomization.yaml ├── resource │ ├── kustomization.yaml │ └── ns.yaml ├── role │ ├── gatewaylbconfiguration_editor_role.yaml │ ├── gatewaylbconfiguration_viewer_role.yaml │ ├── gatewayvmconfiguration_editor_role.yaml │ ├── gatewayvmconfiguration_viewer_role.yaml │ ├── kustomization.yaml │ ├── podendpoint_editor_role.yaml │ ├── podendpoint_viewer_role.yaml │ ├── staticgatewayconfiguration_editor_role.yaml │ └── staticgatewayconfiguration_viewer_role.yaml └── samples │ ├── egressgateway_v1alpha1_gatewaylbconfiguration.yaml │ ├── egressgateway_v1alpha1_gatewaystatus.yaml │ ├── egressgateway_v1alpha1_gatewayvmconfiguration.yaml │ ├── egressgateway_v1alpha1_podendpoint.yaml │ └── egressgateway_v1alpha1_staticgatewayconfiguration.yaml ├── controllers ├── cnimanager │ ├── cnimanager_suite_test.go │ ├── server.go │ └── server_test.go ├── daemon │ ├── podendpoint_controller.go │ ├── podendpoint_controller_test.go │ ├── staticgatewayconfiguration_controller.go │ ├── staticgatewayconfiguration_controller_test.go │ └── suite_test.go └── manager │ ├── gatewaylbconfiguration_controller.go │ ├── gatewaylbconfiguration_controller_test.go │ ├── gatewayvmconfiguration_controller.go │ ├── gatewayvmconfiguration_controller_test.go │ ├── staticgatewayconfiguration_controller.go │ ├── staticgatewayconfiguration_controller_test.go │ └── suite_test.go ├── docker ├── base.Dockerfile ├── cni-ipam.Dockerfile ├── cni.Dockerfile ├── cnimanager.Dockerfile ├── docker-bake.hcl ├── docker-localtag-bake.hcl ├── gwdaemon-init.Dockerfile └── gwdaemon.Dockerfile ├── docs ├── cni.md ├── design.md ├── images │ ├── gateway_provision.png │ ├── health_probe.png │ ├── kube_egress_gateway.png │ └── pod_provision.png ├── install.md ├── samples │ ├── gateway_profile.yaml │ ├── sample_azure_config_msi.yaml │ ├── sample_azure_config_sp.yaml │ └── sample_pod.yaml └── troubleshooting.md ├── e2e ├── e2e_suite_test.go ├── e2e_test.go └── utils │ ├── log.go │ ├── pod.go │ └── utils.go ├── go.mod ├── go.sum ├── hack ├── boilerplate.go.txt ├── create_resource.sh ├── generate_release_note.sh ├── run_e2e.sh ├── test_linux.sh └── update_helm.sh ├── helm ├── kube-egress-gateway │ ├── .helmignore │ ├── Chart.yaml │ ├── README.md │ ├── crds │ │ └── crds.yaml │ ├── templates │ │ ├── _helpers.tpl │ │ ├── azure-config-secret.yaml │ │ ├── cni-uninstall-configmap.yaml │ │ ├── gateway-cni-manager.yaml │ │ ├── gateway-controller-manager.yaml │ │ └── gateway-daemon-manager.yaml │ └── values.yaml └── repo │ ├── index.yaml │ ├── kube-egress-gateway-0.1.0.tgz │ ├── kube-egress-gateway-0.1.1.tgz │ ├── kube-egress-gateway-0.1.2.tgz │ ├── kube-egress-gateway-0.1.3.tgz │ └── kube-egress-gateway-0.1.4.tgz └── pkg ├── azmanager ├── azmanager.go └── azmanager_test.go ├── cni ├── conf │ ├── conf.go │ ├── conf_test.go │ ├── confmanager.go │ ├── confmanager_test.go │ └── testdata │ │ ├── 10-test.conf │ │ ├── 10-test.conflist │ │ ├── 20-fake.conflist │ │ ├── 30-fake.conflist │ │ └── 40-fake.conflist ├── ipam │ ├── delegate.go │ └── fake.go ├── routes │ ├── routes.go │ └── routes_test.go └── wireguard │ ├── nic.go │ ├── nic_suite_test.go │ └── nic_test.go ├── cniprotocol ├── doc.go └── v1 │ ├── cni.pb.go │ ├── cni.proto │ ├── cni_grpc.pb.go │ ├── doc.go │ └── test_server.go ├── config ├── config.go └── config_test.go ├── consts └── const.go ├── healthprobe ├── gw_health.go └── gw_health_test.go ├── imds ├── imds.go └── types.go ├── iptableswrapper ├── fake_iptables.go ├── iptables.go └── mockiptableswrapper │ └── interface.go ├── logger └── factory.go ├── metrics ├── metrics.go └── metrics_test.go ├── netlinkwrapper ├── mocknetlinkwrapper │ └── interface.go └── netlink.go ├── netnswrapper ├── mocknetnswrapper │ ├── interface.go │ └── mocknetns.go └── netns_linux.go ├── utils └── to │ ├── convert.go │ └── convert_test.go └── wgctrlwrapper ├── mockwgctrlwrapper └── interface.go └── wgctrl.go /.codespellignore: -------------------------------------------------------------------------------- 1 | aks 2 | AfterAll 3 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file 2 | # Ignore build and test binaries. 3 | bin/ 4 | testbin/ 5 | hack/ 6 | tests/ 7 | config/ 8 | cmd/kube-egress-cni/testdata/ 9 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | #### What type of PR is this? 4 | 5 | 20 | 21 | #### What this PR does / why we need it: 22 | 23 | 24 | #### Which issue(s) this PR fixes: 25 | 30 | Fixes # 31 | 32 | 33 | #### Special notes for your reviewer 34 | 35 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "gomod" 9 | directory: "/" 10 | schedule: 11 | interval: daily 12 | time: "01:00" 13 | timezone: "Asia/Shanghai" 14 | labels: 15 | - "area/dependency" 16 | - "release-note-none" 17 | - "ok-to-test" 18 | - "kind/cleanup" 19 | ignore: 20 | - dependency-name: "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/*" 21 | groups: 22 | all: 23 | applies-to: version-updates 24 | patterns: 25 | - "*" 26 | update-types: 27 | - "patch" 28 | - "minor" 29 | - package-ecosystem: "docker" 30 | directory: "/docker" 31 | schedule: 32 | interval: "daily" 33 | time: "01:00" 34 | timezone: "Asia/Shanghai" 35 | labels: 36 | - "area/dependency" 37 | - "release-note-none" 38 | - "ok-to-test" 39 | - "kind/cleanup" 40 | - package-ecosystem: "github-actions" 41 | directory: "/" 42 | schedule: 43 | interval: daily 44 | time: "01:00" 45 | timezone: "Asia/Shanghai" 46 | labels: 47 | - "area/dependency" 48 | - "release-note-none" 49 | - "ok-to-test" 50 | - "kind/testing" 51 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | # Controls when the workflow will run 4 | on: 5 | pull_request: 6 | branches: 7 | - 'main' 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | permissions: 16 | packages: write 17 | actions: read 18 | contents: read 19 | id-token: write 20 | steps: 21 | # Get the repository's code 22 | - name: Harden Runner 23 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 24 | with: 25 | egress-policy: audit 26 | 27 | - name: Checkout 28 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 29 | # https://github.com/docker/setup-qemu-action 30 | - name: Set up QEMU 31 | uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0 32 | # https://github.com/docker/setup-buildx-action 33 | - name: Set up Docker Buildx 34 | id: buildx 35 | uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 36 | - name: Docker meta 37 | id: daemon # you'll use this in the next step 38 | uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0 39 | with: 40 | # list of Docker images to use as base name for tags 41 | images: | 42 | ghcr.io/azure/kube-egress-gateway-daemon 43 | tags: | 44 | type=semver,pattern={{raw}} 45 | bake-target: daemon-tags 46 | - name: Docker meta 47 | id: daemoninit # you'll use this in the next step 48 | uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0 49 | with: 50 | # list of Docker images to use as base name for tags 51 | images: | 52 | ghcr.io/azure/kube-egress-gateway-daemon-init 53 | tags: | 54 | type=semver,pattern={{raw}} 55 | bake-target: daemoninit-tags 56 | - name: Docker meta 57 | id: controller # you'll use this in the next step 58 | uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0 59 | with: 60 | # list of Docker images to use as base name for tags 61 | images: | 62 | ghcr.io/azure/kube-egress-gateway-controller 63 | tags: | 64 | type=semver,pattern={{raw}} 65 | bake-target: controller-tags 66 | - name: Docker meta 67 | id: cnimanager # you'll use this in the next step 68 | uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0 69 | with: 70 | # list of Docker images to use as base name for tags 71 | images: | 72 | ghcr.io/azure/kube-egress-gateway-cnimanager 73 | tags: | 74 | type=semver,pattern={{raw}} 75 | bake-target: cnimanager-tags 76 | - name: Docker meta 77 | id: cniplugin # you'll use this in the next step 78 | uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0 79 | with: 80 | # list of Docker images to use as base name for tags 81 | images: | 82 | ghcr.io/azure/kube-egress-gateway-cni 83 | tags: | 84 | type=semver,pattern={{raw}} 85 | bake-target: cni-tags 86 | - name: Docker meta 87 | id: cniipamplugin # you'll use this in the next step 88 | uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0 89 | with: 90 | # list of Docker images to use as base name for tags 91 | images: | 92 | ghcr.io/azure/kube-egress-gateway-cni-ipam 93 | tags: | 94 | type=semver,pattern={{raw}} 95 | bake-target: cni-ipam-tags 96 | - name: Set platform env for pr build 97 | run: | 98 | echo "TARGET_PLATFORMS=linux/amd64,linux/arm64,linux/arm" >> $GITHUB_ENV 99 | - name: Build and push 100 | uses: docker/bake-action@3fc70e1131fee40a422dd8dd0ff22014ae20a1f3 # v5.11.0 101 | env: 102 | PLATFORMS: ${{env.TARGET_PLATFORMS}} 103 | with: 104 | push: false 105 | files: | 106 | docker/docker-bake.hcl 107 | ${{ steps.daemon.outputs.bake-file }} 108 | ${{ steps.daemoninit.outputs.bake-file }} 109 | ${{ steps.controller.outputs.bake-file }} 110 | ${{ steps.cnimanager.outputs.bake-file }} 111 | ${{ steps.cniplugin.outputs.bake-file }} 112 | ${{ steps.cniipamplugin.outputs.bake-file }} 113 | 114 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "main" ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ "main" ] 20 | schedule: 21 | - cron: '42 6 * * 0' 22 | 23 | permissions: 24 | contents: read 25 | 26 | jobs: 27 | analyze: 28 | name: Analyze 29 | runs-on: ubuntu-latest 30 | permissions: 31 | actions: read 32 | contents: read 33 | security-events: write 34 | 35 | strategy: 36 | fail-fast: false 37 | matrix: 38 | language: [ 'go' ] 39 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 40 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 41 | 42 | steps: 43 | - name: Harden Runner 44 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 45 | with: 46 | egress-policy: audit 47 | 48 | - name: Checkout repository 49 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 50 | - name: Set up Go 1.x 51 | uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 52 | with: 53 | go-version-file: go.mod 54 | check-latest: true 55 | cache: true 56 | id: go 57 | 58 | 59 | 60 | # Initializes the CodeQL tools for scanning. 61 | - name: Initialize CodeQL 62 | uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 63 | with: 64 | languages: ${{ matrix.language }} 65 | # If you wish to specify custom queries, you can do so here or in a config file. 66 | # By default, queries listed here will override any specified in a config file. 67 | # Prefix the list here with "+" to use these queries and those in the config file. 68 | 69 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 70 | # queries: security-extended,security-and-quality 71 | 72 | 73 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 74 | # If this step fails, then you should remove it and run the build manually (see below) 75 | - name: Autobuild 76 | uses: github/codeql-action/autobuild@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 77 | 78 | # ℹ️ Command-line programs to run using the OS shell. 79 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 80 | 81 | # If the Autobuild fails above, remove it and uncomment the following three lines. 82 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 83 | 84 | - run: | 85 | make install-dependencies unit-test build 86 | 87 | - name: Coveralls 88 | uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b # v2.3.6 89 | with: 90 | file: profile.cov 91 | format: golang 92 | 93 | - name: Perform CodeQL Analysis 94 | uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 95 | -------------------------------------------------------------------------------- /.github/workflows/codespell.yml: -------------------------------------------------------------------------------- 1 | # GitHub Action to automate the identification of common misspellings in text files. 2 | # https://github.com/codespell-project/actions-codespell 3 | # https://github.com/codespell-project/codespell 4 | name: codespell 5 | on: [pull_request] 6 | permissions: 7 | contents: read 8 | 9 | jobs: 10 | codespell: 11 | name: Check for spelling errors 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Harden Runner 15 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 16 | with: 17 | egress-policy: audit 18 | 19 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 20 | - uses: codespell-project/actions-codespell@406322ec52dd7b488e48c1c4b82e2a8b3a1bf630 # master 21 | with: 22 | check_filenames: true 23 | skip: ./.git,./.github/workflows/codespell.yml,.git,*.png,*.jpg,*.svg,*.sum,./vendor,go.sum,testdata 24 | ignore_words_file: .codespellignore -------------------------------------------------------------------------------- /.github/workflows/dependency-review.yml: -------------------------------------------------------------------------------- 1 | # Dependency Review Action 2 | # 3 | # This Action will scan dependency manifest files that change as part of a Pull Request, 4 | # surfacing known-vulnerable versions of the packages declared or updated in the PR. 5 | # Once installed, if the workflow run is marked as required, 6 | # PRs introducing known-vulnerable packages will be blocked from merging. 7 | # 8 | # Source repository: https://github.com/actions/dependency-review-action 9 | name: 'Dependency Review' 10 | on: [pull_request] 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | dependency-review: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Harden Runner 20 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 21 | with: 22 | egress-policy: audit 23 | 24 | - name: 'Checkout Repository' 25 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 26 | - name: 'Dependency Review' 27 | uses: actions/dependency-review-action@da24556b548a50705dd671f47852072ea4c105d9 # v4.7.1 28 | -------------------------------------------------------------------------------- /.github/workflows/golangci-lint.yml: -------------------------------------------------------------------------------- 1 | name: golangci-lint 2 | on: 3 | push: 4 | tags: 5 | - v* 6 | branches: 7 | - master 8 | - main 9 | pull_request: 10 | permissions: 11 | contents: read 12 | pull-requests: read 13 | jobs: 14 | golangci: 15 | name: lint 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Harden Runner 19 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 20 | with: 21 | egress-policy: audit 22 | 23 | - name: Checkout repository 24 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 25 | - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 26 | with: 27 | go-version-file: go.mod 28 | check-latest: true 29 | cache: true 30 | - name: golangci-lint 31 | uses: golangci/golangci-lint-action@55c2c1448f86e01eaae002a5a3a9624417608d84 # v6.5.2 32 | with: 33 | # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version 34 | version: latest 35 | 36 | # Optional: golangci-lint command line arguments. 37 | args: -v --timeout 10m ./... -------------------------------------------------------------------------------- /.github/workflows/scorecard.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. They are provided 2 | # by a third-party and are governed by separate terms of service, privacy 3 | # policy, and support documentation. 4 | 5 | name: Scorecard supply-chain security 6 | on: 7 | # For Branch-Protection check. Only the default branch is supported. See 8 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection 9 | branch_protection_rule: 10 | # To guarantee Maintained check is occasionally updated. See 11 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained 12 | schedule: 13 | - cron: '38 14 * * 0' 14 | push: 15 | branches: [ "main" ] 16 | 17 | # Declare default permissions as read only. 18 | permissions: read-all 19 | 20 | jobs: 21 | analysis: 22 | name: Scorecard analysis 23 | runs-on: ubuntu-latest 24 | permissions: 25 | # Needed to upload the results to code-scanning dashboard. 26 | security-events: write 27 | # Needed to publish results and get a badge (see publish_results below). 28 | id-token: write 29 | # Uncomment the permissions below if installing in a private repository. 30 | # contents: read 31 | # actions: read 32 | 33 | steps: 34 | - name: Harden Runner 35 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 36 | with: 37 | egress-policy: audit 38 | 39 | - name: "Checkout code" 40 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 41 | with: 42 | persist-credentials: false 43 | 44 | - name: "Run analysis" 45 | uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1 46 | with: 47 | results_file: results.sarif 48 | results_format: sarif 49 | # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: 50 | # - you want to enable the Branch-Protection check on a *public* repository, or 51 | # - you are installing Scorecard on a *private* repository 52 | # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional. 53 | # repo_token: ${{ secrets.SCORECARD_TOKEN }} 54 | 55 | # Public repositories: 56 | # - Publish results to OpenSSF REST API for easy access by consumers 57 | # - Allows the repository to include the Scorecard badge. 58 | # - See https://github.com/ossf/scorecard-action#publishing-results. 59 | # For private repositories: 60 | # - `publish_results` will always be set to `false`, regardless 61 | # of the value entered here. 62 | publish_results: true 63 | 64 | # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF 65 | # format to the repository Actions tab. 66 | - name: "Upload artifact" 67 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v3.pre.node20 68 | with: 69 | name: SARIF file 70 | path: results.sarif 71 | retention-days: 5 72 | 73 | # Upload the results to GitHub's code scanning dashboard (optional). 74 | # Commenting out will disable upload of results to your repo's Code Scanning dashboard 75 | - name: "Upload to code-scanning" 76 | uses: github/codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 77 | with: 78 | sarif_file: results.sarif 79 | -------------------------------------------------------------------------------- /.pipeline/component-governance-detection.yaml: -------------------------------------------------------------------------------- 1 | # This pipeline hosted in ADO will use the auto-injected component detection build task to detect possible incidents 2 | # and report alerts related to OSS consumed by this repository. 3 | trigger: 4 | branches: 5 | include: 6 | - main 7 | paths: 8 | include: 9 | - go.sum 10 | - go.mod 11 | 12 | pool: 13 | vmImage: ubuntu-latest 14 | 15 | steps: 16 | - bash: | 17 | echo "This task is used to trigger code base scan." 18 | displayName: ADO Task -------------------------------------------------------------------------------- /.pipeline/daily-gc.yaml: -------------------------------------------------------------------------------- 1 | schedules: 2 | - cron: "0 12 * * *" 3 | displayName: Daily GC 4 | branches: 5 | include: 6 | - main 7 | always: true 8 | 9 | trigger: none 10 | 11 | pr: none 12 | 13 | variables: 14 | - group: pod-egress-e2e 15 | 16 | jobs: 17 | - job: Run_KubeEgressGateway_GC 18 | timeoutInMinutes: 30 19 | steps: 20 | - bash: | 21 | .pipeline/scripts/gc.sh 22 | displayName: gc egress-gateway-e2e resource groups 23 | env: 24 | AZURE_SUBSCRIPTION_ID: $(AZURE_SUBSCRIPTION_ID) 25 | AZURE_CLIENT_ID: $(AZURE_CLIENT_ID) 26 | AZURE_CLIENT_SECRET: $(AZURE_CLIENT_SECRET) 27 | AZURE_TENANT_ID: $(AZURE_TENANT_ID) 28 | -------------------------------------------------------------------------------- /.pipeline/e2e.yaml: -------------------------------------------------------------------------------- 1 | name: $(Date:yyyyMMdd)$(Rev:.r)_e2e 2 | 3 | trigger: 4 | branches: 5 | include: 6 | - main 7 | 8 | pr: 9 | branches: 10 | include: 11 | - main 12 | 13 | variables: 14 | - group: pod-egress-e2e 15 | - group: aks-ci 16 | - name: kubeconfig_file 17 | value: e2e-kubeconfig 18 | - name: pod_cidr 19 | value: 10.244.0.0/16 20 | - name: service_cidr 21 | value: 10.245.0.0/16 22 | 23 | pool: kube-egress-gateway-e2e-pool 24 | 25 | jobs: 26 | - job: Run_KubeEgressGateway_E2E 27 | timeoutInMinutes: 150 28 | steps: 29 | - task: GoTool@0 30 | inputs: 31 | version: '1.24.3' 32 | - bash: | 33 | echo $(registry.password) | docker login $(registry.url) -u $(registry.username) --password-stdin 34 | displayName: docker login 35 | - bash: | 36 | az extension add -n aks-preview --version 14.0.0b1 37 | az extension list 38 | az login --identity --username $(AZURE_MANAGED_IDENTITY_CLIENT_ID) 39 | az account set -s $(AZURE_SUBSCRIPTION_ID) 40 | displayName: az login 41 | - bash: | 42 | docker buildx create --name multi-arch-builder --bootstrap --use --driver docker-container --driver-opt image=mcr.microsoft.com/oss/moby/buildkit:0.11.6-2 43 | docker buildx ls 44 | displayName: create docker builder 45 | - bash: | 46 | export RESOURCE_GROUP="pod-egress-e2e-$(head /dev/urandom | LC_ALL=C tr -dc a-z0-9 | head -c 6 ; echo '')" 47 | echo "##vso[task.setvariable variable=resource_group]${RESOURCE_GROUP}" 48 | .pipeline/scripts/deploy-testenv.sh 49 | export KUBECONFIG=$(pwd)/${KUBECONFIG_FILE} 50 | echo "##vso[task.setvariable variable=kubeconfig]${KUBECONFIG}" 51 | kubectl get nodes 52 | displayName: create aks cluster 53 | env: 54 | AZURE_SUBSCRIPTION_ID: $(AZURE_SUBSCRIPTION_ID) 55 | AZURE_TENANT_ID: $(AZURE_TENANT_ID) 56 | - bash: | 57 | set -euo pipefail 58 | mv $(pwd)/azure.json $(pwd)/config/azureconfig/azure.json 59 | echo EXCEPTION_CIDRS=${POD_CIDR}","${SERVICE_CIDR} > $(pwd)/config/environment_variables/environment.env 60 | IMAGE_REGISTRY=$(registry.url) E2E_PIPELINE=true make install 61 | kubectl wait --for=condition=ready pod -A -l app=kube-egress-gateway --timeout=300s 62 | kubectl get all -n kube-egress-gateway-system 63 | displayName: build and install kube-egress-gateway components 64 | env: 65 | KUBECONFIG: $(kubeconfig) 66 | - bash: | 67 | set -euo pipefail 68 | mkdir ${ARTIFACT_DIR}/logs 69 | export LOG_DIR=${ARTIFACT_DIR}/logs 70 | make e2e-test 71 | displayName: run e2e tests 72 | env: 73 | KUBECONFIG: $(kubeconfig) 74 | AZURE_SUBSCRIPTION_ID: $(AZURE_SUBSCRIPTION_ID) 75 | AZURE_TENANT_ID: $(AZURE_TENANT_ID) 76 | AZURE_MANAGED_IDENTITY_CLIENT_ID: $(AZURE_MANAGED_IDENTITY_CLIENT_ID) 77 | COLLECT_LOG: true 78 | ARTIFACT_DIR: $(Build.ArtifactStagingDirectory) 79 | - bash: | 80 | [ "$(az group exists -n $(resource_group))" == "true" ] && az group delete -n $(resource_group) --yes --no-wait 81 | displayName: clean up resources 82 | condition: always() 83 | - task: PublishBuildArtifacts@1 84 | inputs: 85 | pathToPublish: '$(Build.ArtifactStagingDirectory)' 86 | artifactName: Logs 87 | condition: succeededOrFailed() 88 | 89 | 90 | -------------------------------------------------------------------------------- /.pipeline/scripts/gc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | CURRENT_DATE="$(date +%s)" 5 | 6 | az login --service-principal -u "${AZURE_CLIENT_ID}" -p "${AZURE_CLIENT_SECRET}" --tenant "${AZURE_TENANT_ID}" 7 | az group list --tag usage=pod-egress-e2e | jq -r '.[].name' | awk '{print $1}' | while read -r RESOURCE_GROUP; do 8 | RG_DATE="$(az group show -g ${RESOURCE_GROUP} | jq -r '.tags.creation_date')" 9 | RG_DATE_TOSEC="$(date --date="${RG_DATE}" +%s)" 10 | DATE_DIFF="$(expr ${CURRENT_DATE} - ${RG_DATE_TOSEC})" 11 | # GC clusters older than 1 day 12 | if (( "${DATE_DIFF}" > 86400 )); then 13 | echo "Deleting resource group: ${RESOURCE_GROUP}" 14 | az group delete --resource-group "${RESOURCE_GROUP}" --yes --no-wait 15 | fi 16 | done 17 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This project welcomes contributions and suggestions. Most contributions require you to 4 | agree to a Contributor License Agreement (CLA) declaring that you have the right to, 5 | and actually do, grant us the rights to use your contribution. For details, visit 6 | https://cla.microsoft.com. 7 | 8 | When you submit a pull request, a CLA-bot will automatically determine whether you need 9 | to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the 10 | instructions provided by the bot. You will only need to do this once across all repositories using our CLA. 11 | 12 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 13 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 14 | or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | NOTICES AND INFORMATION 2 | Do Not Translate or Localize 3 | 4 | This software incorporates material from third parties. 5 | Microsoft makes certain open source code available at https://3rdpartysource.microsoft.com, 6 | or you may send a check or money order for US $5.00, including the product name, 7 | the open source component name, platform, and version number, to: 8 | 9 | Source Code Compliance Team 10 | Microsoft Corporation 11 | One Microsoft Way 12 | Redmond, WA 98052 13 | USA 14 | 15 | Notwithstanding any other terms, you may reverse engineer this software to the extent 16 | required to debug changes to any libraries licensed under the GNU Lesser General Public License. -------------------------------------------------------------------------------- /PROJECT: -------------------------------------------------------------------------------- 1 | domain: kubernetes.azure.com 2 | layout: 3 | - go.kubebuilder.io/v3 4 | projectName: kube-egress-gateway 5 | repo: github.com/Azure/kube-egress-gateway 6 | resources: 7 | - api: 8 | crdVersion: v1 9 | namespaced: true 10 | controller: true 11 | domain: kubernetes.azure.com 12 | group: egressgateway 13 | kind: StaticGatewayConfiguration 14 | path: github.com/Azure/kube-egress-gateway/api/v1alpha1 15 | version: v1alpha1 16 | webhooks: 17 | defaulting: true 18 | validation: true 19 | webhookVersion: v1 20 | - api: 21 | crdVersion: v1 22 | namespaced: true 23 | domain: kubernetes.azure.com 24 | group: egressgateway 25 | kind: PodEndpoint 26 | path: github.com/Azure/kube-egress-gateway/api/v1alpha1 27 | version: v1alpha1 28 | - api: 29 | crdVersion: v1 30 | namespaced: true 31 | controller: true 32 | domain: kubernetes.azure.com 33 | group: egressgateway 34 | kind: GatewayLBConfiguration 35 | path: github.com/Azure/kube-egress-gateway/api/v1alpha1 36 | version: v1alpha1 37 | - api: 38 | crdVersion: v1 39 | namespaced: true 40 | controller: true 41 | domain: kubernetes.azure.com 42 | group: egressgateway 43 | kind: GatewayVMConfiguration 44 | path: github.com/Azure/kube-egress-gateway/api/v1alpha1 45 | version: v1alpha1 46 | - api: 47 | crdVersion: v1 48 | namespaced: true 49 | domain: kubernetes.azure.com 50 | group: egressgateway 51 | kind: GatewayStatus 52 | path: github.com/Azure/kube-egress-gateway/api/v1alpha1 53 | version: v1alpha1 54 | version: "3" 55 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Support 2 | 3 | ## How to file issues and get help 4 | 5 | This project uses GitHub Issues to track bugs and feature requests. Please search the existing 6 | issues before filing new issues to avoid duplicates. For new issues, file your bug or 7 | feature request as a new Issue. 8 | 9 | For help and questions about using this project, please go to [Discussions](https://github.com/Azure/kube-egress-gateway/discussions/) 10 | 11 | ## Microsoft Support Policy 12 | 13 | Support for this **kube-egress-gateway project** is limited to the resources listed above. 14 | -------------------------------------------------------------------------------- /api/v1alpha1/gatewaylbconfiguration_types.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | package v1alpha1 5 | 6 | import ( 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! 11 | // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. 12 | 13 | // GatewayLBConfigurationSpec defines the desired state of GatewayLBConfiguration 14 | type GatewayLBConfigurationSpec struct { 15 | // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster 16 | // Important: Run "make" to regenerate code after modifying this file 17 | 18 | // Name of the gateway nodepool to apply the gateway configuration. 19 | // +optional 20 | GatewayNodepoolName string `json:"gatewayNodepoolName,omitempty"` 21 | 22 | // Profile of the gateway VMSS to apply the gateway configuration. 23 | // +optional 24 | GatewayVmssProfile `json:"gatewayVmssProfile,omitempty"` 25 | 26 | // Whether to provision public IP prefixes for outbound. 27 | //+kubebuilder:default=true 28 | ProvisionPublicIps bool `json:"provisionPublicIps"` 29 | 30 | // BYO Resource ID of public IP prefix to be used as outbound. 31 | // +optional 32 | PublicIpPrefixId string `json:"publicIpPrefixId,omitempty"` 33 | } 34 | 35 | // GatewayLBConfigurationStatus defines the observed state of GatewayLBConfiguration 36 | type GatewayLBConfigurationStatus struct { 37 | // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster 38 | // Important: Run "make" to regenerate code after modifying this file 39 | 40 | // Gateway frontend IP. 41 | FrontendIp string `json:"frontendIp,omitempty"` 42 | 43 | // Listening port of the gateway server. 44 | ServerPort int32 `json:"serverPort,omitempty"` 45 | 46 | // Egress IP Prefix CIDR used for this gateway configuration. 47 | EgressIpPrefix string `json:"egressIpPrefix,omitempty"` 48 | } 49 | 50 | //+kubebuilder:object:root=true 51 | //+kubebuilder:subresource:status 52 | 53 | // GatewayLBConfiguration is the Schema for the gatewaylbconfigurations API 54 | type GatewayLBConfiguration struct { 55 | metav1.TypeMeta `json:",inline"` 56 | metav1.ObjectMeta `json:"metadata,omitempty"` 57 | 58 | Spec GatewayLBConfigurationSpec `json:"spec,omitempty"` 59 | Status *GatewayLBConfigurationStatus `json:"status,omitempty"` 60 | } 61 | 62 | //+kubebuilder:object:root=true 63 | 64 | // GatewayLBConfigurationList contains a list of GatewayLBConfiguration 65 | type GatewayLBConfigurationList struct { 66 | metav1.TypeMeta `json:",inline"` 67 | metav1.ListMeta `json:"metadata,omitempty"` 68 | Items []GatewayLBConfiguration `json:"items"` 69 | } 70 | 71 | func init() { 72 | SchemeBuilder.Register(&GatewayLBConfiguration{}, &GatewayLBConfigurationList{}) 73 | } 74 | -------------------------------------------------------------------------------- /api/v1alpha1/gatewaystatus_types.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | package v1alpha1 5 | 6 | import ( 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! 11 | // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. 12 | 13 | type GatewayConfiguration struct { 14 | // StaticGatewayConfiguration in / pattern 15 | StaticGatewayConfiguration string `json:"staticGatewayConfiguration,omitempty"` 16 | // Network interface name 17 | InterfaceName string `json:"interfaceName,omitempty"` 18 | } 19 | 20 | type PeerConfiguration struct { 21 | // PodEndpoint in / pattern 22 | PodEndpoint string `json:"podEndpoint,omitempty"` 23 | // Network interface name 24 | InterfaceName string `json:"interfaceName,omitempty"` 25 | // Public Key 26 | PublicKey string `json:"publicKey,omitempty"` 27 | } 28 | 29 | // GatewayStatusSpec defines the desired state of GatewayStatus 30 | type GatewayStatusSpec struct { 31 | // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster 32 | // Important: Run "make" to regenerate code after modifying this file 33 | 34 | // List of ready gateway configurations 35 | ReadyGatewayConfigurations []GatewayConfiguration `json:"readyGatewayConfigurations,omitempty"` 36 | // List of ready peer configurations 37 | ReadyPeerConfigurations []PeerConfiguration `json:"readyPeerConfigurations,omitempty"` 38 | } 39 | 40 | // GatewayStatusStatus defines the observed state of GatewayStatus 41 | type GatewayStatusStatus struct { 42 | // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster 43 | // Important: Run "make" to regenerate code after modifying this file 44 | } 45 | 46 | //+kubebuilder:object:root=true 47 | //+kubebuilder:subresource:status 48 | 49 | // GatewayStatus is the Schema for the gatewaystatuses API 50 | type GatewayStatus struct { 51 | metav1.TypeMeta `json:",inline"` 52 | metav1.ObjectMeta `json:"metadata,omitempty"` 53 | 54 | Spec GatewayStatusSpec `json:"spec,omitempty"` 55 | Status GatewayStatusStatus `json:"status,omitempty"` 56 | } 57 | 58 | //+kubebuilder:object:root=true 59 | 60 | // GatewayStatusList contains a list of GatewayStatus 61 | type GatewayStatusList struct { 62 | metav1.TypeMeta `json:",inline"` 63 | metav1.ListMeta `json:"metadata,omitempty"` 64 | Items []GatewayStatus `json:"items"` 65 | } 66 | 67 | func init() { 68 | SchemeBuilder.Register(&GatewayStatus{}, &GatewayStatusList{}) 69 | } 70 | -------------------------------------------------------------------------------- /api/v1alpha1/gatewayvmconfiguration_types.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | package v1alpha1 5 | 6 | import ( 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! 11 | // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. 12 | 13 | // GatewayVMConfigurationSpec defines the desired state of GatewayVMConfiguration 14 | type GatewayVMConfigurationSpec struct { 15 | // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster 16 | // Important: Run "make" to regenerate code after modifying this file 17 | 18 | // Name of the gateway nodepool to apply the gateway configuration. 19 | // +optional 20 | GatewayNodepoolName string `json:"gatewayNodepoolName,omitempty"` 21 | 22 | // Profile of the gateway VMSS to apply the gateway configuration. 23 | // +optional 24 | GatewayVmssProfile `json:"gatewayVmssProfile,omitempty"` 25 | 26 | // Whether to provision public IP prefixes for outbound. 27 | //+kubebuilder:default=true 28 | ProvisionPublicIps bool `json:"provisionPublicIps"` 29 | 30 | // BYO Resource ID of public IP prefix to be used as outbound. 31 | // +optional 32 | PublicIpPrefixId string `json:"publicIpPrefixId,omitempty"` 33 | } 34 | 35 | // GatewayVMConfigurationStatus defines the observed state of GatewayVMConfiguration 36 | type GatewayVMConfigurationStatus struct { 37 | // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster 38 | // Important: Run "make" to regenerate code after modifying this file 39 | 40 | // The egress source IP for traffic using this configuration. 41 | EgressIpPrefix string `json:"egressIpPrefix,omitempty"` 42 | 43 | // Gateway VM profile 44 | GatewayVMProfiles []GatewayVMProfile `json:"gatewayVMProfiles,omitempty"` 45 | } 46 | 47 | // GatewayVMProfile provides details about gateway VM side configuration. 48 | type GatewayVMProfile struct { 49 | NodeName string `json:"nodeName,omitempty"` 50 | PrimaryIP string `json:"primaryIP,omitempty"` 51 | SecondaryIP string `json:"secondaryIP,omitempty"` 52 | } 53 | 54 | //+kubebuilder:object:root=true 55 | //+kubebuilder:subresource:status 56 | 57 | // GatewayVMConfiguration is the Schema for the gatewayvmconfigurations API 58 | type GatewayVMConfiguration struct { 59 | metav1.TypeMeta `json:",inline"` 60 | metav1.ObjectMeta `json:"metadata,omitempty"` 61 | 62 | Spec GatewayVMConfigurationSpec `json:"spec,omitempty"` 63 | Status *GatewayVMConfigurationStatus `json:"status,omitempty"` 64 | } 65 | 66 | //+kubebuilder:object:root=true 67 | 68 | // GatewayVMConfigurationList contains a list of GatewayVMConfiguration 69 | type GatewayVMConfigurationList struct { 70 | metav1.TypeMeta `json:",inline"` 71 | metav1.ListMeta `json:"metadata,omitempty"` 72 | Items []GatewayVMConfiguration `json:"items"` 73 | } 74 | 75 | func init() { 76 | SchemeBuilder.Register(&GatewayVMConfiguration{}, &GatewayVMConfigurationList{}) 77 | } 78 | -------------------------------------------------------------------------------- /api/v1alpha1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | // Package v1alpha1 contains API Schema definitions for the kube-egress-gateway v1alpha1 API group 5 | // +kubebuilder:object:generate=true 6 | // +groupName=egressgateway.kubernetes.azure.com 7 | package v1alpha1 8 | 9 | import ( 10 | "k8s.io/apimachinery/pkg/runtime/schema" 11 | "sigs.k8s.io/controller-runtime/pkg/scheme" 12 | ) 13 | 14 | var ( 15 | // GroupVersion is group version used to register these objects 16 | GroupVersion = schema.GroupVersion{Group: "egressgateway.kubernetes.azure.com", Version: "v1alpha1"} 17 | 18 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 19 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 20 | 21 | // AddToScheme adds the types in this group-version to the given scheme. 22 | AddToScheme = SchemeBuilder.AddToScheme 23 | ) 24 | -------------------------------------------------------------------------------- /api/v1alpha1/podendpoint_types.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | package v1alpha1 5 | 6 | import ( 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! 11 | // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. 12 | 13 | // PodEndpointSpec defines the desired state of PodEndpoint 14 | type PodEndpointSpec struct { 15 | // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster 16 | // Important: Run "make" to regenerate code after modifying this file 17 | 18 | // Name of StaticGatewayConfiguration the pod uses. 19 | StaticGatewayConfiguration string `json:"staticGatewayConfiguration,omitempty"` 20 | 21 | // IPv4 address assigned to the pod. 22 | PodIpAddress string `json:"podIpAddress,omitempty"` 23 | 24 | // public key on pod side. 25 | PodPublicKey string `json:"podPublicKey,omitempty"` 26 | } 27 | 28 | // PodEndpointStatus defines the observed state of PodEndpoint 29 | type PodEndpointStatus struct { 30 | // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster 31 | // Important: Run "make" to regenerate code after modifying this file 32 | } 33 | 34 | //+kubebuilder:object:root=true 35 | //+kubebuilder:subresource:status 36 | 37 | // PodEndpoint is the Schema for the podendpoints API 38 | type PodEndpoint struct { 39 | metav1.TypeMeta `json:",inline"` 40 | metav1.ObjectMeta `json:"metadata,omitempty"` 41 | 42 | Spec PodEndpointSpec `json:"spec,omitempty"` 43 | Status PodEndpointStatus `json:"status,omitempty"` 44 | } 45 | 46 | //+kubebuilder:object:root=true 47 | 48 | // PodEndpointList contains a list of PodEndpoint 49 | type PodEndpointList struct { 50 | metav1.TypeMeta `json:",inline"` 51 | metav1.ListMeta `json:"metadata,omitempty"` 52 | Items []PodEndpoint `json:"items"` 53 | } 54 | 55 | func init() { 56 | SchemeBuilder.Register(&PodEndpoint{}, &PodEndpointList{}) 57 | } 58 | -------------------------------------------------------------------------------- /buf.gen.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | plugins: 3 | - name: go 4 | out: . 5 | path: bin/protoc-gen-go 6 | opt: 7 | - paths=source_relative 8 | - name: go-grpc 9 | out: . 10 | path: bin/protoc-gen-go-grpc 11 | opt: 12 | - paths=source_relative 13 | -------------------------------------------------------------------------------- /buf.lock: -------------------------------------------------------------------------------- 1 | # Generated by buf. DO NOT EDIT. 2 | version: v1 3 | deps: 4 | - remote: buf.build 5 | owner: googleapis 6 | repository: googleapis 7 | commit: c0b37eaf6f1f43ecacbc85e4e4d1a440 8 | -------------------------------------------------------------------------------- /buf.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | breaking: 3 | use: 4 | - FILE 5 | lint: 6 | use: 7 | - STANDARD 8 | deps: 9 | - buf.build/googleapis/googleapis 10 | -------------------------------------------------------------------------------- /cmd/add-netns/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/containernetworking/plugins/pkg/ns" 8 | 9 | "github.com/Azure/kube-egress-gateway/pkg/consts" 10 | "github.com/Azure/kube-egress-gateway/pkg/netnswrapper" 11 | ) 12 | 13 | func ensureNS(nsKit netnswrapper.Interface, namespace string) error { 14 | targetNS, err := nsKit.GetNS(namespace) 15 | if err != nil { 16 | if _, ok := err.(ns.NSPathNotExistErr); ok { 17 | fmt.Printf("Creating new network namespace %q\n", namespace) 18 | targetNS, err = nsKit.NewNS(namespace) 19 | if err != nil { 20 | return fmt.Errorf("failed to create network namespace %q: %w", namespace, err) 21 | } 22 | fmt.Printf("Created new network namespace %q\n", namespace) 23 | } else { 24 | return fmt.Errorf("failed to get network namespace %q: %w", namespace, err) 25 | } 26 | } else { 27 | fmt.Printf("Got network namespace %q, no need to create\n", namespace) 28 | } 29 | defer targetNS.Close() 30 | return nil 31 | } 32 | 33 | func main() { 34 | nsKit := netnswrapper.NewNetNS() 35 | err := ensureNS(nsKit, consts.GatewayNetnsName) 36 | if err != nil { 37 | fmt.Println("Error:", err.Error()) 38 | os.Exit(1) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /cmd/add-netns/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/containernetworking/plugins/pkg/ns" 8 | "github.com/stretchr/testify/assert" 9 | "go.uber.org/mock/gomock" 10 | 11 | "github.com/Azure/kube-egress-gateway/pkg/netnswrapper/mocknetnswrapper" 12 | ) 13 | 14 | func TestEnsureNS_ExistingNS(t *testing.T) { 15 | ctrl := gomock.NewController(t) 16 | defer ctrl.Finish() 17 | mockNetns := mocknetnswrapper.NewMockInterface(ctrl) 18 | mockNetns.EXPECT().GetNS("test-ns").Return(&mocknetnswrapper.MockNetNS{Name: "test-ns"}, nil) 19 | 20 | err := ensureNS(mockNetns, "test-ns") 21 | assert.NoError(t, err) 22 | } 23 | 24 | func TestEnsureNS_UnknownError(t *testing.T) { 25 | ctrl := gomock.NewController(t) 26 | defer ctrl.Finish() 27 | mockNetns := mocknetnswrapper.NewMockInterface(ctrl) 28 | mockNetns.EXPECT().GetNS("test-ns").Return(nil, fmt.Errorf("unknown get error")) 29 | 30 | err := ensureNS(mockNetns, "test-ns") 31 | assert.ErrorContains(t, err, "failed to get network namespace") 32 | } 33 | 34 | func TestEnsureNS_CreateNS(t *testing.T) { 35 | ctrl := gomock.NewController(t) 36 | defer ctrl.Finish() 37 | mockNetns := mocknetnswrapper.NewMockInterface(ctrl) 38 | mockNetns.EXPECT().GetNS("test-ns").Return(nil, ns.NSPathNotExistErr{}) 39 | mockNetns.EXPECT().NewNS("test-ns").Return(&mocknetnswrapper.MockNetNS{Name: "test-ns"}, nil) 40 | 41 | err := ensureNS(mockNetns, "test-ns") 42 | assert.NoError(t, err) 43 | } 44 | -------------------------------------------------------------------------------- /cmd/copy/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "path/filepath" 8 | 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | // copyFile copies file to destination directory 13 | func copyFile(sourceFile, destDir string) error { 14 | file, err := os.Open(sourceFile) 15 | if err != nil { 16 | return fmt.Errorf("failed to open source file: %w", err) 17 | } 18 | defer file.Close() 19 | 20 | sourceDir := filepath.Dir(sourceFile) 21 | fileName, err := filepath.Rel(sourceDir, sourceFile) 22 | if err != nil { 23 | return fmt.Errorf("failed to calculate file name: %w", err) 24 | } 25 | destFilePath := filepath.Join(destDir, fileName) 26 | destFile, err := os.OpenFile(destFilePath, os.O_CREATE|os.O_WRONLY, 0755) 27 | if err != nil { 28 | return fmt.Errorf("failed to open destination file: %w", err) 29 | } 30 | defer destFile.Close() 31 | 32 | _, err = io.Copy(destFile, file) 33 | if err != nil { 34 | return fmt.Errorf("failed to copy %q to %q: %w", sourceFile, destFilePath, err) 35 | } 36 | fmt.Printf("copied %q to %q\n", sourceFile, destFilePath) 37 | return nil 38 | } 39 | 40 | func main() { 41 | var src, dst string 42 | var rootCmd = &cobra.Command{ 43 | Use: "copy", 44 | Short: "Copy file from source to destination directory", 45 | Long: "Copy file from source to destination directory. NOTE that it will not create directory if destination does not exist", 46 | RunE: func(cmd *cobra.Command, args []string) error { 47 | return copyFile(src, dst) 48 | }, 49 | } 50 | 51 | rootCmd.Flags().StringVarP(&src, "source", "s", "", "The path of file to copy") 52 | rootCmd.Flags().StringVarP(&dst, "destination", "d", "", "Destination directory") 53 | rootCmd.MarkFlagRequired("source") //nolint:errcheck 54 | rootCmd.MarkFlagRequired("destination") //nolint:errcheck 55 | 56 | if err := rootCmd.Execute(); err != nil { 57 | os.Exit(1) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /cmd/copy/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestCopyGoodCase(t *testing.T) { 12 | srcDir, err := os.MkdirTemp(os.TempDir(), "source-*") 13 | assert.NoError(t, err) 14 | defer os.RemoveAll(srcDir) 15 | tempFile, err := os.CreateTemp(srcDir, "file-*") 16 | assert.NoError(t, err) 17 | destDir, err := os.MkdirTemp(os.TempDir(), "dest-*") 18 | assert.NoError(t, err) 19 | defer os.RemoveAll(destDir) 20 | 21 | actualErr := copyFile(tempFile.Name(), destDir) 22 | assert.NoError(t, actualErr) 23 | tempFileName, err := filepath.Rel(srcDir, tempFile.Name()) 24 | assert.NoError(t, err) 25 | _, err = os.Stat(filepath.Join(destDir, tempFileName)) 26 | assert.NoError(t, err) 27 | } 28 | 29 | func TestCopyNonExistingFile(t *testing.T) { 30 | srcDir, err := os.MkdirTemp(os.TempDir(), "source-*") 31 | assert.NoError(t, err) 32 | defer os.RemoveAll(srcDir) 33 | tempFilePath := filepath.Join(srcDir, "non-existing-file") 34 | destDir, err := os.MkdirTemp(os.TempDir(), "dest-*") 35 | assert.NoError(t, err) 36 | defer os.RemoveAll(destDir) 37 | 38 | actualErr := copyFile(tempFilePath, destDir) 39 | assert.NotNil(t, actualErr) 40 | assert.ErrorContains(t, actualErr, "failed to open source file") 41 | assert.ErrorContains(t, actualErr, "no such file or directory") 42 | } 43 | 44 | func TestCopyToNonExistingDest(t *testing.T) { 45 | srcDir, err := os.MkdirTemp(os.TempDir(), "source-*") 46 | assert.NoError(t, err) 47 | defer os.RemoveAll(srcDir) 48 | tempFile, err := os.CreateTemp(srcDir, "file-*") 49 | assert.NoError(t, err) 50 | destDir := filepath.Join(os.TempDir(), "non-existing-dir") 51 | 52 | actualErr := copyFile(tempFile.Name(), destDir) 53 | assert.NotNil(t, actualErr) 54 | assert.ErrorContains(t, actualErr, "failed to open destination file") 55 | assert.ErrorContains(t, actualErr, "no such file or directory") 56 | } 57 | -------------------------------------------------------------------------------- /cmd/kube-egress-cni-ipam/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | package main 5 | 6 | import ( 7 | "errors" 8 | "net" 9 | 10 | "github.com/containernetworking/cni/pkg/skel" 11 | "github.com/containernetworking/cni/pkg/types" 12 | type100 "github.com/containernetworking/cni/pkg/types/100" 13 | "github.com/containernetworking/cni/pkg/version" 14 | "github.com/containernetworking/plugins/pkg/ns" 15 | bv "github.com/containernetworking/plugins/pkg/utils/buildversion" 16 | "github.com/vishvananda/netlink" 17 | "golang.org/x/sys/unix" 18 | 19 | "github.com/Azure/kube-egress-gateway/pkg/cni/conf" 20 | "github.com/Azure/kube-egress-gateway/pkg/consts" 21 | "github.com/Azure/kube-egress-gateway/pkg/logger" 22 | ) 23 | 24 | func main() { 25 | skel.PluginMainFuncs(skel.CNIFuncs{Add: cmdAdd, Check: cmdCheck, Del: cmdDel}, version.All, bv.BuildString(consts.KubeEgressIPAMCNIName)) 26 | } 27 | 28 | func cmdAdd(args *skel.CmdArgs) error { 29 | log := logger.GetLogger() 30 | // get cni config 31 | config, err := conf.ParseCNIConfig(args.StdinData) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | // get k8s metadata 37 | k8sInfo, err := conf.LoadK8sInfo(args.Args) 38 | if err != nil { 39 | return err 40 | } 41 | log.V(5).Info("ADD - IPAM configuration successfully read: %+v", *k8sInfo) 42 | 43 | // allocate ip 44 | if config == nil || config.IPAM.Type == "" { 45 | return errors.New("ipam should not be empty") 46 | } 47 | 48 | podNetNS, err := ns.GetNS(args.Netns) 49 | if err != nil { 50 | return err 51 | } 52 | defer podNetNS.Close() 53 | 54 | var v4Address, v6Address net.IPNet 55 | var ipv4AddrFound, ipv6AddrFound bool 56 | var extraRoutes []*types.Route 57 | err = podNetNS.Do(func(netNS ns.NetNS) error { 58 | eth0Link, err := netlink.LinkByName("eth0") 59 | if err != nil { 60 | return err 61 | } 62 | 63 | addrList, err := netlink.AddrList(eth0Link, netlink.FAMILY_V6) 64 | if err != nil { 65 | return err 66 | } 67 | for _, item := range addrList { 68 | if item.Scope == unix.RT_SCOPE_LINK { 69 | v6Address = *item.IPNet 70 | ipv6AddrFound = true 71 | } 72 | } 73 | 74 | addrList, err = netlink.AddrList(eth0Link, netlink.FAMILY_V4) 75 | if err != nil { 76 | return err 77 | } 78 | for _, item := range addrList { 79 | if item.Scope == unix.RT_SCOPE_UNIVERSE { 80 | v4Address = *item.IPNet 81 | ipv4AddrFound = true 82 | } 83 | } 84 | return nil 85 | }) 86 | 87 | if err != nil { 88 | return err 89 | } 90 | if !ipv4AddrFound { 91 | return errors.New("there is no enough ipv4 addr allocated for this pod") 92 | } 93 | if !ipv6AddrFound { 94 | return errors.New("there is no enough ipv6 addr allocated for this pod") 95 | } 96 | 97 | result := &type100.Result{ 98 | CNIVersion: type100.ImplementedSpecVersion, 99 | IPs: []*type100.IPConfig{ 100 | { 101 | Address: v6Address, 102 | Gateway: net.ParseIP(consts.GatewayIP), 103 | }, 104 | { 105 | Address: v4Address, 106 | }, 107 | }, 108 | Routes: extraRoutes, 109 | } 110 | // outputCmdArgs(args) 111 | return types.PrintResult(result, config.CNIVersion) 112 | } 113 | 114 | func cmdDel(args *skel.CmdArgs) error { 115 | // get cni config 116 | config, err := conf.ParseCNIConfig(args.StdinData) 117 | if err != nil { 118 | return err 119 | } 120 | 121 | return types.PrintResult(&type100.Result{}, config.CNIVersion) 122 | } 123 | 124 | func cmdCheck(args *skel.CmdArgs) error { 125 | // get cni config 126 | config, err := conf.ParseCNIConfig(args.StdinData) 127 | if err != nil { 128 | return err 129 | } 130 | 131 | return types.PrintResult(&type100.Result{}, config.CNIVersion) 132 | } 133 | -------------------------------------------------------------------------------- /cmd/kube-egress-cni-ipam/main_suite_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | package main 5 | 6 | import ( 7 | "testing" 8 | 9 | . "github.com/onsi/ginkgo/v2" 10 | . "github.com/onsi/gomega" 11 | ) 12 | 13 | func TestCNIIpamMain(t *testing.T) { 14 | RegisterFailHandler(Fail) 15 | RunSpecs(t, "cmd/kube-egress-cni-ipam") 16 | } 17 | -------------------------------------------------------------------------------- /cmd/kube-egress-cni/main_suite_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | package main 5 | 6 | import ( 7 | "testing" 8 | 9 | . "github.com/onsi/ginkgo/v2" 10 | . "github.com/onsi/gomega" 11 | ) 12 | 13 | func TestCNIMain(t *testing.T) { 14 | RegisterFailHandler(Fail) 15 | RunSpecs(t, "cmd/kube-egress-cni") 16 | } 17 | -------------------------------------------------------------------------------- /cmd/kube-egress-cni/testdata/static: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/kube-egress-gateway/932bd9f8c2b9e0341dab32574a96571b6b9b338c/cmd/kube-egress-cni/testdata/static -------------------------------------------------------------------------------- /cmd/kube-egress-gateway-cnimanager/cmd/root.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | package cmd 4 | 5 | import ( 6 | "os" 7 | 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | // rootCmd represents the base command when called without any subcommands 12 | var rootCmd = &cobra.Command{ 13 | Use: "kube-egress-gateway-cnimanager", 14 | Short: "cniManager installs kube-egress cni, monitors pod creation and runs as proxy between cni and apiServer", 15 | Long: `cniManager installs kube-egress cni, monitors pod creation and runs as proxy between cni and apiServer`, 16 | } 17 | 18 | // Execute adds all child commands to the root command and sets flags appropriately. 19 | // This is called by main.main(). It only needs to happen once to the rootCmd. 20 | func Execute() { 21 | err := rootCmd.Execute() 22 | if err != nil { 23 | os.Exit(1) 24 | } 25 | } 26 | 27 | func init() { 28 | // Here you will define your flags and configuration settings. 29 | // Cobra supports persistent flags, which, if defined here, 30 | // will be global for your application. 31 | 32 | // rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.kube-egress-gateway-cnimanager.yaml)") 33 | 34 | // Cobra also supports local flags, which will only run 35 | // when this action is called directly. 36 | rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 37 | } 38 | -------------------------------------------------------------------------------- /cmd/kube-egress-gateway-cnimanager/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | package main 4 | 5 | import "github.com/Azure/kube-egress-gateway/cmd/kube-egress-gateway-cnimanager/cmd" 6 | 7 | func main() { 8 | cmd.Execute() 9 | } 10 | -------------------------------------------------------------------------------- /cmd/kube-egress-gateway-controller/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | package main 4 | 5 | import "github.com/Azure/kube-egress-gateway/cmd/kube-egress-gateway-controller/cmd" 6 | 7 | func main() { 8 | cmd.Execute() 9 | } 10 | -------------------------------------------------------------------------------- /cmd/kube-egress-gateway-daemon/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | package main 4 | 5 | import "github.com/Azure/kube-egress-gateway/cmd/kube-egress-gateway-daemon/cmd" 6 | 7 | func main() { 8 | cmd.Execute() 9 | } 10 | -------------------------------------------------------------------------------- /config/azureconfig/kustomization.yaml: -------------------------------------------------------------------------------- 1 | configMapGenerator: 2 | - files: 3 | - azure.json 4 | name: kube-egress-gateway-azure-cloud-config 5 | 6 | -------------------------------------------------------------------------------- /config/cnimanager/daemon/cnimanager.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: DaemonSet 3 | metadata: 4 | name: cni-manager 5 | namespace: system 6 | labels: 7 | kube-egress-gateway-control-plane: cni-manager 8 | spec: 9 | selector: 10 | matchLabels: 11 | kube-egress-gateway-control-plane: cni-manager 12 | template: 13 | metadata: 14 | annotations: 15 | kubectl.kubernetes.io/default-container: cnimanager 16 | labels: 17 | kube-egress-gateway-control-plane: cni-manager 18 | spec: 19 | initContainers: 20 | - name: cni-install 21 | image: cni:latest 22 | volumeMounts: 23 | - mountPath: /opt/cni/bin 24 | name: cni-bin 25 | securityContext: 26 | allowPrivilegeEscalation: false 27 | capabilities: 28 | drop: 29 | - ALL 30 | - name: cni-ipam-install 31 | image: cni-ipam:latest 32 | volumeMounts: 33 | - mountPath: /opt/cni/bin 34 | name: cni-bin 35 | securityContext: 36 | allowPrivilegeEscalation: false 37 | capabilities: 38 | drop: 39 | - ALL 40 | containers: 41 | - command: 42 | - /kube-egress-gateway-cnimanager 43 | args: 44 | - serve 45 | - --grpc-server-port=50051 46 | - --exception-cidrs=$(EXCEPTION_CIDRS) 47 | - --cni-conf-file=01-egressgateway.conflist 48 | - --cni-uninstall-configmap-name=cni-uninstall 49 | image: cnimanager:latest 50 | name: cnimanager 51 | securityContext: 52 | allowPrivilegeEscalation: false 53 | capabilities: 54 | drop: 55 | - ALL 56 | env: 57 | - name: MY_NODE_NAME 58 | valueFrom: 59 | fieldRef: 60 | fieldPath: spec.nodeName 61 | - name: MY_POD_NAMESPACE 62 | valueFrom: 63 | fieldRef: 64 | fieldPath: metadata.namespace 65 | volumeMounts: 66 | - mountPath: /etc/cni/net.d 67 | name: cni-conf 68 | ports: 69 | - containerPort: 50051 70 | name: grpc 71 | livenessProbe: 72 | grpc: 73 | port: 50051 74 | initialDelaySeconds: 20 75 | periodSeconds: 5 76 | readinessProbe: 77 | grpc: 78 | port: 50051 79 | initialDelaySeconds: 20 80 | periodSeconds: 5 81 | # TODO(user): Configure the resources accordingly based on the project requirements. 82 | # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ 83 | resources: 84 | limits: 85 | cpu: 500m 86 | memory: 500Mi 87 | requests: 88 | cpu: 10m 89 | memory: 64Mi 90 | nodeSelector: 91 | kubernetes.io/os: linux 92 | hostNetwork: true 93 | serviceAccountName: cni-manager 94 | terminationGracePeriodSeconds: 60 # update to 60 seconds for cni uninstall retry on error 95 | volumes: 96 | - name: cni-bin 97 | hostPath: 98 | path: /opt/cni/bin/ 99 | - name: cni-conf 100 | hostPath: 101 | path: /etc/cni/net.d/ 102 | -------------------------------------------------------------------------------- /config/cnimanager/daemon/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - cnimanager.yaml 3 | -------------------------------------------------------------------------------- /config/cnimanager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - ./daemon 3 | - ./rbac 4 | -------------------------------------------------------------------------------- /config/cnimanager/rbac/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | # All RBAC will be applied under this service account in 3 | # the deployment namespace. You may comment out this resource 4 | # if your manager will use a service account that exists at 5 | # runtime. Be sure to update RoleBinding and ClusterRoleBinding 6 | # subjects if changing service account names. 7 | - service_account.yaml 8 | - role.yaml 9 | - role_binding.yaml 10 | -------------------------------------------------------------------------------- /config/cnimanager/rbac/role.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: cni-manager-role 6 | rules: 7 | - apiGroups: 8 | - "" 9 | resources: 10 | - configmaps 11 | - pods 12 | verbs: 13 | - get 14 | - list 15 | - watch 16 | - apiGroups: 17 | - "" 18 | resources: 19 | - nodes 20 | verbs: 21 | - get 22 | - list 23 | - patch 24 | - update 25 | - watch 26 | - apiGroups: 27 | - egressgateway.kubernetes.azure.com 28 | resources: 29 | - podendpoints 30 | verbs: 31 | - create 32 | - delete 33 | - list 34 | - patch 35 | - update 36 | - watch 37 | - apiGroups: 38 | - egressgateway.kubernetes.azure.com 39 | resources: 40 | - staticgatewayconfigurations 41 | verbs: 42 | - get 43 | - list 44 | - watch 45 | - apiGroups: 46 | - egressgateway.kubernetes.azure.com 47 | resources: 48 | - staticgatewayconfigurations/status 49 | verbs: 50 | - get 51 | -------------------------------------------------------------------------------- /config/cnimanager/rbac/role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: cni-manager-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: cni-manager-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: cni-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/cnimanager/rbac/service_account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: cni-manager 5 | namespace: system 6 | -------------------------------------------------------------------------------- /config/crd/bases/egressgateway.kubernetes.azure.com_gatewaylbconfigurations.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: v0.17.1 7 | name: gatewaylbconfigurations.egressgateway.kubernetes.azure.com 8 | spec: 9 | group: egressgateway.kubernetes.azure.com 10 | names: 11 | kind: GatewayLBConfiguration 12 | listKind: GatewayLBConfigurationList 13 | plural: gatewaylbconfigurations 14 | singular: gatewaylbconfiguration 15 | scope: Namespaced 16 | versions: 17 | - name: v1alpha1 18 | schema: 19 | openAPIV3Schema: 20 | description: GatewayLBConfiguration is the Schema for the gatewaylbconfigurations 21 | API 22 | properties: 23 | apiVersion: 24 | description: |- 25 | APIVersion defines the versioned schema of this representation of an object. 26 | Servers should convert recognized schemas to the latest internal value, and 27 | may reject unrecognized values. 28 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 29 | type: string 30 | kind: 31 | description: |- 32 | Kind is a string value representing the REST resource this object represents. 33 | Servers may infer this from the endpoint the client submits requests to. 34 | Cannot be updated. 35 | In CamelCase. 36 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 37 | type: string 38 | metadata: 39 | type: object 40 | spec: 41 | description: GatewayLBConfigurationSpec defines the desired state of GatewayLBConfiguration 42 | properties: 43 | gatewayNodepoolName: 44 | description: Name of the gateway nodepool to apply the gateway configuration. 45 | type: string 46 | gatewayVmssProfile: 47 | description: Profile of the gateway VMSS to apply the gateway configuration. 48 | properties: 49 | publicIpPrefixSize: 50 | description: Public IP prefix size to be applied to this VMSS. 51 | format: int32 52 | maximum: 31 53 | minimum: 0 54 | type: integer 55 | vmssName: 56 | description: Name of the VMSS 57 | type: string 58 | vmssResourceGroup: 59 | description: Resource group of the VMSS. Must be in the same subscription. 60 | type: string 61 | type: object 62 | provisionPublicIps: 63 | default: true 64 | description: Whether to provision public IP prefixes for outbound. 65 | type: boolean 66 | publicIpPrefixId: 67 | description: BYO Resource ID of public IP prefix to be used as outbound. 68 | type: string 69 | required: 70 | - provisionPublicIps 71 | type: object 72 | status: 73 | description: GatewayLBConfigurationStatus defines the observed state of 74 | GatewayLBConfiguration 75 | properties: 76 | egressIpPrefix: 77 | description: Egress IP Prefix CIDR used for this gateway configuration. 78 | type: string 79 | frontendIp: 80 | description: Gateway frontend IP. 81 | type: string 82 | serverPort: 83 | description: Listening port of the gateway server. 84 | format: int32 85 | type: integer 86 | type: object 87 | type: object 88 | served: true 89 | storage: true 90 | subresources: 91 | status: {} 92 | -------------------------------------------------------------------------------- /config/crd/bases/egressgateway.kubernetes.azure.com_gatewaystatuses.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: v0.17.1 7 | name: gatewaystatuses.egressgateway.kubernetes.azure.com 8 | spec: 9 | group: egressgateway.kubernetes.azure.com 10 | names: 11 | kind: GatewayStatus 12 | listKind: GatewayStatusList 13 | plural: gatewaystatuses 14 | singular: gatewaystatus 15 | scope: Namespaced 16 | versions: 17 | - name: v1alpha1 18 | schema: 19 | openAPIV3Schema: 20 | description: GatewayStatus is the Schema for the gatewaystatuses API 21 | properties: 22 | apiVersion: 23 | description: |- 24 | APIVersion defines the versioned schema of this representation of an object. 25 | Servers should convert recognized schemas to the latest internal value, and 26 | may reject unrecognized values. 27 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 28 | type: string 29 | kind: 30 | description: |- 31 | Kind is a string value representing the REST resource this object represents. 32 | Servers may infer this from the endpoint the client submits requests to. 33 | Cannot be updated. 34 | In CamelCase. 35 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 36 | type: string 37 | metadata: 38 | type: object 39 | spec: 40 | description: GatewayStatusSpec defines the desired state of GatewayStatus 41 | properties: 42 | readyGatewayConfigurations: 43 | description: List of ready gateway configurations 44 | items: 45 | properties: 46 | interfaceName: 47 | description: Network interface name 48 | type: string 49 | staticGatewayConfiguration: 50 | description: StaticGatewayConfiguration in / 51 | pattern 52 | type: string 53 | type: object 54 | type: array 55 | readyPeerConfigurations: 56 | description: List of ready peer configurations 57 | items: 58 | properties: 59 | interfaceName: 60 | description: Network interface name 61 | type: string 62 | podEndpoint: 63 | description: PodEndpoint in / pattern 64 | type: string 65 | publicKey: 66 | description: Public Key 67 | type: string 68 | type: object 69 | type: array 70 | type: object 71 | status: 72 | description: GatewayStatusStatus defines the observed state of GatewayStatus 73 | type: object 74 | type: object 75 | served: true 76 | storage: true 77 | subresources: 78 | status: {} 79 | -------------------------------------------------------------------------------- /config/crd/bases/egressgateway.kubernetes.azure.com_gatewayvmconfigurations.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: v0.17.1 7 | name: gatewayvmconfigurations.egressgateway.kubernetes.azure.com 8 | spec: 9 | group: egressgateway.kubernetes.azure.com 10 | names: 11 | kind: GatewayVMConfiguration 12 | listKind: GatewayVMConfigurationList 13 | plural: gatewayvmconfigurations 14 | singular: gatewayvmconfiguration 15 | scope: Namespaced 16 | versions: 17 | - name: v1alpha1 18 | schema: 19 | openAPIV3Schema: 20 | description: GatewayVMConfiguration is the Schema for the gatewayvmconfigurations 21 | API 22 | properties: 23 | apiVersion: 24 | description: |- 25 | APIVersion defines the versioned schema of this representation of an object. 26 | Servers should convert recognized schemas to the latest internal value, and 27 | may reject unrecognized values. 28 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 29 | type: string 30 | kind: 31 | description: |- 32 | Kind is a string value representing the REST resource this object represents. 33 | Servers may infer this from the endpoint the client submits requests to. 34 | Cannot be updated. 35 | In CamelCase. 36 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 37 | type: string 38 | metadata: 39 | type: object 40 | spec: 41 | description: GatewayVMConfigurationSpec defines the desired state of GatewayVMConfiguration 42 | properties: 43 | gatewayNodepoolName: 44 | description: Name of the gateway nodepool to apply the gateway configuration. 45 | type: string 46 | gatewayVmssProfile: 47 | description: Profile of the gateway VMSS to apply the gateway configuration. 48 | properties: 49 | publicIpPrefixSize: 50 | description: Public IP prefix size to be applied to this VMSS. 51 | format: int32 52 | maximum: 31 53 | minimum: 0 54 | type: integer 55 | vmssName: 56 | description: Name of the VMSS 57 | type: string 58 | vmssResourceGroup: 59 | description: Resource group of the VMSS. Must be in the same subscription. 60 | type: string 61 | type: object 62 | provisionPublicIps: 63 | default: true 64 | description: Whether to provision public IP prefixes for outbound. 65 | type: boolean 66 | publicIpPrefixId: 67 | description: BYO Resource ID of public IP prefix to be used as outbound. 68 | type: string 69 | required: 70 | - provisionPublicIps 71 | type: object 72 | status: 73 | description: GatewayVMConfigurationStatus defines the observed state of 74 | GatewayVMConfiguration 75 | properties: 76 | egressIpPrefix: 77 | description: The egress source IP for traffic using this configuration. 78 | type: string 79 | gatewayVMProfiles: 80 | description: Gateway VM profile 81 | items: 82 | description: GatewayVMProfile provides details about gateway VM 83 | side configuration. 84 | properties: 85 | nodeName: 86 | type: string 87 | primaryIP: 88 | type: string 89 | secondaryIP: 90 | type: string 91 | type: object 92 | type: array 93 | type: object 94 | type: object 95 | served: true 96 | storage: true 97 | subresources: 98 | status: {} 99 | -------------------------------------------------------------------------------- /config/crd/bases/egressgateway.kubernetes.azure.com_podendpoints.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: v0.17.1 7 | name: podendpoints.egressgateway.kubernetes.azure.com 8 | spec: 9 | group: egressgateway.kubernetes.azure.com 10 | names: 11 | kind: PodEndpoint 12 | listKind: PodEndpointList 13 | plural: podendpoints 14 | singular: podendpoint 15 | scope: Namespaced 16 | versions: 17 | - name: v1alpha1 18 | schema: 19 | openAPIV3Schema: 20 | description: PodEndpoint is the Schema for the podendpoints API 21 | properties: 22 | apiVersion: 23 | description: |- 24 | APIVersion defines the versioned schema of this representation of an object. 25 | Servers should convert recognized schemas to the latest internal value, and 26 | may reject unrecognized values. 27 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 28 | type: string 29 | kind: 30 | description: |- 31 | Kind is a string value representing the REST resource this object represents. 32 | Servers may infer this from the endpoint the client submits requests to. 33 | Cannot be updated. 34 | In CamelCase. 35 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 36 | type: string 37 | metadata: 38 | type: object 39 | spec: 40 | description: PodEndpointSpec defines the desired state of PodEndpoint 41 | properties: 42 | podIpAddress: 43 | description: IPv4 address assigned to the pod. 44 | type: string 45 | podPublicKey: 46 | description: public key on pod side. 47 | type: string 48 | staticGatewayConfiguration: 49 | description: Name of StaticGatewayConfiguration the pod uses. 50 | type: string 51 | type: object 52 | status: 53 | description: PodEndpointStatus defines the observed state of PodEndpoint 54 | type: object 55 | type: object 56 | served: true 57 | storage: true 58 | subresources: 59 | status: {} 60 | -------------------------------------------------------------------------------- /config/crd/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # This kustomization.yaml is not intended to be run by itself, 2 | # since it depends on service name and namespace that are out of this kustomize package. 3 | # It should be run by config/default 4 | resources: 5 | - bases/egressgateway.kubernetes.azure.com_staticgatewayconfigurations.yaml 6 | - bases/egressgateway.kubernetes.azure.com_podendpoints.yaml 7 | - bases/egressgateway.kubernetes.azure.com_gatewaylbconfigurations.yaml 8 | - bases/egressgateway.kubernetes.azure.com_gatewayvmconfigurations.yaml 9 | - bases/egressgateway.kubernetes.azure.com_gatewaystatuses.yaml 10 | #+kubebuilder:scaffold:crdkustomizeresource 11 | 12 | configurations: 13 | - kustomizeconfig.yaml 14 | -------------------------------------------------------------------------------- /config/crd/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # This file is for teaching kustomize how to substitute name and namespace reference in CRD 2 | nameReference: 3 | - kind: Service 4 | version: v1 5 | fieldSpecs: 6 | - kind: CustomResourceDefinition 7 | version: v1 8 | group: apiextensions.k8s.io 9 | path: spec/conversion/webhook/clientConfig/service/name 10 | 11 | namespace: 12 | - kind: CustomResourceDefinition 13 | version: v1 14 | group: apiextensions.k8s.io 15 | path: spec/conversion/webhook/clientConfig/service/namespace 16 | create: false 17 | 18 | varReference: 19 | - path: metadata/annotations 20 | -------------------------------------------------------------------------------- /config/daemon/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - ./rbac 3 | - ./manager -------------------------------------------------------------------------------- /config/daemon/manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manager.yaml 3 | -------------------------------------------------------------------------------- /config/daemon/manager/manager.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: DaemonSet 3 | metadata: 4 | name: daemon-manager 5 | namespace: system 6 | labels: 7 | kube-egress-gateway-control-plane: daemon-manager 8 | spec: 9 | selector: 10 | matchLabels: 11 | kube-egress-gateway-control-plane: daemon-manager 12 | template: 13 | metadata: 14 | annotations: 15 | kubectl.kubernetes.io/default-container: daemon 16 | labels: 17 | kube-egress-gateway-control-plane: daemon-manager 18 | spec: 19 | hostNetwork: true 20 | nodeSelector: 21 | kubeegressgateway.azure.com/mode: "true" 22 | tolerations: 23 | - key: kubeegressgateway.azure.com/mode 24 | operator: "Equal" 25 | value: "true" 26 | effect: "NoSchedule" 27 | initContainers: 28 | - name: ip-netns-add 29 | image: daemoninit:latest 30 | securityContext: 31 | privileged: true 32 | volumeMounts: 33 | - mountPath: /var/run/netns 34 | name: hostpath-var 35 | mountPropagation: Bidirectional 36 | containers: 37 | - command: 38 | - /kube-egress-gateway-daemon 39 | args: 40 | - --secret-namespace=$(MY_POD_NAMESPACE) 41 | - --gateway-lb-probe-port=8082 42 | image: daemon:latest 43 | name: daemon 44 | securityContext: 45 | allowPrivilegeEscalation: false 46 | capabilities: 47 | drop: ["ALL"] 48 | add: ["NET_ADMIN", "NET_RAW", "SYS_ADMIN"] 49 | volumeMounts: 50 | - mountPath: /var/run/netns 51 | name: hostpath-var 52 | mountPropagation: HostToContainer 53 | - mountPath: /run/xtables.lock 54 | name: iptableslock 55 | env: 56 | - name: MY_NODE_NAME 57 | valueFrom: 58 | fieldRef: 59 | fieldPath: spec.nodeName 60 | - name: MY_POD_NAMESPACE 61 | valueFrom: 62 | fieldRef: 63 | fieldPath: metadata.namespace 64 | livenessProbe: 65 | httpGet: 66 | path: /healthz 67 | port: 8081 68 | initialDelaySeconds: 15 69 | periodSeconds: 20 70 | readinessProbe: 71 | httpGet: 72 | path: /readyz 73 | port: 8081 74 | initialDelaySeconds: 5 75 | periodSeconds: 10 76 | # TODO(user): Configure the resources accordingly based on the project requirements. 77 | # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ 78 | resources: 79 | limits: 80 | cpu: 500m 81 | memory: 128Mi 82 | requests: 83 | cpu: 10m 84 | memory: 64Mi 85 | serviceAccountName: daemon-manager 86 | terminationGracePeriodSeconds: 10 87 | volumes: 88 | - name: hostpath-var 89 | hostPath: 90 | path: /var/run/netns 91 | - hostPath: 92 | path: /run/xtables.lock 93 | type: FileOrCreate 94 | name: iptableslock 95 | -------------------------------------------------------------------------------- /config/daemon/rbac/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | # All RBAC will be applied under this service account in 3 | # the deployment namespace. You may comment out this resource 4 | # if your manager will use a service account that exists at 5 | # runtime. Be sure to update RoleBinding and ClusterRoleBinding 6 | # subjects if changing service account names. 7 | - service_account.yaml 8 | - role.yaml 9 | - role_binding.yaml 10 | -------------------------------------------------------------------------------- /config/daemon/rbac/role.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: daemon-manager-role 6 | rules: 7 | - apiGroups: 8 | - "" 9 | resources: 10 | - nodes 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - egressgateway.kubernetes.azure.com 17 | resources: 18 | - gatewaystatuses 19 | verbs: 20 | - create 21 | - get 22 | - list 23 | - patch 24 | - update 25 | - watch 26 | - apiGroups: 27 | - egressgateway.kubernetes.azure.com 28 | resources: 29 | - gatewayvmconfigurations 30 | - podendpoints 31 | - staticgatewayconfigurations 32 | verbs: 33 | - get 34 | - list 35 | - watch 36 | - apiGroups: 37 | - egressgateway.kubernetes.azure.com 38 | resources: 39 | - gatewayvmconfigurations/status 40 | verbs: 41 | - get 42 | - apiGroups: 43 | - egressgateway.kubernetes.azure.com 44 | resources: 45 | - podendpoints/status 46 | - staticgatewayconfigurations/status 47 | verbs: 48 | - get 49 | - patch 50 | - update 51 | --- 52 | apiVersion: rbac.authorization.k8s.io/v1 53 | kind: Role 54 | metadata: 55 | name: daemon-manager-role 56 | namespace: kube-egress-gateway-system 57 | rules: 58 | - apiGroups: 59 | - "" 60 | resources: 61 | - secrets 62 | verbs: 63 | - get 64 | - list 65 | - watch 66 | -------------------------------------------------------------------------------- /config/daemon/rbac/role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: daemon-manager-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: daemon-manager-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: daemon-manager 12 | namespace: system 13 | --- 14 | apiVersion: rbac.authorization.k8s.io/v1 15 | kind: RoleBinding 16 | metadata: 17 | name: daemon-manager-rolebinding 18 | roleRef: 19 | apiGroup: rbac.authorization.k8s.io 20 | kind: Role 21 | name: daemon-manager-role 22 | subjects: 23 | - kind: ServiceAccount 24 | name: daemon-manager 25 | namespace: system 26 | -------------------------------------------------------------------------------- /config/daemon/rbac/service_account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: daemon-manager 5 | namespace: system 6 | -------------------------------------------------------------------------------- /config/default/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Adds namespace to all resources. 2 | namespace: kube-egress-gateway-system 3 | 4 | # Value of this field is prepended to the 5 | # names of all resources, e.g. a deployment named 6 | # "wordpress" becomes "alices-wordpress". 7 | # Note that it should also match with the prefix (text before '-') of the namespace 8 | # field above. 9 | namePrefix: kube-egress-gateway- 10 | 11 | # Labels to add to all resources and selectors. 12 | commonLabels: 13 | app: kube-egress-gateway 14 | 15 | apiVersion: kustomize.config.k8s.io/v1beta1 16 | kind: Kustomization 17 | resources: 18 | - ../crd 19 | - ../environment_variables 20 | - ../rbac 21 | - ../resource 22 | - ../azureconfig 23 | - ../manager 24 | - ../daemon 25 | - ../cnimanager 26 | images: 27 | - name: cni 28 | newName: local/kube-egress-gateway-cni 29 | newTag: 4e7fd0e 30 | - name: cni-ipam 31 | newName: local/kube-egress-gateway-cni-ipam 32 | newTag: 4e7fd0e 33 | - name: cnimanager 34 | newName: local/kube-egress-gateway-cnimanager 35 | newTag: 4e7fd0e 36 | - name: controller 37 | newName: local/kube-egress-gateway-controller 38 | newTag: 4e7fd0e 39 | - name: daemon 40 | newName: local/kube-egress-gateway-daemon 41 | newTag: 4e7fd0e 42 | -------------------------------------------------------------------------------- /config/environment_variables/environment.env: -------------------------------------------------------------------------------- 1 | EXCEPTION_CIDRS= 2 | -------------------------------------------------------------------------------- /config/environment_variables/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # This configMap is only used to pass environment variables to Kustomize 2 | configMapGenerator: 3 | - name: environment 4 | envs: 5 | - environment.env 6 | 7 | # Disable the name suffix hash, so `vars` can look for the correct configMap name 8 | generatorOptions: 9 | disableNameSuffixHash: true 10 | 11 | vars: 12 | - name: EXCEPTION_CIDRS 13 | objref: 14 | kind: ConfigMap 15 | name: environment 16 | apiVersion: v1 17 | fieldref: 18 | fieldpath: data.EXCEPTION_CIDRS 19 | -------------------------------------------------------------------------------- /config/manager/certmanager/certificate.yaml: -------------------------------------------------------------------------------- 1 | # The following manifests contain a self-signed issuer CR and a certificate CR. 2 | # More document can be found at https://docs.cert-manager.io 3 | # WARNING: Targets CertManager v1.0. Check https://cert-manager.io/docs/installation/upgrading/ for breaking changes. 4 | apiVersion: cert-manager.io/v1 5 | kind: Issuer 6 | metadata: 7 | name: selfsigned-issuer 8 | namespace: system 9 | spec: 10 | selfSigned: {} 11 | --- 12 | apiVersion: cert-manager.io/v1 13 | kind: Certificate 14 | metadata: 15 | name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml 16 | namespace: system 17 | spec: 18 | # $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize 19 | dnsNames: 20 | - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc 21 | - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local 22 | issuerRef: 23 | kind: Issuer 24 | name: selfsigned-issuer 25 | secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize 26 | -------------------------------------------------------------------------------- /config/manager/certmanager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - certificate.yaml 3 | 4 | configurations: 5 | - kustomizeconfig.yaml 6 | -------------------------------------------------------------------------------- /config/manager/certmanager/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # This configuration is for teaching kustomize how to update name ref and var substitution 2 | nameReference: 3 | - kind: Issuer 4 | group: cert-manager.io 5 | fieldSpecs: 6 | - kind: Certificate 7 | group: cert-manager.io 8 | path: spec/issuerRef/name 9 | 10 | varReference: 11 | - kind: Certificate 12 | group: cert-manager.io 13 | path: spec/commonName 14 | - kind: Certificate 15 | group: cert-manager.io 16 | path: spec/dnsNames 17 | -------------------------------------------------------------------------------- /config/manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | 2 | resources: 3 | - ./rbac 4 | - ./manager 5 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in 6 | # crd/kustomization.yaml 7 | # - ./webhook 8 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. 9 | # - ./certmanager 10 | # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. 11 | #- ./prometheus 12 | 13 | #patches: 14 | # Protect the /metrics endpoint by putting it behind auth. 15 | # If you want your controller-manager to expose the /metrics 16 | # endpoint w/o any authn/z, please comment the following line. 17 | # - path: manager_auth_proxy_patch.yaml 18 | 19 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in 20 | # crd/kustomization.yaml 21 | # - manager_webhook_patch.yaml 22 | 23 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 24 | # Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. 25 | # 'CERTMANAGER' needs to be enabled to use ca injection 26 | # - webhookcainjection_patch.yaml 27 | 28 | # the following config is for teaching kustomize how to do var substitution 29 | # vars: 30 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. 31 | # - name: CERTIFICATE_NAMESPACE # namespace of the certificate CR 32 | # objref: 33 | # kind: Certificate 34 | # group: cert-manager.io 35 | # version: v1 36 | # name: serving-cert # this name should match the one in certificate.yaml 37 | # fieldref: 38 | # fieldpath: metadata.namespace 39 | # - name: CERTIFICATE_NAME 40 | # objref: 41 | # kind: Certificate 42 | # group: cert-manager.io 43 | # version: v1 44 | # name: serving-cert # this name should match the one in certificate.yaml 45 | # - name: SERVICE_NAMESPACE # namespace of the service 46 | # objref: 47 | # kind: Service 48 | # version: v1 49 | # name: webhook-service 50 | # fieldref: 51 | # fieldpath: metadata.namespace 52 | # - name: SERVICE_NAME 53 | # objref: 54 | # kind: Service 55 | # version: v1 56 | # name: webhook-service 57 | -------------------------------------------------------------------------------- /config/manager/manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manager.yaml 3 | generatorOptions: 4 | disableNameSuffixHash: true 5 | 6 | patches: 7 | # Mount the controller config file for loading manager configurations 8 | # through a ComponentConfig type 9 | - path: manager_config_patch.yaml -------------------------------------------------------------------------------- /config/manager/manager/manager.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | labels: 7 | kube-egress-gateway-control-plane: controller-manager 8 | spec: 9 | selector: 10 | matchLabels: 11 | kube-egress-gateway-control-plane: controller-manager 12 | replicas: 1 13 | template: 14 | metadata: 15 | annotations: 16 | kubectl.kubernetes.io/default-container: manager 17 | labels: 18 | kube-egress-gateway-control-plane: controller-manager 19 | spec: 20 | securityContext: 21 | runAsNonRoot: true 22 | # TODO(user): For common cases that do not require escalating privileges 23 | # it is recommended to ensure that all your Pods/Containers are restrictive. 24 | # More info: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted 25 | # Please uncomment the following code if your project does NOT have to work on old Kubernetes 26 | # versions < 1.19 or on vendors versions which do NOT support this field by default (i.e. Openshift < 4.11 ). 27 | # seccompProfile: 28 | # type: RuntimeDefault 29 | containers: 30 | - command: 31 | - /kube-egress-gateway-controller 32 | args: 33 | - --leader-elect 34 | - --leader-election-namespace=$(MY_POD_NAMESPACE) 35 | - --secret-namespace=$(MY_POD_NAMESPACE) 36 | - --cloud-config=/etc/config/azure.json 37 | - --gateway-lb-probe-port=8082 38 | image: controller:latest 39 | name: manager 40 | securityContext: 41 | allowPrivilegeEscalation: false 42 | capabilities: 43 | drop: 44 | - "ALL" 45 | env: 46 | - name: MY_POD_NAMESPACE 47 | valueFrom: 48 | fieldRef: 49 | fieldPath: metadata.namespace 50 | livenessProbe: 51 | httpGet: 52 | path: /healthz 53 | port: 8081 54 | initialDelaySeconds: 15 55 | periodSeconds: 20 56 | readinessProbe: 57 | httpGet: 58 | path: /readyz 59 | port: 8081 60 | initialDelaySeconds: 5 61 | periodSeconds: 10 62 | # TODO(user): Configure the resources accordingly based on the project requirements. 63 | # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ 64 | resources: 65 | limits: 66 | cpu: 500m 67 | memory: 500Mi 68 | requests: 69 | cpu: 10m 70 | memory: 64Mi 71 | serviceAccountName: controller-manager 72 | terminationGracePeriodSeconds: 10 73 | -------------------------------------------------------------------------------- /config/manager/manager/manager_config_patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: manager 11 | volumeMounts: 12 | - name: manager-config-volume 13 | mountPath: /etc/config 14 | volumes: 15 | - name: manager-config-volume 16 | configMap: 17 | name: kube-egress-gateway-azure-cloud-config 18 | -------------------------------------------------------------------------------- /config/manager/manager_webhook_patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: manager 11 | ports: 12 | - containerPort: 9443 13 | name: webhook-server 14 | protocol: TCP 15 | volumeMounts: 16 | - mountPath: /tmp/k8s-webhook-server/serving-certs 17 | name: cert 18 | readOnly: true 19 | volumes: 20 | - name: cert 21 | secret: 22 | defaultMode: 420 23 | secretName: webhook-server-cert 24 | -------------------------------------------------------------------------------- /config/manager/prometheus/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - monitor.yaml 3 | -------------------------------------------------------------------------------- /config/manager/prometheus/monitor.yaml: -------------------------------------------------------------------------------- 1 | 2 | # Prometheus Monitor Service (Metrics) 3 | apiVersion: monitoring.coreos.com/v1 4 | kind: ServiceMonitor 5 | metadata: 6 | labels: 7 | kube-egress-gateway-control-plane: controller-manager 8 | name: controller-manager-metrics-monitor 9 | namespace: system 10 | spec: 11 | endpoints: 12 | - path: /metrics 13 | port: https 14 | scheme: https 15 | bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token 16 | tlsConfig: 17 | insecureSkipVerify: true 18 | selector: 19 | matchLabels: 20 | kube-egress-gateway-control-plane: controller-manager 21 | -------------------------------------------------------------------------------- /config/manager/rbac/auth_proxy_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: proxy-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: proxy-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/manager/rbac/auth_proxy_service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | kube-egress-gateway-control-plane: controller-manager 6 | name: controller-manager-metrics-service 7 | namespace: system 8 | spec: 9 | ports: 10 | - name: https 11 | port: 8443 12 | protocol: TCP 13 | targetPort: https 14 | selector: 15 | kube-egress-gateway-control-plane: controller-manager 16 | -------------------------------------------------------------------------------- /config/manager/rbac/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | # All RBAC will be applied under this service account in 3 | # the deployment namespace. You may comment out this resource 4 | # if your manager will use a service account that exists at 5 | # runtime. Be sure to update RoleBinding and ClusterRoleBinding 6 | # subjects if changing service account names. 7 | - service_account.yaml 8 | - role.yaml 9 | - role_binding.yaml 10 | - leader_election_role.yaml 11 | - leader_election_role_binding.yaml 12 | - auth_proxy_role_binding.yaml 13 | - auth_proxy_service.yaml 14 | 15 | -------------------------------------------------------------------------------- /config/manager/rbac/leader_election_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions to do leader election. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | name: leader-election-role 6 | rules: 7 | - apiGroups: 8 | - coordination.k8s.io 9 | resources: 10 | - leases 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - create 16 | - update 17 | - patch 18 | - delete 19 | - apiGroups: 20 | - "" 21 | resources: 22 | - events 23 | verbs: 24 | - create 25 | - patch 26 | -------------------------------------------------------------------------------- /config/manager/rbac/leader_election_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: leader-election-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: Role 8 | name: leader-election-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/manager/rbac/role.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: manager-role 6 | rules: 7 | - apiGroups: 8 | - "" 9 | resources: 10 | - events 11 | verbs: 12 | - create 13 | - patch 14 | - apiGroups: 15 | - "" 16 | resources: 17 | - nodes 18 | verbs: 19 | - get 20 | - list 21 | - watch 22 | - apiGroups: 23 | - "" 24 | resources: 25 | - secrets 26 | verbs: 27 | - create 28 | - delete 29 | - get 30 | - list 31 | - patch 32 | - update 33 | - watch 34 | - apiGroups: 35 | - egressgateway.kubernetes.azure.com 36 | resources: 37 | - gatewaylbconfigurations 38 | - gatewayvmconfigurations 39 | - staticgatewayconfigurations 40 | verbs: 41 | - create 42 | - delete 43 | - get 44 | - list 45 | - patch 46 | - update 47 | - watch 48 | - apiGroups: 49 | - egressgateway.kubernetes.azure.com 50 | resources: 51 | - gatewaylbconfigurations/finalizers 52 | - gatewayvmconfigurations/finalizers 53 | - staticgatewayconfigurations/finalizers 54 | verbs: 55 | - update 56 | - apiGroups: 57 | - egressgateway.kubernetes.azure.com 58 | resources: 59 | - gatewaylbconfigurations/status 60 | - gatewayvmconfigurations/status 61 | - staticgatewayconfigurations/status 62 | verbs: 63 | - get 64 | - patch 65 | - update 66 | -------------------------------------------------------------------------------- /config/manager/rbac/role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: manager-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: manager-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/manager/rbac/service_account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | -------------------------------------------------------------------------------- /config/manager/webhook/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manifests.yaml 3 | - service.yaml 4 | 5 | configurations: 6 | - kustomizeconfig.yaml 7 | -------------------------------------------------------------------------------- /config/manager/webhook/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # the following config is for teaching kustomize where to look at when substituting vars. 2 | # It requires kustomize v2.1.0 or newer to work properly. 3 | nameReference: 4 | - kind: Service 5 | version: v1 6 | fieldSpecs: 7 | - kind: MutatingWebhookConfiguration 8 | group: admissionregistration.k8s.io 9 | path: webhooks/clientConfig/service/name 10 | - kind: ValidatingWebhookConfiguration 11 | group: admissionregistration.k8s.io 12 | path: webhooks/clientConfig/service/name 13 | 14 | namespace: 15 | - kind: MutatingWebhookConfiguration 16 | group: admissionregistration.k8s.io 17 | path: webhooks/clientConfig/service/namespace 18 | create: true 19 | - kind: ValidatingWebhookConfiguration 20 | group: admissionregistration.k8s.io 21 | path: webhooks/clientConfig/service/namespace 22 | create: true 23 | 24 | varReference: 25 | - path: metadata/annotations 26 | -------------------------------------------------------------------------------- /config/manager/webhook/manifests.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: admissionregistration.k8s.io/v1 3 | kind: MutatingWebhookConfiguration 4 | metadata: 5 | name: mutating-webhook-configuration 6 | webhooks: 7 | - admissionReviewVersions: 8 | - v1 9 | clientConfig: 10 | service: 11 | name: webhook-service 12 | namespace: system 13 | path: /mutate-egressgateway-kubernetes-azure-com-v1alpha1-staticgatewayconfiguration 14 | failurePolicy: Fail 15 | name: mstaticgatewayconfiguration.kb.io 16 | rules: 17 | - apiGroups: 18 | - egressgateway.kubernetes.azure.com 19 | apiVersions: 20 | - v1alpha1 21 | operations: 22 | - CREATE 23 | - UPDATE 24 | resources: 25 | - staticgatewayconfigurations 26 | sideEffects: None 27 | --- 28 | apiVersion: admissionregistration.k8s.io/v1 29 | kind: ValidatingWebhookConfiguration 30 | metadata: 31 | name: validating-webhook-configuration 32 | webhooks: 33 | - admissionReviewVersions: 34 | - v1 35 | clientConfig: 36 | service: 37 | name: webhook-service 38 | namespace: system 39 | path: /validate-egressgateway-kubernetes-azure-com-v1alpha1-staticgatewayconfiguration 40 | failurePolicy: Fail 41 | name: vstaticgatewayconfiguration.kb.io 42 | rules: 43 | - apiGroups: 44 | - egressgateway.kubernetes.azure.com 45 | apiVersions: 46 | - v1alpha1 47 | operations: 48 | - CREATE 49 | - UPDATE 50 | resources: 51 | - staticgatewayconfigurations 52 | sideEffects: None 53 | -------------------------------------------------------------------------------- /config/manager/webhook/service.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: webhook-service 6 | namespace: system 7 | spec: 8 | ports: 9 | - port: 443 10 | protocol: TCP 11 | targetPort: 9443 12 | selector: 13 | kube-egress-gateway-control-plane: controller-manager 14 | -------------------------------------------------------------------------------- /config/manager/webhookcainjection_patch.yaml: -------------------------------------------------------------------------------- 1 | # This patch add annotation to admission webhook config and 2 | # the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. 3 | apiVersion: admissionregistration.k8s.io/v1 4 | kind: MutatingWebhookConfiguration 5 | metadata: 6 | name: mutating-webhook-configuration 7 | annotations: 8 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 9 | --- 10 | apiVersion: admissionregistration.k8s.io/v1 11 | kind: ValidatingWebhookConfiguration 12 | metadata: 13 | name: validating-webhook-configuration 14 | annotations: 15 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 16 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_client_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: metrics-reader 5 | rules: 6 | - nonResourceURLs: 7 | - "/metrics" 8 | verbs: 9 | - get 10 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: proxy-role 5 | rules: 6 | - apiGroups: 7 | - authentication.k8s.io 8 | resources: 9 | - tokenreviews 10 | verbs: 11 | - create 12 | - apiGroups: 13 | - authorization.k8s.io 14 | resources: 15 | - subjectaccessreviews 16 | verbs: 17 | - create 18 | -------------------------------------------------------------------------------- /config/rbac/gatewaystatus_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit gatewaystatuses. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: clusterrole 7 | app.kubernetes.io/instance: gatewaystatus-editor-role 8 | app.kubernetes.io/component: rbac 9 | app.kubernetes.io/created-by: kube-egress-gateway 10 | app.kubernetes.io/part-of: kube-egress-gateway 11 | app.kubernetes.io/managed-by: kustomize 12 | name: gatewaystatus-editor-role 13 | rules: 14 | - apiGroups: 15 | - egressgateway.kubernetes.azure.com 16 | resources: 17 | - gatewaystatuses 18 | verbs: 19 | - create 20 | - delete 21 | - get 22 | - list 23 | - patch 24 | - update 25 | - watch 26 | - apiGroups: 27 | - egressgateway.kubernetes.azure.com 28 | resources: 29 | - gatewaystatuses/status 30 | verbs: 31 | - get 32 | -------------------------------------------------------------------------------- /config/rbac/gatewaystatus_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view gatewaystatuses. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: clusterrole 7 | app.kubernetes.io/instance: gatewaystatus-viewer-role 8 | app.kubernetes.io/component: rbac 9 | app.kubernetes.io/created-by: kube-egress-gateway 10 | app.kubernetes.io/part-of: kube-egress-gateway 11 | app.kubernetes.io/managed-by: kustomize 12 | name: gatewaystatus-viewer-role 13 | rules: 14 | - apiGroups: 15 | - egressgateway.kubernetes.azure.com 16 | resources: 17 | - gatewaystatuses 18 | verbs: 19 | - get 20 | - list 21 | - watch 22 | - apiGroups: 23 | - egressgateway.kubernetes.azure.com 24 | resources: 25 | - gatewaystatuses/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /config/rbac/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - auth_proxy_client_clusterrole.yaml 3 | - auth_proxy_role.yaml 4 | 5 | -------------------------------------------------------------------------------- /config/resource/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - ns.yaml 3 | 4 | -------------------------------------------------------------------------------- /config/resource/ns.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | labels: 5 | kube-egress-gateway-control-plane: controller-manager 6 | name: system 7 | --- -------------------------------------------------------------------------------- /config/role/gatewaylbconfiguration_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit gatewaylbconfigurations. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: clusterrole 7 | app.kubernetes.io/instance: gatewaylbconfiguration-editor-role 8 | app.kubernetes.io/component: rbac 9 | app.kubernetes.io/created-by: kube-egress-gateway 10 | app.kubernetes.io/part-of: kube-egress-gateway 11 | app.kubernetes.io/managed-by: kustomize 12 | name: gatewaylbconfiguration-editor-role 13 | rules: 14 | - apiGroups: 15 | - egressgateway.kubernetes.azure.com 16 | resources: 17 | - gatewaylbconfigurations 18 | verbs: 19 | - create 20 | - delete 21 | - get 22 | - list 23 | - patch 24 | - update 25 | - watch 26 | - apiGroups: 27 | - egressgateway.kubernetes.azure.com 28 | resources: 29 | - gatewaylbconfigurations/status 30 | verbs: 31 | - get 32 | -------------------------------------------------------------------------------- /config/role/gatewaylbconfiguration_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view gatewaylbconfigurations. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: clusterrole 7 | app.kubernetes.io/instance: gatewaylbconfiguration-viewer-role 8 | app.kubernetes.io/component: rbac 9 | app.kubernetes.io/created-by: kube-egress-gateway 10 | app.kubernetes.io/part-of: kube-egress-gateway 11 | app.kubernetes.io/managed-by: kustomize 12 | name: gatewaylbconfiguration-viewer-role 13 | rules: 14 | - apiGroups: 15 | - egressgateway.kubernetes.azure.com 16 | resources: 17 | - gatewaylbconfigurations 18 | verbs: 19 | - get 20 | - list 21 | - watch 22 | - apiGroups: 23 | - egressgateway.kubernetes.azure.com 24 | resources: 25 | - gatewaylbconfigurations/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /config/role/gatewayvmconfiguration_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit gatewayvmconfigurations. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: clusterrole 7 | app.kubernetes.io/instance: gatewayvmconfiguration-editor-role 8 | app.kubernetes.io/component: rbac 9 | app.kubernetes.io/created-by: kube-egress-gateway 10 | app.kubernetes.io/part-of: kube-egress-gateway 11 | app.kubernetes.io/managed-by: kustomize 12 | name: gatewayvmconfiguration-editor-role 13 | rules: 14 | - apiGroups: 15 | - egressgateway.kubernetes.azure.com 16 | resources: 17 | - gatewayvmconfigurations 18 | verbs: 19 | - create 20 | - delete 21 | - get 22 | - list 23 | - patch 24 | - update 25 | - watch 26 | - apiGroups: 27 | - egressgateway.kubernetes.azure.com 28 | resources: 29 | - gatewayvmconfigurations/status 30 | verbs: 31 | - get 32 | -------------------------------------------------------------------------------- /config/role/gatewayvmconfiguration_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view gatewayvmconfigurations. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: clusterrole 7 | app.kubernetes.io/instance: gatewayvmconfiguration-viewer-role 8 | app.kubernetes.io/component: rbac 9 | app.kubernetes.io/created-by: kube-egress-gateway 10 | app.kubernetes.io/part-of: kube-egress-gateway 11 | app.kubernetes.io/managed-by: kustomize 12 | name: gatewayvmconfiguration-viewer-role 13 | rules: 14 | - apiGroups: 15 | - egressgateway.kubernetes.azure.com 16 | resources: 17 | - gatewayvmconfigurations 18 | verbs: 19 | - get 20 | - list 21 | - watch 22 | - apiGroups: 23 | - egressgateway.kubernetes.azure.com 24 | resources: 25 | - gatewayvmconfigurations/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /config/role/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - staticgatewayconfiguration_viewer_role.yaml 3 | - staticgatewayconfiguration_editor_role.yaml 4 | - gatewaylbconfiguration_viewer.role.yaml 5 | - gatewaylbconfiguration_editor.role.yaml 6 | - gatewayvmconfiguration_viewer.role.yaml 7 | - gatewayvmconfiguration_editor.role.yaml 8 | - podendpoint_viewer.role.yaml 9 | - podendpoint_editor.role.yaml -------------------------------------------------------------------------------- /config/role/podendpoint_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit podendpoints. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: podendpoint-editor-role 6 | rules: 7 | - apiGroups: 8 | - egressgateway.kubernetes.azure.com 9 | resources: 10 | - podendpoints 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - egressgateway.kubernetes.azure.com 21 | resources: 22 | - podendpoints/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/role/podendpoint_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view podendpoints. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: podendpoint-viewer-role 6 | rules: 7 | - apiGroups: 8 | - egressgateway.kubernetes.azure.com 9 | resources: 10 | - podendpoints 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - egressgateway.kubernetes.azure.com 17 | resources: 18 | - podendpoints/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/role/staticgatewayconfiguration_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit staticgatewayconfigurations. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: staticgatewayconfiguration-editor-role 6 | rules: 7 | - apiGroups: 8 | - egressgateway.kubernetes.azure.com 9 | resources: 10 | - staticgatewayconfigurations 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - egressgateway.kubernetes.azure.com 21 | resources: 22 | - staticgatewayconfigurations/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/role/staticgatewayconfiguration_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view staticgatewayconfigurations. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: staticgatewayconfiguration-viewer-role 6 | rules: 7 | - apiGroups: 8 | - egressgateway.kubernetes.azure.com 9 | resources: 10 | - staticgatewayconfigurations 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - egressgateway.kubernetes.azure.com 17 | resources: 18 | - staticgatewayconfigurations/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/samples/egressgateway_v1alpha1_gatewaylbconfiguration.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: egressgateway.kubernetes.azure.com/v1alpha1 2 | kind: GatewayLBConfiguration 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: gatewaylbconfiguration 6 | app.kubernetes.io/instance: gatewaylbconfiguration-sample 7 | app.kubernetes.io/part-of: kube-egress-gateway 8 | app.kubernetes.io/managed-by: kustomize 9 | app.kubernetes.io/created-by: kube-egress-gateway 10 | name: gatewaylbconfiguration-sample 11 | spec: 12 | gatewayNodepoolName: gwnodepool 13 | gatewayVmssProfile: {} 14 | provisionPublicIps: true 15 | -------------------------------------------------------------------------------- /config/samples/egressgateway_v1alpha1_gatewaystatus.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: egressgateway.kubernetes.azure.com/v1alpha1 2 | kind: GatewayStatus 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: gatewaystatus 6 | app.kubernetes.io/instance: gatewaystatus-sample 7 | app.kubernetes.io/part-of: kube-egress-gateway 8 | app.kubernetes.io/managed-by: kustomize 9 | app.kubernetes.io/created-by: kube-egress-gateway 10 | name: gatewaystatus-sample 11 | spec: 12 | readyGatewayConfigurations: 13 | - interfaceName: wg-6000 14 | staticGatewayConfiguration: default/staticgatewayconfiguration-sample 15 | readyPeerConfigurations: 16 | - interfaceName: wg-6000 17 | podEndpoint: default/podendpoint-sample 18 | publicKey: ********** 19 | -------------------------------------------------------------------------------- /config/samples/egressgateway_v1alpha1_gatewayvmconfiguration.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: egressgateway.kubernetes.azure.com/v1alpha1 2 | kind: GatewayVMConfiguration 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: gatewayvmconfiguration 6 | app.kubernetes.io/instance: gatewayvmconfiguration-sample 7 | app.kubernekubernetestes.io/part-of: kube-egress-gateway 8 | app.kubernetes.io/managed-by: kustomize 9 | app.kubernetes.io/created-by: kube-egress-gateway 10 | name: gatewayvmconfiguration-sample 11 | spec: 12 | gatewayNodepoolName: gwnodepool 13 | gatewayVmssProfile: {} 14 | provisionPublicIps: true 15 | -------------------------------------------------------------------------------- /config/samples/egressgateway_v1alpha1_podendpoint.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: egressgateway.kubernetes.azure.com/v1alpha1 2 | kind: PodEndpoint 3 | metadata: 4 | name: podendpoint-sample 5 | spec: 6 | staticGatewayConfiguration: staticgatewayconfiguration-sample 7 | podIpAddress: 10.243.0.25 8 | podPublicKey: 9 | -------------------------------------------------------------------------------- /config/samples/egressgateway_v1alpha1_staticgatewayconfiguration.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: egressgateway.kubernetes.azure.com/v1alpha1 2 | kind: StaticGatewayConfiguration 3 | metadata: 4 | name: staticgatewayconfiguration-sample 5 | spec: 6 | gatewayNodepoolName: gwnodepool 7 | gatewayVmssProfile: 8 | vmssResourceGroup: gw-rg 9 | vmssName: gatewaypool1-12345678-vmss 10 | publicIpPrefixSize: 31 11 | # publicIpPrefixId: /subscriptions//resourceGroups//providers/Microsoft.Network/publicIPPrefixes/ 12 | -------------------------------------------------------------------------------- /controllers/cnimanager/cnimanager_suite_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | package cnimanager_test 4 | 5 | import ( 6 | "testing" 7 | 8 | . "github.com/onsi/ginkgo/v2" 9 | . "github.com/onsi/gomega" 10 | ) 11 | 12 | func TestCnimanager(t *testing.T) { 13 | RegisterFailHandler(Fail) 14 | RunSpecs(t, "Cnimanager Suite") 15 | } 16 | -------------------------------------------------------------------------------- /controllers/daemon/suite_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | package daemon 5 | 6 | import ( 7 | "path/filepath" 8 | "testing" 9 | 10 | . "github.com/onsi/ginkgo/v2" 11 | . "github.com/onsi/gomega" 12 | "k8s.io/client-go/kubernetes/scheme" 13 | "k8s.io/client-go/rest" 14 | "sigs.k8s.io/controller-runtime/pkg/client" 15 | "sigs.k8s.io/controller-runtime/pkg/envtest" 16 | logf "sigs.k8s.io/controller-runtime/pkg/log" 17 | "sigs.k8s.io/controller-runtime/pkg/log/zap" 18 | 19 | egressgatewayv1alpha1 "github.com/Azure/kube-egress-gateway/api/v1alpha1" 20 | //+kubebuilder:scaffold:imports 21 | ) 22 | 23 | // These tests use Ginkgo (BDD-style Go testing framework). Refer to 24 | // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. 25 | 26 | var cfg *rest.Config 27 | var k8sClient client.Client 28 | var testEnv *envtest.Environment 29 | 30 | func TestAPIs(t *testing.T) { 31 | RegisterFailHandler(Fail) 32 | 33 | RunSpecs(t, "Controller Suite") 34 | } 35 | 36 | var _ = BeforeSuite(func() { 37 | logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) 38 | 39 | By("bootstrapping test environment") 40 | testEnv = &envtest.Environment{ 41 | CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, 42 | ErrorIfCRDPathMissing: true, 43 | } 44 | 45 | var err error 46 | // cfg is defined in this file globally. 47 | cfg, err = testEnv.Start() 48 | Expect(err).NotTo(HaveOccurred()) 49 | Expect(cfg).NotTo(BeNil()) 50 | 51 | err = egressgatewayv1alpha1.AddToScheme(scheme.Scheme) 52 | Expect(err).NotTo(HaveOccurred()) 53 | 54 | //+kubebuilder:scaffold:scheme 55 | 56 | k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) 57 | Expect(err).NotTo(HaveOccurred()) 58 | Expect(k8sClient).NotTo(BeNil()) 59 | 60 | }) 61 | 62 | var _ = AfterSuite(func() { 63 | By("tearing down the test environment") 64 | err := testEnv.Stop() 65 | Expect(err).NotTo(HaveOccurred()) 66 | }) 67 | -------------------------------------------------------------------------------- /controllers/manager/suite_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | package manager 5 | 6 | import ( 7 | "path/filepath" 8 | "testing" 9 | 10 | . "github.com/onsi/ginkgo/v2" 11 | . "github.com/onsi/gomega" 12 | 13 | "k8s.io/client-go/kubernetes/scheme" 14 | "k8s.io/client-go/rest" 15 | "sigs.k8s.io/controller-runtime/pkg/client" 16 | "sigs.k8s.io/controller-runtime/pkg/envtest" 17 | logf "sigs.k8s.io/controller-runtime/pkg/log" 18 | "sigs.k8s.io/controller-runtime/pkg/log/zap" 19 | 20 | egressgatewayv1alpha1 "github.com/Azure/kube-egress-gateway/api/v1alpha1" 21 | //+kubebuilder:scaffold:imports 22 | ) 23 | 24 | // These tests use Ginkgo (BDD-style Go testing framework). Refer to 25 | // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. 26 | 27 | var cfg *rest.Config 28 | var k8sClient client.Client 29 | var testEnv *envtest.Environment 30 | 31 | func TestControllers(t *testing.T) { 32 | RegisterFailHandler(Fail) 33 | 34 | RunSpecs(t, "Controller Suite") 35 | } 36 | 37 | var _ = BeforeSuite(func() { 38 | logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) 39 | 40 | By("bootstrapping test environment") 41 | testEnv = &envtest.Environment{ 42 | CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, 43 | ErrorIfCRDPathMissing: true, 44 | } 45 | 46 | var err error 47 | // cfg is defined in this file globally. 48 | cfg, err = testEnv.Start() 49 | Expect(err).NotTo(HaveOccurred()) 50 | Expect(cfg).NotTo(BeNil()) 51 | 52 | err = scheme.AddToScheme(scheme.Scheme) 53 | Expect(err).NotTo(HaveOccurred()) 54 | err = egressgatewayv1alpha1.AddToScheme(scheme.Scheme) 55 | Expect(err).NotTo(HaveOccurred()) 56 | //+kubebuilder:scaffold:scheme 57 | 58 | k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) 59 | Expect(err).NotTo(HaveOccurred()) 60 | Expect(k8sClient).NotTo(BeNil()) 61 | 62 | }) 63 | 64 | var _ = AfterSuite(func() { 65 | By("tearing down the test environment") 66 | err := testEnv.Stop() 67 | Expect(err).NotTo(HaveOccurred()) 68 | }) 69 | -------------------------------------------------------------------------------- /docker/base.Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | # Build the manager binary 3 | FROM --platform=$BUILDPLATFORM mcr.microsoft.com/oss/go/microsoft/golang:1.24.3@sha256:3ca204a4606a7596aa67183f74d34e4f3ec4f150719d02999e2201fd635ebe96 AS builder 4 | WORKDIR /workspace 5 | # Copy the Go Modules manifests 6 | COPY go.mod go.mod 7 | COPY go.sum go.sum 8 | # cache deps before building and copying source so that we don't need to re-download as much 9 | # and so that source changes don't invalidate our downloaded layer 10 | RUN go mod download 11 | # Copy the go source 12 | COPY cmd/ cmd/ 13 | COPY api/ api/ 14 | COPY controllers/ controllers/ 15 | COPY pkg/ pkg/ 16 | 17 | ARG MAIN_ENTRY 18 | ARG TARGETARCH 19 | FROM builder AS base 20 | WORKDIR /workspace 21 | # Build 22 | RUN CGO_ENABLED=0 GOOS=linux GOARCH=$TARGETARCH go build -a -o ${MAIN_ENTRY} ./cmd/${MAIN_ENTRY}/main.go 23 | 24 | # Use distroless as minimal base image to package the manager binary 25 | # Refer to https://github.com/GoogleContainerTools/distroless for more details 26 | FROM gcr.io/distroless/static:nonroot@sha256:c0f429e16b13e583da7e5a6ec20dd656d325d88e6819cafe0adb0828976529dc 27 | ARG MAIN_ENTRY 28 | WORKDIR / 29 | COPY --from=base /workspace/${MAIN_ENTRY} . 30 | USER 65532:65532 31 | 32 | ENTRYPOINT ["${MAIN_ENTRY}"] 33 | 34 | 35 | -------------------------------------------------------------------------------- /docker/cni-ipam.Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | FROM gcr.io/distroless/static:latest@sha256:3d0f463de06b7ddff27684ec3bfd0b54a425149d0f8685308b1fdf297b0265e9 3 | WORKDIR /workspace 4 | COPY --from=baseimg /kube-egress-cni-ipam . 5 | COPY --from=tool /copy . 6 | ENTRYPOINT ["./copy", "-s", "./kube-egress-cni-ipam", "-d", "/opt/cni/bin/"] 7 | -------------------------------------------------------------------------------- /docker/cni.Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | FROM gcr.io/distroless/static:latest@sha256:3d0f463de06b7ddff27684ec3bfd0b54a425149d0f8685308b1fdf297b0265e9 3 | WORKDIR /workspace 4 | COPY --from=baseimg /kube-egress-cni . 5 | COPY --from=tool /copy . 6 | ENTRYPOINT ["./copy", "-s", "./kube-egress-cni", "-d", "/opt/cni/bin/"] 7 | -------------------------------------------------------------------------------- /docker/cnimanager.Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | FROM gcr.io/distroless/static:latest@sha256:3d0f463de06b7ddff27684ec3bfd0b54a425149d0f8685308b1fdf297b0265e9 3 | ARG MAIN_ENTRY 4 | COPY --from=baseimg /${MAIN_ENTRY} / 5 | ENTRYPOINT ["/${MAIN_ENTRY}"] 6 | -------------------------------------------------------------------------------- /docker/docker-bake.hcl: -------------------------------------------------------------------------------- 1 | 2 | group "default" { 3 | targets = ["daemon", "controller", "cnimanager", "cni", "cni-ipam", "daemoninit"] 4 | } 5 | 6 | variable "PLATFORMS" { 7 | default = "linux/amd64" 8 | } 9 | 10 | target "base" { 11 | dockerfile = "docker/base.Dockerfile" 12 | platforms = [PLATFORMS] 13 | } 14 | 15 | target "daemon-compile" { 16 | inherits = ["base"] 17 | args = { 18 | MAIN_ENTRY = "kube-egress-gateway-daemon", 19 | } 20 | } 21 | 22 | target "daemon" { 23 | inherits = ["daemon-tags"] 24 | dockerfile = "docker/gwdaemon.Dockerfile" 25 | contexts = { 26 | baseimg = "target:daemon-compile" 27 | } 28 | platforms = [PLATFORMS] 29 | args = { 30 | MAIN_ENTRY = "kube-egress-gateway-daemon", 31 | } 32 | } 33 | 34 | target "add-netns-compile" { 35 | inherits = ["base"] 36 | args = { 37 | MAIN_ENTRY = "add-netns", 38 | } 39 | } 40 | 41 | target "daemoninit" { 42 | inherits = ["daemoninit-tags"] 43 | dockerfile = "docker/gwdaemon-init.Dockerfile" 44 | contexts = { 45 | tool = "target:add-netns-compile" 46 | } 47 | platforms = [PLATFORMS] 48 | } 49 | 50 | target "controller" { 51 | inherits = ["base","controller-tags"] 52 | platforms = [PLATFORMS] 53 | args = { 54 | MAIN_ENTRY = "kube-egress-gateway-controller", 55 | } 56 | } 57 | 58 | target "cnimanager-compile" { 59 | inherits = ["base"] 60 | args = { 61 | MAIN_ENTRY = "kube-egress-gateway-cnimanager", 62 | } 63 | } 64 | 65 | target "cnimanager" { 66 | inherits = ["cnimanager-tags"] 67 | dockerfile = "docker/cnimanager.Dockerfile" 68 | contexts = { 69 | baseimg = "target:cnimanager-compile" 70 | } 71 | platforms = [PLATFORMS] 72 | args = { 73 | MAIN_ENTRY = "kube-egress-gateway-cnimanager", 74 | } 75 | } 76 | 77 | target "cni-compile" { 78 | inherits = ["base"] 79 | args = { 80 | MAIN_ENTRY = "kube-egress-cni", 81 | } 82 | } 83 | 84 | target "copy-compile" { 85 | inherits = ["base"] 86 | args = { 87 | MAIN_ENTRY = "copy", 88 | } 89 | } 90 | 91 | target "cni" { 92 | inherits = ["cni-tags"] 93 | dockerfile = "docker/cni.Dockerfile" 94 | contexts = { 95 | baseimg = "target:cni-compile", 96 | tool = "target:copy-compile" 97 | } 98 | platforms = [PLATFORMS] 99 | } 100 | 101 | target "cni-ipam-compile" { 102 | inherits = ["base"] 103 | args = { 104 | MAIN_ENTRY = "kube-egress-cni-ipam", 105 | } 106 | } 107 | 108 | target "cni-ipam" { 109 | inherits = ["cni-ipam-tags"] 110 | dockerfile = "docker/cni-ipam.Dockerfile" 111 | contexts = { 112 | baseimg = "target:cni-ipam-compile", 113 | tool = "target:copy-compile" 114 | } 115 | platforms = [PLATFORMS] 116 | } -------------------------------------------------------------------------------- /docker/docker-localtag-bake.hcl: -------------------------------------------------------------------------------- 1 | variable "TAG" { 2 | default = "latest" 3 | } 4 | 5 | variable "IMAGE_REGISTRY" { 6 | default = "local" 7 | } 8 | 9 | target "daemon-tags" { 10 | tags = ["${IMAGE_REGISTRY}/kube-egress-gateway-daemon:${TAG}"] 11 | } 12 | 13 | target "daemoninit-tags" { 14 | tags = ["${IMAGE_REGISTRY}/kube-egress-gateway-daemon-init:${TAG}"] 15 | } 16 | 17 | target "controller-tags" { 18 | tags = ["${IMAGE_REGISTRY}/kube-egress-gateway-controller:${TAG}"] 19 | } 20 | 21 | target "cnimanager-tags" { 22 | tags = ["${IMAGE_REGISTRY}/kube-egress-gateway-cnimanager:${TAG}"] 23 | } 24 | 25 | target "cni-tags" { 26 | tags = ["${IMAGE_REGISTRY}/kube-egress-gateway-cni:${TAG}"] 27 | } 28 | 29 | target "cni-ipam-tags" { 30 | tags = ["${IMAGE_REGISTRY}/kube-egress-gateway-cni-ipam:${TAG}"] 31 | } -------------------------------------------------------------------------------- /docker/gwdaemon-init.Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | FROM gcr.io/distroless/static:latest@sha256:3d0f463de06b7ddff27684ec3bfd0b54a425149d0f8685308b1fdf297b0265e9 3 | WORKDIR /workspace 4 | COPY --from=tool /add-netns . 5 | ENTRYPOINT ["./add-netns"] 6 | -------------------------------------------------------------------------------- /docker/gwdaemon.Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | FROM registry.k8s.io/build-image/distroless-iptables:v0.7.4@sha256:a5c4c0995c87928fc634e7f897e5f7916ec7457c2c9a8988034f314ca6ca4821 3 | USER 0:0 4 | ARG MAIN_ENTRY 5 | COPY --from=baseimg /${MAIN_ENTRY} /${MAIN_ENTRY} 6 | ENTRYPOINT ["/${MAIN_ENTRY}"] 7 | -------------------------------------------------------------------------------- /docs/cni.md: -------------------------------------------------------------------------------- 1 | # CNI 2 | 3 | ## Design 4 | 5 | ### Dependencies 6 | 7 | wireguard kernel module should be loaded before cni is invoked. This can be done by executing `modprobe wireguard` in the host. 8 | CNI daemon which is responsible for watching Gateway Config and creating Pod Endnpoint Config should be deployed on every node. 9 | 10 | ### Nic 11 | 12 | Nic is created in init namespace and moved to container ns 13 | This nic is attached as secondary nic so this plugin should be used with multus / danm /genie meta cni plugin 14 | ### IPAM 15 | 16 | ip address is the same as the ipv6 one in eth0. 17 | 18 | ### Routing 19 | 20 | This nic will be the default route for the pod. 21 | But for pod cidr, node cidr and service cidr, we will use the default nic instead. 22 | 23 | ### Configurations 24 | 25 | #### keep-alive 26 | 27 | configured on each node, default to true 28 | 29 | #### preshared-key 30 | 31 | To be discussed. 32 | 33 | #### sample cni config 34 | ```json 35 | { 36 | "cniVersion": "1.0.0", 37 | "name": "mynet", 38 | "plugins": [ 39 | { 40 | "type": "kube-egress-cni", 41 | "ipam": { 42 | "type": "kube-egress-cni-ipam" 43 | } 44 | } 45 | ] 46 | } 47 | ``` 48 | 49 | ### Data Flow 50 | 51 | + parse CNI config and get node cidr, service cidr and pod cidr 52 | + get k8s metadata from cni args (environment) 53 | + generates keypairs 54 | + exchange public keys with cni daemon and get peer ip and keypairs 55 | + configures wireguard interface and routes 56 | 57 | ### Deployment 58 | 59 | cni should be deployed by cni daemon 60 | 61 | ## Reference 62 | 63 | + Wireguard implementation details: [Routing & Network Namespace Integration](https://www.wireguard.com/netns/) 64 | + [whereabouts](https://github.com/k8snetworkplumbingwg/whereabouts/blob/master/doc/extended-configuration.md) 65 | -------------------------------------------------------------------------------- /docs/images/gateway_provision.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/kube-egress-gateway/932bd9f8c2b9e0341dab32574a96571b6b9b338c/docs/images/gateway_provision.png -------------------------------------------------------------------------------- /docs/images/health_probe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/kube-egress-gateway/932bd9f8c2b9e0341dab32574a96571b6b9b338c/docs/images/health_probe.png -------------------------------------------------------------------------------- /docs/images/kube_egress_gateway.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/kube-egress-gateway/932bd9f8c2b9e0341dab32574a96571b6b9b338c/docs/images/kube_egress_gateway.png -------------------------------------------------------------------------------- /docs/images/pod_provision.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/kube-egress-gateway/932bd9f8c2b9e0341dab32574a96571b6b9b338c/docs/images/pod_provision.png -------------------------------------------------------------------------------- /docs/install.md: -------------------------------------------------------------------------------- 1 | # Installation Guide 2 | 3 | ## Prerequisites 4 | * [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) 5 | * [Helm 3](https://helm.sh/docs/intro/install/) 6 | * A Kubernetes cluster on Azure with a dedicated nodepool backed by Azure Virtual Machine Scale Set (VMSS) with at least one node (VMSS instance). 7 | * The nodes should be tainted with `kubeegressgateway.azure.com/mode=true:NoSchedule` so that no other workload can land on. 8 | * The nodes should be labeled with `kubeegressgateway.azure.com/mode: true` so that kube-egress-gateway DaemonSets NodeSelector can identify these. 9 | * The nodes should be linux only. 10 | * The nodes should be excluded from cluster autoscaler. 11 | 12 | ## Create Azure credentials 13 | kube-egress-gateway components communicates with Azure Resource Manager (ARM) to manipulate Azure resources. It needs an identity to access the APIs. kube-egress-gateway supports both [Managed Identity](https://learn.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview) and [Service Principal](https://learn.microsoft.com/en-us/cli/azure/create-an-azure-service-principal-azure-cli). 14 | 15 | ### Use UserAssigned Managed Identity 16 | 1. Create a UserAssigned managed identity. This identity can be created in any resource group as long as permissions are set correctly. 17 | ``` 18 | identityName="" 19 | resourceGroup="" 20 | az identity create -g $resourceGroup -n $identityName 21 | ``` 22 | 2. Retrieve the `identityID` and `clientID` from the identity you just created. 23 | ``` 24 | identityClientId=$(az identity show -g $resourceGroup -n $identityName -o tsv --query "clientId") 25 | identityId=$(az identity show -g $resourceGroup -n $identityName -o tsv --query "id") 26 | ``` 27 | 3. Assign "Network Contributor" and "Virtual Machine Contributor" roles to the identity. kube-egress-gateway components need these two roles to configure Load Balancer, Public IP Prefix, and VMSS resources. 28 | ``` 29 | networkResourceGroup="" 30 | vmssResourceGroup="" 31 | networkRGID="/subscriptions//resourceGroups/$networkResourceGroup" 32 | vmssRGID="/subscriptions//resourceGroups/$vmssResourceGroup" 33 | vmssID="/subscriptions//resourceGroups/$vmssResourceGroup/providers/Microsoft.Compute/virtualMachineScaleSets/" 34 | 35 | # assign Network Contributor role on scope networkResourceGroup and vmssResourceGroup to the identity 36 | az role assignment create --role "Network Contributor" --assignee $identityClientId --scope $networkRGID 37 | az role assignment create --role "Network Contributor" --assignee $identityClientId --scope $vmssRGID 38 | 39 | # assign Virtual Machine Contributor role on scope gateway vmss to the identity 40 | az role assignment create --role "Virtual Machine Contributor" --assignee $identityClientId --scope $vmssID 41 | ``` 42 | 4. Fill the identity clientID in your Azure cloud config file. See [sample_cloud_config_msi.yaml](samples/sample_azure_config_msi.yaml) for example. 43 | ``` 44 | useManagedIdentityExtension: true 45 | userAssignedIdentityID: "$identityClientId" 46 | ``` 47 | 48 | ### Use Service Principal 49 | 1. Create a service principal and assign Contributor role on network resource group and vmss resource group scopes. And you get sp clientID and secret from the output. 50 | ``` 51 | appName="" 52 | networkRGID="/subscriptions//resourceGroups/$networkResourceGroup" 53 | vmssRGID="/subscriptions//resourceGroups/$vmssResourceGroup" 54 | az ad sp create-for-rbac -n $appName --role Contributor --scopes $networkRGID $vmssRGID 55 | ``` 56 | 2. Fill the sp clientID and secret in your Azure cloud config file. See [sample_cloud_config_sp.yaml](samples/sample_azure_config_sp.yaml) for example. 57 | ``` 58 | useManagedIdentityExtension: false 59 | aadClientId: "" 60 | aadClientSecret: "" 61 | ``` 62 | 63 | ## Install kube-egress-gateway as Helm Chart 64 | See details [here](../helm/kube-egress-gateway/README.md). -------------------------------------------------------------------------------- /docs/samples/gateway_profile.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: egressgateway.kubernetes.azure.com/v1alpha1 2 | kind: StaticGatewayConfiguration 3 | metadata: 4 | name: sgw1 5 | spec: 6 | gatewayVmssProfile: 7 | vmssResourceGroup: myResourceGroup # required 8 | vmssName: gatewayVMSS # required 9 | publicIpPrefixSize: 31 # required 10 | publicIpPrefixId: /subscriptions//resourcegroups//providers/Microsoft.Network/publicipprefixes/ # optional 11 | excludeCIDRs: # optional 12 | - 13 | - 14 | -------------------------------------------------------------------------------- /docs/samples/sample_azure_config_msi.yaml: -------------------------------------------------------------------------------- 1 | config: 2 | azureCloudConfig: 3 | cloud: "AzurePublicCloud" 4 | tenantId: "" 5 | subscriptionId: "" 6 | useManagedIdentityExtension: true 7 | userAssignedIdentityID: "" 8 | userAgent: "kube-egress-gateway-controller" 9 | resourceGroup: "" 10 | location: "" 11 | gatewayLoadBalancerName: "kubeegressgateway-ilb" 12 | loadBalancerResourceGroup: "" 13 | vnetName: "" 14 | vnetResourceGroup: "" 15 | subnetName: "" 16 | 17 | -------------------------------------------------------------------------------- /docs/samples/sample_azure_config_sp.yaml: -------------------------------------------------------------------------------- 1 | config: 2 | azureCloudConfig: 3 | cloud: "AzurePublicCloud" 4 | tenantId: "" 5 | subscriptionId: "" 6 | useManagedIdentityExtension: false 7 | aadClientId: "" 8 | aadClientSecret: "" 9 | userAgent: "kube-egress-gateway-controller" 10 | resourceGroup: "" 11 | location: "" 12 | gatewayLoadBalancerName: "kubeegressgateway-ilb" 13 | loadBalancerResourceGroup: "" 14 | vnetName: "" 15 | vnetResourceGroup: "" 16 | subnetName: "" 17 | 18 | -------------------------------------------------------------------------------- /docs/samples/sample_pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: samplepod 5 | annotations: 6 | kubernetes.azure.com/static-gateway-configuration: sgw1 # required 7 | spec: 8 | containers: 9 | - name: samplepod 10 | command: ["/bin/ash", "-c", "trap : TERM INT; sleep infinity & wait"] 11 | image: alpine 12 | -------------------------------------------------------------------------------- /e2e/e2e_suite_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | package e2e 5 | 6 | import ( 7 | "testing" 8 | 9 | . "github.com/onsi/ginkgo/v2" 10 | . "github.com/onsi/gomega" 11 | logf "sigs.k8s.io/controller-runtime/pkg/log" 12 | "sigs.k8s.io/controller-runtime/pkg/log/zap" 13 | ) 14 | 15 | func TestE2E(t *testing.T) { 16 | RegisterFailHandler(Fail) 17 | RunSpecs(t, "kube-egress-gateway e2e suite") 18 | } 19 | 20 | var _ = BeforeSuite(func() { 21 | logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) 22 | }) 23 | -------------------------------------------------------------------------------- /e2e/utils/log.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | package utils 5 | 6 | import ( 7 | "fmt" 8 | "runtime" 9 | "time" 10 | 11 | . "github.com/onsi/ginkgo/v2" 12 | ) 13 | 14 | func log(level string, format string, args ...interface{}) { 15 | current := time.Now().Format(time.StampMilli) 16 | _, file, line, _ := runtime.Caller(2) 17 | extra := fmt.Sprintf(" [%s:%d]", file, line) 18 | 19 | fmt.Fprintf(GinkgoWriter, current+": "+level+": "+format+extra+"\n", args...) 20 | } 21 | 22 | // Logf prints info logs 23 | func Logf(format string, args ...interface{}) { 24 | log("INFO", format, args...) 25 | } 26 | -------------------------------------------------------------------------------- /hack/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) Microsoft Corporation. 3 | Licensed under the MIT license. 4 | */ -------------------------------------------------------------------------------- /hack/create_resource.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | 3 | if [ "$1" != "webhook" ] && [ "$1" != "api" ]; then 4 | echo "Unrecognized first argument. Must be , \"$1\" provided." 5 | exit 1 6 | fi 7 | 8 | workdir=$(pwd) 9 | echo "workdir: $workdir" 10 | 11 | make kubebuilder 12 | cp cmd/kube-egress-gateway-controller/cmd/root.go cmd/kube-egress-gateway-controller/cmd/root.go.bak 13 | cp cmd/kube-egress-gateway-controller/cmd/root.go.bak main.go 14 | ./bin/kubebuilder create $@ 15 | if [ $? -ne 0 ]; then 16 | echo "kubebuilder create $1 failed" 17 | rm -f cmd/kube-egress-gateway-controller/cmd/root.go.bak main.go 18 | exit 1 19 | fi 20 | 21 | diff --brief <(cat cmd/kube-egress-gateway-controller/cmd/root.go.bak) <(sort main.go) >/dev/null 22 | comp_value=$? 23 | 24 | if [ $comp_value -eq 1 ] 25 | then 26 | mv main.go cmd/kube-egress-gateway-controller/cmd/root.go 27 | fi 28 | 29 | rm -f cmd/kube-egress-gateway-controller/cmd/root.go.bak main.go 30 | 31 | make manifests build 32 | -------------------------------------------------------------------------------- /hack/generate_release_note.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -x 4 | 5 | RELEASE=$1 6 | OUTPUT=${2:-release-notes.md} 7 | 8 | install_cli() { 9 | if ! [[ -x "$(command -v release-notes)" ]]; then 10 | echo "CLI release-notes not found, installing..." 11 | GO111MODULE=on go install k8s.io/release/cmd/release-notes@latest 12 | else 13 | echo "CLI release-notes found, skip installing. If you want to upgrade, run 'GO111MODULE=on go install k8s.io/release/cmd/release-notes@latest'" 14 | fi 15 | } 16 | 17 | generate() { 18 | FROM_TAG=$1 19 | TO_TAG=$2 20 | BRANCH=$3 21 | FROM_COMMIT=$(git rev-list --no-merges ${FROM_TAG}..${TO_TAG} | tail -1) # exclude the ${FROM_TAG} commit 22 | TO_COMMIT=$(git rev-parse ${TO_TAG}^{commit}) 23 | 24 | echo "Generating release notes for ${FROM_TAG}..${TO_TAG} (${FROM_COMMIT}..${TO_COMMIT}) on branch ${BRANCH}" 25 | 26 | rm -f ${OUTPUT} 27 | release-notes --repo=kube-egress-gateway \ 28 | --org=Azure \ 29 | --branch=${BRANCH} \ 30 | --start-sha=${FROM_COMMIT} \ 31 | --end-sha=${TO_COMMIT} \ 32 | --markdown-links=true \ 33 | --required-author='' \ 34 | --output=${OUTPUT} 35 | 36 | read -r -d '' HEAD < ${OUTPUT} 41 | 42 | } 43 | 44 | help() { 45 | echo "Usage: $0 [output]" 46 | echo " release: release tag, e.g. v1.21.0." 47 | echo " output: output file, default to release-notes.md." 48 | echo " GITHUB_TOKEN: The GitHub token to use for API requests. (required environment variable)" 49 | echo "Example: $0 v1.21.0" 50 | echo "Example: $0 v1.21.0 release-notes.md" 51 | } 52 | 53 | main() { 54 | if [[ -z "${RELEASE}" || -z "${GITHUB_TOKEN}" ]]; then 55 | help 56 | exit 1 57 | fi 58 | 59 | install_cli 60 | 61 | VERSION="${RELEASE#[vV]}" 62 | VERSION_MAJOR="${VERSION%%\.*}" 63 | VERSION_MINOR="${VERSION#*.}" 64 | VERSION_MINOR="${VERSION_MINOR%.*}" 65 | VERSION_PATCH="${VERSION##*.}" 66 | 67 | # when release a new minor version, generate release note from the last minor version 68 | # example: 69 | # when release v1.21.0, generate release note from v1.20.0 (v1.20.0..v1.21.0) 70 | # when release v1.21.3, generate release note from v1.21.2 (v1.21.2..v1.21.3) 71 | if [[ "${VERSION_PATCH}" = "0" ]]; then 72 | START_TAG=v${VERSION_MAJOR}.$((VERSION_MINOR-1)).0 73 | else 74 | START_TAG=v${VERSION_MAJOR}.${VERSION_MINOR}.$((VERSION_PATCH-1)) 75 | fi 76 | END_TAG=${RELEASE} 77 | 78 | generate ${START_TAG} ${END_TAG} main 79 | } 80 | 81 | main 82 | -------------------------------------------------------------------------------- /hack/run_e2e.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | # Check if required variables are present 5 | : "${AZURE_SUBSCRIPTION_ID:?Environment variable empty or not defined.}" 6 | : "${AZURE_TENANT_ID:?Environment variable empty or not defined.}" 7 | 8 | # Check if collecting log is needed 9 | if [[ "${COLLECT_LOG}" == "true" ]]; then 10 | if [[ -z "${LOG_DIR}" ]]; then 11 | echo "LOG_DIR is not set" 12 | exit 1 13 | fi 14 | fi 15 | 16 | collect_pod_specs() { 17 | echo "Describing kube-egress-gateway pods..." 18 | kubectl get pod -A -l app=kube-egress-gateway -o json | jq -r '.items[] | .metadata.namespace + " " + .metadata.name' | while read -r NS POD; do 19 | mkdir -p "${LOG_DIR}/pods-spec/${NS}" 20 | kubectl describe pod ${POD} -n ${NS} > "${LOG_DIR}/pods-spec/${NS}/${POD}" || echo "Cannot describe pod ${NS}/${POD}" 21 | done 22 | } 23 | 24 | collect_pod_logs() { 25 | echo "Collecting kube-egress-gateway pods logs..." 26 | kubectl get pod -A -l app=kube-egress-gateway -o json | jq -r '.items[] | .metadata.namespace + " " + .metadata.name' | while read -r NS POD; do 27 | mkdir -p "${LOG_DIR}/pods-log/${NS}" 28 | kubectl logs ${POD} -n ${NS} > "${LOG_DIR}/pods-log/${NS}/${POD}.log" || echo "Cannot collect log from pod ${NS}/${POD}" 29 | done 30 | } 31 | 32 | collect_node_logs() { 33 | echo "Collecting logs of all nodes" 34 | mkdir -p "${LOG_DIR}/nodes-log" 35 | LOG_IMAGE="nginx" 36 | # TODO: switch to kubelet node log API once it is supported 37 | # https://kubernetes.io/docs/concepts/cluster-administration/system-logs/#log-query 38 | kubectl get node | grep "aks-" | awk '{printf("%s\n",$1)}' | while read -r NODE; do 39 | NODE_LOG_DIR="${LOG_DIR}/nodes-log/${NODE}" 40 | mkdir -p "${NODE_LOG_DIR}" 41 | kubectl debug node/${NODE} -it --image=${LOG_IMAGE} -- cat /host/var/log/syslog > "${NODE_LOG_DIR}/syslog" || echo "Cannot collect syslog.log from node ${NODE}" 42 | kubectl debug node/${NODE} -it --image=${LOG_IMAGE} -- cat /host/var/log/kern.log > "${NODE_LOG_DIR}/kern.log" || echo "Cannot collect kern.log from node ${NODE}" 43 | kubectl debug node/${NODE} -it --image=${LOG_IMAGE} -- cat /host/var/log/messages > "${NODE_LOG_DIR}/messages" || echo "Cannot collect messages from node ${NODE}" 44 | kubectl debug node/${NODE} -it --image=${LOG_IMAGE} -- cat /host/etc/cni/net.d/01-egressgateway.conflist > "${NODE_LOG_DIR}/01-egressgateway.conflist" || echo "Cannot collect cni conflist from node ${NODE}" 45 | done 46 | } 47 | 48 | cleanup() { 49 | echo "Listing nodes..." 50 | kubectl get node -owide || echo "Unable to get nodes" 51 | echo "Listing pods..." 52 | kubectl get pod --all-namespaces=true -owide || echo "Unable to get pods" 53 | 54 | if [[ ${COLLECT_LOG} != "true" ]]; then 55 | return 56 | fi 57 | echo "Collecting logs..." 58 | collect_pod_specs 59 | collect_pod_logs 60 | collect_node_logs 61 | } 62 | trap cleanup EXIT 63 | 64 | REPO_ROOT=$(git rev-parse --show-toplevel) 65 | 66 | # Run e2e tests 67 | go test ${REPO_ROOT}/e2e/ -v --timeout 120m --ginkgo.timeout=115m -------------------------------------------------------------------------------- /hack/test_linux.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Run kube-egress-gateway unit tests. 4 | # 5 | set -e 6 | 7 | # switch into the repo root directory 8 | GIT_ROOT=$(git rev-parse --show-toplevel) 9 | pushd $GIT_ROOT 10 | 11 | echo "Running unit tests" 12 | 13 | rm -rf ./testcoverage 14 | mkdir -p ./testcoverage 15 | 16 | mkdir -p /tmp/cni-rootless 17 | declare -a pkg_need_root=("github.com/Azure/kube-egress-gateway/cmd/kube-egress-cni" "github.com/Azure/kube-egress-gateway/cmd/kube-egress-cni-ipam") 18 | 19 | PKG=${PKG:-$(go list ./... | xargs echo)} 20 | 21 | # Enable unprivileged user namespace 22 | sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0 23 | 24 | for t in ${PKG}; do 25 | if [[ "${pkg_need_root[*]}" == *"${t}"* ]]; 26 | then 27 | bash -c "export XDG_RUNTIME_DIR=/tmp/cni-rootless; unshare -rmn bash -c 'ip link set lo up; go test -cover ${t} -args -test.gocoverdir=${PWD}/testcoverage'" 28 | elif [[ "${t}" != *"e2e"* ]]; 29 | then 30 | go test -v -cover ${t} -args -test.gocoverdir="${PWD}/testcoverage" 31 | fi 32 | done 33 | 34 | go tool covdata textfmt -i=${PWD}/testcoverage -o ./profile.cov.tmp 35 | cat ./profile.cov.tmp | grep -v "zz_generated.deepcopy.go" > ./profile.cov 36 | go tool cover -func ./profile.cov 37 | 38 | rm -rf /tmp/cni-rootless 39 | rm -rf ./testcoverage 40 | rm ./profile.cov.tmp 41 | popd 42 | -------------------------------------------------------------------------------- /hack/update_helm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Update kube-egress-gateway helm chart. 4 | # 5 | 6 | set -o nounset 7 | set -o errexit 8 | 9 | REPO_ROOT=$(git rev-parse --show-toplevel) 10 | 11 | helm package ${REPO_ROOT}/helm/kube-egress-gateway -d ${REPO_ROOT}/helm/repo/new 12 | helm repo index ${REPO_ROOT}/helm/repo/new 13 | helm repo index --merge ${REPO_ROOT}/helm/repo/index.yaml ${REPO_ROOT}/helm/repo/new 14 | mv ${REPO_ROOT}/helm/repo/new/index.yaml ${REPO_ROOT}/helm/repo/index.yaml 15 | mv ${REPO_ROOT}/helm/repo/new/*.tgz ${REPO_ROOT}/helm/repo 16 | rm -r ${REPO_ROOT}/helm/repo/new -------------------------------------------------------------------------------- /helm/kube-egress-gateway/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /helm/kube-egress-gateway/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: kube-egress-gateway 3 | description: A Helm chart for installing Azure kube-egress-gateway components 4 | type: application 5 | maintainers: 6 | - email: jwtty0919@gmail.com 7 | name: Wantong Jiang 8 | - email: fanshangxiang@gmail.com 9 | name: Shangxiang Fan 10 | - email: mali_no2@hotmail.com 11 | name: Li Ma 12 | sources: 13 | - https://github.com/Azure/kube-egress-gateway 14 | version: 0.1.4 15 | -------------------------------------------------------------------------------- /helm/kube-egress-gateway/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* 2 | Determine gateway-controller-manager image 3 | */}} 4 | {{- define "image.gatewayControllerManager" -}} 5 | {{- if hasKey .Values.gatewayControllerManager "imageTag" -}} 6 | {{- printf "%s/%s:%s" .Values.gatewayControllerManager.imageRepository .Values.gatewayControllerManager.imageName .Values.gatewayControllerManager.imageTag -}} 7 | {{- else -}} 8 | {{- printf "%s/%s:%s" .Values.common.imageRepository .Values.gatewayControllerManager.imageName .Values.common.imageTag -}} 9 | {{- end -}} 10 | {{- end -}} 11 | 12 | {{/* 13 | Determine gateway-daemon-manager image 14 | */}} 15 | {{- define "image.gatewayDaemonManager" -}} 16 | {{- if hasKey .Values.gatewayDaemonManager "imageTag" -}} 17 | {{- printf "%s/%s:%s" .Values.gatewayDaemonManager.imageRepository .Values.gatewayDaemonManager.imageName .Values.gatewayDaemonManager.imageTag -}} 18 | {{- else -}} 19 | {{- printf "%s/%s:%s" .Values.common.imageRepository .Values.gatewayDaemonManager.imageName .Values.common.imageTag -}} 20 | {{- end -}} 21 | {{- end -}} 22 | 23 | {{/* 24 | Determine gateway-daemon-manager image 25 | */}} 26 | {{- define "image.gatewayDaemonManagerInit" -}} 27 | {{- if hasKey .Values.gatewayDaemonManagerInit "imageTag" -}} 28 | {{- printf "%s/%s:%s" .Values.gatewayDaemonManagerInit.imageRepository .Values.gatewayDaemonManagerInit.imageName .Values.gatewayDaemonManagerInit.imageTag -}} 29 | {{- else -}} 30 | {{- printf "%s/%s:%s" .Values.common.imageRepository .Values.gatewayDaemonManagerInit.imageName .Values.common.imageTag -}} 31 | {{- end -}} 32 | {{- end -}} 33 | 34 | {{/* 35 | Determine gateway-CNI-manager image 36 | */}} 37 | {{- define "image.gatewayCNIManager" -}} 38 | {{- if hasKey .Values.gatewayCNIManager "imageTag" -}} 39 | {{- printf "%s/%s:%s" .Values.gatewayCNIManager.imageRepository .Values.gatewayCNIManager.imageName .Values.gatewayCNIManager.imageTag -}} 40 | {{- else -}} 41 | {{- printf "%s/%s:%s" .Values.common.imageRepository .Values.gatewayCNIManager.imageName .Values.common.imageTag -}} 42 | {{- end -}} 43 | {{- end -}} 44 | 45 | {{/* 46 | Determine gateway-CNI image 47 | */}} 48 | {{- define "image.gatewayCNI" -}} 49 | {{- if hasKey .Values.gatewayCNI "imageTag" -}} 50 | {{- printf "%s/%s:%s" .Values.gatewayCNI.imageRepository .Values.gatewayCNI.imageName .Values.gatewayCNI.imageTag -}} 51 | {{- else -}} 52 | {{- printf "%s/%s:%s" .Values.common.imageRepository .Values.gatewayCNI.imageName .Values.common.imageTag -}} 53 | {{- end -}} 54 | {{- end -}} 55 | 56 | {{/* 57 | Determine gateway-CNI-Ipam image 58 | */}} 59 | {{- define "image.gatewayCNIIpam" -}} 60 | {{- if hasKey .Values.gatewayCNIIpam "imageTag" -}} 61 | {{- printf "%s/%s:%s" .Values.gatewayCNIIpam.imageRepository .Values.gatewayCNIIpam.imageName .Values.gatewayCNIIpam.imageTag -}} 62 | {{- else -}} 63 | {{- printf "%s/%s:%s" .Values.common.imageRepository .Values.gatewayCNIIpam.imageName .Values.common.imageTag -}} 64 | {{- end -}} 65 | {{- end -}} 66 | -------------------------------------------------------------------------------- /helm/kube-egress-gateway/templates/azure-config-secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: kube-egress-gateway-azure-cloud-config 5 | namespace: {{ .Release.Namespace }} 6 | type: Opaque 7 | data: 8 | azure-cloud-config.json: {{ .Values.config.azureCloudConfig | toJson | indent 4 | b64enc | quote }} -------------------------------------------------------------------------------- /helm/kube-egress-gateway/templates/cni-uninstall-configmap.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.gatewayCNIManager.enabled }} 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | name: {{ .Values.gatewayCNIManager.cniUninstallConfigMapName }} 6 | namespace: {{ .Release.Namespace }} 7 | data: 8 | uninstall: {{ printf "%t" .Values.gatewayCNIManager.cniUninstall | quote }} 9 | {{- end }} 10 | -------------------------------------------------------------------------------- /helm/kube-egress-gateway/values.yaml: -------------------------------------------------------------------------------- 1 | common: 2 | imageRepository: "local" 3 | imageTag: "test" 4 | gatewayLbProbePort: 8082 5 | 6 | gatewayControllerManager: 7 | enabled: true 8 | # imageRepository: "local" 9 | imageName: "kube-egress-gateway-controller" 10 | # imageTag: "" 11 | imagePullPolicy: "IfNotPresent" 12 | replicas: 1 13 | leaderElect: "true" 14 | metricsBindPort: 8080 15 | healthProbeBindPort: 8081 16 | nodeSelector: {} 17 | tolerations: [] 18 | 19 | gatewayCNIManager: 20 | enabled: true 21 | # imageRepository: "local" 22 | imageName: "kube-egress-gateway-cnimanager" 23 | # imageTag: "" 24 | imagePullPolicy: "IfNotPresent" 25 | grpcServerPort: 50051 26 | exceptionCidrs: 27 | - "" 28 | cniConfigFileName: "01-egressgateway.conflist" 29 | cniUninstallConfigMapName: "cni-uninstall" 30 | cniUninstall: false 31 | nodeSelector: 32 | kubernetes.io/os: linux 33 | tolerations: [] 34 | 35 | gatewayDaemonManager: 36 | enabled: true 37 | # imageRepository: "local" 38 | imageName: "kube-egress-gateway-daemon" 39 | # imageTag: "" 40 | imagePullPolicy: "IfNotPresent" 41 | metricsBindPort: 8080 42 | healthProbeBindPort: 8081 43 | 44 | gatewayDaemonManagerInit: 45 | # imageRepository: "local" 46 | imageName: "kube-egress-gateway-daemon-init" 47 | # imageTag: "" 48 | imagePullPolicy: "IfNotPresent" 49 | 50 | gatewayCNI: 51 | # imageRepository: "local" 52 | imageName: "kube-egress-gateway-cni" 53 | # imageTag: "" 54 | imagePullPolicy: "IfNotPresent" 55 | 56 | gatewayCNIIpam: 57 | # imageRepository: "local" 58 | imageName: "kube-egress-gateway-cni-ipam" 59 | # imageTag: "" 60 | imagePullPolicy: "IfNotPresent" 61 | 62 | config: 63 | azureCloudConfig: 64 | cloud: "AzurePublicCloud" 65 | tenantId: "" 66 | subscriptionId: "" 67 | useManagedIdentityExtension: false 68 | userAssignedIdentityID: "" 69 | aadClientId: "" 70 | aadClientSecret: "" 71 | userAgent: "kube-egress-gateway-controller" 72 | resourceGroup: "" 73 | location: "" 74 | gatewayLoadBalancerName: "kubeegressgateway-ilb" 75 | loadBalancerResourceGroup: "" 76 | vnetName: "" 77 | vnetResourceGroup: "" 78 | subnetName: "" 79 | -------------------------------------------------------------------------------- /helm/repo/index.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | entries: 3 | kube-egress-gateway: 4 | - apiVersion: v2 5 | created: "2024-09-02T05:25:38.764549908Z" 6 | description: A Helm chart for installing Azure kube-egress-gateway components 7 | digest: 2452902896003cb53eb21532ff07d08c3d2cffd8474cb76c697ce7db3a75e12e 8 | maintainers: 9 | - email: jwtty0919@gmail.com 10 | name: Wantong Jiang 11 | - email: fanshangxiang@gmail.com 12 | name: Shangxiang Fan 13 | - email: mali_no2@hotmail.com 14 | name: Li Ma 15 | name: kube-egress-gateway 16 | sources: 17 | - https://github.com/Azure/kube-egress-gateway 18 | type: application 19 | urls: 20 | - kube-egress-gateway-0.1.4.tgz 21 | version: 0.1.4 22 | - apiVersion: v2 23 | created: "2024-08-05T06:45:54.269241169Z" 24 | description: A Helm chart for installing Azure kube-egress-gateway components 25 | digest: dec258bb39f9fc167a674707eb2624a96402eee95c754c0018bfd93fe52979a6 26 | maintainers: 27 | - email: jwtty0919@gmail.com 28 | name: Wantong Jiang 29 | - email: fanshangxiang@gmail.com 30 | name: Shangxiang Fan 31 | - email: mali_no2@hotmail.com 32 | name: Li Ma 33 | name: kube-egress-gateway 34 | sources: 35 | - https://github.com/Azure/kube-egress-gateway 36 | type: application 37 | urls: 38 | - kube-egress-gateway-0.1.3.tgz 39 | version: 0.1.3 40 | - apiVersion: v2 41 | created: "2024-03-09T16:46:15.139200257Z" 42 | description: A Helm chart for installing Azure kube-egress-gateway components 43 | digest: 5c3d7c12455897d2e24fa27fef9f9df79d9882da9db1c99ef2850af0f4c7e87e 44 | maintainers: 45 | - email: jwtty0919@gmail.com 46 | name: Wantong Jiang 47 | - email: fanshangxiang@gmail.com 48 | name: Shangxiang Fan 49 | - email: mali_no2@hotmail.com 50 | name: Li Ma 51 | name: kube-egress-gateway 52 | sources: 53 | - https://github.com/Azure/kube-egress-gateway 54 | type: application 55 | urls: 56 | - kube-egress-gateway-0.1.2.tgz 57 | version: 0.1.2 58 | - apiVersion: v2 59 | created: "2024-01-15T10:24:05.345702405Z" 60 | description: A Helm chart for installing Azure kube-egress-gateway components 61 | digest: 32fb0dd0c6a3b28a4dc3b2bb981d1dfa356ab192370ef92bdbbe8355b2263506 62 | maintainers: 63 | - email: jwtty0919@gmail.com 64 | name: Wantong Jiang 65 | - email: fanshangxiang@gmail.com 66 | name: Shangxiang Fan 67 | - email: mali_no2@hotmail.com 68 | name: Li Ma 69 | name: kube-egress-gateway 70 | sources: 71 | - https://github.com/Azure/kube-egress-gateway 72 | type: application 73 | urls: 74 | - kube-egress-gateway-0.1.1.tgz 75 | version: 0.1.1 76 | - apiVersion: v2 77 | created: "2023-12-20T07:57:40.425052071Z" 78 | description: A Helm chart for installing Azure kube-egress-gateway components 79 | digest: 806bcbd2e96b4a6bfbbecf08cdfb8d75f86cf99bb609969f5bfc46c4cbabbb04 80 | maintainers: 81 | - email: jwtty0919@gmail.com 82 | name: Wantong Jiang 83 | - email: fanshangxiang@gmail.com 84 | name: Shangxiang Fan 85 | - email: mali_no2@hotmail.com 86 | name: Li Ma 87 | name: kube-egress-gateway 88 | sources: 89 | - https://github.com/Azure/kube-egress-gateway 90 | type: application 91 | urls: 92 | - kube-egress-gateway-0.1.0.tgz 93 | version: 0.1.0 94 | generated: "2024-09-02T05:25:38.763910599Z" 95 | -------------------------------------------------------------------------------- /helm/repo/kube-egress-gateway-0.1.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/kube-egress-gateway/932bd9f8c2b9e0341dab32574a96571b6b9b338c/helm/repo/kube-egress-gateway-0.1.0.tgz -------------------------------------------------------------------------------- /helm/repo/kube-egress-gateway-0.1.1.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/kube-egress-gateway/932bd9f8c2b9e0341dab32574a96571b6b9b338c/helm/repo/kube-egress-gateway-0.1.1.tgz -------------------------------------------------------------------------------- /helm/repo/kube-egress-gateway-0.1.2.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/kube-egress-gateway/932bd9f8c2b9e0341dab32574a96571b6b9b338c/helm/repo/kube-egress-gateway-0.1.2.tgz -------------------------------------------------------------------------------- /helm/repo/kube-egress-gateway-0.1.3.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/kube-egress-gateway/932bd9f8c2b9e0341dab32574a96571b6b9b338c/helm/repo/kube-egress-gateway-0.1.3.tgz -------------------------------------------------------------------------------- /helm/repo/kube-egress-gateway-0.1.4.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure/kube-egress-gateway/932bd9f8c2b9e0341dab32574a96571b6b9b338c/helm/repo/kube-egress-gateway-0.1.4.tgz -------------------------------------------------------------------------------- /pkg/cni/conf/conf.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | package conf 4 | 5 | import ( 6 | "encoding/json" 7 | "fmt" 8 | 9 | "github.com/containernetworking/cni/pkg/types" 10 | "github.com/containernetworking/cni/pkg/version" 11 | ) 12 | 13 | type CNIConfig struct { 14 | types.NetConf 15 | 16 | ExcludedCIDRs []string `json:"excludedCIDRs"` 17 | SocketPath string `json:"socketPath"` 18 | } 19 | 20 | func ParseCNIConfig(stdin []byte) (*CNIConfig, error) { 21 | conf := &CNIConfig{} 22 | if err := json.Unmarshal(stdin, &conf); err != nil { 23 | return nil, fmt.Errorf("failed to parse network configuration: %v", err) 24 | } 25 | 26 | // Parse previous result. This will parse, validate, and place the 27 | // previous result object into conf.PrevResult. If you need to modify 28 | // or inspect the PrevResult you will need to convert it to a concrete 29 | // versioned Result struct. 30 | if err := version.ParsePrevResult(&conf.NetConf); err != nil { 31 | return nil, fmt.Errorf("could not parse prevResult: %v", err) 32 | } 33 | return conf, nil 34 | } 35 | 36 | // K8sArgs is the valid CNI_ARGS used for Kubernetes 37 | 38 | type K8sConfig struct { 39 | types.CommonArgs 40 | K8S_POD_NAME types.UnmarshallableString 41 | K8S_POD_NAMESPACE types.UnmarshallableString 42 | K8S_POD_INFRA_CONTAINER_ID types.UnmarshallableString 43 | } 44 | 45 | func LoadK8sInfo(args string) (*K8sConfig, error) { 46 | k8sInfo := &K8sConfig{} 47 | if err := types.LoadArgs(args, k8sInfo); err != nil { 48 | return k8sInfo, err 49 | } 50 | return k8sInfo, nil 51 | } 52 | -------------------------------------------------------------------------------- /pkg/cni/conf/conf_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | package conf 4 | 5 | import ( 6 | "reflect" 7 | "testing" 8 | 9 | "github.com/containernetworking/cni/pkg/types" 10 | ) 11 | 12 | func TestParseCNIConfig(t *testing.T) { 13 | tests := map[string]struct { 14 | StdinData string 15 | Expected *CNIConfig 16 | }{ 17 | "test cni config without prevResult": { 18 | StdinData: `{"cniVersion":"1.0.0","excludedCIDRs":["1.2.3.4/32","10.1.0.0/16"],"socketPath":"testPath","gatewayName":"test","ipam":{"type":"kube-egress-cni-ipam"},"name":"mynet","type":"kube-egress-cni"}`, 19 | Expected: &CNIConfig{ 20 | NetConf: types.NetConf{ 21 | CNIVersion: "1.0.0", 22 | Name: "mynet", 23 | Type: "kube-egress-cni", 24 | IPAM: types.IPAM{Type: "kube-egress-cni-ipam"}, 25 | }, 26 | ExcludedCIDRs: []string{"1.2.3.4/32", "10.1.0.0/16"}, 27 | SocketPath: "testPath", 28 | }, 29 | }, 30 | "test cni config with preResult": { 31 | StdinData: `{"cniVersion":"1.0.0","excludedCIDRs":[],"socketPath":"testPath","gatewayName":"test","ipam":{"type":"kube-egress-cni-ipam"},"name":"mynet","prevResult":{"cniVersion":"1.0.0","interfaces":[{"name":"wg0","sandbox":"somepath"}],"ips":[{"interface":0,"address":"fe80::1/64"},{"address":"10.2.0.1/24"}],"dns":{}},"type":"kube-egress-cni"}`, 32 | Expected: &CNIConfig{ 33 | NetConf: types.NetConf{ 34 | CNIVersion: "1.0.0", 35 | Name: "mynet", 36 | Type: "kube-egress-cni", 37 | IPAM: types.IPAM{Type: "kube-egress-cni-ipam"}, 38 | }, 39 | ExcludedCIDRs: []string{}, 40 | SocketPath: "testPath", 41 | }, 42 | }, 43 | } 44 | 45 | for name, test := range tests { 46 | t.Run(name, func(t *testing.T) { 47 | res, err := ParseCNIConfig([]byte(test.StdinData)) 48 | if err != nil { 49 | t.Fatalf("failed to parse CNI config: %v", err) 50 | } 51 | if !reflect.DeepEqual(test.Expected.ExcludedCIDRs, res.ExcludedCIDRs) || 52 | test.Expected.CNIVersion != res.CNIVersion || 53 | test.Expected.Name != res.Name || 54 | test.Expected.Type != res.Type || 55 | test.Expected.IPAM != res.IPAM { 56 | t.Fatalf("got different cniConfig from ParseCNIConfig, expected: %#v, got: %#v", *test.Expected, *res) 57 | } 58 | }) 59 | } 60 | } 61 | 62 | func TestLoadK8sInfo(t *testing.T) { 63 | testArg := `IgnoreUnknown=true;K8S_POD_NAMESPACE=testns;K8S_POD_NAME=testpod;K8S_POD_INFRA_CONTAINER_ID=1234567890;K8S_POD_UID=12345678-1234-1234-1234-123456789012` 64 | expected := &K8sConfig{ 65 | CommonArgs: types.CommonArgs{IgnoreUnknown: types.UnmarshallableBool(true)}, 66 | K8S_POD_NAME: types.UnmarshallableString("testpod"), 67 | K8S_POD_NAMESPACE: types.UnmarshallableString("testns"), 68 | K8S_POD_INFRA_CONTAINER_ID: types.UnmarshallableString("1234567890"), 69 | } 70 | res, err := LoadK8sInfo(testArg) 71 | if err != nil { 72 | t.Fatalf("unexpected error when loading k8s information: %v", err) 73 | } 74 | if !reflect.DeepEqual(res, expected) { 75 | t.Fatalf("got different k8sConfig from LoadK8sInfo, expected: %#v, got: %#v", *expected, *res) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /pkg/cni/conf/testdata/10-test.conf: -------------------------------------------------------------------------------- 1 | { 2 | "cniVersion": "0.3.1", 3 | "name": "testnet", 4 | "type": "bridge", 5 | "bridge": "cbr0", 6 | "mtu": 1500, 7 | "addIf": "eth0", 8 | "isGateway": true, 9 | "ipMasq": false, 10 | "promiscMode": true, 11 | "hairpinMode": false, 12 | "ipam": { 13 | "type": "host-local", 14 | "ranges": [[{"subnet": "10.1.0.0/24"}]], 15 | "routes": [{"dst": "0.0.0.0/0"}] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /pkg/cni/conf/testdata/10-test.conflist: -------------------------------------------------------------------------------- 1 | { 2 | "cniVersion": "0.3.1", 3 | "name": "testnet", 4 | "plugins": [{ 5 | "type": "bridge", 6 | "bridge": "cbr0", 7 | "mtu": 1500, 8 | "addIf": "eth0", 9 | "isGateway": true, 10 | "ipMasq": false, 11 | "promiscMode": true, 12 | "hairpinMode": false, 13 | "ipam": { 14 | "type": "host-local", 15 | "ranges": [[{"subnet": "10.1.0.0/24"}]], 16 | "routes": [{"dst": "0.0.0.0/0"}] 17 | } 18 | }, 19 | { 20 | "type": "portmap", 21 | "capabilities": {"portMappings": true}, 22 | "externalSetMarkChain": "KUBE-MARK-MASQ" 23 | }] 24 | } 25 | -------------------------------------------------------------------------------- /pkg/cni/conf/testdata/20-fake.conflist: -------------------------------------------------------------------------------- 1 | { 2 | "cniVersion": "0.3.1", 3 | "name": "testnet", 4 | "plugins": [] 5 | } 6 | -------------------------------------------------------------------------------- /pkg/cni/conf/testdata/30-fake.conflist: -------------------------------------------------------------------------------- 1 | { 2 | "cniVersion": "0.3.1", 3 | "name": "testnet" 4 | } 5 | -------------------------------------------------------------------------------- /pkg/cni/conf/testdata/40-fake.conflist: -------------------------------------------------------------------------------- 1 | { 2 | "cniVersion": "0.3.1", 3 | "name": "testnet", 4 | "plugins": [{ 5 | "bridge": "cbr0", 6 | "mtu": 1500, 7 | "addIf": "eth0", 8 | "isGateway": true, 9 | "ipMasq": false, 10 | "promiscMode": true, 11 | "hairpinMode": false, 12 | "ipam": { 13 | "type": "host-local", 14 | "ranges": [[{"subnet": "10.1.0.0/24"}]], 15 | "routes": [{"dst": "0.0.0.0/0"}] 16 | } 17 | }] 18 | } 19 | -------------------------------------------------------------------------------- /pkg/cni/ipam/delegate.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | package ipam 4 | 5 | import ( 6 | "errors" 7 | "fmt" 8 | 9 | current "github.com/containernetworking/cni/pkg/types/100" 10 | "github.com/containernetworking/plugins/pkg/ipam" 11 | ) 12 | 13 | type IPWrapper struct { 14 | pluginType string 15 | netConf []byte 16 | } 17 | 18 | type IPProvider interface { 19 | WithIP(configFunc func(ipamResult *current.Result) error) (err error) 20 | DeleteIP() error 21 | } 22 | 23 | func New(pluginType string, netConf []byte) IPProvider { 24 | return &IPWrapper{ 25 | pluginType: pluginType, 26 | netConf: netConf, 27 | } 28 | } 29 | 30 | func (wrapper *IPWrapper) WithIP(configFunc func(ipamResult *current.Result) error) (err error) { 31 | r, err := ipam.ExecAdd(wrapper.pluginType, wrapper.netConf) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | // release IP in case of failure 37 | defer func() { 38 | if err != nil { 39 | recoverErr := wrapper.DeleteIP() 40 | if recoverErr != nil { 41 | err = fmt.Errorf("error occurred %w and failed to delete ip: %s", err, recoverErr.Error()) 42 | } 43 | } 44 | }() 45 | ipamResult, err := current.NewResultFromResult(r) 46 | if err != nil { 47 | return err 48 | } 49 | 50 | if configFunc == nil { 51 | return errors.New("configure function is nil") 52 | } 53 | 54 | return configFunc(ipamResult) 55 | } 56 | 57 | func (wrapper *IPWrapper) DeleteIP() error { 58 | return ipam.ExecDel(wrapper.pluginType, wrapper.netConf) 59 | } 60 | -------------------------------------------------------------------------------- /pkg/cni/ipam/fake.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | package ipam 4 | 5 | import current "github.com/containernetworking/cni/pkg/types/100" 6 | 7 | type fakeIPProvider struct { 8 | result current.Result 9 | } 10 | 11 | func NewFakeIPProvider(ipamResult *current.Result) IPProvider { 12 | return &fakeIPProvider{result: *ipamResult} 13 | } 14 | 15 | func (fake *fakeIPProvider) WithIP(configFunc func(ipamResult *current.Result) error) (err error) { 16 | return configFunc(&fake.result) 17 | } 18 | 19 | func (*fakeIPProvider) DeleteIP() error { 20 | return nil 21 | } 22 | -------------------------------------------------------------------------------- /pkg/cni/wireguard/nic_suite_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | package wireguard 4 | 5 | import ( 6 | "testing" 7 | 8 | . "github.com/onsi/ginkgo/v2" 9 | . "github.com/onsi/gomega" 10 | ) 11 | 12 | func TestWiregurad(t *testing.T) { 13 | RegisterFailHandler(Fail) 14 | RunSpecs(t, "pkg/cni/wireguard") 15 | } 16 | -------------------------------------------------------------------------------- /pkg/cniprotocol/doc.go: -------------------------------------------------------------------------------- 1 | package cniprotocol 2 | -------------------------------------------------------------------------------- /pkg/cniprotocol/v1/cni.proto: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | syntax = "proto3"; 5 | 6 | option go_package = "github.com/Azure/kube-egress-gateway/pkg/cniprotocol/v1"; 7 | package pkg.cniprotocol.v1; 8 | 9 | message PodInfo{ 10 | string pod_name = 1; 11 | string pod_namespace = 2; 12 | } 13 | 14 | enum DefaultRoute { 15 | DEFAULT_ROUTE_UNSPECIFIED = 0; 16 | DEFAULT_ROUTE_STATIC_EGRESS_GATEWAY = 1; 17 | DEFAULT_ROUTE_AZURE_NETWORKING = 2; 18 | } 19 | 20 | // CNIAddRequest is the request for cni add function. 21 | message NicAddRequest { 22 | PodInfo pod_config = 1; 23 | int32 listen_port = 2; 24 | string allowed_ip = 3; 25 | string public_key = 4; 26 | string gateway_name = 5; 27 | } 28 | 29 | // CNIAddResponse is the response for cni add function. 30 | message NicAddResponse { 31 | string endpoint_ip = 1; 32 | int32 listen_port = 2; 33 | string public_key = 3; 34 | repeated string exception_cidrs = 4; 35 | DefaultRoute default_route = 5; 36 | } 37 | 38 | // CNIDeleteRequest is the request for cni del function. 39 | message NicDelRequest { 40 | PodInfo pod_config = 1; 41 | } 42 | 43 | // CNIDeleteResponse is the response for cni del function. 44 | message NicDelResponse { 45 | } 46 | 47 | // PodRetrieveRequest is the request for retrieving pod function. 48 | message PodRetrieveRequest { 49 | PodInfo pod_config = 1; 50 | } 51 | 52 | // PodRetrieveResponse is the response for retrieving pod function. 53 | message PodRetrieveResponse { 54 | map annotations = 1; 55 | } 56 | 57 | // NicService is the nic management service. 58 | service NicService { 59 | // NicAdd: send pod public key and return gateway public key and endpoint ip 60 | rpc NicAdd(NicAddRequest) returns (NicAddResponse) ; 61 | 62 | // NicDel: delete pod endpoint resource 63 | rpc NicDel(NicDelRequest) returns (NicDelResponse) ; 64 | 65 | // PodRetrieve: send pod information and return pod information 66 | rpc PodRetrieve(PodRetrieveRequest) returns (PodRetrieveResponse) ; 67 | } 68 | 69 | -------------------------------------------------------------------------------- /pkg/cniprotocol/v1/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | package v1 4 | -------------------------------------------------------------------------------- /pkg/cniprotocol/v1/test_server.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | package v1 5 | 6 | import ( 7 | "context" 8 | "fmt" 9 | "net" 10 | "os" 11 | 12 | "google.golang.org/grpc" 13 | ) 14 | 15 | const ( 16 | TEST_SERVER_WIREGUARD_SERVER_IP = "10.2.0.4" 17 | TEST_SERVER_WIREGUARD_SERVER_PORT int32 = 6000 18 | TEST_SERVER_WIREGUARD_PUBLIC_KEY = "aPxGwq8zERHQ3Q1cOZFdJ+cvJX5Ka4mLN38AyYKYF10=" 19 | TEST_SERVER_WIREGUARD_PRIVATE_KEY = "GHuMwljFfqd2a7cs6BaUOmHflK23zME8VNvC5B37S3k=" 20 | ) 21 | 22 | type TestServer struct { 23 | Received chan interface{} 24 | UnimplementedNicServiceServer 25 | 26 | lis net.Listener 27 | grpcServer *grpc.Server 28 | podAnnotations map[string]string 29 | exceptionCidrs []string 30 | } 31 | 32 | func (s *TestServer) NicAdd(ctx context.Context, in *NicAddRequest) (*NicAddResponse, error) { 33 | s.Received <- in 34 | return &NicAddResponse{ 35 | EndpointIp: TEST_SERVER_WIREGUARD_SERVER_IP, 36 | ListenPort: TEST_SERVER_WIREGUARD_SERVER_PORT, 37 | PublicKey: TEST_SERVER_WIREGUARD_PUBLIC_KEY, 38 | ExceptionCidrs: s.exceptionCidrs, 39 | }, nil 40 | } 41 | 42 | func (s *TestServer) NicDel(ctx context.Context, in *NicDelRequest) (*NicDelResponse, error) { 43 | s.Received <- in 44 | return &NicDelResponse{}, nil 45 | } 46 | 47 | func (s *TestServer) PodRetrieve(ctx context.Context, in *PodRetrieveRequest) (*PodRetrieveResponse, error) { 48 | s.Received <- in 49 | return &PodRetrieveResponse{Annotations: s.podAnnotations}, nil 50 | } 51 | 52 | func (s *TestServer) GracefulStop() { 53 | s.grpcServer.GracefulStop() 54 | s.lis.Close() 55 | } 56 | 57 | func (s *TestServer) startServer() { 58 | err := s.grpcServer.Serve(s.lis) 59 | if err != nil { 60 | fmt.Fprintf(os.Stderr, "failed to start grpc server: %v", err) 61 | } 62 | } 63 | 64 | func StartTestServer(addr string, exceptionCidrs []string, podAnnotations map[string]string) (s *TestServer, err error) { 65 | s = &TestServer{ 66 | Received: make(chan interface{}, 2), 67 | grpcServer: grpc.NewServer(), 68 | podAnnotations: podAnnotations, 69 | exceptionCidrs: exceptionCidrs, 70 | } 71 | 72 | s.lis, err = net.Listen("tcp", addr) 73 | if err != nil { 74 | return nil, fmt.Errorf("failed to listen on tcp addr %s: %v", addr, err) 75 | } 76 | 77 | RegisterNicServiceServer(s.grpcServer, s) 78 | go s.startServer() 79 | return s, nil 80 | } 81 | -------------------------------------------------------------------------------- /pkg/config/config.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | package config 4 | 5 | import ( 6 | "fmt" 7 | "strings" 8 | 9 | "sigs.k8s.io/cloud-provider-azure/pkg/azclient" 10 | "sigs.k8s.io/cloud-provider-azure/pkg/azclient/policy/ratelimit" 11 | 12 | "github.com/Azure/kube-egress-gateway/pkg/consts" 13 | ) 14 | 15 | type RateLimitConfig ratelimit.Config 16 | type CloudConfig struct { 17 | azclient.ARMClientConfig `json:",inline" mapstructure:",squash"` 18 | azclient.AzureAuthConfig `json:",inline" mapstructure:",squash"` 19 | *RateLimitConfig `json:",inline" mapstructure:",squash"` 20 | // azure resource location 21 | Location string `json:"location,omitempty" mapstructure:"location,omitempty"` 22 | // subscription ID 23 | SubscriptionID string `json:"subscriptionID,omitempty" mapstructure:"subscriptionID,omitempty"` 24 | // default resource group where the gateway nodes are deployed 25 | ResourceGroup string `json:"resourceGroup,omitempty" mapstructure:"resourceGroup,omitempty"` 26 | // name of the gateway ILB 27 | LoadBalancerName string `json:"gatewayLoadBalancerName,omitempty" mapstructure:"gatewayLoadBalancerName,omitempty"` 28 | // resource group where the gateway ILB belongs 29 | LoadBalancerResourceGroup string `json:"loadBalancerResourceGroup,omitempty" mapstructure:"loadBalancerResourceGroup,omitempty"` 30 | // name of the virtual network where the gateway ILB is deployed 31 | VnetName string `json:"vnetName,omitempty" mapstructure:"vnetName,omitempty"` 32 | // name of the resource group where the virtual network is deployed 33 | VnetResourceGroup string `json:"vnetResourceGroup,omitempty" mapstructure:"vnetResourceGroup,omitempty"` 34 | // name of the subnet in the vnet where the gateway ILB is deployed 35 | SubnetName string `json:"subnetName,omitempty" mapstructure:"subnetName,omitempty"` 36 | // Enable exponential backoff to manage resource request retries 37 | CloudProviderBackoff bool `json:"cloudProviderBackoff,omitempty" mapstructure:"cloudProviderBackoff,omitempty"` 38 | } 39 | 40 | func (cfg *CloudConfig) DefaultAndValidate() error { 41 | cfg.trimSpace() 42 | 43 | if cfg.Cloud == "" { 44 | return fmt.Errorf("cloud is empty") 45 | } 46 | 47 | if cfg.Location == "" { 48 | return fmt.Errorf("location is empty") 49 | } 50 | 51 | if cfg.SubscriptionID == "" { 52 | return fmt.Errorf("subscription ID is empty") 53 | } 54 | 55 | if !cfg.UseManagedIdentityExtension { 56 | if cfg.UserAssignedIdentityID != "" { 57 | return fmt.Errorf("useManagedIdentityExtension needs to be true when userAssignedIdentityID is provided") 58 | } 59 | if cfg.AADClientID == "" || cfg.AADClientSecret == "" { 60 | return fmt.Errorf("AAD client ID or AAD client secret is empty") 61 | } 62 | } 63 | 64 | if cfg.ResourceGroup == "" { 65 | return fmt.Errorf("resource group is empty") 66 | } 67 | 68 | if cfg.VnetName == "" { 69 | return fmt.Errorf("virtual network name is empty") 70 | } 71 | 72 | if cfg.SubnetName == "" { 73 | return fmt.Errorf("virtual network subnet name is empty") 74 | } 75 | 76 | // default values 77 | if cfg.UserAgent == "" { 78 | cfg.UserAgent = consts.DefaultUserAgent 79 | } 80 | 81 | if cfg.LoadBalancerResourceGroup == "" { 82 | cfg.LoadBalancerResourceGroup = cfg.ResourceGroup 83 | } 84 | 85 | if cfg.VnetResourceGroup == "" { 86 | cfg.VnetResourceGroup = cfg.ResourceGroup 87 | } 88 | 89 | if cfg.RateLimitConfig == nil { 90 | cfg.RateLimitConfig = &RateLimitConfig{ 91 | CloudProviderRateLimit: false, 92 | } 93 | } 94 | 95 | return nil 96 | } 97 | 98 | func (cfg *CloudConfig) trimSpace() { 99 | cfg.Cloud = strings.TrimSpace(cfg.Cloud) 100 | cfg.Location = strings.TrimSpace(cfg.Location) 101 | cfg.SubscriptionID = strings.TrimSpace(cfg.SubscriptionID) 102 | cfg.TenantID = strings.TrimSpace(cfg.TenantID) 103 | cfg.UserAssignedIdentityID = strings.TrimSpace(cfg.UserAssignedIdentityID) 104 | cfg.AADClientID = strings.TrimSpace(cfg.AADClientID) 105 | cfg.AADClientSecret = strings.TrimSpace(cfg.AADClientSecret) 106 | cfg.UserAgent = strings.TrimSpace(cfg.UserAgent) 107 | cfg.ResourceGroup = strings.TrimSpace(cfg.ResourceGroup) 108 | cfg.LoadBalancerName = strings.TrimSpace(cfg.LoadBalancerName) 109 | cfg.LoadBalancerResourceGroup = strings.TrimSpace(cfg.LoadBalancerResourceGroup) 110 | cfg.VnetName = strings.TrimSpace(cfg.VnetName) 111 | cfg.VnetResourceGroup = strings.TrimSpace(cfg.VnetResourceGroup) 112 | cfg.SubnetName = strings.TrimSpace(cfg.SubnetName) 113 | } 114 | -------------------------------------------------------------------------------- /pkg/consts/const.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | package consts 4 | 5 | const ( 6 | // StaticGatewayConfiguration finalizer name 7 | SGCFinalizerName = "static-gateway-configuration-controller.microsoft.com" 8 | 9 | // GatewayLBConfiguration finalizer name 10 | LBConfigFinalizerName = "gateway-lb-configuration-controller.microsoft.com" 11 | 12 | // GatewayVMConfiguration finalizer name 13 | VMConfigFinalizerName = "gateway-vm-configuration-controller.microsoft.com" 14 | 15 | // Default gateway LoadBalancer name 16 | DefaultGatewayLBName = "kubeegressgateway-ilb" 17 | 18 | // Prefix for managed Azure resources (public IPPrefix, VMSS ipConfig, etc) 19 | ManagedResourcePrefix = "egressgateway-" 20 | 21 | // Key name in the wireugard private key secret 22 | WireguardPrivateKeyName = "PrivateKey" 23 | 24 | // Key name in the wireugard private key secret 25 | WireguardPublicKeyName = "PublicKey" 26 | 27 | // Wireguard listening port range start, inclusive 28 | WireguardPortStart int32 = 6000 29 | 30 | // Wireguard listening port range end, exclusive 31 | WireguardPortEnd int32 = 7000 32 | 33 | // Gateway lb health probe path 34 | GatewayHealthProbeEndpoint = "/gw/" 35 | 36 | // nodepool name tag key in aks clusters 37 | AKSNodepoolTagKey = "aks-managed-poolName" 38 | 39 | // nodepool name label key in aks clusters 40 | AKSNodepoolNameLabel = "kubernetes.azure.com/agentpool" 41 | 42 | // nodepool mode label key in aks clusters 43 | AKSNodepoolModeLabel = "kubernetes.azure.com/mode" 44 | 45 | // nodepool mode label value for upstream usage 46 | UpstreamNodepoolModeLabel = "kubeegressgateway.azure.com/mode" 47 | 48 | // nodepool mode label value in aks clusters 49 | AKSNodepoolModeValue = "gateway" 50 | 51 | // gateway nodepool ip prefix size tag key in aks clusters 52 | AKSNodepoolIPPrefixSizeTagKey = "aks-managed-gatewayIPPrefixSize" 53 | 54 | // Owning StaticGatewayConfiguration namespace key on secret label 55 | OwningSGCNamespaceLabel = "egressgateway.kubernetes.azure.com/owning-gateway-config-namespace" 56 | 57 | // Owning StaticGatewayConfiguration name key on secret label 58 | OwningSGCNameLabel = "egressgateway.kubernetes.azure.com/owning-gateway-config-name" 59 | 60 | // Default user agent for Azure SDK 61 | DefaultUserAgent = "kube-egress-gateway-controller" 62 | ) 63 | 64 | const ( 65 | // gateway network namespace name 66 | GatewayNetnsName = "ns-static-egress-gateway" 67 | 68 | // wireguard link name in gateway namespace 69 | WireguardLinkName = "wg0" 70 | 71 | // wireguard link name prefix in gateway namespace 72 | WiregaurdLinkNamePrefix = "wg-" 73 | 74 | // host veth pair link name in host namespace 75 | HostVethLinkName = "host-gateway" 76 | 77 | // host link name in gateway namespace 78 | HostLinkName = "host0" 79 | 80 | // gateway IP 81 | GatewayIP = "fe80::1/64" 82 | 83 | // post routing chain name 84 | PostRoutingChain = "POSTROUTING" 85 | 86 | // pre routing chain name 87 | PreRoutingChain = "PREROUTING" 88 | 89 | // output chain name 90 | OutputChain = "OUTPUT" 91 | 92 | // nat table name 93 | NatTable = "nat" 94 | 95 | // mangle table name 96 | MangleTable = "mangle" 97 | 98 | // environment variable name for pod namespace 99 | PodNamespaceEnvKey = "MY_POD_NAMESPACE" 100 | 101 | // environment variable name for nodeName 102 | NodeNameEnvKey = "MY_NODE_NAME" 103 | 104 | // mark for traffic from eth0 in pod namespace - 0x2222 105 | Eth0Mark int = 8738 106 | 107 | // ilb ip address label 108 | ILBIPLabel = "eth0:egress" 109 | ) 110 | 111 | const ( 112 | CNIConfDir = "/etc/cni/net.d" 113 | 114 | CNIGatewayAnnotationKey = "kubernetes.azure.com/static-gateway-configuration" 115 | 116 | // this taint is applied to AKS nodes when cniManager is not ready 117 | CNIManagerNotReadyTaintKey = "egressgateway.kubernetes.azure.com/cni-not-ready" 118 | ) 119 | 120 | const ( 121 | KubeEgressCNIName = "kube-egress-cni" 122 | KubeEgressIPAMCNIName = "kube-egress-cni-ipam" 123 | ) 124 | -------------------------------------------------------------------------------- /pkg/healthprobe/gw_health.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | package healthprobe 4 | 5 | import ( 6 | "context" 7 | "errors" 8 | "net" 9 | "net/http" 10 | "strconv" 11 | "strings" 12 | "sync" 13 | "time" 14 | 15 | "sigs.k8s.io/controller-runtime/pkg/log" 16 | 17 | "github.com/Azure/kube-egress-gateway/pkg/consts" 18 | ) 19 | 20 | type LBProbeServer struct { 21 | lock sync.RWMutex 22 | activeGateways map[string]bool 23 | listenPort int 24 | } 25 | 26 | func NewLBProbeServer(listenPort int) *LBProbeServer { 27 | return &LBProbeServer{ 28 | activeGateways: make(map[string]bool), 29 | listenPort: listenPort, 30 | } 31 | } 32 | 33 | func (svr *LBProbeServer) Start(ctx context.Context) error { 34 | log := log.FromContext(ctx) 35 | 36 | mux := http.NewServeMux() 37 | mux.HandleFunc(consts.GatewayHealthProbeEndpoint, svr.serveHTTP) 38 | 39 | httpServer := &http.Server{ 40 | Addr: net.JoinHostPort("", strconv.Itoa(svr.listenPort)), 41 | Handler: mux, 42 | MaxHeaderBytes: 1 << 20, 43 | IdleTimeout: 90 * time.Second, // matches http.DefaultTransport keep-alive timeout 44 | ReadHeaderTimeout: 32 * time.Second, 45 | } 46 | 47 | go func() { 48 | log.Info("Starting gateway lb health probe server") 49 | if err := httpServer.ListenAndServe(); err != nil { 50 | if errors.Is(err, http.ErrServerClosed) { 51 | return 52 | } 53 | log.Error(err, "failed to start gateway lb health probe server") 54 | } 55 | }() 56 | 57 | // Shutdown the server when stop is closed. 58 | <-ctx.Done() 59 | log.Info("Stopping gateway lb health probe server") 60 | if err := httpServer.Close(); err != nil { 61 | log.Error(err, "failed to close gateway lb health probe server") 62 | return err 63 | } 64 | return nil 65 | } 66 | 67 | func (svr *LBProbeServer) AddGateway(gatewayUID string) error { 68 | svr.lock.Lock() 69 | defer svr.lock.Unlock() 70 | 71 | svr.activeGateways[gatewayUID] = true 72 | return nil 73 | } 74 | 75 | func (svr *LBProbeServer) RemoveGateway(gatewayUID string) error { 76 | svr.lock.Lock() 77 | defer svr.lock.Unlock() 78 | 79 | delete(svr.activeGateways, gatewayUID) 80 | return nil 81 | } 82 | 83 | func (svr *LBProbeServer) GetGateways() []string { 84 | var res []string 85 | svr.lock.RLock() 86 | defer svr.lock.RUnlock() 87 | 88 | for gatewayUID := range svr.activeGateways { 89 | res = append(res, gatewayUID) 90 | } 91 | return res 92 | } 93 | 94 | func (svr *LBProbeServer) serveHTTP(resp http.ResponseWriter, req *http.Request) { 95 | reqPath := req.URL.Path 96 | subPaths := strings.Split(reqPath, "/") 97 | if len(subPaths) != 3 { 98 | resp.WriteHeader(http.StatusBadRequest) 99 | return 100 | } 101 | gatewayUID := subPaths[2] 102 | 103 | svr.lock.RLock() 104 | _, ok := svr.activeGateways[gatewayUID] 105 | svr.lock.RUnlock() 106 | 107 | if !ok { 108 | resp.WriteHeader(http.StatusServiceUnavailable) 109 | } else { 110 | resp.WriteHeader(http.StatusOK) 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /pkg/healthprobe/gw_health_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | package healthprobe 4 | 5 | import ( 6 | "net/http" 7 | "net/http/httptest" 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestGatewayHealthServer(t *testing.T) { 14 | svr := NewLBProbeServer(1000) 15 | assert.Empty(t, svr.GetGateways(), "active gateway map should be empty at the beginning") 16 | 17 | // test unexpected request path 18 | testHandler(svr, "", http.StatusBadRequest, t) 19 | testHandler(svr, "/gw", http.StatusBadRequest, t) 20 | testHandler(svr, "/gw/123/123", http.StatusBadRequest, t) 21 | 22 | // Add gateway 23 | err := svr.AddGateway("123") 24 | assert.Nil(t, err, "AddGateway should not report error") 25 | assert.Equal(t, 1, len(svr.GetGateways()), "active gateway map should have 1 element") 26 | testHandler(svr, "/gw/123", http.StatusOK, t) 27 | testHandler(svr, "/gw/456", http.StatusServiceUnavailable, t) 28 | 29 | // Remove gateway 30 | err = svr.RemoveGateway("456") 31 | assert.Nil(t, err, "RemoveGateway should not report error") 32 | assert.Equal(t, 1, len(svr.GetGateways()), "active gateway map should have 1 element") 33 | err = svr.RemoveGateway("123") 34 | assert.Nil(t, err, "RemoveGateway should not report error") 35 | assert.Empty(t, svr.GetGateways(), "active gateway map should be empty") 36 | testHandler(svr, "/gw/123", http.StatusServiceUnavailable, t) 37 | testHandler(svr, "/gw/456", http.StatusServiceUnavailable, t) 38 | 39 | // Add multiple gateways 40 | err = svr.AddGateway("abc") 41 | assert.Nil(t, err, "AddGateway should not report error") 42 | err = svr.AddGateway("def") 43 | assert.Nil(t, err, "AddGateway should not report error") 44 | err = svr.AddGateway("ghi") 45 | assert.Nil(t, err, "AddGateway should not report error") 46 | assert.Equal(t, 3, len(svr.GetGateways()), "active gateway map should have 3 elements") 47 | testHandler(svr, "/gw/def", http.StatusOK, t) 48 | testHandler(svr, "/gw/xyz", http.StatusServiceUnavailable, t) 49 | 50 | // Delete multiple gateways 51 | err = svr.RemoveGateway("def") 52 | assert.Nil(t, err, "RemoveGateway should not report error") 53 | err = svr.RemoveGateway("abc") 54 | assert.Nil(t, err, "RemoveGateway should not report error") 55 | assert.Equal(t, 1, len(svr.GetGateways()), "active gateway map should have 1 element") 56 | testHandler(svr, "/gw/abc", http.StatusServiceUnavailable, t) 57 | testHandler(svr, "/gw/ghi", http.StatusOK, t) 58 | } 59 | 60 | func testHandler(svr *LBProbeServer, requestPath string, status int, t *testing.T) { 61 | req, err := http.NewRequest("GET", requestPath, nil) 62 | assert.Nil(t, err, "testHandler: failed to create new http request") 63 | resp := httptest.NewRecorder() 64 | 65 | svr.serveHTTP(resp, req) 66 | assert.Equal(t, status, resp.Code, "testHandler: got unexpected http status code") 67 | } 68 | -------------------------------------------------------------------------------- /pkg/imds/imds.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | package imds 4 | 5 | import ( 6 | "encoding/json" 7 | "fmt" 8 | "io" 9 | "net/http" 10 | ) 11 | 12 | const ( 13 | // ImdsInstanceAPIVersion is the imds instance api version 14 | ImdsInstanceAPIVersion = "2021-10-01" 15 | // ImdsLoadBalancerAPIVersion is the imds load balancer api version 16 | ImdsLoadBalancerAPIVersion = "2020-10-01" 17 | // ImdsServer is the imds server endpoint 18 | ImdsServer = "http://169.254.169.254" 19 | // ImdsInstanceURI is the imds instance uri 20 | ImdsInstanceURI = "/metadata/instance" 21 | // ImdsLoadBalancerURI is the imds load balancer uri 22 | ImdsLoadBalancerURI = "/metadata/loadbalancer" 23 | // ImdsUserAgent is the user agent to query Imds 24 | ImdsUserAgent = "golang/kube-egress-gateway" 25 | ) 26 | 27 | func GetInstanceMetadata() (*InstanceMetadata, error) { 28 | data, err := getImdsResponse(ImdsInstanceURI, ImdsInstanceAPIVersion) 29 | if err != nil { 30 | return nil, fmt.Errorf("failed to get instance metadata: %w", err) 31 | } 32 | obj := InstanceMetadata{} 33 | err = json.Unmarshal(data, &obj) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | return &obj, nil 39 | } 40 | 41 | func GetLoadBalancerMetadata() (*LoadBalancerMetadata, error) { 42 | data, err := getImdsResponse(ImdsLoadBalancerURI, ImdsLoadBalancerAPIVersion) 43 | if err != nil { 44 | return nil, fmt.Errorf("failed to get loadbalancer metadata: %w", err) 45 | } 46 | obj := LoadBalancerMetadata{} 47 | err = json.Unmarshal(data, &obj) 48 | if err != nil { 49 | return nil, err 50 | } 51 | 52 | return &obj, nil 53 | } 54 | 55 | func getImdsResponse(resourceURI, apiVersion string) ([]byte, error) { 56 | req, err := http.NewRequest("GET", ImdsServer+resourceURI, nil) 57 | if err != nil { 58 | return nil, err 59 | } 60 | req.Header.Add("Metadata", "True") 61 | req.Header.Add("User-Agent", ImdsUserAgent) 62 | 63 | q := req.URL.Query() 64 | q.Add("format", "json") 65 | q.Add("api-version", apiVersion) 66 | req.URL.RawQuery = q.Encode() 67 | 68 | client := &http.Client{} 69 | resp, err := client.Do(req) 70 | if err != nil { 71 | return nil, err 72 | } 73 | defer resp.Body.Close() 74 | 75 | if resp.StatusCode != http.StatusOK { 76 | return nil, fmt.Errorf("failure of querying imds with response %q", resp.Status) 77 | } 78 | 79 | data, err := io.ReadAll(resp.Body) 80 | if err != nil { 81 | return nil, err 82 | } 83 | 84 | return data, nil 85 | } 86 | -------------------------------------------------------------------------------- /pkg/imds/types.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | package imds 4 | 5 | type InstanceMetadata struct { 6 | Compute *ComputeMetadata `json:"compute"` 7 | Network *NetworkMetadata `json:"network"` 8 | } 9 | 10 | type ComputeMetadata struct { 11 | AzEnvironment string `json:"azEnvironment"` 12 | Location string `json:"location"` 13 | Name string `json:"name"` 14 | OSType string `json:"osType"` 15 | OSProfile OSProfile `json:"osProfile"` 16 | ResourceGroupName string `json:"resourceGroupName"` 17 | ResourceID string `json:"resourceId"` 18 | SubscriptionID string `json:"subscriptionId"` 19 | Tags string `json:"tags"` 20 | VMScaleSetName string `json:"vmScaleSetName"` 21 | } 22 | 23 | type NetworkMetadata struct { 24 | Interface []NetworkInterface `json:"interface"` 25 | } 26 | 27 | type OSProfile struct { 28 | ComputerName string `json:"computerName"` 29 | } 30 | 31 | type NetworkInterface struct { 32 | IPv4 IPData `json:"ipv4"` 33 | MacAddress string `json:"macAddress"` 34 | } 35 | 36 | type IPData struct { 37 | IPAddress []IPAddress `json:"ipAddress"` 38 | Subnet []Subnet `json:"subnet"` 39 | } 40 | 41 | type IPAddress struct { 42 | PrivateIP string `json:"privateIpAddress"` 43 | PublicIP string `json:"publicIpAddress"` 44 | } 45 | type Subnet struct { 46 | Address string `json:"address"` 47 | Prefix string `json:"prefix"` 48 | } 49 | 50 | type LoadBalancerMetadata struct { 51 | LoadBalancer LBData `json:"loadbalancer"` 52 | } 53 | 54 | type LBData struct { 55 | PublicIPAddresses []PublicIPMetadata `json:"publicIpAddresses"` 56 | } 57 | 58 | type PublicIPMetadata struct { 59 | FrontendIPAddress string `json:"frontendIpAddress"` 60 | PrivateIPAddress string `json:"privateIpAddress"` 61 | } 62 | -------------------------------------------------------------------------------- /pkg/iptableswrapper/iptables.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | package iptableswrapper 4 | 5 | import "github.com/coreos/go-iptables/iptables" 6 | 7 | type IpTables interface { 8 | // ApendUnique appends given rulespec if not exists 9 | AppendUnique(table, chain string, rulespec ...string) error 10 | // Exists checks if given rulespec in specified table/chain exists 11 | Exists(table, chain string, rulespec ...string) (bool, error) 12 | // Insert inserts given rulespec to specified table/chain at specified position 13 | Insert(table, chain string, pos int, rulespec ...string) error 14 | // Delete removes rulespec in specified table/chain 15 | Delete(table, chain string, rulespec ...string) error 16 | // List lists rules in specified table/chain 17 | List(table, chain string) ([]string, error) 18 | } 19 | 20 | type Interface interface { 21 | // New creates a new IpTables instance 22 | New() (IpTables, error) 23 | } 24 | 25 | type ipTable struct{} 26 | 27 | func NewIPTables() Interface { 28 | return &ipTable{} 29 | } 30 | 31 | func (*ipTable) New() (IpTables, error) { 32 | return iptables.New() 33 | } 34 | -------------------------------------------------------------------------------- /pkg/logger/factory.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | package logger 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/go-logr/logr" 9 | "github.com/go-logr/zapr" 10 | "go.uber.org/zap" 11 | ) 12 | 13 | var defaultLogger logr.Logger 14 | 15 | // SetLogger sets the default logger 16 | func SetDefaultLogger(logger logr.Logger) { 17 | defaultLogger = logger 18 | } 19 | 20 | // GetLogger returns the default logger 21 | func GetLogger() logr.Logger { 22 | return defaultLogger 23 | } 24 | 25 | func init() { 26 | zapLog, err := zap.NewProduction() 27 | if err != nil { 28 | panic(fmt.Sprintf("who watches the watchmen (%v)?", err)) 29 | } 30 | SetDefaultLogger(zapr.NewLogger(zapLog)) 31 | } 32 | -------------------------------------------------------------------------------- /pkg/metrics/metrics.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | package metrics 4 | 5 | import ( 6 | "time" 7 | 8 | "github.com/prometheus/client_golang/prometheus" 9 | ) 10 | 11 | var ( 12 | ControllerReconcileFailCount = prometheus.NewCounterVec( 13 | prometheus.CounterOpts{ 14 | Name: "controller_reconcile_fail_count", 15 | Help: "Number of failed static egress gateway controller reconciliations", 16 | }, 17 | []string{"namespace", "operation", "subscription_id", "resource_group", "resource"}, 18 | ) 19 | 20 | ControllerReconcileLatency = prometheus.NewHistogramVec( 21 | prometheus.HistogramOpts{ 22 | Name: "controller_reconcile_latency", 23 | Help: "Latency of static egress gateway controller reconciliations", 24 | Buckets: []float64{0.1, 0.2, 0.5, 1, 5, 10, 15, 20, 30, 40, 50, 60, 100, 200, 300, 600, 1200}, // seconds 25 | }, 26 | []string{"namespace", "operation", "subscription_id", "resource_group"}, 27 | ) 28 | ) 29 | 30 | type MetricsContext struct { 31 | start time.Time 32 | labels []string 33 | } 34 | 35 | func NewMetricsContext(namespace, operation, subscriptionID, resourceGroup, resource string) *MetricsContext { 36 | return &MetricsContext{ 37 | start: time.Now(), 38 | labels: []string{namespace, operation, subscriptionID, resourceGroup, resource}, 39 | } 40 | } 41 | 42 | func (mc *MetricsContext) ObserveControllerReconcileMetrics(succeeded bool) { 43 | if !succeeded { 44 | ControllerReconcileFailCount.WithLabelValues(mc.labels...).Inc() 45 | } 46 | latency := time.Since(mc.start).Seconds() 47 | mc.observe(latency) 48 | } 49 | 50 | func (mc *MetricsContext) observe(latency float64) { 51 | // trim the last "resource" label 52 | ControllerReconcileLatency.WithLabelValues(mc.labels[:4]...).Observe(latency) 53 | } 54 | -------------------------------------------------------------------------------- /pkg/netlinkwrapper/netlink.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | package netlinkwrapper 4 | 5 | import "github.com/vishvananda/netlink" 6 | 7 | type Interface interface { 8 | // LinkByName finds a link by name 9 | LinkByName(name string) (netlink.Link, error) 10 | // LinkAdd adds a new link device 11 | LinkAdd(link netlink.Link) error 12 | // LinkList lists all link devices 13 | LinkList() ([]netlink.Link, error) 14 | // LinkDel deletes link device 15 | LinkDel(link netlink.Link) error 16 | // LinkSetUp enables the link device 17 | LinkSetUp(link netlink.Link) error 18 | // LinkSetDown disables the link device 19 | LinkSetDown(link netlink.Link) error 20 | // LinkSetNsFd puts the device into a new network namespace 21 | LinkSetNsFd(link netlink.Link, fd int) error 22 | // LinkSetName sets the name of the link device 23 | LinkSetName(link netlink.Link, name string) error 24 | // LinkSetAlias sets the alias of the link device 25 | LinkSetAlias(link netlink.Link, name string) error 26 | // AddrList gets a list of IP addresses in the system 27 | AddrList(link netlink.Link, family int) ([]netlink.Addr, error) 28 | // AddrAdd adds an IP address to a link device 29 | AddrAdd(link netlink.Link, addr *netlink.Addr) error 30 | // AddrDel deletes an IP address from a link device 31 | AddrDel(link netlink.Link, addr *netlink.Addr) error 32 | // AddrReplace replaces (or, if not present, adds) an IP address on a link device 33 | AddrReplace(link netlink.Link, addr *netlink.Addr) error 34 | // RouteReplace adds a route to the system 35 | RouteReplace(route *netlink.Route) error 36 | // RouteDel deletes a route from the system 37 | RouteDel(route *netlink.Route) error 38 | // RouteList gets a list of routes in the system 39 | RouteList(link netlink.Link, family int) ([]netlink.Route, error) 40 | // RuleAdd adds a rule 41 | RuleAdd(rule *netlink.Rule) error 42 | } 43 | 44 | type nl struct{} 45 | 46 | func NewNetLink() Interface { 47 | return &nl{} 48 | } 49 | 50 | func (*nl) LinkByName(name string) (netlink.Link, error) { 51 | return netlink.LinkByName(name) 52 | } 53 | 54 | func (*nl) LinkAdd(link netlink.Link) error { 55 | return netlink.LinkAdd(link) 56 | } 57 | 58 | func (*nl) LinkList() ([]netlink.Link, error) { 59 | return netlink.LinkList() 60 | } 61 | 62 | func (*nl) LinkDel(link netlink.Link) error { 63 | return netlink.LinkDel(link) 64 | } 65 | 66 | func (*nl) LinkSetUp(link netlink.Link) error { 67 | return netlink.LinkSetUp(link) 68 | } 69 | 70 | func (*nl) LinkSetDown(link netlink.Link) error { 71 | return netlink.LinkSetDown(link) 72 | } 73 | 74 | func (*nl) LinkSetNsFd(link netlink.Link, fd int) error { 75 | return netlink.LinkSetNsFd(link, fd) 76 | } 77 | 78 | func (*nl) LinkSetName(link netlink.Link, name string) error { 79 | return netlink.LinkSetName(link, name) 80 | } 81 | 82 | func (*nl) LinkSetAlias(link netlink.Link, name string) error { 83 | return netlink.LinkSetAlias(link, name) 84 | } 85 | 86 | func (*nl) AddrList(link netlink.Link, family int) ([]netlink.Addr, error) { 87 | return netlink.AddrList(link, family) 88 | } 89 | 90 | func (*nl) AddrAdd(link netlink.Link, addr *netlink.Addr) error { 91 | return netlink.AddrAdd(link, addr) 92 | } 93 | 94 | func (*nl) AddrDel(link netlink.Link, addr *netlink.Addr) error { 95 | return netlink.AddrDel(link, addr) 96 | } 97 | 98 | func (*nl) AddrReplace(link netlink.Link, addr *netlink.Addr) error { 99 | return netlink.AddrReplace(link, addr) 100 | } 101 | 102 | func (*nl) RouteReplace(route *netlink.Route) error { 103 | return netlink.RouteReplace(route) 104 | } 105 | 106 | func (*nl) RouteDel(route *netlink.Route) error { 107 | return netlink.RouteDel(route) 108 | } 109 | 110 | func (*nl) RouteList(link netlink.Link, family int) ([]netlink.Route, error) { 111 | return netlink.RouteList(link, family) 112 | } 113 | 114 | func (*nl) RuleAdd(rule *netlink.Rule) error { 115 | return netlink.RuleAdd(rule) 116 | } 117 | -------------------------------------------------------------------------------- /pkg/netnswrapper/mocknetnswrapper/interface.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | 5 | // Code generated by MockGen. DO NOT EDIT. 6 | // Source: pkg/netnswrapper/netns_linux.go 7 | 8 | // Package mocknetnswrapper is a generated GoMock package. 9 | package mocknetnswrapper 10 | 11 | import ( 12 | reflect "reflect" 13 | 14 | ns "github.com/containernetworking/plugins/pkg/ns" 15 | gomock "go.uber.org/mock/gomock" 16 | ) 17 | 18 | // MockInterface is a mock of Interface interface. 19 | type MockInterface struct { 20 | ctrl *gomock.Controller 21 | recorder *MockInterfaceMockRecorder 22 | } 23 | 24 | // MockInterfaceMockRecorder is the mock recorder for MockInterface. 25 | type MockInterfaceMockRecorder struct { 26 | mock *MockInterface 27 | } 28 | 29 | // NewMockInterface creates a new mock instance. 30 | func NewMockInterface(ctrl *gomock.Controller) *MockInterface { 31 | mock := &MockInterface{ctrl: ctrl} 32 | mock.recorder = &MockInterfaceMockRecorder{mock} 33 | return mock 34 | } 35 | 36 | // EXPECT returns an object that allows the caller to indicate expected use. 37 | func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder { 38 | return m.recorder 39 | } 40 | 41 | // GetNS mocks base method. 42 | func (m *MockInterface) GetNS(nsName string) (ns.NetNS, error) { 43 | m.ctrl.T.Helper() 44 | ret := m.ctrl.Call(m, "GetNS", nsName) 45 | ret0, _ := ret[0].(ns.NetNS) 46 | ret1, _ := ret[1].(error) 47 | return ret0, ret1 48 | } 49 | 50 | // GetNS indicates an expected call of GetNS. 51 | func (mr *MockInterfaceMockRecorder) GetNS(nsName interface{}) *gomock.Call { 52 | mr.mock.ctrl.T.Helper() 53 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNS", reflect.TypeOf((*MockInterface)(nil).GetNS), nsName) 54 | } 55 | 56 | // GetNSByPath mocks base method. 57 | func (m *MockInterface) GetNSByPath(nsPath string) (ns.NetNS, error) { 58 | m.ctrl.T.Helper() 59 | ret := m.ctrl.Call(m, "GetNSByPath", nsPath) 60 | ret0, _ := ret[0].(ns.NetNS) 61 | ret1, _ := ret[1].(error) 62 | return ret0, ret1 63 | } 64 | 65 | // GetNSByPath indicates an expected call of GetNSByPath. 66 | func (mr *MockInterfaceMockRecorder) GetNSByPath(nsPath interface{}) *gomock.Call { 67 | mr.mock.ctrl.T.Helper() 68 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNSByPath", reflect.TypeOf((*MockInterface)(nil).GetNSByPath), nsPath) 69 | } 70 | 71 | // ListNS mocks base method. 72 | func (m *MockInterface) ListNS() ([]string, error) { 73 | m.ctrl.T.Helper() 74 | ret := m.ctrl.Call(m, "ListNS") 75 | ret0, _ := ret[0].([]string) 76 | ret1, _ := ret[1].(error) 77 | return ret0, ret1 78 | } 79 | 80 | // ListNS indicates an expected call of ListNS. 81 | func (mr *MockInterfaceMockRecorder) ListNS() *gomock.Call { 82 | mr.mock.ctrl.T.Helper() 83 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListNS", reflect.TypeOf((*MockInterface)(nil).ListNS)) 84 | } 85 | 86 | // NewNS mocks base method. 87 | func (m *MockInterface) NewNS(nsName string) (ns.NetNS, error) { 88 | m.ctrl.T.Helper() 89 | ret := m.ctrl.Call(m, "NewNS", nsName) 90 | ret0, _ := ret[0].(ns.NetNS) 91 | ret1, _ := ret[1].(error) 92 | return ret0, ret1 93 | } 94 | 95 | // NewNS indicates an expected call of NewNS. 96 | func (mr *MockInterfaceMockRecorder) NewNS(nsName interface{}) *gomock.Call { 97 | mr.mock.ctrl.T.Helper() 98 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewNS", reflect.TypeOf((*MockInterface)(nil).NewNS), nsName) 99 | } 100 | 101 | // UnmountNS mocks base method. 102 | func (m *MockInterface) UnmountNS(nsName string) error { 103 | m.ctrl.T.Helper() 104 | ret := m.ctrl.Call(m, "UnmountNS", nsName) 105 | ret0, _ := ret[0].(error) 106 | return ret0 107 | } 108 | 109 | // UnmountNS indicates an expected call of UnmountNS. 110 | func (mr *MockInterfaceMockRecorder) UnmountNS(nsName interface{}) *gomock.Call { 111 | mr.mock.ctrl.T.Helper() 112 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnmountNS", reflect.TypeOf((*MockInterface)(nil).UnmountNS), nsName) 113 | } 114 | -------------------------------------------------------------------------------- /pkg/netnswrapper/mocknetnswrapper/mocknetns.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | package mocknetnswrapper 5 | 6 | import "github.com/containernetworking/plugins/pkg/ns" 7 | 8 | // MockNetNS: implements ns.NetNS interface 9 | type MockNetNS struct { 10 | Name string 11 | } 12 | 13 | func (ns *MockNetNS) Do(toRun func(ns.NetNS) error) error { 14 | return toRun(ns) 15 | } 16 | 17 | func (*MockNetNS) Set() error { 18 | return nil 19 | } 20 | 21 | func (*MockNetNS) Path() string { 22 | return "" 23 | } 24 | 25 | func (*MockNetNS) Fd() uintptr { 26 | return 0 27 | } 28 | 29 | func (*MockNetNS) Close() error { 30 | return nil 31 | } 32 | -------------------------------------------------------------------------------- /pkg/utils/to/convert.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | package to 4 | 5 | import ( 6 | "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" 7 | ) 8 | 9 | func Ptr[T any](v T) *T { 10 | return to.Ptr(v) 11 | } 12 | 13 | func Val[T any](v *T) T { 14 | var empty T 15 | if v == nil { 16 | return empty 17 | } 18 | return *v 19 | } 20 | -------------------------------------------------------------------------------- /pkg/utils/to/convert_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | package to 4 | 5 | import "testing" 6 | 7 | func TestPtr(t *testing.T) { 8 | v := "test" 9 | if *Ptr(v) != v { 10 | t.Fatalf("to: Ptr() returned wrong value(%v), expected %v", *Ptr(v), v) 11 | } 12 | 13 | v = "" 14 | if *Ptr(v) != v { 15 | t.Fatalf("to: Ptr() returned wrong value(%v), expected %v", *Ptr(v), v) 16 | } 17 | 18 | b := true 19 | if *Ptr(b) != b { 20 | t.Fatalf("to: Ptr() returned wrong value(%v), expected %v", *Ptr(b), b) 21 | } 22 | 23 | b = false 24 | if *Ptr(b) != b { 25 | t.Fatalf("to: Ptr() returned wrong value(%v), expected %v", *Ptr(b), b) 26 | } 27 | 28 | var i int32 = 10 29 | if *Ptr(i) != i { 30 | t.Fatalf("to: Ptr() returned wrong value(%v), expected %v", *Ptr(i), i) 31 | } 32 | 33 | i = 0 34 | if *Ptr(i) != i { 35 | t.Fatalf("to: Ptr() returned wrong value(%v), expected %v", *Ptr(i), i) 36 | } 37 | } 38 | 39 | func TestVal(t *testing.T) { 40 | v := "" 41 | if Val(&v) != v { 42 | t.Fatalf("to: Val() returned wrong value(%v), expected %v", Val(&v), v) 43 | } 44 | 45 | var pv *string 46 | if Val(pv) != "" { 47 | t.Fatalf("to: Val() returned wrong value(%v), expected %v", "", Val(pv)) 48 | } 49 | 50 | b := true 51 | if Val(&b) != b { 52 | t.Fatalf("to: Val() returned wrong value(%v), expected %v", Val(&b), b) 53 | } 54 | 55 | var pb *bool 56 | if Val(pb) != false { 57 | t.Fatalf("to: Val() returned wrong value(%v), expected %v", Val(pb), false) 58 | } 59 | 60 | var i int32 = 10 61 | if Val(&i) != i { 62 | t.Fatalf("to: Val() returned wrong value(%v), expected %v", Val(&i), i) 63 | } 64 | 65 | var pi *int32 66 | if Val(pi) != int32(0) { 67 | t.Fatalf("to: Val() returned wrong value(%v), expected %v", Val(pi), 0) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /pkg/wgctrlwrapper/mockwgctrlwrapper/interface.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | // 4 | 5 | // Code generated by MockGen. DO NOT EDIT. 6 | // Source: pkg/wgctrlwrapper/wgctrl.go 7 | 8 | // Package mockwgctrlwrapper is a generated GoMock package. 9 | package mockwgctrlwrapper 10 | 11 | import ( 12 | reflect "reflect" 13 | 14 | gomock "go.uber.org/mock/gomock" 15 | wgtypes "golang.zx2c4.com/wireguard/wgctrl/wgtypes" 16 | 17 | wgctrlwrapper "github.com/Azure/kube-egress-gateway/pkg/wgctrlwrapper" 18 | ) 19 | 20 | // MockClient is a mock of Client interface. 21 | type MockClient struct { 22 | ctrl *gomock.Controller 23 | recorder *MockClientMockRecorder 24 | } 25 | 26 | // MockClientMockRecorder is the mock recorder for MockClient. 27 | type MockClientMockRecorder struct { 28 | mock *MockClient 29 | } 30 | 31 | // NewMockClient creates a new mock instance. 32 | func NewMockClient(ctrl *gomock.Controller) *MockClient { 33 | mock := &MockClient{ctrl: ctrl} 34 | mock.recorder = &MockClientMockRecorder{mock} 35 | return mock 36 | } 37 | 38 | // EXPECT returns an object that allows the caller to indicate expected use. 39 | func (m *MockClient) EXPECT() *MockClientMockRecorder { 40 | return m.recorder 41 | } 42 | 43 | // Close mocks base method. 44 | func (m *MockClient) Close() error { 45 | m.ctrl.T.Helper() 46 | ret := m.ctrl.Call(m, "Close") 47 | ret0, _ := ret[0].(error) 48 | return ret0 49 | } 50 | 51 | // Close indicates an expected call of Close. 52 | func (mr *MockClientMockRecorder) Close() *gomock.Call { 53 | mr.mock.ctrl.T.Helper() 54 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockClient)(nil).Close)) 55 | } 56 | 57 | // ConfigureDevice mocks base method. 58 | func (m *MockClient) ConfigureDevice(name string, cfg wgtypes.Config) error { 59 | m.ctrl.T.Helper() 60 | ret := m.ctrl.Call(m, "ConfigureDevice", name, cfg) 61 | ret0, _ := ret[0].(error) 62 | return ret0 63 | } 64 | 65 | // ConfigureDevice indicates an expected call of ConfigureDevice. 66 | func (mr *MockClientMockRecorder) ConfigureDevice(name, cfg interface{}) *gomock.Call { 67 | mr.mock.ctrl.T.Helper() 68 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConfigureDevice", reflect.TypeOf((*MockClient)(nil).ConfigureDevice), name, cfg) 69 | } 70 | 71 | // Device mocks base method. 72 | func (m *MockClient) Device(name string) (*wgtypes.Device, error) { 73 | m.ctrl.T.Helper() 74 | ret := m.ctrl.Call(m, "Device", name) 75 | ret0, _ := ret[0].(*wgtypes.Device) 76 | ret1, _ := ret[1].(error) 77 | return ret0, ret1 78 | } 79 | 80 | // Device indicates an expected call of Device. 81 | func (mr *MockClientMockRecorder) Device(name interface{}) *gomock.Call { 82 | mr.mock.ctrl.T.Helper() 83 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Device", reflect.TypeOf((*MockClient)(nil).Device), name) 84 | } 85 | 86 | // MockInterface is a mock of Interface interface. 87 | type MockInterface struct { 88 | ctrl *gomock.Controller 89 | recorder *MockInterfaceMockRecorder 90 | } 91 | 92 | // MockInterfaceMockRecorder is the mock recorder for MockInterface. 93 | type MockInterfaceMockRecorder struct { 94 | mock *MockInterface 95 | } 96 | 97 | // NewMockInterface creates a new mock instance. 98 | func NewMockInterface(ctrl *gomock.Controller) *MockInterface { 99 | mock := &MockInterface{ctrl: ctrl} 100 | mock.recorder = &MockInterfaceMockRecorder{mock} 101 | return mock 102 | } 103 | 104 | // EXPECT returns an object that allows the caller to indicate expected use. 105 | func (m *MockInterface) EXPECT() *MockInterfaceMockRecorder { 106 | return m.recorder 107 | } 108 | 109 | // New mocks base method. 110 | func (m *MockInterface) New() (wgctrlwrapper.Client, error) { 111 | m.ctrl.T.Helper() 112 | ret := m.ctrl.Call(m, "New") 113 | ret0, _ := ret[0].(wgctrlwrapper.Client) 114 | ret1, _ := ret[1].(error) 115 | return ret0, ret1 116 | } 117 | 118 | // New indicates an expected call of New. 119 | func (mr *MockInterfaceMockRecorder) New() *gomock.Call { 120 | mr.mock.ctrl.T.Helper() 121 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "New", reflect.TypeOf((*MockInterface)(nil).New)) 122 | } 123 | -------------------------------------------------------------------------------- /pkg/wgctrlwrapper/wgctrl.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | package wgctrlwrapper 4 | 5 | import ( 6 | "golang.zx2c4.com/wireguard/wgctrl" 7 | "golang.zx2c4.com/wireguard/wgctrl/wgtypes" 8 | ) 9 | 10 | type Client interface { 11 | // Device retrieves a WireGuard device by its interface name 12 | Device(name string) (*wgtypes.Device, error) 13 | // ConfigureDevice configures a WireGuard device by its interface name 14 | ConfigureDevice(name string, cfg wgtypes.Config) error 15 | // Close releases resources used by a Client 16 | Close() error 17 | } 18 | 19 | type Interface interface { 20 | // New creates a new wireguard client 21 | New() (Client, error) 22 | } 23 | 24 | type wg struct{} 25 | 26 | func NewWgCtrl() Interface { 27 | return &wg{} 28 | } 29 | 30 | func (*wg) New() (Client, error) { 31 | return wgctrl.New() 32 | } 33 | --------------------------------------------------------------------------------