10.7 KiB · text History 4b6fe3b
import Config

# config/runtime.exs is executed for all environments, including
# during releases. It is executed after compilation and before the
# system starts, so it is typically used to load production configuration
# and secrets from environment variables or elsewhere. Do not define
# any compile-time configuration in here, as it won't be applied.
# The block below contains prod specific runtime configuration.

# ## Using releases
#
# If you use `mix release`, you need to explicitly enable the server
# by passing the PHX_SERVER=true when you start it:
#
#     PHX_SERVER=true bin/oh_my_ex_oh_ircd start
#
# Alternatively, you can use `mix phx.gen.release` to generate a `bin/server`
# script that automatically sets the env var above.
if System.get_env("PHX_SERVER") do
  config :oh_my_ex_oh_ircd, OhMyExOhIrcdWeb.Endpoint, server: true
end

if port = System.get_env("PORT") do
  config :oh_my_ex_oh_ircd, OhMyExOhIrcdWeb.Endpoint,
    http: [port: String.to_integer(port)]
end

# Only override IRC listener config when the corresponding env vars are
# explicitly set, so compile-time config (dev.exs, test.exs) wins by default.
if irc_port = System.get_env("IRC_PORT") do
  config :oh_my_ex_oh_ircd, OhMyExOhIrcd.IRC.Listener,
    port: String.to_integer(irc_port)
end

if name = System.get_env("IRC_SERVER_NAME") do
  config :oh_my_ex_oh_ircd, OhMyExOhIrcd.IRC.Listener, server_name: name
end

if net = System.get_env("IRC_NETWORK_NAME") do
  config :oh_my_ex_oh_ircd, OhMyExOhIrcd.IRC.Listener, network_name: net
end

# Where ImgServ stores hosted image files. In a multi-node deployment this
# should be a shared volume mounted at the same path on every node.
if dir = System.get_env("IMGSERV_UPLOAD_DIR") do
  config :oh_my_ex_oh_ircd, :imgserv, upload_dir: dir
end

# Expect a PROXY v1/v2 header on every connection. Required when the
# IRC pod is behind a TCP proxy (Traefik IngressRouteTCP, HAProxy, AWS
# NLB...) — otherwise every client looks like the proxy's IP.
if pp = System.get_env("IRC_PROXY_PROTOCOL") do
  config :oh_my_ex_oh_ircd, OhMyExOhIrcd.IRC.Listener,
    proxy_protocol: pp in ~w(1 true yes on)
end

# The port clients reach TLS on when the proxy terminates it in front of
# the plain listener (Traefik's `ircds` entrypoint, which forwards to 6667
# with a PROXY header). Only for the home page's connection details;
# defaults to 6697 there.
if port = System.get_env("IRC_PROXY_TLS_PORT") do
  config :oh_my_ex_oh_ircd, :proxy_tls_port, String.to_integer(port)
end

# Host cloaking base domain. Client IPs are shown as `<hash>.<cloak_host>`
# to non-opers. Explicit `IRC_CLOAK_HOST` wins; otherwise prod defaults to
# `PHX_HOST` so it's on out of the box. Disable with
# `IRC_CLOAK_HOST=off` (or empty/false/none).
cloak_host =
  case System.get_env("IRC_CLOAK_HOST") do
    nil -> if config_env() == :prod, do: System.get_env("PHX_HOST")
    v when v in ["", "off", "false", "none"] -> nil
    v -> v
  end

if cloak_host do
  config :oh_my_ex_oh_ircd, OhMyExOhIrcd.IRC.Listener, cloak_host: cloak_host
end

# Anonymous IRC access — default ON in prod releases (set
# IRC_ALLOW_ANONYMOUS=false to lock it down). Dev/test compile-time
# config wins outside prod.
if config_env() == :prod do
  allow_anonymous =
    case System.get_env("IRC_ALLOW_ANONYMOUS") do
      nil -> true
      val -> val in ~w(1 true yes on)
    end

  config :oh_my_ex_oh_ircd, OhMyExOhIrcd.IRC.Listener,
    allow_anonymous: allow_anonymous
end

# Web user registration mode. Default invite-only at runtime (prod
# releases). Explicit env-gating so dev/test configs win when set.
# Mint invite tokens with `mix invites.create` (or `bin/… eval` in a
# running release).
if config_env() == :prod do
  registration_mode =
    case System.get_env("REGISTRATION_MODE") do
      "open" -> :open
      _ -> :invite_only
    end

  config :oh_my_ex_oh_ircd, :registration_mode, registration_mode

  # Run pending migrations automatically when the release boots. On by
  # default in prod; set MIGRATE_ON_START=false to opt out (e.g. if you
  # run migrations from a separate k8s Job / initContainer instead).
  config :oh_my_ex_oh_ircd,
         :migrate_on_start,
         System.get_env("MIGRATE_ON_START", "true") not in ["0", "false", "no"]

  # WebIRC LiveView toggle. Off by default in prod (matches config.exs);
  # set WEBIRC_ENABLED=true to let logged-in users open /webirc.
  config :oh_my_ex_oh_ircd,
         :webirc,
         enabled: System.get_env("WEBIRC_ENABLED", "false") in ~w(1 true yes on)
end

# TLS listener cert paths from env. In the :kubernetes build these point at
# a Secret mounted by the kubelet (typically from cert-manager); in the
# :standalone build they're optional and override site_encrypt's defaults.
tls_enabled =
  System.get_env("IRC_TLS_ENABLED") in ~w(1 true yes on) or
    (System.get_env("IRC_TLS_CERT_PATH") != nil and System.get_env("IRC_TLS_KEY_PATH") != nil)

if tls_enabled do
  config :oh_my_ex_oh_ircd, OhMyExOhIrcd.IRC.TLSListener,
    enabled: true,
    port: String.to_integer(System.get_env("IRC_TLS_PORT", "6697")),
    cert_path: System.get_env("IRC_TLS_CERT_PATH"),
    key_path: System.get_env("IRC_TLS_KEY_PATH"),
    cacert_path: System.get_env("IRC_TLS_CACERT_PATH")
end

# ACME / site_encrypt config (:standalone builds only — the :kubernetes
# build doesn't reference this).
acme_domains = System.get_env("ACME_DOMAINS")
acme_emails = System.get_env("ACME_EMAILS")

if is_binary(acme_domains) and is_binary(acme_emails) do
  acme_directory_url =
    case System.get_env("ACME_DIRECTORY_URL") do
      nil ->
        {:internal, [port: String.to_integer(System.get_env("ACME_INTERNAL_PORT", "4002"))]}

      url ->
        url
    end

  config :oh_my_ex_oh_ircd, :acme,
    client: :native,
    domains: String.split(acme_domains, ",", trim: true),
    emails: String.split(acme_emails, ",", trim: true),
    db_folder: System.get_env("ACME_DB_FOLDER", "/var/lib/oh_my_ex_oh_ircd/site_encrypt"),
    directory_url: acme_directory_url
end

# libcluster topology. Default keeps gossip; set CLUSTER_STRATEGY=kubernetes
# (with CLUSTER_K8S_SERVICE and CLUSTER_K8S_APP_NAME) to use the K8s API
# strategy for peer discovery.
if System.get_env("CLUSTER_STRATEGY") == "kubernetes" do
  config :libcluster,
    topologies: [
      oh_my_ex_oh: [
        strategy: Cluster.Strategy.Kubernetes,
        config: [
          mode: :ip,
          # Peers are found by listing *pods* with the selector. The
          # default (:endpoints) filters Endpoints objects by it, and
          # those carry their Service's labels, not the pods' — so with
          # an unlabelled headless Service it found nobody and every
          # replica ran alone, without a word in the log. The pods
          # carry the label; the ircd-cluster Role lists them.
          kubernetes_ip_lookup_mode: :pods,
          kubernetes_node_basename: System.get_env("CLUSTER_K8S_APP_NAME", "ircd"),
          kubernetes_selector:
            System.get_env("CLUSTER_K8S_SELECTOR", "app=ircd"),
          kubernetes_service_name:
            System.get_env("CLUSTER_K8S_SERVICE", "ircd-headless"),
          kubernetes_namespace:
            System.get_env("CLUSTER_K8S_NAMESPACE", "ircd"),
          polling_interval: 10_000
        ]
      ]
    ]
end

if config_env() == :prod do
  database_url =
    System.get_env("DATABASE_URL") ||
      raise """
      environment variable DATABASE_URL is missing.
      For example: ecto://USER:PASS@HOST/DATABASE
      """

  maybe_ipv6 = if System.get_env("ECTO_IPV6") in ~w(true 1), do: [:inet6], else: []

  config :oh_my_ex_oh_ircd, OhMyExOhIrcd.Repo,
    # ssl: true,
    url: database_url,
    pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"),
    # For machines with several cores, consider starting multiple pools of `pool_size`
    # pool_count: 4,
    socket_options: maybe_ipv6

  # The secret key base is used to sign/encrypt cookies and other secrets.
  # A default value is used in config/dev.exs and config/test.exs but you
  # want to use a different value for prod and you most likely don't want
  # to check this value into version control, so we use an environment
  # variable instead.
  secret_key_base =
    System.get_env("SECRET_KEY_BASE") ||
      raise """
      environment variable SECRET_KEY_BASE is missing.
      You can generate one by calling: mix phx.gen.secret
      """

  host = System.get_env("PHX_HOST") || "example.com"

  config :oh_my_ex_oh_ircd, :dns_cluster_query, System.get_env("DNS_CLUSTER_QUERY")

  config :oh_my_ex_oh_ircd, OhMyExOhIrcdWeb.Endpoint,
    url: [host: host, port: 443, scheme: "https"],
    http: [
      # Enable IPv6 and bind on all interfaces.
      # Set it to  {0, 0, 0, 0, 0, 0, 0, 1} for local network only access.
      # See the documentation on https://hexdocs.pm/bandit/Bandit.html#t:options/0
      # for details about using IPv6 vs IPv4 and loopback vs public addresses.
      ip: {0, 0, 0, 0, 0, 0, 0, 0}
    ],
    secret_key_base: secret_key_base

  # ## SSL Support
  #
  # To get SSL working, you will need to add the `https` key
  # to your endpoint configuration:
  #
  #     config :oh_my_ex_oh_ircd, OhMyExOhIrcdWeb.Endpoint,
  #       https: [
  #         ...,
  #         port: 443,
  #         cipher_suite: :strong,
  #         keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"),
  #         certfile: System.get_env("SOME_APP_SSL_CERT_PATH")
  #       ]
  #
  # The `cipher_suite` is set to `:strong` to support only the
  # latest and more secure SSL ciphers. This means old browsers
  # and clients may not be supported. You can set it to
  # `:compatible` for wider support.
  #
  # `:keyfile` and `:certfile` expect an absolute path to the key
  # and cert in disk or a relative path inside priv, for example
  # "priv/ssl/server.key". For all supported SSL configuration
  # options, see https://hexdocs.pm/plug/Plug.SSL.html#configure/1
  #
  # We also recommend setting `force_ssl` in your config/prod.exs,
  # ensuring no data is ever sent via http, always redirecting to https:
  #
  #     config :oh_my_ex_oh_ircd, OhMyExOhIrcdWeb.Endpoint,
  #       force_ssl: [hsts: true]
  #
  # Check `Plug.SSL` for all available options in `force_ssl`.

  # ## Configuring the mailer
  #
  # In production you need to configure the mailer to use a different adapter.
  # Here is an example configuration for Mailgun:
  #
  #     config :oh_my_ex_oh_ircd, OhMyExOhIrcd.Mailer,
  #       adapter: Swoosh.Adapters.Mailgun,
  #       api_key: System.get_env("MAILGUN_API_KEY"),
  #       domain: System.get_env("MAILGUN_DOMAIN")
  #
  # Most non-SMTP adapters require an API client. Swoosh supports Req, Hackney,
  # and Finch out-of-the-box. This configuration is typically done at
  # compile-time in your config/prod.exs:
  #
  #     config :swoosh, :api_client, Swoosh.ApiClient.Req
  #
  # See https://hexdocs.pm/swoosh/Swoosh.html#module-installation for details.
end