Plug Documentation

repository·main·Indexed 25 days ago

https://github.com/elixir-plug/plug

A specification for composing web applications using functions, providing connection adapters for Erlang VM web servers like Cowboy and Bandit. Plug serves as the foundation for frameworks such as Phoenix and includes tools for request dispatching via Plug.Router, testing with Plug.Test, and a variety of built-in plugs for authentication, session management, and SSL/TLS configuration.

Tokens
9.7K
Snippets
24
Records
58
Agent score
84%

What's inside Plug

  1. Bind to Privileged Ports (Port 443)

    main

    By default, HTTPS clients expect port 443. Since binding to ports under 1024 requires privileges, you can use one of the following strategies on Linux:

    • Reverse Proxy/Load Balancer: Use Nginx or HAProxy to listen on 443 and forward traffic to your Elixir app on an unprivileged port.
    • IPTables: Create a rule to forward packets from 443 to your application's port.
    • setcap: Grant the Erlang/OTP runtime permission to bind to privileged ports (e.g., sudo setcap 'cap_net_bind_service=+ep' /path/to/beam.smp).
    • authbind: Use authbind to allow an unprivileged user to bind to specific ports.
  2. Convert Certificates and Keys using OpenSSL

    main

    If your certificates or keys are not in PEM format, use these OpenSSL commands to convert them:

    From DER to PEM

    • Convert a DER certificate: openssl x509 -in server.crt -inform der -out cert.pem
    • Convert an RSA private key: openssl rsa -in privkey.der -inform der -out privkey.pem (use ec instead of rsa for Elliptic Curve keys).

    From PKCS#12 to PEM

    • Extract all certificates (including CA chain): openssl pkcs12 -in server.p12 -nokeys -out fullchain.pem
    • Extract a private key: openssl pkcs12 -in server.p12 -nocerts -nodes -out privkey.pem
    # DER to PEM
    openssl x509 -in server.crt -inform der -out cert.pem
    openssl rsa -in privkey.der -inform der -out privkey.pem
    
    # PKCS#12 to PEM
    openssl pkcs12 -in server.p12 -nokeys -out fullchain.pem
    openssl pkcs12 -in server.p12 -nocerts -nodes -out privkey.pem
  3. Configure Plug for TLS Offloading

    main

    When terminating TLS at a proxy or load balancer, you must ensure your application correctly identifies the protocol and client IP.

    Updating Scheme and Port Use Plug.SSL in your pipeline to update the :scheme and :port fields in the Plug.Conn struct based on headers like X-Forwarded-Proto. This is critical because Plug.Session and Plug.Conn.put_resp_cookie/4 only set the 'secure' flag if :scheme is set to :https.

    Handling Client IP When proxying, the :remote_ip field usually contains the proxy's IP. Use plugs like Plug.RewriteOn to extract the original client IP from headers like X-Forwarded-For.

    Warning: Ensure your proxy filters these headers so clients cannot spoof their IP addresses.

  4. Use application 'priv' directory for certificates

    main

    When bundling certificates with your application (e.g., for a release), store them in the priv directory. You must set the otp_app option to the name of your OTP application so that Plug can resolve the relative paths correctly.

    Plug.Cowboy.https MyApp.MyPlug, [],
      port: 8443,
      cipher_suite: :strong,
      certfile: "priv/cert/selfsigned.pem",
      keyfile: "priv/cert/selfsigned_key.pem",
      otp_app: :my_app
  5. Generate Diffie-Hellman (DH) Parameters with OpenSSL

    main

    If you need to configure DH parameters for compatibility (specifically for ciphers with "DHE" in their name), you can generate them using OpenSSL.

    Recommended: Use a Standardized Group (Fast & Secure) Use the IETF RFC 7919 standardized groups. To generate a 4096-bit ffdeh4096 group:

    Alternative: Generate Custom Parameters (Secure & Slow) To generate custom 4096-bit parameters:

  6. Enable HTTP Strict Transport Security (HSTS)

    main

    To prevent downgrade attacks, include Plug.SSL in your Plug pipeline. By default, Plug.SSL sets the Strict-Transport-Security header.

    Warning: HSTS is difficult to revert once a browser has cached it. Start with a short :expires value during testing.

    To disable HSTS, set hsts: false in the Plug.SSL options.

  7. Configure Custom Diffie-Hellman (DH) Parameters

    main

    For ciphers using the

    Plug.Cowboy.https MyApp.MyPlug, [],
      port: 8443,
      cipher_suite: :strong,
      certfile: "priv/cert/selfsigned.pem",
      keyfile: "priv/cert/selfsigned_key.pem",
      dhfile: "priv/cert/dhparams.pem",
      otp_app: :my_app
  8. Test Plugs with Plug.Test

    main

    Use Plug.Test to test your plugs or routers. You can create a test connection using conn/2 (e.g., conn(:get, "/path")), invoke the plug's call/2 function, and then assert against the connection state, status, or body.

    defmodule MyPlugTest do
      use ExUnit.Case, async: true
      import Plug.Test
      import Plug.Conn
      
      @opts MyRouter.init([])
    
      test "returns hello world" do
        # Create a test connection
        conn = conn(:get, "/hello")
    
        # Invoke the plug
        conn = MyRouter.call(conn, @opts)
    
        # Assert the response and status
        assert conn.state == :sent
        assert conn.status == 200
        assert conn.resp_body == "world"
      end
    end
  9. Set up an HTTPS listener with Plug.Cowboy

    main

    To serve HTTP over TLS (HTTPS) using Plug.Cowboy, define an HTTPS listener by providing the certificate files, private key, and port. If your certificate file contains the full chain, you do not need to provide a cacertfile.

    Plug.Cowboy.https MyApp.MyPlug, [],
      port: 8443,
      cipher_suite: :strong,
      certfile: "/etc/letsencrypt/live/example.net/cert.pem",
      keyfile: "/etc/letsencrypt/live/example.net/privkey.pem",
      cacertfile: "/etc/letsencrypt/live/example.net/chain.pem"
  10. Use encrypted private keys

    main

    If your private key is stored in an encrypted PEM format, specify the password using the :password option.

    To encrypt an existing RSA key using OpenSSL: openssl rsa -in privkey.pem -out privkey_aes.pem -aes128. Use ec instead of rsa for ECDSA certificates.

    Plug.Cowboy.https MyApp.MyPlug, [],
      port: 8443,
      certfile: "/etc/letsencrypt/live/example.net/cert.pem",
      keyfile: "/etc/letsencrypt/live/example.net/privkey_aes.pem",
      cacertfile: "/etc/letsencrypt/live/example.net/chain.pem",
      password: "SECRET"
  11. Install Plug with Cowboy or Bandit

    main

    To use Plug, you must include a webserver and its Plug bindings in your mix.exs dependencies. You can choose between the Erlang-based Cowboy or the Elixir-based Bandit.

    Option 1: Cowboy (Erlang-based) Add plug_cowboy to your dependencies.

    Option 2: Bandit (Elixir-based) Add bandit to your dependencies.

    # For Cowboy
    def deps do
      [ {:plug_cowboy, "~> 2.0"} ]
    end
    
    # For Bandit
    def deps do
      [ {:bandit, "~> 1.0"} ]
    end
  12. Run Plug in a Supervised Application

    main

    For production, run your Plug pipeline under an OTP supervision tree. Create a project with mix new my_app --sup and add the webserver (e.g., plug_cowboy) to your dependencies. In your Application module, add the webserver as a child process to the supervisor.

    # lib/my_app/application.ex
    defmodule MyApp.Application do
      use Application
    
      def start(_type, _args) do
        children = [
          {Plug.Cowboy, scheme: :http, plug: MyPlug, options: [port: 4001]}
        ]
    
        opts = [strategy: :one_for_one, name: MyApp.Supervisor]
        Supervisor.start_link(children, opts)
      end
    end