├── .gitignore ├── .gitattributes ├── proxy ├── .dockerignore ├── ops │ ├── docker_boot.service │ └── docker-compose.yml ├── Dockerfile └── src │ ├── healthcheck.sh │ ├── set_public_ip_and_start.sh │ ├── generate-certs.sh │ └── proxy_config.cfg ├── docs └── deployments.md ├── charts ├── whatsapp-proxy-chart │ ├── templates │ │ ├── serviceaccount.yaml │ │ ├── tests │ │ │ └── test-connection.yaml │ │ ├── hpa.yaml │ │ ├── NOTES.txt │ │ ├── _helpers.tpl │ │ ├── service.yaml │ │ ├── ingress.yaml │ │ └── deployment.yaml │ ├── deployments │ │ ├── overrides │ │ │ ├── values.gke.yaml │ │ │ └── values.eks.yaml │ │ └── README.md │ ├── Chart.yaml │ └── values.yaml ├── lb.yaml ├── README.md └── wa_logo.svg ├── .github ├── workflows │ ├── helm-chart.yml │ ├── docker-build-pr.yml │ ├── publish.yml │ ├── docker-image.yml │ └── ci.yml └── ISSUE_TEMPLATE │ └── bug_report.md ├── cloud └── cloud-init.yml ├── LICENSE ├── CONTRIBUTING.md ├── CODE_OF_CONDUCT.md ├── FAQ.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | tmp/ 2 | helm/whatsapp-proxy-chart*.tgz 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Explicitly declare sh files to always be normalized and have LF file endings 2 | *.sh text eol=lf 3 | -------------------------------------------------------------------------------- /proxy/.dockerignore: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # License found in the LICENSE file in the root directory 4 | # of this source tree. 5 | 6 | ops/ 7 | -------------------------------------------------------------------------------- /docs/deployments.md: -------------------------------------------------------------------------------- 1 | # Deployments 2 | 3 | ## Cloud-init deployment 4 | To quickly deploy a proxy on a new server without additional linux knowledge, follow these steps: 5 | 6 | 1. Copy the contents of `cloud/cloud-init.yml` to your clipboard. 7 | 1. Create a new server instance with your desired cloud provider. 8 | 1. During the instance creation process, look for an option to specify "cloud-init" or "user data". 9 | 1. Paste the contents of `cloud/cloud-init.yml` into this field. 10 | 1. Finish creating the server instance. -------------------------------------------------------------------------------- /proxy/ops/docker_boot.service: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # License found in the LICENSE file in the root directory 4 | # of this source tree. 5 | 6 | ## Systemd definition for an example service to startup the proxy container on host reboot 7 | 8 | [Unit] 9 | description=docker boot 10 | After=docker.service 11 | 12 | [Service] 13 | Type=oneshot 14 | RemainAfterExit=yes 15 | WorkingDirectory=/root/whatsapp_proxy/proxy/ops 16 | ExecStart=docker compose up -d 17 | ExecStop=docker compose stop 18 | 19 | [Install] 20 | WantedBy=multi-user.target 21 | -------------------------------------------------------------------------------- /charts/whatsapp-proxy-chart/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # License found in the LICENSE file in the root directory 4 | # of this source tree. 5 | {{- if .Values.serviceAccount.create }} 6 | apiVersion: v1 7 | kind: ServiceAccount 8 | metadata: 9 | name: {{ include "whatsapp-proxy-chart.serviceAccountName" . }} 10 | labels: 11 | {{- include "whatsapp-proxy-chart.labels" . | nindent 4 }} 12 | {{- with .Values.serviceAccount.annotations }} 13 | annotations: 14 | {{- toYaml . | nindent 4 }} 15 | {{- end }} 16 | {{- end }} 17 | -------------------------------------------------------------------------------- /.github/workflows/helm-chart.yml: -------------------------------------------------------------------------------- 1 | name: Helm Chart CI 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | 7 | jobs: 8 | release: 9 | permissions: 10 | contents: write 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | with: 16 | fetch-depth: 0 17 | 18 | - name: Configure Git 19 | run: | 20 | git config user.name "$GITHUB_ACTOR" 21 | git config user.email "$GITHUB_ACTOR@users.noreply.github.com" 22 | 23 | - name: Run chart-releaser 24 | uses: helm/chart-releaser-action@v1.7.0 25 | env: 26 | CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 27 | -------------------------------------------------------------------------------- /charts/whatsapp-proxy-chart/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # License found in the LICENSE file in the root directory 4 | # of this source tree. 5 | apiVersion: v1 6 | kind: Pod 7 | metadata: 8 | name: "{{ include "whatsapp-proxy-chart.fullname" . }}-test-connection" 9 | labels: 10 | {{- include "whatsapp-proxy-chart.labels" . | nindent 4 }} 11 | annotations: 12 | "helm.sh/hook": test 13 | spec: 14 | containers: 15 | - name: wget 16 | image: busybox 17 | command: ['wget'] 18 | args: ['{{ include "whatsapp-proxy-chart.fullname" . }}:{{ .Values.service.stats_port }}'] 19 | restartPolicy: Never 20 | -------------------------------------------------------------------------------- /charts/whatsapp-proxy-chart/deployments/overrides/values.gke.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # License found in the LICENSE file in the root directory 4 | # of this source tree. 5 | 6 | # Values override for whatsapp-proxy-chart for deploying to Google Kubernetes Engine (GKE). 7 | # Note: This values override file is not part of the core project. 8 | # Rather, it is provided here as an example override file to help with 9 | # deployment to Google Kubernetes Engine. 10 | 11 | service: 12 | type: LoadBalancer 13 | annotations: 14 | cloud.google.com/l4-rbs: "enabled" 15 | 16 | podSecurityContext: 17 | sysctls: 18 | - name: net.ipv4.ip_unprivileged_port_start 19 | value: "0" 20 | 21 | -------------------------------------------------------------------------------- /charts/whatsapp-proxy-chart/deployments/overrides/values.eks.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # License found in the LICENSE file in the root directory 4 | # of this source tree. 5 | 6 | # Values override for whatsapp-proxy-chart for deploying to Amazon EKS. 7 | # Note: This values override file is not part of the core project. 8 | # Rather, it is provided here as an example override file to help with 9 | # deployment to Amazon's EKS service. 10 | 11 | service: 12 | type: LoadBalancer 13 | annotations: 14 | service.beta.kubernetes.io/aws-load-balancer-type: "external" 15 | service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "ip" 16 | service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing" 17 | 18 | -------------------------------------------------------------------------------- /cloud/cloud-init.yml: -------------------------------------------------------------------------------- 1 | #cloud-config 2 | 3 | package_update: true 4 | 5 | package_upgrade: true 6 | 7 | packages: 8 | - ca-certificates 9 | - curl 10 | - gnupg 11 | - lsb-release 12 | - git 13 | 14 | runcmd: 15 | - sudo mkdir -p /etc/apt/keyrings 16 | - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg 17 | - echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null 18 | - sudo apt-get update 19 | - sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin 20 | - git clone https://github.com/WhatsApp/proxy.git $HOME/whatsapp-proxy 21 | - docker compose -f $HOME/whatsapp-proxy/proxy/ops/docker-compose.yml up -d 22 | 23 | -------------------------------------------------------------------------------- /proxy/ops/docker-compose.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # License found in the LICENSE file in the root directory 4 | # of this source tree. 5 | version: '3.3' 6 | 7 | services: 8 | proxy: 9 | container_name: whatsapp_proxy 10 | build: ../ 11 | restart: unless-stopped 12 | ports: 13 | - "80:80" # HTTP 14 | - "443:443" # HTTPS 15 | - "5222:5222" # JABBER 16 | - "8199:8199" # HAPROXY statistics page 17 | - "8080:8080" # HTTP with accept-proxy processing 18 | - "8443:8443" # HTTPS with accept-proxy processing 19 | - "8222:8222" # JABBER with accept-proxy processing 20 | - "587:587" # whatsapp.net 21 | - "7777:7777" # whatsapp.net 22 | healthcheck: 23 | test: /usr/local/bin/healthcheck.sh 24 | interval: 10s 25 | start_period: 5s 26 | environment: 27 | - PUBLIC_IP=10.0.0.1 28 | -------------------------------------------------------------------------------- /.github/workflows/docker-build-pr.yml: -------------------------------------------------------------------------------- 1 | name: Docker Image Build for PRs 2 | 3 | on: 4 | pull_request: 5 | types: [opened, reopened, sychronize] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v3 13 | 14 | - name: Docker metadata 15 | id: meta 16 | uses: docker/metadata-action@v4 17 | with: 18 | images: facebook/whatsapp_proxy 19 | tags: | 20 | type=raw,value={{date 'YYYYMMDD'}} 21 | 22 | - name: Set up QEMU 23 | uses: docker/setup-qemu-action@v2 24 | 25 | - name: Set up Docker Buildx 26 | uses: docker/setup-buildx-action@v2 27 | 28 | - name: Build PR 29 | uses: docker/build-push-action@v3 30 | with: 31 | context: ./proxy 32 | platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 33 | push: false 34 | tags: | 35 | ${{ steps.meta.outputs.tags }} 36 | facebook/whatsapp_proxy:latest 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Report a bug for the proxy 4 | --- 5 | **Before reporting a bug, please see our FAQ in [FAQ.md](https://github.com/whatsapp/proxy/blob/main/FAQ.md)!** 6 | 7 | #### Description 8 | 9 | 10 | #### Failed Step 11 | 12 | 27 | 28 | #### More Details 29 | 30 | 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Meta Platforms, Inc. and affiliates. 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 | -------------------------------------------------------------------------------- /charts/whatsapp-proxy-chart/Chart.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # License found in the LICENSE file in the root directory 4 | # of this source tree. 5 | apiVersion: v2 6 | name: whatsapp-proxy-chart 7 | description: A Helm chart for Kubernetes for the WhatsApp Proxy infrastructure 8 | icon: https://github.com/whatsapp/proxy/blob/main/helm/wa_logo.svc 9 | maintainers: 10 | - name: slawlor 11 | email: seanlawlor@meta.com 12 | 13 | # Application charts are a collection of templates that can be packaged into versioned archives 14 | # to be deployed. 15 | type: application 16 | 17 | # This is the chart version. This version number should be incremented each time you make changes 18 | # to the chart and its templates, including the app version. 19 | # Versions are expected to follow Semantic Versioning (https://semver.org/) 20 | version: 1.2.0 21 | 22 | # This is the version number of the application being deployed. This version number should be 23 | # incremented each time you make changes to the application. Versions are not expected to 24 | # follow Semantic Versioning. They should reflect the version the application is using. 25 | # It is recommended to use it with quotes. 26 | appVersion: "1.16.0" 27 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # License found in the LICENSE file in the root directory 4 | # of this source tree. 5 | name: Publish 6 | 7 | on: 8 | push: 9 | tags: 10 | - 'v*.*.*' 11 | 12 | jobs: 13 | publish: 14 | runs-on: ${{ matrix.os }} 15 | strategy: 16 | matrix: 17 | os: [ubuntu-latest] 18 | 19 | steps: 20 | # The :11 strips the first 11 characters from the environment variable GITHUB_REF which contains refs/tags/v1.2.3 so it yields 1.2.3 21 | - name: Set release version (i.e. tag) 22 | id: vars 23 | run: echo ::set-output name=tag::${GITHUB_REF#refs/*/v} 24 | 25 | - name: Print the release version (i.e. tag) 26 | run: echo "TAG = ${{ steps.vars.outputs.tag }}" 27 | 28 | - name: Login to Docker Hub 29 | uses: docker/login-action@v2 30 | with: 31 | username: ${{ secrets.DOCKERHUB_USERNAME }} 32 | password: ${{ secrets.DOCKERHUB_TOKEN }} 33 | 34 | - name: Build and push 35 | uses: docker/build-push-action@v3 36 | with: 37 | context: ./proxy 38 | push: true 39 | tags: seanlawlor/whatsapp_proxy:${{ steps.vars.outputs.tag }} 40 | -------------------------------------------------------------------------------- /charts/whatsapp-proxy-chart/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # License found in the LICENSE file in the root directory 4 | # of this source tree. 5 | {{- if .Values.autoscaling.enabled }} 6 | apiVersion: autoscaling/v2 7 | kind: HorizontalPodAutoscaler 8 | metadata: 9 | name: {{ include "whatsapp-proxy-chart.fullname" . }} 10 | labels: 11 | {{- include "whatsapp-proxy-chart.labels" . | nindent 4 }} 12 | spec: 13 | scaleTargetRef: 14 | apiVersion: apps/v1 15 | kind: Deployment 16 | name: {{ include "whatsapp-proxy-chart.fullname" . }} 17 | minReplicas: {{ .Values.autoscaling.minReplicas }} 18 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 19 | metrics: 20 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | - type: Resource 22 | resource: 23 | name: cpu 24 | target: 25 | type: Utilization 26 | averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 27 | {{- end }} 28 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 29 | - type: Resource 30 | resource: 31 | name: memory 32 | target: 33 | type: Utilization 34 | averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 35 | {{- end }} 36 | {{- end }} 37 | -------------------------------------------------------------------------------- /.github/workflows/docker-image.yml: -------------------------------------------------------------------------------- 1 | name: Docker Build and PR for Main 2 | 3 | # Run on every push to main + weekly, Sunday @ midnight 4 | # to keep the image fresh 5 | on: 6 | push: 7 | branches: [ "main" ] 8 | schedule: 9 | - cron: "0 0 * * 0" 10 | 11 | jobs: 12 | 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - name: Docker metadata 21 | id: meta 22 | uses: docker/metadata-action@v4 23 | with: 24 | images: facebook/whatsapp_proxy 25 | tags: | 26 | type=raw,value={{date 'YYYYMMDD'}} 27 | 28 | - name: Set up QEMU 29 | uses: docker/setup-qemu-action@v2 30 | 31 | - name: Set up Docker Buildx 32 | uses: docker/setup-buildx-action@v2 33 | 34 | - name: Login to dockerhub 35 | uses: docker/login-action@v2 36 | with: 37 | username: ${{ secrets.DOCKERHUB_USERNAME }} 38 | password: ${{ secrets.DOCKERHUB_TOKEN }} 39 | 40 | - name: Build and push 41 | uses: docker/build-push-action@v3 42 | with: 43 | context: ./proxy 44 | platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 45 | push: ${{ github.event_name != 'pull_request' }} 46 | tags: | 47 | ${{ steps.meta.outputs.tags }} 48 | facebook/whatsapp_proxy:latest 49 | -------------------------------------------------------------------------------- /charts/lb.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # License found in the LICENSE file in the root directory 4 | # of this source tree. 5 | apiVersion: v1 6 | kind: Service 7 | metadata: 8 | name: whatsapp-proxy-lb-1 9 | annotations: 10 | service.beta.kubernetes.io/do-loadbalancer-name: "whatsapp-proxy-lb-1" 11 | service.beta.kubernetes.io/do-loadbalancer-size-unit: "100" 12 | service.beta.kubernetes.io/do-loadbalancer-disable-lets-encrypt-dns-records: "true" 13 | service.beta.kubernetes.io/do-loadbalancer-healthcheck-port: "80" 14 | service.beta.kubernetes.io/do-loadbalancer-healthcheck-protocol: "tcp" 15 | service.beta.kubernetes.io/do-loadbalancer-healthcheck-check-interval-seconds: "30" 16 | service.beta.kubernetes.io/do-loadbalancer-healthcheck-response-timeout-seconds: "5" 17 | service.beta.kubernetes.io/do-loadbalancer-healthcheck-unhealthy-threshold: "2" 18 | service.beta.kubernetes.io/do-loadbalancer-healthcheck-healthy-threshold: "5" 19 | service.beta.kubernetes.io/do-loadbalancer-enable-proxy-protocol: "true" 20 | spec: 21 | type: LoadBalancer 22 | selector: 23 | app: whatsapp-proxy 24 | ports: 25 | - name: http 26 | protocol: TCP 27 | port: 80 28 | targetPort: 8080 29 | - name: https 30 | protocol: TCP 31 | port: 443 32 | targetPort: 8443 33 | - name: jabber 34 | protocol: TCP 35 | port: 5222 36 | targetPort: 8222 37 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 5 | # Contributing to this library 6 | We want to make contributing to this project as easy and transparent as 7 | possible. 8 | 9 | ## Pull Requests 10 | We actively welcome your pull requests. 11 | 12 | 1. Fork the repo and create your branch from `main`. 13 | 2. If you've added code that should be tested, add tests. 14 | 3. If you've changed APIs, update the documentation. 15 | 4. Ensure the test suite passes. 16 | 5. If you haven't already, complete the Contributor License Agreement ("CLA"). 17 | 18 | ## Contributor License Agreement ("CLA") 19 | In order to accept your pull request, we need you to submit a CLA. You only need 20 | to do this once to work on any of Facebook's open source projects. 21 | 22 | Complete your CLA here: 23 | 24 | ## Issues 25 | We use GitHub issues to track public bugs. Please ensure your description is 26 | clear and has sufficient instructions to be able to reproduce the issue. 27 | 28 | Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe 29 | disclosure of security bugs. In those cases, please go through the process 30 | outlined on that page and do not file a public issue. 31 | 32 | ## License 33 | By contributing to akd, you agree that your contributions will be 34 | licensed under the LICENSE file in the root directory of this source tree. 35 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # License found in the LICENSE file in the root directory 4 | # of this source tree. 5 | 6 | name: CI 7 | 8 | on: 9 | push: 10 | branches: [ "main" ] 11 | pull_request: 12 | types: [opened, reopened, synchronize] 13 | 14 | jobs: 15 | build: 16 | runs-on: ${{ matrix.os }} 17 | strategy: 18 | matrix: 19 | os: [ubuntu-latest] 20 | 21 | steps: 22 | - uses: actions/checkout@v3 23 | with: 24 | # ct needs history to compare 25 | fetch-depth: 0 26 | 27 | - name: Build the Docker image 28 | run: docker build ./proxy/ --tag whatsapp_proxy:$(date +%s) 29 | 30 | - name: Set up Helm 31 | uses: azure/setup-helm@v3 32 | with: 33 | version: v3.10.0 34 | 35 | - uses: actions/setup-python@v4 36 | with: 37 | python-version: '3.9' 38 | check-latest: true 39 | 40 | - name: Helm - Set up chart-testing 41 | uses: helm/chart-testing-action@v2.3.1 42 | 43 | - name: Helm - Run chart-testing (list-changed) 44 | id: list-changed 45 | run: | 46 | changed=$(ct list-changed --target-branch ${{ github.event.repository.default_branch }}) 47 | if [[ -n "$changed" ]]; then 48 | echo "::set-output name=changed::true" 49 | fi 50 | 51 | - name: Helm - Run chart-testing (lint) 52 | run: ct lint --target-branch ${{ github.event.repository.default_branch }} 53 | -------------------------------------------------------------------------------- /charts/whatsapp-proxy-chart/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | 1. Get the application URL by running these commands: 2 | {{- if .Values.ingress.enabled }} 3 | {{- range $host := .Values.ingress.hosts }} 4 | {{- range .paths }} 5 | http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} 6 | {{- end }} 7 | {{- end }} 8 | {{- else if contains "NodePort" .Values.service.type }} 9 | export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "whatsapp-proxy-chart.fullname" . }}) 10 | export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") 11 | echo http://$NODE_IP:$NODE_PORT 12 | {{- else if contains "LoadBalancer" .Values.service.type }} 13 | NOTE: It may take a few minutes for the LoadBalancer IP to be available. 14 | You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "whatsapp-proxy-chart.fullname" . }}' 15 | export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "whatsapp-proxy-chart.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") 16 | echo http://$SERVICE_IP:{{ .Values.service.port }} 17 | {{- else if contains "ClusterIP" .Values.service.type }} 18 | export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "whatsapp-proxy-chart.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") 19 | export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") 20 | echo "Visit http://127.0.0.1:8080 to use your application" 21 | kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT 22 | {{- end }} 23 | -------------------------------------------------------------------------------- /proxy/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # License found in the LICENSE file in the root directory 4 | # of this source tree. 5 | FROM haproxy:lts-alpine 6 | 7 | # Install dependencies for healthcheck support 8 | USER root 9 | RUN apk --no-cache add curl openssl jq bash 10 | 11 | # Customization variables for certificate generation 12 | ARG SSL_IP 13 | ARG SSL_DNS 14 | 15 | # Generate + copy the self-signed certificate settings 16 | WORKDIR /certs 17 | COPY src/generate-certs.sh /usr/local/bin/generate-certs.sh 18 | RUN chmod +x /usr/local/bin/generate-certs.sh && \ 19 | /usr/local/bin/generate-certs.sh && \ 20 | mkdir --parents /etc/haproxy/ssl/ && \ 21 | mv /certs/proxy.whatsapp.net.pem /etc/haproxy/ssl/proxy.whatsapp.net.pem && \ 22 | chown -R haproxy:haproxy /etc/haproxy/ 23 | WORKDIR / 24 | 25 | # Copy the public-ip setting + sshd startup script 26 | COPY --chown=haproxy:haproxy src/set_public_ip_and_start.sh /usr/local/bin/set_public_ip_and_start.sh 27 | RUN chmod +x /usr/local/bin/set_public_ip_and_start.sh 28 | 29 | # Copy the HAProxy configuration 30 | COPY --chown=haproxy:haproxy src/proxy_config.cfg /usr/local/etc/haproxy/haproxy.cfg 31 | RUN chown haproxy:haproxy /usr/local/etc/haproxy 32 | 33 | # Copy + define the healthcheck 34 | COPY src/healthcheck.sh /usr/local/bin/healthcheck.sh 35 | RUN chmod +x /usr/local/bin/healthcheck.sh 36 | HEALTHCHECK --interval=10s --start-period=5s CMD bash /usr/local/bin/healthcheck.sh 37 | 38 | RUN mkdir --parents /home/haproxy/certs && chown haproxy:haproxy /home/haproxy/certs 39 | 40 | # Validate the HAProxy configuration file (sanity check) 41 | RUN haproxy -c -V -f /usr/local/etc/haproxy/haproxy.cfg 42 | 43 | # Revert to the haproxy user for runtime operation 44 | USER haproxy 45 | 46 | # Expose the container-supported network ports 47 | EXPOSE 80/tcp 48 | EXPOSE 8080/tcp 49 | EXPOSE 443/tcp 50 | EXPOSE 8443/tcp 51 | EXPOSE 5222/tcp 52 | EXPOSE 8222/tcp 53 | EXPOSE 8199/tcp 54 | EXPOSE 587/tcp 55 | EXPOSE 7777/tcp 56 | 57 | # This is the startup command which also runs a background job to manage the WAPOX IPs 58 | CMD /usr/local/bin/set_public_ip_and_start.sh 59 | -------------------------------------------------------------------------------- /charts/whatsapp-proxy-chart/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* 2 | Copyright (c) Meta Platforms, Inc. and affiliates. 3 | 4 | License found in the LICENSE file in the root directory 5 | of this source tree. 6 | */}} 7 | {{/* 8 | Expand the name of the chart. 9 | */}} 10 | {{- define "whatsapp-proxy-chart.name" -}} 11 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 12 | {{- end }} 13 | 14 | {{/* 15 | Create a default fully qualified app name. 16 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 17 | If release name contains chart name it will be used as a full name. 18 | */}} 19 | {{- define "whatsapp-proxy-chart.fullname" -}} 20 | {{- if .Values.fullnameOverride }} 21 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} 22 | {{- else }} 23 | {{- $name := default .Chart.Name .Values.nameOverride }} 24 | {{- if contains $name .Release.Name }} 25 | {{- .Release.Name | trunc 63 | trimSuffix "-" }} 26 | {{- else }} 27 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 28 | {{- end }} 29 | {{- end }} 30 | {{- end }} 31 | 32 | {{/* 33 | Create chart name and version as used by the chart label. 34 | */}} 35 | {{- define "whatsapp-proxy-chart.chart" -}} 36 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} 37 | {{- end }} 38 | 39 | {{/* 40 | Common labels 41 | */}} 42 | {{- define "whatsapp-proxy-chart.labels" -}} 43 | helm.sh/chart: {{ include "whatsapp-proxy-chart.chart" . }} 44 | {{ include "whatsapp-proxy-chart.selectorLabels" . }} 45 | {{- if .Chart.AppVersion }} 46 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 47 | {{- end }} 48 | app.kubernetes.io/managed-by: {{ .Release.Service }} 49 | {{- end }} 50 | 51 | {{/* 52 | Selector labels 53 | */}} 54 | {{- define "whatsapp-proxy-chart.selectorLabels" -}} 55 | app.kubernetes.io/name: {{ include "whatsapp-proxy-chart.name" . }} 56 | app.kubernetes.io/instance: {{ .Release.Name }} 57 | app: {{ .Release.Name }} 58 | {{- end }} 59 | 60 | {{/* 61 | Create the name of the service account to use 62 | */}} 63 | {{- define "whatsapp-proxy-chart.serviceAccountName" -}} 64 | {{- if .Values.serviceAccount.create }} 65 | {{- default (include "whatsapp-proxy-chart.fullname" .) .Values.serviceAccount.name }} 66 | {{- else }} 67 | {{- default "default" .Values.serviceAccount.name }} 68 | {{- end }} 69 | {{- end }} 70 | -------------------------------------------------------------------------------- /charts/whatsapp-proxy-chart/templates/service.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # License found in the LICENSE file in the root directory 4 | # of this source tree. 5 | apiVersion: v1 6 | kind: Service 7 | metadata: 8 | name: {{ include "whatsapp-proxy-chart.fullname" . }} 9 | labels: 10 | {{- include "whatsapp-proxy-chart.labels" . | nindent 4 }} 11 | {{- with .Values.service.annotations }} 12 | annotations: 13 | {{- toYaml . | nindent 4 }} 14 | {{- end }} 15 | 16 | spec: 17 | type: {{ .Values.service.type }} 18 | ports: 19 | {{- if .Values.service.http_proxy_port }} 20 | - port: {{ .Values.service.http_proxy_port }} 21 | targetPort: 8080 22 | protocol: TCP 23 | name: http-proxy 24 | {{- end}} 25 | {{- if .Values.service.https_proxy_port }} 26 | - port: {{ .Values.service.https_proxy_port }} 27 | targetPort: 8443 28 | protocol: TCP 29 | name: https-proxy 30 | {{- end}} 31 | {{- if .Values.service.jabber_proxy_port }} 32 | - port: {{ .Values.service.jabber_proxy_port }} 33 | targetPort: 8222 34 | protocol: TCP 35 | name: jabber-proxy 36 | {{- end}} 37 | {{- if .Values.service.jabber_port }} 38 | - port: {{ .Values.service.jabber_port }} 39 | targetPort: 5222 40 | protocol: TCP 41 | name: jabber 42 | {{- end}} 43 | {{- if .Values.service.http_port }} 44 | - port: {{ .Values.service.http_port }} 45 | targetPort: 80 46 | protocol: TCP 47 | name: http 48 | {{- end}} 49 | {{- if .Values.service.https_port }} 50 | - port: {{ .Values.service.https_port }} 51 | targetPort: 443 52 | protocol: TCP 53 | name: https 54 | {{- end}} 55 | {{- if .Values.service.stats_port }} 56 | - port: {{ .Values.service.stats_port }} 57 | targetPort: 8199 58 | protocol: TCP 59 | name: stats 60 | {{- end}} 61 | {{- if .Values.service.media_port }} 62 | - port: {{ .Values.service.media_port }} 63 | targetPort: 587 64 | protocol: TCP 65 | name: media 66 | {{- end}} 67 | {{- if .Values.service.media_proxy_port }} 68 | - port: {{ .Values.service.media_proxy_port }} 69 | targetPort: 7777 70 | protocol: TCP 71 | name: media 72 | {{- end}} 73 | 74 | 75 | selector: 76 | {{- include "whatsapp-proxy-chart.selectorLabels" . | nindent 4 }} 77 | -------------------------------------------------------------------------------- /charts/README.md: -------------------------------------------------------------------------------- 1 | 5 | # WhatsApp Proxy Helm Charts 6 | 7 | This guide outlines how to utilize the [Helm chart](https://helm.sh/) for whatsapp-proxy in order to run the proxy in a [kubernetes](https://kubernetes.io/) cluster. 8 | 9 | **NOTE**: This is quite an advanced topic and requires general knowledge around kubernetes and deployments of containers in distributed infrastructure. A healthy knowledge of kubernetes is required to deploy this. 10 | 11 | [github](https://github.com/WhatsApp/proxy) 12 | [build status](https://github.com/WhatsApp/proxy/actions?query=branch%3Amain) 13 | 14 | ## Before you begin 15 | 16 | ### Setup a Kubernetes Cluster 17 | 18 | The quickest way to setup a Kubernetes cluster is with [Azure Kubernetes Service](https://azure.microsoft.com/en-us/services/kubernetes-service/), [AWS Elastic Kubernetes Service](https://aws.amazon.com/eks/) or [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine/) using their respective quick-start guides. 19 | 20 | For setting up Kubernetes on other cloud platforms or bare-metal servers refer to the Kubernetes [getting started guide](http://kubernetes.io/docs/getting-started-guides/). 21 | 22 | ### Install Helm 23 | 24 | Get the latest [Helm release](https://github.com/helm/helm#install). 25 | 26 | ### Add Helm chart repo 27 | 28 | Once you have Helm installed, add the repo as follows: 29 | 30 | **TBD** 31 | 32 | 36 | 37 | WhatsApp Proxy Helm charts can be also found on [ArtifactHub](https://artifacthub.io/packages/search?repo=WhatsApp). 38 | 39 | ## Search and install charts 40 | 41 | ```console 42 | helm search repo WhatsApp/ 43 | helm install my-release WhatsApp/ 44 | ``` 45 | 46 | **_NOTE_**: For instructions on how to install a chart follow instructions in its `README.md`. 47 | 48 | ## Contributing 49 | 50 | We welcome all contributions. Please refer to [guidelines](../CONTRIBUTING.md) on how to make a contribution. 51 | -------------------------------------------------------------------------------- /proxy/src/healthcheck.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # 4 | # License found in the LICENSE file in the root directory 5 | # of this source tree. 6 | 7 | curl -s -w 2 "http://127.0.0.1:8199/;csv" > /tmp/stats.txt || exit 1 8 | 9 | # First trim off the leading line which is just "#" 10 | # Then convert the ugly CSV to slightly less ugly JSON 11 | # Filter out the lines for *.whatsapp_net backend status 12 | # Select the "check_desc" field (Description of the check result) 13 | # and take all results that do NOT equal "Layer4 check passed" from HAProxy 14 | RESULT=$(tail -n +1 /tmp/stats.txt | jq -R 'split(",")' | jq -c 'select(.[1] != null) | select(.[1] | contains("whatsapp_net"))' | jq --raw-output '.[65]| select(. | test("Layer4 check passed") | not)') 15 | 16 | # # CSV output header row: 17 | # # ["# pxname","svname","qcur","qmax","scur","smax","slim","stot","bin","bout","dreq","dresp","ereq","econ","eresp","wretr","wredis","status","weight","act","bck","chkfail","chkdown","lastchg","downtime","qlimit","pid","iid","sid","throttle","lbtot","tracked","type","rate","rate_lim","rate_max","check_status","check_code","check_duration","hrsp_1xx","hrsp_2xx","hrsp_3xx","hrsp_4xx","hrsp_5xx","hrsp_other","hanafail","req_rate","req_rate_max","req_tot","cli_abrt","srv_abrt","comp_in","comp_out","comp_byp","comp_rsp","lastsess","last_chk","last_agt","qtime","ctime","rtime","ttime","agent_status","agent_code","agent_duration","check_desc","agent_desc","check_rise","check_fall","check_health","agent_rise","agent_fall","agent_health","addr","cookie","mode","algo","conn_rate","conn_rate_max","conn_tot","intercepted","dcon","dses","wrew","connect","reuse","cache_lookups","cache_hits","srv_icur","src_ilim","qtime_max","ctime_max","rtime_max","ttime_max","eint","idle_conn_cur","safe_conn_cur","used_conn_cur","need_conn_est","uweight","agg_server_check_status","-","ssl_sess","ssl_reused_sess","ssl_failed_handshake","h2_headers_rcvd","h2_data_rcvd","h2_settings_rcvd","h2_rst_stream_rcvd","h2_goaway_rcvd","h2_detected_conn_protocol_errors","h2_detected_strm_protocol_errors","h2_rst_stream_resp","h2_goaway_resp","h2_open_connections","h2_backend_open_streams","h2_total_connections","h2_backend_total_streams",""] 18 | 19 | if [ "$RESULT" != "" ] 20 | then 21 | echo "[HEALTHCHECKER] Container failed healthchecks, L4 healthcheck on *.whatsapp.net failed" 22 | echo "[HEALTKCHECKER] Result $RESULT" 23 | exit -1; 24 | fi 25 | 26 | exit 0; 27 | -------------------------------------------------------------------------------- /charts/whatsapp-proxy-chart/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # License found in the LICENSE file in the root directory 4 | # of this source tree. 5 | {{- if .Values.ingress.enabled -}} 6 | {{- $fullName := include "whatsapp-proxy-chart.fullname" . -}} 7 | {{- $svcPort := .Values.service.port -}} 8 | {{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} 9 | {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} 10 | {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} 11 | {{- end }} 12 | {{- end }} 13 | {{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion }} 14 | apiVersion: networking.k8s.io/v1 15 | {{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion }} 16 | apiVersion: networking.k8s.io/v1beta1 17 | {{- else }} 18 | apiVersion: extensions/v1beta1 19 | {{- end }} 20 | kind: Ingress 21 | metadata: 22 | name: {{ $fullName }} 23 | labels: 24 | {{- include "whatsapp-proxy-chart.labels" . | nindent 4 }} 25 | {{- with .Values.ingress.annotations }} 26 | annotations: 27 | {{- toYaml . | nindent 4 }} 28 | {{- end }} 29 | spec: 30 | {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} 31 | ingressClassName: {{ .Values.ingress.className }} 32 | {{- end }} 33 | {{- if .Values.ingress.tls }} 34 | tls: 35 | {{- range .Values.ingress.tls }} 36 | - hosts: 37 | {{- range .hosts }} 38 | - {{ . | quote }} 39 | {{- end }} 40 | secretName: {{ .secretName }} 41 | {{- end }} 42 | {{- end }} 43 | rules: 44 | {{- range .Values.ingress.hosts }} 45 | - host: {{ .host | quote }} 46 | http: 47 | paths: 48 | {{- range .paths }} 49 | - path: {{ .path }} 50 | {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} 51 | pathType: {{ .pathType }} 52 | {{- end }} 53 | backend: 54 | {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} 55 | service: 56 | name: {{ $fullName }} 57 | port: 58 | number: {{ $svcPort }} 59 | {{- else }} 60 | serviceName: {{ $fullName }} 61 | servicePort: {{ $svcPort }} 62 | {{- end }} 63 | {{- end }} 64 | {{- end }} 65 | {{- end }} 66 | -------------------------------------------------------------------------------- /charts/whatsapp-proxy-chart/values.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # License found in the LICENSE file in the root directory 4 | # of this source tree. 5 | 6 | # Default values for whatsapp-proxy-chart. 7 | # This is a YAML-formatted file. 8 | # Declare variables to be passed into your templates. 9 | 10 | replicaCount: 10 11 | 12 | image: 13 | repository: facebook/whatsapp_proxy 14 | pullPolicy: IfNotPresent 15 | # Overrides the image tag whose default is the chart appVersion. 16 | tag: "latest" 17 | 18 | public_ip: "10.0.0.1" 19 | 20 | imagePullSecrets: {} 21 | nameOverride: "" 22 | fullnameOverride: "" 23 | 24 | serviceAccount: 25 | # Specifies whether a service account should be created 26 | create: true 27 | # Annotations to add to the service account 28 | annotations: {} 29 | # The name of the service account to use. 30 | # If not set and create is true, a name is generated using the fullname template 31 | name: "whatsapp-proxy" 32 | 33 | podAnnotations: {} 34 | 35 | podSecurityContext: 36 | # fsGroup: 2000 37 | sysctls: 38 | - name: net.ipv4.ip_unprivileged_port_start 39 | value: "0" 40 | 41 | 42 | securityContext: {} 43 | # capabilities: 44 | # drop: 45 | # - ALL 46 | # readOnlyRootFilesystem: true 47 | # runAsNonRoot: true 48 | # runAsUser: 1000 49 | 50 | service: 51 | type: LoadBalancer 52 | annotations: 53 | service.beta.kubernetes.io/aws-load-balancer-type: nlb 54 | port: 8080 55 | http_port: 80 56 | https_port: 443 57 | jabber_port: 5222 58 | media_port: 587 59 | # http_proxy_port: 8080 60 | # https_proxy_port: 8443 61 | # jabber_proxy_port: 8222 62 | # media_proxy_port: 7777 63 | # stats_port: 8199 64 | 65 | ingress: 66 | enabled: false 67 | className: "" 68 | annotations: {} 69 | # kubernetes.io/ingress.class: nginx 70 | # kubernetes.io/tls-acme: "true" 71 | hosts: 72 | - host: waprox.local 73 | paths: 74 | - path: / 75 | pathType: ImplementationSpecific 76 | tls: [] 77 | # - secretName: chart-example-tls 78 | # hosts: 79 | # - chart-example.local 80 | 81 | resources: {} 82 | # We usually recommend not to specify default resources and to leave this as a conscious 83 | # choice for the user. This also increases chances charts run on environments with little 84 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 85 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 86 | # limits: 87 | # cpu: 100m 88 | # memory: 128Mi 89 | # requests: 90 | # cpu: 100m 91 | # memory: 128Mi 92 | 93 | autoscaling: 94 | enabled: false 95 | minReplicas: 1 96 | maxReplicas: 100 97 | targetCPUUtilizationPercentage: 80 98 | # targetMemoryUtilizationPercentage: 80 99 | 100 | nodeSelector: {} 101 | 102 | tolerations: [] 103 | 104 | affinity: {} 105 | -------------------------------------------------------------------------------- /proxy/src/set_public_ip_and_start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # 4 | # License found in the LICENSE file in the root directory 5 | # of this source tree. 6 | 7 | ## About: 8 | # This script replaces instances of #PUBLIC_IP in the HaProxy configuration files 9 | # with the real public ip. There's an order of priority here which is 10 | # 1. Environment variable 11 | # 2. AWS EC2 Metadata endpoint 12 | # 3. Third-party sources 13 | # If all fails, we'll just not set the destination IP address 14 | 15 | CONFIG_FILE="/usr/local/etc/haproxy/haproxy.cfg" 16 | 17 | ## Custom function to use as curl wrapper 18 | # --silent: to reduce the nois eof response 19 | # --show-error: to show errors in the response 20 | # --fail: to fail on non-200 responses 21 | # --ipv4: to force ipv4 resolution 22 | # --max-time: to set a timeout 23 | function fetch() { 24 | curl --silent --show-error --fail --ipv4 --max-time 2 "$@" 25 | } 26 | 27 | ## PUBLIC_IP supplied from environment variable 28 | if [[ $PUBLIC_IP == '' ]] 29 | then 30 | echo "[PROXYHOST] No public IP address was supplied as an environment variable." 31 | fi 32 | 33 | ## PUBLIC_IP retrieved from AWS EC2 metadata endpoint 34 | if [[ $PUBLIC_IP == '' ]] 35 | then 36 | # Attempt retrieval of the public ip from the meta-data instance 37 | PUBLIC_IP=$(fetch http://169.254.169.254/latest/meta-data/public-ipv4) 38 | if [[ $PUBLIC_IP == '' ]] 39 | then 40 | echo "[PROXYHOST] Failed to retrieve public ip address from AWS URI within 2s" 41 | fi 42 | fi 43 | 44 | ## PUBLIC_IP retrieved from third-party sources 45 | if [[ $PUBLIC_IP == '' ]] 46 | then 47 | urls=( 48 | 'https://icanhazip.com/' 49 | 'https://ipinfo.io/ip' 50 | 'https://domains.google.com/checkip' 51 | ) 52 | 53 | # Attempt retrieval of the public ip from the third-party sources 54 | for url in "${urls[@]}"; do 55 | PUBLIC_IP="$(fetch "${url}")" && break 56 | done 57 | if [[ $PUBLIC_IP == '' ]] 58 | then 59 | echo "[PROXYHOST] Failed to retrieve public ip address from third-party sources within 2s" 60 | fi 61 | fi 62 | 63 | # Now if the public IP is available (test is for not-empty) 64 | # then replace the instances in all haproxy config lines 65 | if [[ -n "$PUBLIC_IP" ]] 66 | then 67 | echo "[PROXYHOST] Public IP address ($PUBLIC_IP) in-place replacement occurring on $CONFIG_FILE" 68 | # Replace all instances of #PUBLIC_IP with the 69 | # haproxy configuration statement for the frontend which set's the destination 70 | # ip to the public ip of the container (which is necessary to determine our IP's 71 | # internally within WA) 72 | sed -i "s/#PUBLIC\_IP/tcp-request connection set-dst ipv4($PUBLIC_IP)/g" $CONFIG_FILE 73 | fi 74 | 75 | # Setup a new, on-the-fly certificate for the HTTPS port (so this re-generates each restart) 76 | pushd /home/haproxy/certs 77 | /usr/local/bin/generate-certs.sh 78 | mv proxy.whatsapp.net.pem /etc/haproxy/ssl/proxy.whatsapp.net.pem 79 | chown haproxy:haproxy /etc/haproxy/ssl/proxy.whatsapp.net.pem 80 | popd 81 | 82 | # Start HAProxy 83 | haproxy -f "$CONFIG_FILE" 84 | 85 | -------------------------------------------------------------------------------- /proxy/src/generate-certs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # 4 | # License found in the LICENSE file in the root directory 5 | # of this source tree. 6 | 7 | echo "----------------------------" 8 | echo "| SSL Certificate Generation |" 9 | echo "----------------------------" 10 | echo 11 | 12 | export RANDOM_CA=$(head -c 60 /dev/urandom | tr -dc 'a-zA-Z0-9') 13 | export CA_KEY="ca-key.pem" 14 | export CA_CERT="ca.pem" 15 | export CA_SUBJECT="${RANDOM_CA}" 16 | export CA_EXPIRE="36500" # 100 years 17 | 18 | export SSL_CONFIG="openssl.cnf" 19 | export SSL_KEY="key.pem" 20 | export SSL_CSR="key.csr" 21 | export SSL_CERT="cert.pem" 22 | export SSL_SIZE="4096" 23 | export SSL_EXPIRE="3650" # 10 years 24 | 25 | export RANDOM_SSL=$(head -c 60 /dev/urandom | tr -dc 'a-zA-Z0-9') 26 | export SSL_SUBJECT="${RANDOM_SSL}.net" 27 | export SSL_DNS=${SSL_DNS} 28 | export SSL_IP=${SSL_IP} 29 | 30 | export DEBUG=${DEBUG:=1} 31 | 32 | echo "--> Certificate Authority" 33 | echo "Generating certs for ${SSL_SUBJECT}" 34 | 35 | if [[ -e ./${CA_KEY} ]]; then 36 | echo "====> Using existing CA Key ${CA_KEY}" 37 | else 38 | echo "====> Generating new CA key ${CA_KEY}" 39 | openssl genrsa -out ${CA_KEY} 4096 40 | fi 41 | 42 | if [[ -e ./${CA_CERT} ]]; then 43 | echo "====> Using existing CA Certificate ${CA_CERT}" 44 | else 45 | echo "====> Generating new CA Certificate ${CA_CERT}" 46 | openssl req -x509 -new -nodes -key ${CA_KEY} -days ${CA_EXPIRE} -out ${CA_CERT} -subj "/CN=${CA_SUBJECT}" || exit 1 47 | fi 48 | 49 | [[ -n $DEBUG ]] && cat $CA_CERT 50 | 51 | echo "====> Generating new config file ${SSL_CONFIG}" 52 | cat > ${SSL_CONFIG} <> ${SSL_CONFIG} <> ${SSL_CONFIG} 74 | done 75 | 76 | if [[ -n ${SSL_IP} ]]; then 77 | ip=(${SSL_IP}) 78 | for i in "${!ip[@]}"; do 79 | echo IP.$((i+1)) = ${ip[$i]} >> ${SSL_CONFIG} 80 | done 81 | fi 82 | fi 83 | 84 | echo "====> Generating new SSL KEY ${SSL_KEY}" 85 | openssl genrsa -out ${SSL_KEY} ${SSL_SIZE} || exit 1 86 | 87 | echo "====> Generating new SSL CSR ${SSL_CSR}" 88 | openssl req -new -key ${SSL_KEY} -out ${SSL_CSR} -subj "/CN=${SSL_SUBJECT}" -config ${SSL_CONFIG} || exit 1 89 | 90 | echo "====> Generating new SSL CERT ${SSL_CERT}" 91 | openssl x509 -req -in ${SSL_CSR} -CA ${CA_CERT} -CAkey ${CA_KEY} -CAcreateserial -out ${SSL_CERT} \ 92 | -days ${SSL_EXPIRE} -extensions v3_req -extfile ${SSL_CONFIG} || exit 1 93 | 94 | echo "====> Generating SSL CERT / KEY COMBO proxy.whatsapp.net.pem" 95 | cat ${SSL_KEY} > proxy.whatsapp.net.pem 96 | cat ${SSL_CERT} >> proxy.whatsapp.net.pem 97 | 98 | echo "Certificate generation completed." 99 | 100 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 5 | # Code of Conduct 6 | 7 | ## Our Pledge 8 | 9 | In the interest of fostering an open and welcoming environment, we as 10 | contributors and maintainers pledge to make participation in our project and 11 | our community a harassment-free experience for everyone, regardless of age, body 12 | size, disability, ethnicity, sex characteristics, gender identity and expression, 13 | level of experience, education, socio-economic status, nationality, personal 14 | appearance, race, religion, or sexual identity and orientation. 15 | 16 | ## Our Standards 17 | 18 | Examples of behavior that contributes to creating a positive environment 19 | include: 20 | 21 | * Using welcoming and inclusive language 22 | * Being respectful of differing viewpoints and experiences 23 | * Gracefully accepting constructive criticism 24 | * Focusing on what is best for the community 25 | * Showing empathy towards other community members 26 | 27 | Examples of unacceptable behavior by participants include: 28 | 29 | * The use of sexualized language or imagery and unwelcome sexual attention or 30 | advances 31 | * Trolling, insulting/derogatory comments, and personal or political attacks 32 | * Public or private harassment 33 | * Publishing others' private information, such as a physical or electronic 34 | address, without explicit permission 35 | * Other conduct which could reasonably be considered inappropriate in a 36 | professional setting 37 | 38 | ## Our Responsibilities 39 | 40 | Project maintainers are responsible for clarifying the standards of acceptable 41 | behavior and are expected to take appropriate and fair corrective action in 42 | response to any instances of unacceptable behavior. 43 | 44 | Project maintainers have the right and responsibility to remove, edit, or 45 | reject comments, commits, code, wiki edits, issues, and other contributions 46 | that are not aligned to this Code of Conduct, or to ban temporarily or 47 | permanently any contributor for other behaviors that they deem inappropriate, 48 | threatening, offensive, or harmful. 49 | 50 | ## Scope 51 | 52 | This Code of Conduct applies within all project spaces, and it also applies when 53 | an individual is representing the project or its community in public spaces. 54 | Examples of representing a project or community include using an official 55 | project e-mail address, posting via an official social media account, or acting 56 | as an appointed representative at an online or offline event. Representation of 57 | a project may be further defined and clarified by project maintainers. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported by contacting the project team at . All 63 | complaints will be reviewed and investigated and will result in a response that 64 | is deemed necessary and appropriate to the circumstances. The project team is 65 | obligated to maintain confidentiality with regard to the reporter of an incident. 66 | Further details of specific enforcement policies may be posted separately. 67 | 68 | Project maintainers who do not follow or enforce the Code of Conduct in good 69 | faith may face temporary or permanent repercussions as determined by other 70 | members of the project's leadership. 71 | 72 | ## Attribution 73 | 74 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 75 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 76 | 77 | [homepage]: https://www.contributor-covenant.org 78 | 79 | For answers to common questions about this code of conduct, see 80 | https://www.contributor-covenant.org/faq 81 | -------------------------------------------------------------------------------- /proxy/src/proxy_config.cfg: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # License found in the LICENSE file in the root directory 4 | # of this source tree. 5 | 6 | ## This file contains the HAProxy configuration for the WhatsApp proxy host use-case 7 | 8 | # Documentation 9 | # https://cbonte.github.io/haproxy-dconv/2.5/configuration.html#maxconn 10 | 11 | global 12 | # Default buffer size is 16kB and we need 2 buffers/conn. 13 | # For WA we want a lower memory footprint, so we lower it to 4kB. 14 | tune.bufsize 4096 15 | 16 | # We limit the connection count to 27.5K connections concurrently such that 17 | # to fail healthchecks we'd have to actually have health problems rather than just reject connections 18 | # 19 | # Upon the 27501'th connection on a proxy port, it'll be REJECTED in favor of reconnecting to a different proxy host 20 | # which will mean the existing connection will be serviced without the host being recycled 21 | maxconn 27500 22 | 23 | # Adds some randomness on the interval delay between two consecutive health checks 24 | spread-checks 5 25 | 26 | # # Log to local rsyslogd (levels: emerg alert crit err warning notice info debug) 27 | # log 127.0.0.1 local0 notice 28 | ssl-server-verify none 29 | 30 | # ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 31 | # ssl-default-bind-options prefer-client-ciphers no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets 32 | # ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 33 | # ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets 34 | 35 | 36 | defaults 37 | mode tcp 38 | # we don't retain this information for long since connections are load balanced they'll end up on a new host 39 | timeout client-fin 1s 40 | timeout server-fin 1s 41 | timeout connect 5s 42 | timeout client 200s 43 | timeout server 200s 44 | # log global 45 | # option tcplog 46 | default-server inter 10s fastinter 1s downinter 3s error-limit 50 47 | 48 | listen stats 49 | bind :::8199 50 | mode http 51 | http-request use-service prometheus-exporter if { path /metrics } 52 | stats uri / 53 | 54 | # These expect direct connections from clients or through NLB balanced 55 | # connections 56 | frontend haproxy_v4_http 57 | maxconn 27495 58 | #PUBLIC_IP 59 | 60 | bind ipv4@*:80 61 | bind ipv4@*:8080 accept-proxy 62 | 63 | default_backend wa_http 64 | 65 | frontend haproxy_v4_https 66 | maxconn 27495 67 | #PUBLIC_IP 68 | 69 | bind ipv4@*:443 ssl crt /etc/haproxy/ssl/proxy.whatsapp.net.pem 70 | bind ipv4@*:8443 ssl crt /etc/haproxy/ssl/proxy.whatsapp.net.pem accept-proxy 71 | 72 | default_backend wa 73 | 74 | frontend haproxy_v4_xmpp 75 | maxconn 27495 76 | #PUBLIC_IP 77 | 78 | bind ipv4@*:5222 79 | bind ipv4@*:8222 accept-proxy 80 | 81 | default_backend wa 82 | 83 | frontend haproxy_v4_whatsapp_net 84 | maxconn 27495 85 | #PUBLIC_IP 86 | 87 | bind ipv4@*:587 88 | bind ipv4@*:7777 89 | 90 | default_backend wa_whatsapp_net 91 | 92 | backend wa_whatsapp_net 93 | default-server check inter 60000 observe layer4 94 | server whatsapp_net_443 whatsapp.net:443 95 | 96 | backend wa 97 | default-server check inter 60000 observe layer4 send-proxy 98 | server g_whatsapp_net_5222 g.whatsapp.net:5222 99 | 100 | backend wa_http 101 | default-server check inter 60000 observe layer4 send-proxy 102 | server g_whatsapp_net_80 g.whatsapp.net:80 103 | -------------------------------------------------------------------------------- /charts/whatsapp-proxy-chart/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # License found in the LICENSE file in the root directory 4 | # of this source tree. 5 | apiVersion: apps/v1 6 | kind: Deployment 7 | metadata: 8 | name: {{ include "whatsapp-proxy-chart.fullname" . }} 9 | labels: 10 | {{- include "whatsapp-proxy-chart.labels" . | nindent 4 }} 11 | spec: 12 | {{- if not .Values.autoscaling.enabled }} 13 | replicas: {{ .Values.replicaCount }} 14 | {{- end }} 15 | selector: 16 | matchLabels: 17 | {{- include "whatsapp-proxy-chart.selectorLabels" . | nindent 6 }} 18 | template: 19 | metadata: 20 | {{- with .Values.podAnnotations }} 21 | annotations: 22 | {{- toYaml . | nindent 8 }} 23 | {{- end }} 24 | labels: 25 | {{- include "whatsapp-proxy-chart.selectorLabels" . | nindent 8 }} 26 | spec: 27 | {{- with .Values.imagePullSecrets }} 28 | imagePullSecrets: 29 | {{- toYaml . | nindent 8 }} 30 | {{- end }} 31 | serviceAccountName: {{ include "whatsapp-proxy-chart.serviceAccountName" . }} 32 | securityContext: 33 | {{- toYaml .Values.podSecurityContext | nindent 8 }} 34 | containers: 35 | - name: {{ .Chart.Name }} 36 | securityContext: 37 | {{- toYaml .Values.securityContext | nindent 12 }} 38 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" 39 | imagePullPolicy: {{ .Values.image.pullPolicy }} 40 | ports: 41 | {{- if .Values.service.http_proxy_port }} 42 | - name: http-proxy 43 | containerPort: 8080 44 | protocol: TCP 45 | {{- end}} 46 | {{- if .Values.service.http_port }} 47 | - name: http 48 | containerPort: 80 49 | protocol: TCP 50 | {{- end}} 51 | {{- if .Values.service.https_proxy_port }} 52 | - name: https-proxy 53 | containerPort: 8443 54 | protocol: TCP 55 | {{- end}} 56 | {{- if .Values.service.https_port }} 57 | - name: https 58 | containerPort: 443 59 | protocol: TCP 60 | {{- end}} 61 | {{- if .Values.service.jabber_proxy_port }} 62 | - name: jabber-proxy 63 | containerPort: 8222 64 | protocol: TCP 65 | {{- end}} 66 | {{- if .Values.service.jabber_port }} 67 | - name: jabber 68 | containerPort: 5222 69 | protocol: TCP 70 | {{- end}} 71 | {{- if .Values.service.media_port }} 72 | - name: media 73 | containerPort: 587 74 | protocol: TCP 75 | {{- end}} 76 | {{- if .Values.service.media_proxy_port}} 77 | - name: media-proxy 78 | containerPort: 7777 79 | protocol: TCP 80 | {{- end}} 81 | {{- if .Values.service.stats_port }} 82 | - name: stats 83 | containerPort: 8199 84 | protocol: TCP 85 | {{- end}} 86 | readinessProbe: 87 | exec: 88 | command: 89 | - /usr/local/bin/healthcheck.sh 90 | initialDelaySeconds: 30 91 | periodSeconds: 30 92 | resources: 93 | {{- toYaml .Values.resources | nindent 12 }} 94 | env: 95 | - name: "PUBLIC_IP" 96 | value: "{{ .Values.public_ip }}" 97 | {{- with .Values.nodeSelector }} 98 | nodeSelector: 99 | {{- toYaml . | nindent 8 }} 100 | {{- end }} 101 | {{- with .Values.affinity }} 102 | affinity: 103 | {{- toYaml . | nindent 8 }} 104 | {{- end }} 105 | {{- with .Values.tolerations }} 106 | tolerations: 107 | {{- toYaml . | nindent 8 }} 108 | {{- end }} 109 | -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | # Frequently asked questions 2 | 3 | ## Getting started 4 | 5 | First you need to clone the repository. You can do this with 6 | 7 | ```bash 8 | git clone https://github.com/WhatsApp/proxy.git 9 | ``` 10 | 11 | ## Common issues 12 | 13 | ### (1) The container won't build on Windows with `set_public_ip_and_start.sh: Not found` 14 | 15 | This is likely a line encoding issue since the application is expecting unix-style line 16 | encoding (EOL not CRLF). This resolved in PR [72](https://github.com/WhatsApp/proxy/pull/72) 17 | and you should just need to pull the latest changes and try again. 18 | 19 | ### (2) I want to share my proxy to the community 20 | 21 | This is great! We have created a dedicated GitHub issue to share these proxies. Access it here: [Issue #92](https://github.com/WhatsApp/proxy/issues/92) 22 | 23 | ### (3) My proxy isn't accessible publicly 24 | 25 | Some common problems to investigate 26 | 27 | 1. Are the necessary ports open on your host? 28 | 2. If running in a cloud, are the necessary ports open on the cloud provider's firewall? 29 | 3. Can you access the statistics port (8199) locally? (at `http://127.0.0.1:8199`) If not, can you check the health of the container (if it's up and running) with `docker ps`? 30 | 31 | ### (4) What is the port configuration for this service + the client? 32 | 33 | When only a host is specified in the app (ip or domain name), the client will attempt to connect to port 443 by default. 34 | 35 | You can also re-map the ports exposed from the proxy to whatever you want. For example if you have a different service running on port 80, you can send the proxy container's port 80 to port 8081 for example. You can do this by changing the port mapping from `80:80` to `8081:80`. The format for these ports is `{HOST_MACHINE_PORT}:{CONTAINER_PORT}` so you're stating that 36 | the container's port 80 binds to my machine's 8081, which is what will be exposed to the internet. An example of these port mapping can be found in the provided [docker-compose.yml](https://github.com/WhatsApp/proxy/blob/main/proxy/ops/docker-compose.yml#L14) 37 | 38 | **NOTE** There is a caveat however to re-mapping port 443. Port 443 on the proxy runs a TLS encryption and the client knows to utilize TLS for connections to that port. All **OTHER** ports are expected to not have TLS. There is currently no way to configure this in the client so if you re-map the container's port 443 then it won't be able to connect. You can however safely remap ports 80 and 5222 freely and they should just work. 39 | 40 | ### (5) Does the proxy support HTTP(S) or SOCKS? 41 | 42 | WhatsApp currently does **NOT** support anything besides TCP proxying. This is just copying the incoming bytes 43 | to WhatsApp on the other end. So we don't support running through any intermediary that is a HTTP proxy. 44 | 45 | You are free to run your own pure TCP proxy as you see fit however, as long as it forwards to `g.whatsapp.net`. You aren't required to use this realization. 46 | 47 | ### (6) haproxy `cannot bind socket (Permission denied)` 48 | 49 | See https://github.com/docker-library/haproxy/issues/160 for possible solutions. 50 | 51 | ### (7) Why do I need to expose 7 ports? 52 | 53 | The short answer is you don't. The primary ports WhatsApp uses can be 80, 443, or 5222. The other 3 connection ports are if you're hosting the 54 | proxy in an environment that will send the PROXY header (if you don't know what this is, you likely don't need to expose these ports). 55 | 56 | The last port is 8199 which is the "statistics" port of the underlying proxy process, HAProxy. We find this port is helpful for testing if your system is alive 57 | and running properly or not. While the other ports 80 and 443 look like normal HTTP and HTTPS ports, your browser will not be able to connect to them as they 58 | are just TCP forwarding the traffic to a server that is **NOT** HTTP based. For this reason, the statistics is a quick and easy way to check host health. 59 | 60 | That being said, if you're worried about detection, once running we recommend disabling all non-necessary ports. A **typical** host configuration would likely 61 | just expose 80, 443, and 5222. You may re-map those however as you see fit, see point (4) above. 62 | 63 | ### (8) I'm seeing something like `executor failed running [/bin/sh -c apk --no-cache add curl openssl jq bash]: exit code: 4` 64 | 65 | Please try re-building the container without the docker cache enabled. 66 | 67 | ```bash 68 | docker build --no-cache proxy/ -t whatsapp-proxy:1.0 69 | ``` 70 | 71 | If you're still seeing a problem, you may fill out a bug report in the issues filling out all the requested information in the template. 72 | 73 | ### (9) Container is stuck after certificate generation 74 | 75 | Actually thanks to recent community fixes, HAProxy is no longer printing 76 | any warning messages. Your host is actually running in interactive mode. You should be able to navigate to the host's port 8199 to view the statistics page ([http://localhost:8199](http://localhost:8199) on the machine running the proxy). 77 | 78 | Related issue [#71](https://github.com/WhatsApp/proxy/issues/71) 79 | 80 | ### (10) Why isn't there a pre-built image on DockerHub? 81 | 82 | Apologies in the delay, but it takes some time to organize access to the 83 | correct repositories. We're happy to announce there is now a pre-built image 84 | based on the latest version in this repository. We'll strive to keep it 85 | up-to-date as well. You can pull it (without needing to build locally) from 86 | 87 | ```bash 88 | docker pull facebook/whatsapp_proxy:latest 89 | ``` 90 | 91 | After you've pulled the image, you can then run it with the same run commands as before except substituting in `facebook/whatsapp_proxy:latest` instead of `whatsapp_proxy:1.0`. This will point to the latest image for you without having to worry about building it yourself. Example run command might be 92 | 93 | ```bash 94 | docker run -it -p 80:80 -p 443:443 -p 5222:5222 facebook/whatsapp_proxy:latest 95 | ``` 96 | -------------------------------------------------------------------------------- /charts/wa_logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 5 | # WhatsApp Chat Proxy 6 | 7 | [github](https://github.com/WhatsApp/proxy) 8 | [![CI](https://github.com/WhatsApp/proxy/actions/workflows/ci.yml/badge.svg)](https://github.com/WhatsApp/proxy/actions/workflows/ci.yml) 9 | 10 | If you are unable to connect directly to WhatsApp, a proxy can be used as a gateway between you and our servers. To help yourself or others re-establish connection to WhatsApp, you can set up a proxy server. 11 | 12 | If you already have a proxy to use, you can connect it to WhatsApp by following the steps in this [article](https://faq.whatsapp.com/520504143274092). 13 | 14 | ## Frequently asked questions 15 | 16 | **PLEASE READ THIS BEFORE OPENING AN ISSUE** We have an FAQ, which you can find here: [FAQ.md](https://github.com/whatsapp/proxy/blob/main/FAQ.md) 17 | 18 | ## What you'll need 19 | 20 | 1. [Docker](https://docs.docker.com/engine/install/) (enable Docker on startup if your host system allows) 21 | 2. [Docker compose](https://docs.docker.com/compose/) (optional) 22 | 23 | ## Setting up your proxy 24 | 25 | **UPDATE** There is now a pre-built image hosted in Meta's DockerHub repository. You no longer need to build the default image (if you don't want to customize it of course). 26 | 27 | ```bash 28 | docker pull facebook/whatsapp_proxy:latest 29 | ``` 30 | 31 | You can then skip down to **Running the proxy** and substitute any tag of `whatsapp_proxy:1.0` with `facebook/whatsapp_proxy:latest`. 32 | 33 | ### 1. Clone the repository to your local machine 34 | 35 | ```bash 36 | git clone https://github.com/WhatsApp/proxy.git 37 | ``` 38 | 39 | You should see a folder called `proxy` created in the current directory. 40 | 41 | ### 2. [Install Docker](https://docs.docker.com/get-docker/) for your system 42 | 43 | To confirm Docker is successfully installed: 44 | 45 | ```bash 46 | docker --version 47 | ``` 48 | 49 | should display a line similar to `Docker version 20.10.21, build baeda1f`. 50 | 51 | ### 2. (Optional) Install Docker compose 52 | 53 | For Linux users, if your [version of Docker](https://docs.docker.com/desktop/install/linux-install/) doesn't come pre-installed with Docker compose, you can install a one-off version (For Linux). 54 | 55 | ```bash 56 | # Download the pkg 57 | sudo curl -L https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m) -o /usr/bin/docker-compose 58 | # Enable execution of the script 59 | sudo chmod +x /usr/bin/docker-compose 60 | ``` 61 | 62 | ### 3. Build the proxy host container 63 | 64 | Navigate to the repo directory 65 | 66 | ```bash 67 | cd proxy 68 | ``` 69 | 70 | Build the proxy host container with 71 | 72 | ```bash 73 | docker build proxy/ -t whatsapp_proxy:1.0 74 | ``` 75 | 76 | You should see a message similar to `[+] Building 6.6s (18/18) FINISHED`. The container will be compiled and tagged as `whatsapp_proxy:1.0` for easy reference. 77 | 78 | ## Running the proxy 79 | 80 | ### Manually execute the container 81 | 82 | You can manually execute the Docker container with the following `docker` command 83 | 84 | ```bash 85 | docker run -it -p 80:80 -p 443:443 -p 5222:5222 -p 8080:8080 -p 8443:8443 -p 8222:8222 -p 8199:8199 -p 587:587 -p 7777:7777 whatsapp_proxy:1.0 86 | ``` 87 | 88 | You will see lines ending with `Certificate generation completed.`. The HAProxy is running in the background and will continue to do so until you close this process. 89 | 90 | ### Check your connection 91 | 92 | To confirm HAProxy is running, visit `http://:8199` where `` is your **public** IP address. You can also use this link to monitor proxy statistics. 93 | 94 | > NOTE: If your public IP address is not accessible, you will need to enable port forwarding (for the ports above) for the router/gateway you are using. Since this operation is device-specific, we are not going to go into it in details in this doc. 95 | 96 | If you prefer OpenMetrics output you can use `http://:8199/metrics` for monitoring HAProxy metrics. 97 | 98 | # Miscellanous 99 | 100 | ## An Overview of the WhatsApp Proxy Architecture 101 | 102 | Depending on the scenario in which you utilize your proxy, the proxy container exposes multiple ports. The basic ports may include: 103 | 104 | 1. 80: Standard web traffic (HTTP) 105 | 2. 443: Standard web traffic, encrypted (HTTPS) 106 | 3. 5222: Jabber protocol traffic (WhatsApp default) 107 | 4. 587 or 7777: *.whatsapp.net traffic including media (HTTPS) 108 | 109 | There are also ports configured which accept incoming [proxy headers](https://www.haproxy.com/blog/use-the-proxy-protocol-to-preserve-a-clients-ip-address/) (version 1 or 2) 110 | on connections. If you have a network load balancer you can preserve the client IP address if you want. 111 | 112 | 1. 8080: Standard web traffic (HTTP) with PROXY protocol expected 113 | 2. 8443: Standard web traffic, encrypted (HTTPS) with PROXY protocol expected 114 | 3. 8222: Jabber protocol traffic (WhatsApp default) with PROXY protocol expected 115 | 116 | ## Certificate generation for SSL encrypted ports 117 | 118 | Ports 443 and 8443 are protected by a self-signed encryption certificate generated at container start time. There are some custom options should you wish to tweak the settings of the generated certificates 119 | 120 | * `SSL_DNS` comma separate list of alternative hostnames, no default 121 | * `SSL_IP` comma separate list of alternative IPs, no default 122 | 123 | They can be set with commands like 124 | 125 | ```bash 126 | docker build . --build-arg SSL_DNS=test.example.com 127 | ``` 128 | 129 | ## Advanced 130 | 131 | ### Automate the container lifecycle with Docker compose 132 | 133 | Docker Compose is an automated tool to run multi-container deployments, but it also helps automate the command-line arguments necessary to run a single container. It is a YAML definition file that denotes all the settings to start up and run the container. It also has restart strategies in the event the container crashes or self-restarts. Docker Compose helps manage your container setup and necessary port forwards without user interaction. We recommend utilizing Docker Compose because you usually don’t want to manually run the container outside of testing scenarios. 134 | 135 | We provide a sample [docker-compose.yml](./proxy/ops/docker-compose.yml) file for you which defines a standard deployment of the proxy container. 136 | 137 | Once Docker compose is installed, you can test your specific configuration by running Docker compose interactively with: 138 | 139 | ```bash 140 | docker compose -f /path/to/this/repo/docker-compose.yml up 141 | ``` 142 | 143 | This will allow you to see the output from the build + container hosting process and check that everything is set up correctly. 144 | 145 | When you are ready to run the container as a service, do\*: 146 | 147 | ```bash 148 | docker compose -f /path/to/this/repo/docker-compose.yml up -d 149 | ``` 150 | 151 | *\*Note the `-d` flag which means "daemonize" and run as a service.* 152 | 153 | To stop the container you can similarly do: 154 | 155 | ```bash 156 | docker compose down 157 | ``` 158 | 159 | ### Automate host reboots with Docker compose 160 | 161 | Once you have Docker compose set up, you can also automate the deployment for host reboots by utilizing a `systemd` service (if your hosting environment supports it). 162 | 163 | We provide a sample [`docker_boot.service`](./proxy/ops/docker_boot.service) service definition for you which you should customize to your own environment. 164 | 165 | To install and setup the `systemd` service\*: 166 | 167 | ```bash 168 | # Copy the service definition to systemd folder 169 | cp -v docker_boot.service /etc/systemd/system/ 170 | # Enable starting the service on startup 171 | systemctl enable docker_boot.service 172 | # Start the service (will docker compose up the container) 173 | systemctl start docker_boot.service 174 | # Check container status with 175 | docker ps 176 | ``` 177 | 178 | *\*Make sure to update the path to your specific `docker-compose.yml` file in the service definition `docker_boot.service`* 179 | 180 | ## Kubernetes deployment 181 | 182 | If you would like to configure your proxy using Kubernetes, or run the Docker runtime through Kubernetes, please see our [Helm chart README](./charts/README.md) 183 | 184 | Read more about other type of deployments [here](/docs/deployments.md). 185 | 186 | # Contributors 187 | 188 | ------------ 189 | 190 | The authors of this code are Sean Lawlor ([@slawlor](https://github.com/slawlor)). 191 | 192 | To learn more about contributing to this project, [see this document](https://github.com/whatsapp/proxy/blob/main/CONTRIBUTING.md). 193 | 194 | # License 195 | 196 | ------------ 197 | 198 | This project is licensed under [MIT](https://github.com/novifinancial/akd/blob/main/LICENSE-MIT). 199 | -------------------------------------------------------------------------------- /charts/whatsapp-proxy-chart/deployments/README.md: -------------------------------------------------------------------------------- 1 | # Chart Deployment 2 | This document explains how to create a Kuberentes cluster in one of the popular public cloud providers (e.g. AWS, GCP), and deploy the WhatsApp proxy Helm chart to your Kubernetes cluster. 3 | 4 | ## Prerequisites 5 | 6 | ### Helm 7 | 8 | Install Helm using one of the methods outlined in this page: https://helm.sh/docs/intro/install/ 9 | 10 | ## Create a Kubernetes cluster 11 | To deploy your Helm chart, you will first need to create a Kubernetes cluster. You can create a simple Kubernetes cluster on a single server using one of the following tools: 12 | 13 | * minikube (https://minikube.sigs.k8s.io/) 14 | * microk8s (https://microk8s.io/) 15 | * K3s (https://k3s.io/) 16 | * kind (https://kind.sigs.k8s.io/) 17 | 18 | These single-node Kubernetes clusters are relatively straightforward to set up, and are therefore not discussed here further. To see installation steps, please follow the links provided for each of the tools above. 19 | 20 | The rest of the document will explain how to create a Kubernetes cluster on a managed Kubernetes service from one of the public cloud providers, such as: 21 | 22 | * AWS: Amazon EKS (Elastic Kubernetes Service) 23 | * GCP: GKE (Google Kubernetes Engine) 24 | 25 | ### Create a Kubernetes cluster on Amazon Elastic Kubernetes Service (EKS) 26 | To create a Kubernetes cluster on Amazon EKS, you will need an AWS account: https://aws.amazon.com/ 27 | 28 | #### Install AWS CLI 29 | 30 | Once you have created an AWS account, download the AWS CLI tool by following the steps for your operating system as outlined in this page: https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html#getting-started-install-instructions 31 | 32 | Once the AWS CLI tool has been installed, you will need to configure it. You will first need an access key for your AWS user account. If you don't have an access key ID and secret access key pair to use, follow the steps below: 33 | 34 | 1. Go to https://console.aws.amazon.com/iamv2/home#/users. 35 | 2. In the list of users, click on your user name. 36 | 3. Go to the 'security credentials' tab. 37 | 4. Under 'Access keys', click 'Create access key'. If that button is grayed out and there are already two existing keys, then you already have two active access keys. Either use one of those keys, or delete one and create a new key. 38 | 5. In the dialog box that opens, make sure to download the CSV file that contains your access key ID and secret access key, as the secret access key is only displayed to you once. 39 | 40 | Now, in a terminal window, type: 41 | 42 | ``` 43 | aws configure 44 | ``` 45 | 46 | Follow the steps to enter your access key ID and secret access key. Enter the region that you will be using (if you are unsure, you can use `us-east-1`). For output type, use `json`. 47 | 48 | #### Install eksctl 49 | 50 | Next, you will need to install the `eksctl` command-line tool. This tool will help you create a Kubernetes cluster. 51 | 52 | > **Note**: You can also use the AWS Management Console to create a Kubernetes cluster. However, depending on the configuration parameters you select during cluster creation, you may have issues connecting to your cluster using the `kubectl` tool. This is due to the way EKS assigns the cluster creator identity when creating the cluster through the AWS Management Console, and requires that the identity accessing the cluster be the same as the cluster creator identity. By using the `eksctl` tool to create the cluster, you will not have issues accessing the cluster using `kubectl`. Therefore, in this document, we will be using `eksctl` to create the cluster. 53 | > 54 | > For more information on this issue, see https://docs.aws.amazon.com/eks/latest/userguide/troubleshooting.html#unauthorized 55 | 56 | Install the `eksctl` tool for your operating system as outlined in this page: https://docs.aws.amazon.com/eks/latest/userguide/eksctl.html 57 | 58 | #### Create your Kubernetes cluster 59 | 60 | To create your Kubernetes cluster, run the following command: 61 | 62 | ``` 63 | eksctl create cluster --name --region 64 | ``` 65 | 66 | Replace `` with a name of your choosing for your Kubernetes cluster, and `` with the name of the region that you chose to use in AWS (e.g. `us-east-1`). 67 | 68 | **Note**: This step will take about 15-20 minutes to complete. The logs will show the status of the cluster creation process. 69 | 70 | **Note**: If you get an error like below: 71 | 72 | ``` 73 | Cannot create cluster 'sample-cluster' because us-east-1d, the targeted availability zone, does not currently have sufficient capacity to support the cluster. Retry and choose from these availability zones: us-east-1a, us-east-1b, us-east-1c 74 | ``` 75 | 76 | Rerun the above command, adding the `--zones` paramter with at least two availability zones from the list of suggested availability zones shown in the error. Your modified command should look similar to the following: 77 | 78 | ``` 79 | eksctl create cluster --name --region --zones us-east-1a,us-east-1b 80 | ``` 81 | 82 | #### Install kubectl 83 | 84 | Next, you will need to install the `kubectl` tool. `kubectl` is your main point of interaction with your Kubenernetes cluster, once you create your cluster. 85 | 86 | Follow the instructions for your operating system as outlined in this page: https://kubernetes.io/docs/tasks/tools/ 87 | 88 | > **Important**: The version of `kubectl` you install is important. The minor version of the installed `kubectl` cannot be more than 1 version different from your Kubernetes cluster version. 89 | > 90 | > You can check the version of your Kubernetes cluster in EKS by going to https://console.aws.amazon.com/eks/. In the list, you can see the Kubernetes version for your cluster. 91 | > 92 | > For example, if your cluster's Kubernetes version is 1.23, the `kubectl` version that you install cannot be lower than 1.22.x or higher than 1.24.x. You should always install the latest version of `kubectl` that is compatible with your Kubernetes cluster, so in this case, the latest `kubectl` version that you can install is 1.24.9. 93 | 94 | #### Connect `kubectl` to your Kubernetes cluster 95 | 96 | To access your Kubernetes from `kubectl`, run the following command: 97 | 98 | ``` 99 | aws eks --region update-kubeconfig --name 100 | ``` 101 | 102 | Replace `` with the name of the AWS region you used, and `` with the name of the cluster you created. 103 | 104 | If this command was successful, you should now be able to access your Kubernetes cluster using `kubectl`. Run the following command: 105 | 106 | ``` 107 | kubectl get svc 108 | ``` 109 | 110 | You should get an output like the following: 111 | 112 | ``` 113 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 114 | kubernetes ClusterIP 10.100.0.1 443/TCP 2d15h 115 | ``` 116 | 117 | #### Install the AWS Load Balancer Controller 118 | 119 | To enable public access to the proxy service, Amazon EKS can deploy an AWS Network Load Balancer for the Kubernetes Service resource. To do so, you will need to install the AWS Load Balancer Controller. To learn more, see the home page for the AWS Load Balancer Controller project: https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.4/ 120 | 121 | We will need to install the AWS Load Balancer Controller add-on, as outlined in this page: https://docs.aws.amazon.com/eks/latest/userguide/aws-load-balancer-controller.html 122 | 123 | First, download the IAM policy file: 124 | 125 | ``` 126 | curl -O https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.4.6/docs/install/iam_policy.json 127 | ``` 128 | 129 | Next, create an IAM policy using the file that you downloaded, by running the following command: 130 | 131 | ``` 132 | aws iam create-policy \ 133 | --policy-name AWSLoadBalancerControllerIAMPolicy \ 134 | --policy-document file://iam_policy.json 135 | ``` 136 | 137 | Then, create a service account role for the load balancer controller, by running the following command: 138 | 139 | ``` 140 | eksctl create iamserviceaccount \ 141 | --cluster= \ 142 | --namespace=kube-system \ 143 | --name=aws-load-balancer-controller \ 144 | --role-name AmazonEKSLoadBalancerControllerRole \ 145 | --attach-policy-arn=arn:aws:iam::111122223333:policy/AWSLoadBalancerControllerIAMPolicy \ 146 | --approve 147 | ``` 148 | 149 | In the above command, replace `` with the name of your cluster and `111122223333` with your account ID. 150 | 151 | Next, add the `eks-charts` repository to helm as follows: 152 | 153 | ``` 154 | helm repo add eks https://aws.github.io/eks-charts 155 | ``` 156 | 157 | Update your local helm repository index as follows: 158 | 159 | ``` 160 | helm repo update 161 | ``` 162 | 163 | Finally, run the following command to install the AWS Load Balancer Controller add-on: 164 | 165 | ``` 166 | helm install aws-load-balancer-controller eks/aws-load-balancer-controller \ 167 | -n kube-system \ 168 | --set clusterName= \ 169 | --set serviceAccount.create=false \ 170 | --set serviceAccount.name=aws-load-balancer-controller 171 | ``` 172 | 173 | Replace `` with the name of your cluster. 174 | 175 | > **Note**: If the AWS region you are using is not `us-west-2`, add this line to the command: 176 | > 177 | > `--set image.repository=602401143452.dkr.ecr.region-code.amazonaws.com/amazon/aws-load-balancer-controller` 178 | > 179 | > Replace `602401143452` with the registry code for your region (find the registry code from the list [here](https://docs.aws.amazon.com/eks/latest/userguide/add-ons-images.html)), and replace `region-code` with the name of the AWS region you are using. 180 | > 181 | > For example, if you are using region `us-east-1`, the command you run should look like the following: 182 | > ``` 183 | > helm install aws-load-balancer-controller eks/aws-load-balancer-controller \ 184 | > -n kube-system \ 185 | > --set clusterName= \ 186 | > --set serviceAccount.create=false \ 187 | > --set serviceAccount.name=aws-load-balancer-controller \ 188 | > --set image.repository=602401143452.dkr.ecr.us-east-1.amazonaws.com/amazon/aws-load-balancer-controller 189 | > ``` 190 | 191 | If the previous command was successful, the running the following: 192 | 193 | ``` 194 | kubectl get deployment -n kube-system aws-load-balancer-controller 195 | ``` 196 | 197 | You should see an output like the following: 198 | 199 | ``` 200 | NAME READY UP-TO-DATE AVAILABLE AGE 201 | aws-load-balancer-controller 2/2 2 2 84s 202 | ``` 203 | 204 | Your Kubernetes cluster is now ready for deployments. 205 | 206 | ### Create a Kubernetes cluster on Google Kubernetes Engine (GKE) 207 | 208 | To create a Kubernetes cluster on Google Kubernetes Engine, you will need a Google Cloud Platform account. Go to https://console.cloud.google.com/ to create an account. 209 | 210 | #### Install `gcloud` CLI 211 | 212 | First, start by installing the `gcloud` CLI tool. Follow the instructions for your operatint systems as outlined on this page: https://cloud.google.com/sdk/docs/install 213 | 214 | Once `gcloud` has been installed, run the following command to authenticate the tool with your account: 215 | 216 | ``` 217 | gcloud auth login 218 | ``` 219 | 220 | Follow the steps in the browser window that opens to log in to your Google Cloud Platform account, and give permissions to the `gcloud` CLI tool to access your account. 221 | 222 | #### Create a Kubernetes cluster on GKE 223 | 224 | To create a Kubernetes cluter on GKE, we can use either the Google Cloud console, or the `gcloud` CLI tool. In this document, we will use the Google Cloud console. 225 | 226 | 1. Go to https://console.cloud.google.com/kubernetes. 227 | * If you see an 'Enable API' button, click it. 228 | 2. Once the GKE dashboard shows, click the 'Create' button at the top of the page. 229 | 3. GKE gives you two options for Kubernetes cluster creation and management: Standard and Autopilot. In this document, we will choose Standard. Click the 'Configure' button next to Standard. 230 | 4. In the next page, give your Kubernetes cluster a name. 231 | 5. Under 'Location type', if 'Zonal' is selected, note the selected zone name. If 'Regional' is selected, note the selected region name. 232 | 6. For the purposes of this document, you can leave the other options unchanged. 233 | 7. Click 'Create'. 234 | 235 | It will take a few minutes for GKE to create the Kubernetes cluster. 236 | 237 | Once the Kubernetes cluster has been created, continue to the next section. 238 | 239 | #### Install kubectl 240 | 241 | Next, you will need to install the `kubectl` tool. `kubectl` is your main point of interaction with your Kubenernetes cluster, once you create your cluster. 242 | 243 | Follow the instructions for your operating system as outlined in this page: https://kubernetes.io/docs/tasks/tools/ 244 | 245 | > **Important**: The version of `kubectl` you install is important. The minor version of the installed `kubectl` cannot be more than 1 version different from your Kubernetes cluster version. 246 | > 247 | > You can check the version of your Kubernetes cluster in GKE by going to https://console.cloud.google.com/kubernetes. In the list of clusters, click on the name of your cluster. In the cluster details page, under the 'Cluster basics' section, you can see the Kubernetes version for your cluster. 248 | > 249 | > For example, if your cluster's Kubernetes version is 1.24.7-..., the `kubectl` version that you install cannot be lower than 1.23.x or higher than 1.25.x. You should always install the latest version of `kubectl` that is compatible with your Kubernetes cluster, so in this case, the latest `kubectl` version that you can install is 1.25.5. 250 | 251 | #### Connect `kubectl` to your Kubernetes cluster 252 | 253 | To access your Kubernetes from `kubectl`, first install an authentication plugin for the `gcloud` CLI tool by running the following command: 254 | 255 | ``` 256 | gcloud components install gke-gcloud-auth-plugin 257 | ``` 258 | 259 | Once the plugin has been installed, connect `kubectl` to your Kubernetes cluster by running one of the following commands: 260 | 261 | * If your cluster's location type was zonal, run the following command: 262 | 263 | ``` 264 | gcloud container clusters get-credentials --zone 265 | ``` 266 | 267 | Replace `` with the name you used for your cluster, and `` with the name of the zone your cluster was created in. 268 | 269 | * If your cluster's location type was regional, run the following command: 270 | 271 | ``` 272 | gcloud container clusters get-credentials --region 273 | ``` 274 | 275 | Replace `` with the name you used for your cluster, and `` with the name of the region your cluster was created in. 276 | 277 | If the above command was successful, the running the following command: 278 | 279 | ``` 280 | kubectl get svc 281 | ``` 282 | 283 | should output something like the following: 284 | 285 | ``` 286 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 287 | kubernetes ClusterIP 10.72.0.1 443/TCP 19h 288 | ``` 289 | 290 | You Kubernetes cluster is now ready for deployments. 291 | 292 | ## Deploying the Helm Chart to Your Kubernetes Cluster 293 | 294 | After you have created your Kubernetes cluster and set up `kubectl` to access it, deploying the WhatsApp proxy Helm chart is very simple. 295 | 296 | From the root of the chart directory (`whatsapp-proxy-chart/`), run one of the following commands: 297 | 298 | * If you created your Kubernetes cluster on Amazon EKS, run the following command: 299 | ``` 300 | helm install whatsapp-proxy . -f deployments/overrides/values.eks.yaml 301 | ``` 302 | * If you created your Kubernetes cluster on GKE, run the following command: 303 | ``` 304 | helm install whatsapp-proxy . -f deployments/overrides/values.gke.yaml 305 | ``` 306 | 307 | The above command will install the Helm chart on your Kubernetes cluster. 308 | 309 | To get the public endpoint for your deployment, run the following: 310 | 311 | ``` 312 | kubectl get svc 313 | ``` 314 | 315 | The output should look like the following: 316 | 317 | ``` 318 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 319 | kubernetes ClusterIP 10.72.0.1 443/TCP 19h 320 | whatsapp-proxy-whatsapp-proxy-chart LoadBalancer 10.72.2.240 8080:32220/TCP,8443:32287/TCP,8222:30054/TCP,5222:32734/TCP,80:31717/TCP,443:32498/TCP,8199:30617/TCP 3s 321 | ``` 322 | 323 | The public endpoint for your deployment will appear under EXTERNAL-IP. --------------------------------------------------------------------------------