Search code examples
sslwebsocketamazon-elastic-beanstalkdjango-channels

Websocket + SSL on AWS Application Load Balancer


I have a Django Application deployed on ElastickBeanstalk. I recently migrated the load balancer from Classic -> Application in order to support Websocket (layer formed by: Django-channels (~=1.1.8, channels-api==0.4.0), Redis Elasticache AWS, and Daphne (~=1.4)). HTTP, HTTPS and Web Socket protocol are working fine.

But I can't find a way to deploy Websocket over Secure SSL. It's killing me, and it is blocking, as HTTPS connection from the browser will cut a non secure ws:// peer requests.

Here is my ALB Configuration Does anyone as a solution?

enter image description here


Solution

  • After 2 more days investigating, I finally cracked this config!

    Here is the answer:

    1. The right, and MINIMUM, aws - ALB Config: enter image description here Indeed, we need to

      • Decode SSL ( this is not a End-to-End encryption )
      • Forward All traffic to Daphne. The reason why I did not go for the very spread among the web conf : "/ws/*" routing to Daphne, is that It provided me indeed the HandShake OK, but afterward, nothing, nada, websocket could not be pushed back to the subscriber. The reason, I believe, is that the push back from Daphne does not respect the custom base trailing URL you customize in your conf. Also, I cannot be sure of this interpretation. What I am sure of however is that if I don't forward all traffic to Daphne, it doesn't work after handshake.

        1. The minimum Deployment CONF
      • NO NEED of complet .ebextension override proxy in deployment:

    enter image description here .ebextensions/05_channels.config

    files:
      "/opt/elasticbeanstalk/hooks/appdeploy/post/start_supervisor.sh":
      mode: "000755"
      owner: root
      group: root
      content: |
        #!/usr/bin/env bash
        sudo virtualenv -p /usr/bin/python2.7 /tmp/senv
        source /tmp/senv/bin/activate && source /opt/python/current/env
        sudo python --version > /tmp/version_check.txt
        sudo pip install supervisor
    
        sudo /usr/local/bin/supervisord -c /opt/python/current/app/fxf/custom_eb_deployment/supervisord.conf
        sudo /usr/local/bin/supervisorctl -c /opt/python/current/app/fxf/custom_eb_deployment/supervisord.conf reread
        sudo /usr/local/bin/supervisorctl -c /opt/python/current/app/fxf/custom_eb_deployment/supervisord.conf update
        sudo /usr/local/bin/supervisorctl -c /opt/python/current/app/fxf/custom_eb_deployment/supervisord.conf restart all
        sudo /usr/local/bin/supervisorctl -c /opt/python/current/app/fxf/custom_eb_deployment/supervisord.conf status
    
    • start_daphne.sh ( remark I'm choosing the 8001 port, according to my ALB conf )

      #!/usr/bin/env bash source /opt/python/run/venv/bin/activate && source /opt/python/current/env /opt/python/run/venv/bin/daphne -b 0.0.0.0 -p 8001 fxf.asgi:channel_layer

    • start_worker.sh

      #!/usr/bin/env bash source /opt/python/run/venv/bin/activate && source /opt/python/current/env python /opt/python/current/app/fxf/manage.py runworker

    • supervisord.conf

    `

    [unix_http_server]
    file=/tmp/supervisor.sock   ; (the path to the socket file)
    
    [supervisord]
    logfile=/tmp/supervisord.log ; supervisord log file
    loglevel=error ; info, debug, warn, trace
    logfile_maxbytes=50MB        ; (max main logfile bytes b4 rotation;default 50MB)
    logfile_backups=10           ; (num of main logfile rotation backups;default 10)
    pidfile=/tmp/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
    nodaemon=false               ; (start in foreground if true;default false)
    minfds=1024                  ; (min. avail startup file descriptors;default 1024)
    minprocs=200                 ; (min. avail process descriptors;default 200)
    
    ; the below section must remain in the config file for RPC
    ; (supervisorctl/web interface) to work, additional interfaces may be
    ; added by defining them in separate rpcinterface: sections
    [rpcinterface:supervisor]
    supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
    
    [supervisorctl]
    serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL  for a unix socket
    
    [program:Daphne]
    environment=PATH="/opt/python/run/venv/bin"
    command=sh /opt/python/current/app/fxf/custom_eb_deployment/start_daphne.sh --log-file /tmp/start_daphne.log
    directory=/opt/python/current/app
    autostart=true
    autorestart=true
    redirect_stderr=true
    stdout_logfile=/tmp/daphne.out.log
    stderr_logfile=/tmp/daphne.err.log
    
    [program:Worker]
    environment=PATH="/opt/python/run/venv/bin"
    command=sh /opt/python/current/app/fxf/custom_eb_deployment/start_worker.sh --log-file /tmp/start_worker.log
    directory=/opt/python/current/app
    process_name=%(program_name)s_%(process_num)02d
    numprocs=2
    autostart=true
    autorestart=true
    redirect_stderr=true
    stdout_logfile=/tmp/workers.out.log
    stderr_logfile=/tmp/workers.err.log
    
    ; When resorting to send SIGKILL to the program to terminate it
    ; send SIGKILL to its whole process group instead,
    ; taking care of its children as well.
    killasgroup=true
    

    `

    If some are still struggling with this conf, I might post a tuto on medium or something. Don't hesitate to push me for it on answers ;)