mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 10:18:54 +00:00
chore(infra): Deploy domain app on a separate instance and enable background jobs on it (#4160)
Closes #3801
This commit is contained in:
4
.github/workflows/_build_artifacts.yml
vendored
4
.github/workflows/_build_artifacts.yml
vendored
@@ -82,6 +82,10 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- image_name: domain
|
||||
target: runtime
|
||||
build-args: |
|
||||
APPLICATION_NAME=domain
|
||||
- image_name: api
|
||||
target: runtime
|
||||
build-args: |
|
||||
|
||||
2
.github/workflows/_deploy_production.yml
vendored
2
.github/workflows/_deploy_production.yml
vendored
@@ -48,7 +48,7 @@ jobs:
|
||||
- name: Pull and push images
|
||||
run: |
|
||||
set -xe
|
||||
IMAGES=(client relay gateway api web)
|
||||
IMAGES=(domain api web gateway relay client)
|
||||
MAJOR_VERSION="${VERSION%%.*}"
|
||||
MAJOR_MINOR_VERSION="${VERSION%.*}"
|
||||
|
||||
|
||||
12
.github/workflows/_integration_tests.yml
vendored
12
.github/workflows/_integration_tests.yml
vendored
@@ -3,6 +3,14 @@ run-name: Triggered from ${{ github.event_name }} by ${{ github.actor }}
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
domain_image:
|
||||
required: false
|
||||
type: string
|
||||
default: 'us-east1-docker.pkg.dev/firezone-staging/firezone/domain'
|
||||
domain_tag:
|
||||
required: false
|
||||
type: string
|
||||
default: ${{ github.sha }}
|
||||
api_image:
|
||||
required: false
|
||||
type: string
|
||||
@@ -61,6 +69,8 @@ jobs:
|
||||
id-token: write
|
||||
pull-requests: write
|
||||
env:
|
||||
DOMAIN_IMAGE: ${{ inputs.domain_image }}
|
||||
DOMAIN_TAG: ${{ inputs.domain_tag }}
|
||||
API_IMAGE: ${{ inputs.api_image }}
|
||||
API_TAG: ${{ inputs.api_tag }}
|
||||
WEB_IMAGE: ${{ inputs.web_image }}
|
||||
@@ -99,7 +109,7 @@ jobs:
|
||||
run: |
|
||||
# Start one-by-one to avoid variability in service startup order
|
||||
docker compose up -d dns.httpbin httpbin
|
||||
docker compose up -d api web --no-build
|
||||
docker compose up -d api web domain --no-build
|
||||
docker compose up -d relay --no-build
|
||||
docker compose up -d gateway --no-build
|
||||
docker compose up -d client --no-build
|
||||
|
||||
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -185,7 +185,7 @@ jobs:
|
||||
|
||||
# Start services in the same order each time for the tests
|
||||
docker compose up -d iperf3
|
||||
docker compose up -d api web --no-build
|
||||
docker compose up -d api web domain --no-build
|
||||
docker compose up -d relay --no-build
|
||||
docker compose up -d gateway --no-build
|
||||
docker compose up -d client --no-build
|
||||
|
||||
2
.github/workflows/publish.yml
vendored
2
.github/workflows/publish.yml
vendored
@@ -41,7 +41,7 @@ jobs:
|
||||
- name: Pull and push
|
||||
run: |
|
||||
set -xe
|
||||
IMAGES=(relay api gateway web client)
|
||||
IMAGES=(domain api web gateway relay client)
|
||||
|
||||
MAJOR_VERSION="${VERSION%%.*}"
|
||||
MAJOR_MINOR_VERSION="${VERSION%.*}"
|
||||
|
||||
@@ -68,7 +68,7 @@ services:
|
||||
# Erlang
|
||||
ERLANG_DISTRIBUTION_PORT: 9000
|
||||
ERLANG_CLUSTER_ADAPTER: "Elixir.Cluster.Strategy.Epmd"
|
||||
ERLANG_CLUSTER_ADAPTER_CONFIG: '{"hosts":["api@api.cluster.local","web@web.cluster.local"]}'
|
||||
ERLANG_CLUSTER_ADAPTER_CONFIG: '{"hosts":["api@api.cluster.local","web@web.cluster.local","domain@web.cluster.local"]}'
|
||||
RELEASE_COOKIE: "NksuBhJFBhjHD1uUa9mDOHV"
|
||||
RELEASE_HOSTNAME: "web.cluster.local"
|
||||
RELEASE_NAME: "web"
|
||||
@@ -87,8 +87,68 @@ services:
|
||||
LIVE_VIEW_SIGNING_SALT: "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"
|
||||
COOKIE_SIGNING_SALT: "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"
|
||||
COOKIE_ENCRYPTION_SALT: "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"
|
||||
# Telemetry
|
||||
TELEMETRY_ENABLED: "false"
|
||||
# Debugging
|
||||
LOG_LEVEL: "debug"
|
||||
# Emails
|
||||
OUTBOUND_EMAIL_FROM: "public-noreply@firez.one"
|
||||
OUTBOUND_EMAIL_ADAPTER: "Elixir.Swoosh.Adapters.Postmark"
|
||||
## Warning: The token is for the blackhole Postmark server created in a separate isolated account,
|
||||
## that WILL NOT send any actual emails, but you can see and debug them in the Postmark dashboard.
|
||||
OUTBOUND_EMAIL_ADAPTER_OPTS: '{"api_key":"7da7d1cd-111c-44a7-b5ac-4027b9d230e5"}'
|
||||
# Seeds
|
||||
STATIC_SEEDS: "true"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -f localhost:8080/healthz"]
|
||||
start_period: 10s
|
||||
interval: 30s
|
||||
retries: 5
|
||||
timeout: 5s
|
||||
depends_on:
|
||||
vault:
|
||||
condition: "service_healthy"
|
||||
postgres:
|
||||
condition: "service_healthy"
|
||||
networks:
|
||||
- app
|
||||
|
||||
api:
|
||||
build:
|
||||
context: elixir
|
||||
cache_from:
|
||||
- type=registry,ref=us-east1-docker.pkg.dev/firezone-staging/cache/api:main
|
||||
args:
|
||||
APPLICATION_NAME: api
|
||||
image: ${API_IMAGE:-us-east1-docker.pkg.dev/firezone-staging/firezone/api}:${API_TAG:-main}
|
||||
hostname: api.cluster.local
|
||||
ports:
|
||||
- 8081:8081/tcp
|
||||
environment:
|
||||
# Web Server
|
||||
EXTERNAL_URL: http://localhost:8081/
|
||||
PHOENIX_HTTP_API_PORT: "8081"
|
||||
PHOENIX_SECURE_COOKIES: "false"
|
||||
# Erlang
|
||||
ERLANG_DISTRIBUTION_PORT: 9000
|
||||
ERLANG_CLUSTER_ADAPTER: "Elixir.Cluster.Strategy.Epmd"
|
||||
ERLANG_CLUSTER_ADAPTER_CONFIG: '{"hosts":["api@api.cluster.local","web@web.cluster.local","domain@web.cluster.local"]}'
|
||||
RELEASE_COOKIE: "NksuBhJFBhjHD1uUa9mDOHV"
|
||||
RELEASE_HOSTNAME: "api.cluster.local"
|
||||
RELEASE_NAME: "api"
|
||||
# Database
|
||||
DATABASE_HOST: postgres
|
||||
DATABASE_PORT: 5432
|
||||
DATABASE_NAME: firezone_dev
|
||||
DATABASE_USER: postgres
|
||||
DATABASE_PASSWORD: postgres
|
||||
# Auth
|
||||
AUTH_PROVIDER_ADAPTERS: "email,openid_connect,userpass,token,google_workspace,microsoft_entra,okta"
|
||||
# Secrets
|
||||
TOKENS_KEY_BASE: "5OVYJ83AcoQcPmdKNksuBhJFBhjHD1uUa9mDOHV/6EIdBQ6pXksIhkVeWIzFk5S2"
|
||||
TOKENS_SALT: "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"
|
||||
SECRET_KEY_BASE: "5OVYJ83AcoQcPmdKNksuBhJFBhjHD1uUa9mDOHV/6EIdBQ6pXksIhkVeWIzFk5S2"
|
||||
LIVE_VIEW_SIGNING_SALT: "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"
|
||||
COOKIE_SIGNING_SALT: "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"
|
||||
COOKIE_ENCRYPTION_SALT: "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"
|
||||
# Debugging
|
||||
LOG_LEVEL: "debug"
|
||||
# Emails
|
||||
@@ -104,6 +164,122 @@ services:
|
||||
condition: "service_healthy"
|
||||
postgres:
|
||||
condition: "service_healthy"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -f localhost:8081/healthz"]
|
||||
start_period: 10s
|
||||
interval: 30s
|
||||
retries: 5
|
||||
timeout: 5s
|
||||
networks:
|
||||
- app
|
||||
|
||||
domain:
|
||||
build:
|
||||
context: elixir
|
||||
cache_from:
|
||||
- type=registry,ref=us-east1-docker.pkg.dev/firezone-staging/cache/domain:main
|
||||
args:
|
||||
APPLICATION_NAME: domain
|
||||
image: ${API_IMAGE:-us-east1-docker.pkg.dev/firezone-staging/firezone/domain}:${API_TAG:-main}
|
||||
hostname: domain.cluster.local
|
||||
environment:
|
||||
# Erlang
|
||||
ERLANG_DISTRIBUTION_PORT: 9000
|
||||
ERLANG_CLUSTER_ADAPTER: "Elixir.Cluster.Strategy.Epmd"
|
||||
ERLANG_CLUSTER_ADAPTER_CONFIG: '{"hosts":["api@api.cluster.local","web@web.cluster.local","domain@domain.cluster.local"]}'
|
||||
RELEASE_COOKIE: "NksuBhJFBhjHD1uUa9mDOHV"
|
||||
RELEASE_HOSTNAME: "domain.cluster.local"
|
||||
RELEASE_NAME: "domain"
|
||||
# Database
|
||||
DATABASE_HOST: postgres
|
||||
DATABASE_PORT: 5432
|
||||
DATABASE_NAME: firezone_dev
|
||||
DATABASE_USER: postgres
|
||||
DATABASE_PASSWORD: postgres
|
||||
# Auth
|
||||
AUTH_PROVIDER_ADAPTERS: "email,openid_connect,userpass,token,google_workspace,microsoft_entra,okta"
|
||||
# Secrets
|
||||
TOKENS_KEY_BASE: "5OVYJ83AcoQcPmdKNksuBhJFBhjHD1uUa9mDOHV/6EIdBQ6pXksIhkVeWIzFk5S2"
|
||||
TOKENS_SALT: "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"
|
||||
SECRET_KEY_BASE: "5OVYJ83AcoQcPmdKNksuBhJFBhjHD1uUa9mDOHV/6EIdBQ6pXksIhkVeWIzFk5S2"
|
||||
LIVE_VIEW_SIGNING_SALT: "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"
|
||||
COOKIE_SIGNING_SALT: "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"
|
||||
COOKIE_ENCRYPTION_SALT: "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"
|
||||
# Debugging
|
||||
LOG_LEVEL: "debug"
|
||||
# Emails
|
||||
OUTBOUND_EMAIL_FROM: "public-noreply@firez.one"
|
||||
OUTBOUND_EMAIL_ADAPTER: "Elixir.Swoosh.Adapters.Postmark"
|
||||
## Warning: The token is for the blackhole Postmark server created in a separate isolated account,
|
||||
## that WILL NOT send any actual emails, but you can see and debug them in the Postmark dashboard.
|
||||
OUTBOUND_EMAIL_ADAPTER_OPTS: '{"api_key":"7da7d1cd-111c-44a7-b5ac-4027b9d230e5"}'
|
||||
# Seeds
|
||||
STATIC_SEEDS: "true"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -f localhost:4000/healthz"]
|
||||
start_period: 10s
|
||||
interval: 30s
|
||||
retries: 5
|
||||
timeout: 5s
|
||||
depends_on:
|
||||
vault:
|
||||
condition: "service_healthy"
|
||||
postgres:
|
||||
condition: "service_healthy"
|
||||
networks:
|
||||
- app
|
||||
|
||||
# This is a service container which allows to run mix tasks for local development
|
||||
# without having to install Elixir and Erlang on the host machine.
|
||||
elixir:
|
||||
build:
|
||||
context: elixir
|
||||
target: compiler
|
||||
cache_from:
|
||||
- type=registry,ref=us-east1-docker.pkg.dev/firezone-staging/cache/elixir:main
|
||||
args:
|
||||
APPLICATION_NAME: api
|
||||
image: ${ELIXIR_IMAGE:-us-east1-docker.pkg.dev/firezone-staging/firezone/elixir}:${ELIXIR_TAG:-main}
|
||||
hostname: elixir
|
||||
environment:
|
||||
# Web Server
|
||||
EXTERNAL_URL: http://localhost:8081/
|
||||
# Erlang
|
||||
ERLANG_DISTRIBUTION_PORT: 9000
|
||||
RELEASE_COOKIE: "NksuBhJFBhjHD1uUa9mDOHV"
|
||||
RELEASE_HOSTNAME: "mix.cluster.local"
|
||||
RELEASE_NAME: "mix"
|
||||
# Database
|
||||
DATABASE_HOST: postgres
|
||||
DATABASE_PORT: 5432
|
||||
DATABASE_NAME: firezone_dev
|
||||
DATABASE_USER: postgres
|
||||
DATABASE_PASSWORD: postgres
|
||||
# Auth
|
||||
AUTH_PROVIDER_ADAPTERS: "email,openid_connect,userpass,token,google_workspace,microsoft_entra,okta"
|
||||
# Secrets
|
||||
TOKENS_KEY_BASE: "5OVYJ83AcoQcPmdKNksuBhJFBhjHD1uUa9mDOHV/6EIdBQ6pXksIhkVeWIzFk5S2"
|
||||
TOKENS_SALT: "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"
|
||||
SECRET_KEY_BASE: "5OVYJ83AcoQcPmdKNksuBhJFBhjHD1uUa9mDOHV/6EIdBQ6pXksIhkVeWIzFk5S2"
|
||||
LIVE_VIEW_SIGNING_SALT: "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"
|
||||
COOKIE_SIGNING_SALT: "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"
|
||||
COOKIE_ENCRYPTION_SALT: "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"
|
||||
# Higher log level not to make seeds output too verbose
|
||||
LOG_LEVEL: "info"
|
||||
# Emails
|
||||
OUTBOUND_EMAIL_FROM: "public-noreply@firez.one"
|
||||
OUTBOUND_EMAIL_ADAPTER: "Elixir.Swoosh.Adapters.Postmark"
|
||||
## Warning: The token is for the blackhole Postmark server created in a separate isolated account,
|
||||
## that WILL NOT send any actual emails, but you can see and debug them in the Postmark dashboard.
|
||||
OUTBOUND_EMAIL_ADAPTER_OPTS: '{"api_key":"7da7d1cd-111c-44a7-b5ac-4027b9d230e5"}'
|
||||
# Mix env should be set to prod to use secrets declared above,
|
||||
# otherwise seeds will generate invalid tokens
|
||||
MIX_ENV: "prod"
|
||||
# Seeds
|
||||
STATIC_SEEDS: "true"
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: "service_healthy"
|
||||
networks:
|
||||
- app
|
||||
|
||||
@@ -240,125 +416,6 @@ services:
|
||||
app:
|
||||
ipv4_address: 172.28.0.101
|
||||
|
||||
api:
|
||||
build:
|
||||
context: elixir
|
||||
cache_from:
|
||||
- type=registry,ref=us-east1-docker.pkg.dev/firezone-staging/cache/api:main
|
||||
args:
|
||||
APPLICATION_NAME: api
|
||||
image: ${API_IMAGE:-us-east1-docker.pkg.dev/firezone-staging/cache/api}:${API_TAG:-main}
|
||||
hostname: api.cluster.local
|
||||
ports:
|
||||
- 8081:8081/tcp
|
||||
environment:
|
||||
# Web Server
|
||||
EXTERNAL_URL: http://localhost:8081/
|
||||
PHOENIX_HTTP_API_PORT: "8081"
|
||||
PHOENIX_SECURE_COOKIES: "false"
|
||||
# Erlang
|
||||
ERLANG_DISTRIBUTION_PORT: 9000
|
||||
ERLANG_CLUSTER_ADAPTER: "Elixir.Cluster.Strategy.Epmd"
|
||||
ERLANG_CLUSTER_ADAPTER_CONFIG: '{"hosts":["api@api.cluster.local","web@web.cluster.local"]}'
|
||||
RELEASE_COOKIE: "NksuBhJFBhjHD1uUa9mDOHV"
|
||||
RELEASE_HOSTNAME: "api.cluster.local"
|
||||
RELEASE_NAME: "api"
|
||||
# Database
|
||||
DATABASE_HOST: postgres
|
||||
DATABASE_PORT: 5432
|
||||
DATABASE_NAME: firezone_dev
|
||||
DATABASE_USER: postgres
|
||||
DATABASE_PASSWORD: postgres
|
||||
# Auth
|
||||
AUTH_PROVIDER_ADAPTERS: "email,openid_connect,userpass,token,google_workspace,microsoft_entra,okta"
|
||||
# Secrets
|
||||
TOKENS_KEY_BASE: "5OVYJ83AcoQcPmdKNksuBhJFBhjHD1uUa9mDOHV/6EIdBQ6pXksIhkVeWIzFk5S2"
|
||||
TOKENS_SALT: "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"
|
||||
SECRET_KEY_BASE: "5OVYJ83AcoQcPmdKNksuBhJFBhjHD1uUa9mDOHV/6EIdBQ6pXksIhkVeWIzFk5S2"
|
||||
LIVE_VIEW_SIGNING_SALT: "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"
|
||||
COOKIE_SIGNING_SALT: "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"
|
||||
COOKIE_ENCRYPTION_SALT: "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"
|
||||
# Telemetry
|
||||
TELEMETRY_ENABLED: "false"
|
||||
# Debugging
|
||||
LOG_LEVEL: "debug"
|
||||
# Emails
|
||||
OUTBOUND_EMAIL_FROM: "public-noreply@firez.one"
|
||||
OUTBOUND_EMAIL_ADAPTER: "Elixir.Swoosh.Adapters.Postmark"
|
||||
## Warning: The token is for the blackhole Postmark server created in a separate isolated account,
|
||||
## that WILL NOT send any actual emails, but you can see and debug them in the Postmark dashboard.
|
||||
OUTBOUND_EMAIL_ADAPTER_OPTS: '{"api_key":"7da7d1cd-111c-44a7-b5ac-4027b9d230e5"}'
|
||||
# Seeds
|
||||
STATIC_SEEDS: "true"
|
||||
depends_on:
|
||||
vault:
|
||||
condition: "service_healthy"
|
||||
postgres:
|
||||
condition: "service_healthy"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -f localhost:8081/healthz"]
|
||||
start_period: 10s
|
||||
interval: 30s
|
||||
retries: 5
|
||||
timeout: 5s
|
||||
networks:
|
||||
- app
|
||||
|
||||
# This is a service container which allows to run mix tasks for local development
|
||||
# without having to install Elixir and Erlang on the host machine.
|
||||
elixir:
|
||||
build:
|
||||
context: elixir
|
||||
target: compiler
|
||||
cache_from:
|
||||
- type=registry,ref=us-east1-docker.pkg.dev/firezone-staging/cache/elixir:main
|
||||
args:
|
||||
APPLICATION_NAME: api
|
||||
image: ${ELIXIR_IMAGE:-us-east1-docker.pkg.dev/firezone-staging/firezone/elixir}:${ELIXIR_TAG:-main}
|
||||
hostname: elixir
|
||||
environment:
|
||||
# Web Server
|
||||
EXTERNAL_URL: http://localhost:8081/
|
||||
# Erlang
|
||||
ERLANG_DISTRIBUTION_PORT: 9000
|
||||
RELEASE_COOKIE: "NksuBhJFBhjHD1uUa9mDOHV"
|
||||
RELEASE_HOSTNAME: "mix.cluster.local"
|
||||
RELEASE_NAME: "mix"
|
||||
# Database
|
||||
DATABASE_HOST: postgres
|
||||
DATABASE_PORT: 5432
|
||||
DATABASE_NAME: firezone_dev
|
||||
DATABASE_USER: postgres
|
||||
DATABASE_PASSWORD: postgres
|
||||
# Auth
|
||||
AUTH_PROVIDER_ADAPTERS: "email,openid_connect,userpass,token,google_workspace,microsoft_entra,okta"
|
||||
# Secrets
|
||||
TOKENS_KEY_BASE: "5OVYJ83AcoQcPmdKNksuBhJFBhjHD1uUa9mDOHV/6EIdBQ6pXksIhkVeWIzFk5S2"
|
||||
TOKENS_SALT: "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"
|
||||
SECRET_KEY_BASE: "5OVYJ83AcoQcPmdKNksuBhJFBhjHD1uUa9mDOHV/6EIdBQ6pXksIhkVeWIzFk5S2"
|
||||
LIVE_VIEW_SIGNING_SALT: "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"
|
||||
COOKIE_SIGNING_SALT: "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"
|
||||
COOKIE_ENCRYPTION_SALT: "t01wa0K4lUd7mKa0HAtZdE+jFOPDDej2"
|
||||
# Telemetry
|
||||
TELEMETRY_ENABLED: "false"
|
||||
# Higher log level not to make seeds output too verbose
|
||||
LOG_LEVEL: "info"
|
||||
# Emails
|
||||
OUTBOUND_EMAIL_FROM: "public-noreply@firez.one"
|
||||
OUTBOUND_EMAIL_ADAPTER: "Elixir.Swoosh.Adapters.Postmark"
|
||||
## Warning: The token is for the blackhole Postmark server created in a separate isolated account,
|
||||
## that WILL NOT send any actual emails, but you can see and debug them in the Postmark dashboard.
|
||||
OUTBOUND_EMAIL_ADAPTER_OPTS: '{"api_key":"7da7d1cd-111c-44a7-b5ac-4027b9d230e5"}'
|
||||
# Mix env should be set to prod to use secrets declared above,
|
||||
# otherwise seeds will generate invalid tokens
|
||||
MIX_ENV: "prod"
|
||||
# Seeds
|
||||
STATIC_SEEDS: "true"
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: "service_healthy"
|
||||
networks:
|
||||
- app
|
||||
|
||||
# IPv6 is currently causing flakiness with GH actions and on our testbed.
|
||||
# Disabling until there's more time to debug.
|
||||
|
||||
@@ -37,10 +37,10 @@ defmodule Domain.Application do
|
||||
Domain.Relays,
|
||||
Domain.Gateways,
|
||||
Domain.Clients,
|
||||
Domain.Billing
|
||||
Domain.Billing,
|
||||
|
||||
# Observability
|
||||
# Domain.Telemetry
|
||||
Domain.Telemetry
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -39,6 +39,13 @@ defmodule Domain.Config.Definitions do
|
||||
|
||||
def doc_sections do
|
||||
[
|
||||
{"Background Jobs",
|
||||
"""
|
||||
You need to make sure that at least one of the nodes in the cluster has background jobs enabled.
|
||||
""",
|
||||
[
|
||||
:background_jobs_enabled
|
||||
]},
|
||||
{"WebServer",
|
||||
[
|
||||
:external_url,
|
||||
@@ -114,28 +121,30 @@ defmodule Domain.Config.Definitions do
|
||||
[
|
||||
:instrumentation_client_logs_enabled,
|
||||
:instrumentation_client_logs_bucket
|
||||
]},
|
||||
{"Telemetry",
|
||||
[
|
||||
:telemetry_enabled,
|
||||
:telemetry_id
|
||||
]}
|
||||
]
|
||||
end
|
||||
|
||||
##############################################
|
||||
## Background Jobs
|
||||
##############################################
|
||||
|
||||
@doc """
|
||||
Enabled or disable background job workers (eg. syncing IdP directory) for the app instance.
|
||||
"""
|
||||
defconfig(:background_jobs_enabled, :boolean, default: false)
|
||||
|
||||
##############################################
|
||||
## Web Server
|
||||
##############################################
|
||||
|
||||
@doc """
|
||||
The external URL the web UI will be accessible at.
|
||||
The external URL the UI/API will be accessible at.
|
||||
|
||||
Must be a valid and public FQDN for ACME SSL issuance to function.
|
||||
|
||||
You can add a path suffix if you want to serve firezone from a non-root path,
|
||||
eg: `https://firezone.mycorp.com/vpn/`.
|
||||
If this field is not set or set to `nil`, the server for `api` and `web` apps will not start.
|
||||
"""
|
||||
defconfig(:external_url, :string,
|
||||
default: nil,
|
||||
changeset: fn changeset, key ->
|
||||
changeset
|
||||
|> Domain.Validator.validate_uri(key, require_trailing_slash: true)
|
||||
@@ -151,7 +160,7 @@ defmodule Domain.Config.Definitions do
|
||||
defconfig(:phoenix_listen_address, Types.IP, default: "0.0.0.0")
|
||||
|
||||
@doc """
|
||||
Internal port to listen on for the Phoenix web server.
|
||||
Internal port to listen on for the Phoenix server for the `web` application.
|
||||
"""
|
||||
defconfig(:phoenix_http_web_port, :integer,
|
||||
default: 13_000,
|
||||
@@ -164,7 +173,7 @@ defmodule Domain.Config.Definitions do
|
||||
)
|
||||
|
||||
@doc """
|
||||
Internal port to listen on for the Phoenix api server.
|
||||
Internal port to listen on for the Phoenix server for the `api` application.
|
||||
"""
|
||||
defconfig(:phoenix_http_api_port, :integer,
|
||||
default: 13_000,
|
||||
@@ -444,25 +453,6 @@ defmodule Domain.Config.Definitions do
|
||||
"""
|
||||
defconfig(:instrumentation_client_logs_bucket, :string, default: "logs")
|
||||
|
||||
##############################################
|
||||
## Telemetry
|
||||
##############################################
|
||||
|
||||
@doc """
|
||||
Enable or disable the Firezone telemetry collection.
|
||||
|
||||
For more details see https://docs.firezone.dev/reference/telemetry/.
|
||||
"""
|
||||
defconfig(:telemetry_enabled, :boolean, default: true)
|
||||
|
||||
defconfig(:telemetry_id, :string,
|
||||
default: fn ->
|
||||
:crypto.hash(:sha256, compile_config!(:external_url))
|
||||
|> Base.url_encode64(padding: false)
|
||||
end,
|
||||
legacy_keys: [{:env, "TID", nil}]
|
||||
)
|
||||
|
||||
##############################################
|
||||
## Gateways
|
||||
##############################################
|
||||
|
||||
@@ -1,189 +1,50 @@
|
||||
# TODO: when app starts for migrations set env to disable connectivity checks and telemetry
|
||||
# defmodule Domain.Telemetry do
|
||||
# @moduledoc """
|
||||
# Functions for various telemetry events.
|
||||
# """
|
||||
# use Supervisor
|
||||
# alias Domain.Telemetry.{Timer, PostHog}
|
||||
# require Logger
|
||||
defmodule Domain.Telemetry do
|
||||
use Supervisor
|
||||
import Telemetry.Metrics
|
||||
alias Domain.Telemetry
|
||||
|
||||
# def start_link(opts) do
|
||||
# Supervisor.start_link(__MODULE__, opts, name: __MODULE__)
|
||||
# end
|
||||
def start_link(arg) do
|
||||
Supervisor.start_link(__MODULE__, arg, name: __MODULE__)
|
||||
end
|
||||
|
||||
# def init(_opts) do
|
||||
# config = Domain.Config.fetch_env!(:domain, Domain.Telemetry)
|
||||
@impl true
|
||||
def init(_arg) do
|
||||
children = [
|
||||
# We start a /healthz endpoint that is used for liveness probes
|
||||
{Bandit, plug: Telemetry.HealthzPlug, scheme: :http, port: 4000},
|
||||
|
||||
# if Keyword.fetch!(config, :enabled) == true do
|
||||
# children = [Timer]
|
||||
# Supervisor.init(children, strategy: :one_for_one)
|
||||
# else
|
||||
# :ignore
|
||||
# end
|
||||
# end
|
||||
# Telemetry poller will execute the given period measurements
|
||||
# every 10_000ms. Learn more here: https://hexdocs.pm/telemetry_metrics
|
||||
{:telemetry_poller, measurements: periodic_measurements(), period: 10_000}
|
||||
# Add reporters as children of your supervision tree.
|
||||
# {Telemetry.Metrics.ConsoleReporter, metrics: metrics()}
|
||||
]
|
||||
|
||||
# def create_api_token do
|
||||
# PostHog.capture("add_api_token", common_fields())
|
||||
# :ok
|
||||
# end
|
||||
Supervisor.init(children, strategy: :one_for_one)
|
||||
end
|
||||
|
||||
# def delete_api_token(api_token) do
|
||||
# PostHog.capture(
|
||||
# "delete_api_token",
|
||||
# common_fields() ++
|
||||
# [
|
||||
# api_token_created_at: api_token.inserted_at
|
||||
# ]
|
||||
# )
|
||||
def metrics do
|
||||
[
|
||||
# Database Metrics
|
||||
summary("domain.repo.query.total_time", unit: {:native, :millisecond}),
|
||||
summary("domain.repo.query.decode_time", unit: {:native, :millisecond}),
|
||||
summary("domain.repo.query.query_time", unit: {:native, :millisecond}),
|
||||
summary("domain.repo.query.queue_time", unit: {:native, :millisecond}),
|
||||
summary("domain.repo.query.idle_time", unit: {:native, :millisecond}),
|
||||
|
||||
# :ok
|
||||
# end
|
||||
# VM Metrics
|
||||
summary("vm.memory.total", unit: {:byte, :kilobyte}),
|
||||
summary("vm.total_run_queue_lengths.total"),
|
||||
summary("vm.total_run_queue_lengths.cpu"),
|
||||
summary("vm.total_run_queue_lengths.io")
|
||||
]
|
||||
end
|
||||
|
||||
# def add_client do
|
||||
# PostHog.capture("add_client", common_fields())
|
||||
# :ok
|
||||
# end
|
||||
|
||||
# def add_actor do
|
||||
# PostHog.capture("add_actor", common_fields())
|
||||
# :ok
|
||||
# end
|
||||
|
||||
# def add_rule do
|
||||
# PostHog.capture("add_rule", common_fields())
|
||||
# :ok
|
||||
# end
|
||||
|
||||
# def delete_client do
|
||||
# PostHog.capture("delete_client", common_fields())
|
||||
# :ok
|
||||
# end
|
||||
|
||||
# def delete_actor do
|
||||
# PostHog.capture("delete_actor", common_fields())
|
||||
# :ok
|
||||
# end
|
||||
|
||||
# def delete_rule do
|
||||
# PostHog.capture("delete_rule", common_fields())
|
||||
# :ok
|
||||
# end
|
||||
|
||||
# def login do
|
||||
# PostHog.capture("login", common_fields())
|
||||
# :ok
|
||||
# end
|
||||
|
||||
# def enable_actor do
|
||||
# PostHog.capture("enable_actor", common_fields())
|
||||
# :ok
|
||||
# end
|
||||
|
||||
# def disable_actor do
|
||||
# PostHog.capture("disable_actor", common_fields())
|
||||
# :ok
|
||||
# end
|
||||
|
||||
# def domain_started do
|
||||
# PostHog.capture("domain_started", common_fields())
|
||||
# :ok
|
||||
# end
|
||||
|
||||
# def ping do
|
||||
# PostHog.capture("ping", ping_data())
|
||||
# :ok
|
||||
# end
|
||||
|
||||
# # How far back to count handshakes as an active client
|
||||
# # @active_client_window 86_400
|
||||
# def ping_data do
|
||||
# %{
|
||||
# local_auth_enabled: {_, local_auth_enabled},
|
||||
# logo: {_, logo}
|
||||
# } =
|
||||
# Domain.Config.fetch_resolved_configs_with_sources!([
|
||||
# :local_auth_enabled,
|
||||
# :logo
|
||||
# ])
|
||||
|
||||
# common_fields() ++
|
||||
# [
|
||||
# # clients_active_within_24h: Clients.count_active_within(@active_client_window),
|
||||
# # admin_count: Users.count_by_role(:account_admin_user),
|
||||
# # actor_count: Users.count(),
|
||||
# in_docker: in_docker?(),
|
||||
# # client_count: Clients.count(),
|
||||
# # max_clients_for_actors: Clients.count_maximum_for_a_actor(),
|
||||
# # actors_with_mfa: MFA.count_actors_with_mfa_enabled(),
|
||||
# # actors_with_mfa_totp: MFA.count_actors_with_totp_method(),
|
||||
# local_authentication: local_auth_enabled,
|
||||
# # outbound_email: Web.Mailer.active?(),
|
||||
# external_database:
|
||||
# external_database?(Map.new(Domain.Config.fetch_env!(:domain, Domain.Repo))),
|
||||
# logo_type: Domain.Config.Logo.type(logo)
|
||||
# ]
|
||||
# end
|
||||
|
||||
# defp in_docker? do
|
||||
# File.exists?("/.dockerenv")
|
||||
# end
|
||||
|
||||
# defp common_fields do
|
||||
# [
|
||||
# distinct_id: id(),
|
||||
# fqdn: fqdn(),
|
||||
# version: version(),
|
||||
# kernel_version: "#{os_type()} #{os_version()}"
|
||||
# ]
|
||||
# end
|
||||
|
||||
# def id do
|
||||
# Domain.Config.fetch_env!(:domain, __MODULE__)
|
||||
# |> Keyword.fetch!(:id)
|
||||
# end
|
||||
|
||||
# defp fqdn do
|
||||
# :web
|
||||
# |> Domain.Config.fetch_env!(Web.Endpoint)
|
||||
# |> Keyword.get(:url)
|
||||
# |> Keyword.get(:host)
|
||||
# end
|
||||
|
||||
# defp version do
|
||||
# Application.spec(:domain, :vsn) |> to_string()
|
||||
# end
|
||||
|
||||
# defp external_database?(repo_conf) when is_map_key(repo_conf, :hostname) do
|
||||
# is_external_db?(repo_conf.hostname)
|
||||
# end
|
||||
|
||||
# defp external_database?(repo_conf) when is_map_key(repo_conf, :url) do
|
||||
# %{host: host} = URI.parse(repo_conf.url)
|
||||
|
||||
# is_external_db?(host)
|
||||
# end
|
||||
|
||||
# defp is_external_db?(host) do
|
||||
# host != "localhost" && host != "127.0.0.1"
|
||||
# end
|
||||
|
||||
# defp os_type do
|
||||
# case :os.type() do
|
||||
# {:unix, type} ->
|
||||
# "#{type}"
|
||||
|
||||
# _ ->
|
||||
# "other"
|
||||
# end
|
||||
# end
|
||||
|
||||
# defp os_version do
|
||||
# case :os.version() do
|
||||
# {major, minor, patch} ->
|
||||
# "#{major}.#{minor}.#{patch}"
|
||||
|
||||
# _ ->
|
||||
# "0.0.0"
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
defp periodic_measurements do
|
||||
[
|
||||
# A module, function and arguments to be invoked periodically.
|
||||
# This function must call :telemetry.execute/3 and a metric must be added above.
|
||||
# {Web, :count_users, []}
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
19
elixir/apps/domain/lib/domain/telemetry/healthz_plug.ex
Normal file
19
elixir/apps/domain/lib/domain/telemetry/healthz_plug.ex
Normal file
@@ -0,0 +1,19 @@
|
||||
defmodule Domain.Telemetry.HealthzPlug do
|
||||
@moduledoc """
|
||||
A plug that returns a 200 OK response for health checks.
|
||||
"""
|
||||
import Plug.Conn
|
||||
|
||||
@behaviour Plug
|
||||
|
||||
@impl true
|
||||
def init(opts), do: opts
|
||||
|
||||
@impl true
|
||||
def call(conn, _opts) do
|
||||
conn
|
||||
|> put_resp_content_type("application/json")
|
||||
|> send_resp(200, Jason.encode!(%{status: :ok}))
|
||||
|> halt()
|
||||
end
|
||||
end
|
||||
@@ -60,7 +60,11 @@ defmodule Domain.MixProject do
|
||||
{:libcluster, "~> 3.3"},
|
||||
|
||||
# Observability and Runtime debugging
|
||||
{:bandit, "~> 1.0"},
|
||||
{:plug, "~> 1.15"},
|
||||
{:telemetry, "~> 1.0"},
|
||||
{:telemetry_poller, "~> 1.0"},
|
||||
{:telemetry_metrics, "~> 0.6.2"},
|
||||
{:logger_json, "~> 5.1"},
|
||||
{:recon, "~> 2.5"},
|
||||
{:observer_cli, "~> 1.7"},
|
||||
|
||||
@@ -97,7 +97,7 @@ defmodule Domain.ConfigTest do
|
||||
|
||||
test "raises an error when value is missing", %{account: account} do
|
||||
message = """
|
||||
Missing required configuration value for 'external_url'.
|
||||
Missing required configuration value for 'secret_key_base'.
|
||||
|
||||
## How to fix?
|
||||
|
||||
@@ -105,24 +105,19 @@ defmodule Domain.ConfigTest do
|
||||
|
||||
You can set this configuration via environment variable by adding it to `.env` file:
|
||||
|
||||
EXTERNAL_URL=YOUR_VALUE
|
||||
SECRET_KEY_BASE=YOUR_VALUE
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
The external URL the web UI will be accessible at.
|
||||
|
||||
Must be a valid and public FQDN for ACME SSL issuance to function.
|
||||
|
||||
You can add a path suffix if you want to serve firezone from a non-root path,
|
||||
eg: `https://firezone.mycorp.com/vpn/`.
|
||||
Primary secret key base for the Phoenix application.
|
||||
|
||||
|
||||
You can find more information on configuration here: https://www.firezone.dev/docs/reference/env-vars/#environment-variable-listing
|
||||
"""
|
||||
|
||||
assert_raise RuntimeError, message, fn ->
|
||||
fetch_resolved_configs!(account.id, [:external_url])
|
||||
fetch_resolved_configs!(account.id, [:secret_key_base])
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -151,7 +146,7 @@ defmodule Domain.ConfigTest do
|
||||
|
||||
test "raises an error when value is missing", %{account: account} do
|
||||
message = """
|
||||
Missing required configuration value for 'external_url'.
|
||||
Missing required configuration value for 'secret_key_base'.
|
||||
|
||||
## How to fix?
|
||||
|
||||
@@ -159,24 +154,19 @@ defmodule Domain.ConfigTest do
|
||||
|
||||
You can set this configuration via environment variable by adding it to `.env` file:
|
||||
|
||||
EXTERNAL_URL=YOUR_VALUE
|
||||
SECRET_KEY_BASE=YOUR_VALUE
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
The external URL the web UI will be accessible at.
|
||||
|
||||
Must be a valid and public FQDN for ACME SSL issuance to function.
|
||||
|
||||
You can add a path suffix if you want to serve firezone from a non-root path,
|
||||
eg: `https://firezone.mycorp.com/vpn/`.
|
||||
Primary secret key base for the Phoenix application.
|
||||
|
||||
|
||||
You can find more information on configuration here: https://www.firezone.dev/docs/reference/env-vars/#environment-variable-listing
|
||||
"""
|
||||
|
||||
assert_raise RuntimeError, message, fn ->
|
||||
fetch_resolved_configs_with_sources!(account.id, [:external_url])
|
||||
fetch_resolved_configs_with_sources!(account.id, [:secret_key_base])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -192,12 +182,9 @@ defmodule Domain.ConfigTest do
|
||||
|
||||
## Documentation
|
||||
|
||||
The external URL the web UI will be accessible at.
|
||||
The external URL the UI/API will be accessible at.
|
||||
|
||||
Must be a valid and public FQDN for ACME SSL issuance to function.
|
||||
|
||||
You can add a path suffix if you want to serve firezone from a non-root path,
|
||||
eg: `https://firezone.mycorp.com/vpn/`.
|
||||
If this field is not set or set to `nil`, the server for `api` and `web` apps will not start.
|
||||
|
||||
|
||||
You can find more information on configuration here: https://www.firezone.dev/docs/reference/env-vars/#environment-variable-listing
|
||||
|
||||
@@ -49,13 +49,7 @@ defmodule Web.Telemetry do
|
||||
summary("phoenix.channel_handled_in.duration",
|
||||
tags: [:event],
|
||||
unit: {:native, :millisecond}
|
||||
),
|
||||
|
||||
# VM Metrics
|
||||
summary("vm.memory.total", unit: {:byte, :kilobyte}),
|
||||
summary("vm.total_run_queue_lengths.total"),
|
||||
summary("vm.total_run_queue_lengths.cpu"),
|
||||
summary("vm.total_run_queue_lengths.io")
|
||||
)
|
||||
]
|
||||
end
|
||||
|
||||
|
||||
@@ -18,15 +18,6 @@ if config_env() == :prod do
|
||||
ssl_opts: compile_config!(:database_ssl_opts),
|
||||
parameters: compile_config!(:database_parameters)
|
||||
|
||||
external_url = compile_config!(:external_url)
|
||||
|
||||
%{
|
||||
scheme: external_url_scheme,
|
||||
host: external_url_host,
|
||||
port: external_url_port,
|
||||
path: external_url_path
|
||||
} = URI.parse(external_url)
|
||||
|
||||
config :domain, Domain.Tokens,
|
||||
key_base: compile_config!(:tokens_key_base),
|
||||
salt: compile_config!(:tokens_salt)
|
||||
@@ -35,10 +26,6 @@ if config_env() == :prod do
|
||||
gateway_ipv4_masquerade: compile_config!(:gateway_ipv4_masquerade),
|
||||
gateway_ipv6_masquerade: compile_config!(:gateway_ipv6_masquerade)
|
||||
|
||||
config :domain, Domain.Telemetry,
|
||||
enabled: compile_config!(:telemetry_enabled),
|
||||
id: compile_config!(:telemetry_id)
|
||||
|
||||
config :domain, Domain.Auth.Adapters.GoogleWorkspace.APIClient,
|
||||
finch_transport_opts: compile_config!(:http_client_ssl_opts)
|
||||
|
||||
@@ -78,70 +65,91 @@ if config_env() == :prod do
|
||||
|
||||
config :domain, outbound_email_adapter_configured?: !!compile_config!(:outbound_email_adapter)
|
||||
|
||||
###############################
|
||||
##### Web #####################
|
||||
###############################
|
||||
# Enable background jobs only on dedicated nodes
|
||||
config :domain, Domain.Tokens.Jobs, enabled: compile_config!(:background_jobs_enabled)
|
||||
|
||||
config :web, Web.Endpoint,
|
||||
http: [
|
||||
ip: compile_config!(:phoenix_listen_address).address,
|
||||
port: compile_config!(:phoenix_http_web_port),
|
||||
protocol_options: compile_config!(:phoenix_http_protocol_options)
|
||||
],
|
||||
url: [
|
||||
config :domain, Domain.Auth.Adapters.GoogleWorkspace.Jobs,
|
||||
enabled: compile_config!(:background_jobs_enabled)
|
||||
|
||||
config :domain, Domain.Auth.Adapters.MicrosoftEntra.Jobs,
|
||||
enabled: compile_config!(:background_jobs_enabled)
|
||||
|
||||
config :domain, Domain.Auth.Adapters.Okta.Jobs,
|
||||
enabled: compile_config!(:background_jobs_enabled)
|
||||
|
||||
if external_url = compile_config!(:external_url) do
|
||||
%{
|
||||
scheme: external_url_scheme,
|
||||
host: external_url_host,
|
||||
port: external_url_port,
|
||||
path: external_url_path
|
||||
],
|
||||
secret_key_base: compile_config!(:secret_key_base),
|
||||
check_origin: [
|
||||
"#{external_url_scheme}://#{external_url_host}:#{external_url_port}",
|
||||
"#{external_url_scheme}://*.#{external_url_host}:#{external_url_port}",
|
||||
"#{external_url_scheme}://#{external_url_host}",
|
||||
"#{external_url_scheme}://*.#{external_url_host}"
|
||||
],
|
||||
live_view: [
|
||||
signing_salt: compile_config!(:live_view_signing_salt)
|
||||
]
|
||||
} = URI.parse(external_url)
|
||||
|
||||
config :web,
|
||||
external_trusted_proxies: compile_config!(:phoenix_external_trusted_proxies),
|
||||
private_clients: compile_config!(:phoenix_private_clients)
|
||||
###############################
|
||||
##### Web #####################
|
||||
###############################
|
||||
|
||||
config :web,
|
||||
cookie_secure: compile_config!(:phoenix_secure_cookies),
|
||||
cookie_signing_salt: compile_config!(:cookie_signing_salt),
|
||||
cookie_encryption_salt: compile_config!(:cookie_encryption_salt)
|
||||
config :web, Web.Endpoint,
|
||||
http: [
|
||||
ip: compile_config!(:phoenix_listen_address).address,
|
||||
port: compile_config!(:phoenix_http_web_port),
|
||||
protocol_options: compile_config!(:phoenix_http_protocol_options)
|
||||
],
|
||||
url: [
|
||||
scheme: external_url_scheme,
|
||||
host: external_url_host,
|
||||
port: external_url_port,
|
||||
path: external_url_path
|
||||
],
|
||||
secret_key_base: compile_config!(:secret_key_base),
|
||||
check_origin: [
|
||||
"#{external_url_scheme}://#{external_url_host}:#{external_url_port}",
|
||||
"#{external_url_scheme}://*.#{external_url_host}:#{external_url_port}",
|
||||
"#{external_url_scheme}://#{external_url_host}",
|
||||
"#{external_url_scheme}://*.#{external_url_host}"
|
||||
],
|
||||
live_view: [
|
||||
signing_salt: compile_config!(:live_view_signing_salt)
|
||||
]
|
||||
|
||||
config :web, api_url_override: compile_config!(:api_url_override)
|
||||
config :web,
|
||||
external_trusted_proxies: compile_config!(:phoenix_external_trusted_proxies),
|
||||
private_clients: compile_config!(:phoenix_private_clients)
|
||||
|
||||
###############################
|
||||
##### API #####################
|
||||
###############################
|
||||
config :web,
|
||||
cookie_secure: compile_config!(:phoenix_secure_cookies),
|
||||
cookie_signing_salt: compile_config!(:cookie_signing_salt),
|
||||
cookie_encryption_salt: compile_config!(:cookie_encryption_salt)
|
||||
|
||||
config :api, API.Endpoint,
|
||||
http: [
|
||||
ip: compile_config!(:phoenix_listen_address).address,
|
||||
port: compile_config!(:phoenix_http_api_port),
|
||||
protocol_options: compile_config!(:phoenix_http_protocol_options)
|
||||
],
|
||||
url: [
|
||||
scheme: external_url_scheme,
|
||||
host: external_url_host,
|
||||
port: external_url_port,
|
||||
path: external_url_path
|
||||
],
|
||||
secret_key_base: compile_config!(:secret_key_base)
|
||||
config :web, api_url_override: compile_config!(:api_url_override)
|
||||
|
||||
config :api,
|
||||
cookie_secure: compile_config!(:phoenix_secure_cookies),
|
||||
cookie_signing_salt: compile_config!(:cookie_signing_salt),
|
||||
cookie_encryption_salt: compile_config!(:cookie_encryption_salt)
|
||||
###############################
|
||||
##### API #####################
|
||||
###############################
|
||||
|
||||
config :api,
|
||||
external_trusted_proxies: compile_config!(:phoenix_external_trusted_proxies),
|
||||
private_clients: compile_config!(:phoenix_private_clients)
|
||||
config :api, API.Endpoint,
|
||||
http: [
|
||||
ip: compile_config!(:phoenix_listen_address).address,
|
||||
port: compile_config!(:phoenix_http_api_port),
|
||||
protocol_options: compile_config!(:phoenix_http_protocol_options)
|
||||
],
|
||||
url: [
|
||||
scheme: external_url_scheme,
|
||||
host: external_url_host,
|
||||
port: external_url_port,
|
||||
path: external_url_path
|
||||
],
|
||||
secret_key_base: compile_config!(:secret_key_base)
|
||||
|
||||
config :api,
|
||||
cookie_secure: compile_config!(:phoenix_secure_cookies),
|
||||
cookie_signing_salt: compile_config!(:cookie_signing_salt),
|
||||
cookie_encryption_salt: compile_config!(:cookie_encryption_salt)
|
||||
|
||||
config :api,
|
||||
external_trusted_proxies: compile_config!(:phoenix_external_trusted_proxies),
|
||||
private_clients: compile_config!(:phoenix_private_clients)
|
||||
end
|
||||
|
||||
###############################
|
||||
##### Third-party configs #####
|
||||
|
||||
@@ -63,6 +63,15 @@ defmodule Firezone.MixProject do
|
||||
|
||||
defp releases do
|
||||
[
|
||||
domain: [
|
||||
include_executables_for: [:unix],
|
||||
validate_compile_env: true,
|
||||
applications: [
|
||||
domain: :permanent,
|
||||
opentelemetry_exporter: :permanent,
|
||||
opentelemetry: :temporary
|
||||
]
|
||||
],
|
||||
web: [
|
||||
include_executables_for: [:unix],
|
||||
validate_compile_env: true,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
%{
|
||||
"acceptor_pool": {:hex, :acceptor_pool, "1.0.0", "43c20d2acae35f0c2bcd64f9d2bde267e459f0f3fd23dab26485bf518c281b21", [:rebar3], [], "hexpm", "0cbcd83fdc8b9ad2eee2067ef8b91a14858a5883cb7cd800e6fcd5803e158788"},
|
||||
"argon2_elixir": {:hex, :argon2_elixir, "4.0.0", "7f6cd2e4a93a37f61d58a367d82f830ad9527082ff3c820b8197a8a736648941", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "f9da27cf060c9ea61b1bd47837a28d7e48a8f6fa13a745e252556c14f9132c7f"},
|
||||
"bandit": {:hex, :bandit, "1.3.0", "6a4e8d7c9ea721edd02c389e2cc867890cd96f83116e71ddf1ccbdd80661550c", [:mix], [{:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "bda37d6c614d74778a5dc43b8bcdc3245cd30619eab0342f58042f968f2165da"},
|
||||
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
|
||||
"bureaucrat": {:hex, :bureaucrat, "0.2.9", "d98e4d2b9bdbf22e4a45c2113ce8b38b5b63278506c6ff918e3b943a4355d85b", [:mix], [{:inflex, ">= 1.10.0", [hex: :inflex, repo: "hexpm", optional: false]}, {:phoenix, ">= 1.2.0", [hex: :phoenix, repo: "hexpm", optional: true]}, {:plug, ">= 1.0.0", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 1.5 or ~> 2.0 or ~> 3.0 or ~> 4.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "111c8dd84382a62e1026ae011d592ceee918553e5203fe8448d9ba6ccbdfff7d"},
|
||||
"bypass": {:hex, :bypass, "2.1.0", "909782781bf8e20ee86a9cabde36b259d44af8b9f38756173e8f5e2e1fabb9b1", [:mix], [{:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "d9b5df8fa5b7a6efa08384e9bbecfe4ce61c77d28a4282f79e02f1ef78d96b80"},
|
||||
@@ -99,6 +100,7 @@
|
||||
"telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"},
|
||||
"telemetry_registry": {:hex, :telemetry_registry, "0.3.1", "14a3319a7d9027bdbff7ebcacf1a438f5f5c903057b93aee484cca26f05bdcba", [:mix, :rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6d0ca77b691cf854ed074b459a93b87f4c7f5512f8f7743c635ca83da81f939e"},
|
||||
"tesla": {:hex, :tesla, "1.8.0", "d511a4f5c5e42538d97eef7c40ec4f3e44effdc5068206f42ed859e09e51d1fd", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "10501f360cd926a309501287470372af1a6e1cbed0f43949203a4c13300bc79f"},
|
||||
"thousand_island": {:hex, :thousand_island, "1.3.5", "6022b6338f1635b3d32406ff98d68b843ba73b3aa95cfc27154223244f3a6ca5", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2be6954916fdfe4756af3239fb6b6d75d0b8063b5df03ba76fd8a4c87849e180"},
|
||||
"tls_certificate_check": {:hex, :tls_certificate_check, "1.21.0", "042ab2c0c860652bc5cf69c94e3a31f96676d14682e22ec7813bd173ceff1788", [:rebar3], [{:ssl_verify_fun, "~> 1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "6cee6cffc35a390840d48d463541d50746a7b0e421acaadb833cfc7961e490e7"},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"},
|
||||
"wallaby": {:hex, :wallaby, "0.30.6", "7dc4c1213f3b52c4152581d126632bc7e06892336d3a0f582853efeeabd45a71", [:mix], [{:ecto_sql, ">= 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}, {:httpoison, "~> 0.12 or ~> 1.0 or ~> 2.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix_ecto, ">= 3.0.0", [hex: :phoenix_ecto, repo: "hexpm", optional: true]}, {:web_driver_client, "~> 0.2.0", [hex: :web_driver_client, repo: "hexpm", optional: false]}], "hexpm", "50950c1d968549b54c20e16175c68c7fc0824138e2bb93feb11ef6add8eb23d4"},
|
||||
|
||||
@@ -481,10 +481,6 @@ locals {
|
||||
value = var.stripe_default_price_id
|
||||
},
|
||||
# Telemetry
|
||||
{
|
||||
name = "TELEMETRY_ENABLED"
|
||||
value = "false"
|
||||
},
|
||||
{
|
||||
name = "INSTRUMENTATION_CLIENT_LOGS_ENABLED"
|
||||
value = true
|
||||
|
||||
@@ -14,12 +14,12 @@ terraform {
|
||||
|
||||
google = {
|
||||
source = "hashicorp/google"
|
||||
version = "~> 5.2"
|
||||
version = "~> 5.20"
|
||||
}
|
||||
|
||||
google-beta = {
|
||||
source = "hashicorp/google-beta"
|
||||
version = "~> 5.2"
|
||||
version = "~> 5.20"
|
||||
}
|
||||
|
||||
tls = {
|
||||
|
||||
86
terraform/environments/staging/.terraform.lock.hcl
generated
86
terraform/environments/staging/.terraform.lock.hcl
generated
@@ -24,65 +24,65 @@ provider "registry.terraform.io/cyrilgdn/postgresql" {
|
||||
}
|
||||
|
||||
provider "registry.terraform.io/hashicorp/aws" {
|
||||
version = "5.35.0"
|
||||
constraints = ">= 3.29.0, >= 5.20.0"
|
||||
version = "5.41.0"
|
||||
constraints = ">= 3.29.0, >= 5.30.0"
|
||||
hashes = [
|
||||
"h1:MKNFmhsOIirK7Qzr6TWkVaBcVGN81lCU0BPiaPOeQ8s=",
|
||||
"zh:3a2a6f40db82d30ea8c5e3e251ca5e16b08e520570336e7e342be823df67e945",
|
||||
"zh:420a23b69b412438a15b8b2e2c9aac2cf2e4976f990f117e4bf8f630692d3949",
|
||||
"zh:4d8b887f6a71b38cff77ad14af9279528433e279eed702d96b81ea48e16e779c",
|
||||
"zh:4edd41f8e1c7d29931608a7b01a7ae3d89d6f95ef5502cf8200f228a27917c40",
|
||||
"zh:6337544e2ded5cf37b55a70aa6ce81c07fd444a2644ff3c5aad1d34680051bdc",
|
||||
"zh:668faa3faaf2e0758bf319ea40d2304340f4a2dc2cd24460ddfa6ab66f71b802",
|
||||
"zh:79ddc6d7c90e59fdf4a51e6ea822ba9495b1873d6a9d70daf2eeaf6fc4eb6ff3",
|
||||
"zh:885822027faf1aa57787f980ead7c26e7d0e55b4040d926b65709b764f804513",
|
||||
"zh:8c50a8f397b871388ff2e048f5eb280af107faa2e8926694f1ffd9f32a7a7cdf",
|
||||
"h1:DiX7N35G2NUQRyRGy90+gyePnhP4w77f8LrJUronotE=",
|
||||
"zh:0553331a6287c146353b6daf6f71987d8c000f407b5e29d6e004ea88faec2e67",
|
||||
"zh:1a11118984bb2950e8ee7ef17b0f91fc9eb4a42c8e7a9cafd7eb4aca771d06e4",
|
||||
"zh:236fedd266d152a8233a7fe27ffdd99ca27d9e66a9618a988a4c3da1ac24a33f",
|
||||
"zh:34bc482ea04cf30d4d216afa55eecf66854e1acf93892cb28a6b5af91d43c9b7",
|
||||
"zh:39d7eb15832fe339bf46e3bab9852280762a1817bf1afc459eecd430e20e3ad5",
|
||||
"zh:39fb07429c51556b05170ec2b6bd55e2487adfe1606761eaf1f2a43c4bb20e47",
|
||||
"zh:71d7cd3013e2f3fa0f65194af29ee6f5fa905e0df2b72b723761dc953f4512ea",
|
||||
"zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
|
||||
"zh:a2f5d2553df5573a060641f18ee7585587047c25ba73fd80617f59b5893d22b4",
|
||||
"zh:c43833ae2a152213ee92eb5be7653f9493779eddbe0ce403ea49b5f1d87fd766",
|
||||
"zh:dab01527a3a55b4f0f958af6f46313d775e27f9ad9d10bedbbfea4a35a06dc5f",
|
||||
"zh:ed49c65620ec42718d681a7fc00c166c295ff2795db6cede2c690b83f9fb3e65",
|
||||
"zh:f0a358c0ae1087c466d0fbcc3b4da886f33f881a145c3836ec43149878b86a1a",
|
||||
"zh:9b271ae12394e7e2ce6da568b42226a146e90fd705e02a670fcb93618c4aa19f",
|
||||
"zh:a884dd978859d001709681f9513ba0fbb0753d1d459a7f3434ecc5f1b8699c49",
|
||||
"zh:b8c3c7dc10ae4f6143168042dcf8dee63527b103cc37abc238ea06150af38b6e",
|
||||
"zh:ba94ffe0893ad60c0b70c402e163b4df2cf417e93474a9cc1a37535bba18f22d",
|
||||
"zh:d5ba851d971ff8d796afd9a100acf55eaac0c197c6ab779787797ce66f419f0e",
|
||||
"zh:e8c090d0c4f730c4a610dc4f0c22b177a0376d6f78679fc3f1d557b469e656f4",
|
||||
"zh:ed7623acde26834672969dcb5befdb62900d9f216d32e7478a095d2b040a0ea7",
|
||||
]
|
||||
}
|
||||
|
||||
provider "registry.terraform.io/hashicorp/google" {
|
||||
version = "5.18.0"
|
||||
version = "5.20.0"
|
||||
constraints = "~> 5.2"
|
||||
hashes = [
|
||||
"h1:WicMa7ZYUyJlYmePh4jCaPfxAeM8U/8/8x632gBMMPY=",
|
||||
"zh:1f9c144b0bd83c125d27b20c0d3970ba817bbd8eaea8778291c74b54f79e9de5",
|
||||
"zh:4c34f6e875919623bcd6fdc67ee51c54aa469d5184b6a3899cfc97c6dfb83a81",
|
||||
"zh:661443c63c3f976448eff875262e98a5564fef4bc11047b2d841b71034c7fb26",
|
||||
"zh:6a1ace1d2a42d4c7fdc13f67cd582d11ffb27b62309d353fb24609032f992acd",
|
||||
"zh:886bcc6cb6757aa38c6dc31988be1de3aa1880067a8e984aa2592c436f62781f",
|
||||
"zh:92e4a74ca1bc5128054ba207f996b665f474c0b338e675dafd0d08615faf2693",
|
||||
"zh:945a41443df9f3dbe16414a9612cbdf8f41f654307207c0d94822b94d2b5d26e",
|
||||
"zh:9cd6395a435539c088f8d8713599cb05d944f6d348ef08b5804aee71dc956022",
|
||||
"zh:c526621d5353adc12d3d277eb18738619542f400dc8d1d9938ebdaca1dc32390",
|
||||
"zh:c896deccd58176e9530023fe58b673269a0e235affae0f344a1b49faf938806e",
|
||||
"zh:f4dbfab40f58044669215387348565aeb94e6d6ef4916c0594ca5db98ad91e37",
|
||||
"h1:zVFayCyqprY+NCbUCWe9RgpH3BGuAtOyLzqhA8f3VE8=",
|
||||
"zh:10197fce4ddf2be32717fb3a5992b45ee1a134f8c66207ba7a2d89339bcad17e",
|
||||
"zh:428c22bc9ae637adaa3c99e7ec2f5df3828c1625cebfc0ef680e520abdaff820",
|
||||
"zh:4474139669a13997abd3d8282dc00905a08c695895b1b3d09e5a87753be54a24",
|
||||
"zh:4bcd7a09577303d71146899f70413a7376531c940015eb6bfa047df55778d2ef",
|
||||
"zh:5c5083c8deae3093a63d9a7ff18844985e35f2108fca49a812be85f3ec8e7409",
|
||||
"zh:72ab7bf2a7a1ae98bf1ed790cc3babcd6399db58aa2c9bcef005f9b709b8ad98",
|
||||
"zh:95cf50e8aa4fe5495b983aca1478cd7909fafc92c0e1942b764343076f367e3f",
|
||||
"zh:9efd8613897beee98c11befc1f8ebfea14b5a0e88b0fdd2f737a1a2acd5e2a2f",
|
||||
"zh:b91ddced2f7916338e1f7d0fa003a15bb5700d8d4f389d906f2944334d67bbc2",
|
||||
"zh:db0270eb90eda9ce98668afc517365d5876c81bb12f0375d6837d204231f6df5",
|
||||
"zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
|
||||
"zh:f58e490c192698ff8655081d467e57ee13558aa47f950f1d249318bf5cc93e5e",
|
||||
]
|
||||
}
|
||||
|
||||
provider "registry.terraform.io/hashicorp/google-beta" {
|
||||
version = "5.18.0"
|
||||
version = "5.20.0"
|
||||
constraints = "~> 5.2"
|
||||
hashes = [
|
||||
"h1:jTtimo6WpBCeznYLOcvFM+pP0CT6rQ5ZV4FxeeP3+ZU=",
|
||||
"zh:2340414b1aaa37b8625b7ebb49d7724923a1970e93d183345779a4344c4932aa",
|
||||
"zh:36a65d4ca5baebe59c232971052b13e01a67e8d514e4360c86747b631e2068a4",
|
||||
"zh:40c2e336ea593f69af062a3fffeca34a081f311bf38862ed0ec87257e54b3942",
|
||||
"zh:418d78834ea0051e0e9b15a609b676c5079cd2cf279c80f9847b71634c280c40",
|
||||
"zh:79c004b57c5034bc2a5a08c07848b59eecf75ef901524da5f5868ec865cc774d",
|
||||
"zh:9608a333f6dfe7278fdbbbd6aeb2864fe7e539fa4a5ebca42304cc3b25374d72",
|
||||
"zh:9bcae66eeaf5d532baa17d5e9c84c0997e8d4d558106c74c3e3d13852a94cfc8",
|
||||
"zh:9c0a9b6faa2b5fc2fa9190f8222da9d12ea0d6ddcf37f55f9d86c0efc02f594d",
|
||||
"zh:a21603acb95c8403bcd2b9b9f2d38d1aadbb8e329f0072d1e6c3abf10fa1944b",
|
||||
"zh:a7ebe06b056571693948004f0015dba209a8304713cbb65507d3f6020e5c1ac4",
|
||||
"zh:e009cdd91f19e847912ca522efd592d0937dbca297addce9672caa747bcd0a3f",
|
||||
"h1:NV6nLDJYo9Y0d06ggDM05WJapYg0rFF52RmjxGkD+88=",
|
||||
"zh:2792639ca660f373ce0c0d152f28d1d2e59b590c19d960eddea3c7b70be2e811",
|
||||
"zh:5a29c775934d5fdf3960687222b0c1505741104ade9a94e42a11d6bef73c1656",
|
||||
"zh:71fcdf323e7e5bf91d12450ad7f948eef3df935e1875764d5f3c6316b57faa1b",
|
||||
"zh:89ca5d8cb4d17d7855b7ee7c347a3bb57d5c935c4cc6d18ac78098c9c1c6008a",
|
||||
"zh:c2a1e2d093ade9a5a4d5170fa4d439c2542f7d01a114af2a5a92b071268d193e",
|
||||
"zh:c4999b0bb00ce68bea7b203fdd6685c8133f7593d8ce7a9214cedac4844c22b0",
|
||||
"zh:c912e8f6baa182780678b1caf44863e7d829942c0e9f4a3f04bd623e1bf5f430",
|
||||
"zh:d553df714bc0d94b3bcdba37a3fca2eb099d7e494502a64a20b3d0f5213b979b",
|
||||
"zh:d92266f87f3679b179a238b2a3df5309da6b9d86bdcb9311cce5f4b9537938fb",
|
||||
"zh:e0daa80c3691c6b977c64d22a0c4f6e180d5d3dc888306f7057adb179baab761",
|
||||
"zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
|
||||
"zh:fee824aeb0f16837d3aaeadbabdf9e40d162344415acba8c521e43cf76d0efa8",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -433,10 +433,6 @@ locals {
|
||||
value = var.stripe_default_price_id
|
||||
},
|
||||
# Telemetry
|
||||
{
|
||||
name = "TELEMETRY_ENABLED"
|
||||
value = "false"
|
||||
},
|
||||
{
|
||||
name = "INSTRUMENTATION_CLIENT_LOGS_ENABLED"
|
||||
value = true
|
||||
@@ -485,6 +481,70 @@ locals {
|
||||
]
|
||||
}
|
||||
|
||||
module "domain" {
|
||||
source = "../../modules/elixir-app"
|
||||
project_id = module.google-cloud-project.project.project_id
|
||||
|
||||
compute_instance_type = "n1-standard-2"
|
||||
compute_instance_region = local.region
|
||||
compute_instance_availability_zones = ["${local.region}-d"]
|
||||
|
||||
dns_managed_zone_name = module.google-cloud-dns.zone_name
|
||||
|
||||
vpc_network = module.google-cloud-vpc.self_link
|
||||
vpc_subnetwork = google_compute_subnetwork.apps.self_link
|
||||
|
||||
container_registry = module.google-artifact-registry.url
|
||||
|
||||
image_repo = module.google-artifact-registry.repo
|
||||
image = "domain"
|
||||
image_tag = var.image_tag
|
||||
|
||||
scaling_horizontal_replicas = 2
|
||||
|
||||
observability_log_level = "debug"
|
||||
|
||||
erlang_release_name = "firezone"
|
||||
erlang_cluster_cookie = random_password.erlang_cluster_cookie.result
|
||||
|
||||
application_name = "domain"
|
||||
application_version = replace(var.image_tag, ".", "-")
|
||||
|
||||
application_ports = [
|
||||
{
|
||||
name = "http"
|
||||
protocol = "TCP"
|
||||
port = 4000
|
||||
|
||||
health_check = {
|
||||
initial_delay_sec = 60
|
||||
|
||||
check_interval_sec = 15
|
||||
timeout_sec = 10
|
||||
healthy_threshold = 1
|
||||
unhealthy_threshold = 2
|
||||
|
||||
http_health_check = {
|
||||
request_path = "/healthz"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
application_environment_variables = concat([
|
||||
# Background Jobs
|
||||
{
|
||||
name = "BACKGROUND_JOBS_ENABLED"
|
||||
value = "true"
|
||||
},
|
||||
], local.shared_application_environment_variables)
|
||||
|
||||
application_labels = {
|
||||
"cluster_name" = local.cluster.name
|
||||
"cluster_version" = split(".", var.image_tag)[0]
|
||||
}
|
||||
}
|
||||
|
||||
module "web" {
|
||||
source = "../../modules/elixir-app"
|
||||
project_id = module.google-cloud-project.project.project_id
|
||||
@@ -551,6 +611,10 @@ module "web" {
|
||||
name = "API_URL_OVERRIDE"
|
||||
value = "wss://api.${local.tld}"
|
||||
},
|
||||
{
|
||||
name = "BACKGROUND_JOBS_ENABLED"
|
||||
value = "false"
|
||||
},
|
||||
], local.shared_application_environment_variables)
|
||||
|
||||
application_labels = {
|
||||
@@ -621,6 +685,10 @@ module "api" {
|
||||
name = "PHOENIX_HTTP_API_PORT"
|
||||
value = "8080"
|
||||
},
|
||||
{
|
||||
name = "BACKGROUND_JOBS_ENABLED"
|
||||
value = "false"
|
||||
},
|
||||
], local.shared_application_environment_variables)
|
||||
|
||||
application_labels = {
|
||||
@@ -677,7 +745,7 @@ resource "google_compute_firewall" "erlang-distribution" {
|
||||
}
|
||||
|
||||
source_ranges = [google_compute_subnetwork.apps.ip_cidr_range]
|
||||
target_tags = concat(module.web.target_tags, module.api.target_tags)
|
||||
target_tags = concat(module.web.target_tags, module.api.target_tags, module.domain.target_tags)
|
||||
}
|
||||
|
||||
## Allow service account to list running instances
|
||||
@@ -696,8 +764,9 @@ resource "google_project_iam_custom_role" "erlang-discovery" {
|
||||
|
||||
resource "google_project_iam_member" "application" {
|
||||
for_each = {
|
||||
api = module.api.service_account.email
|
||||
web = module.web.service_account.email
|
||||
api = module.api.service_account.email
|
||||
web = module.web.service_account.email
|
||||
domain = module.domain.service_account.email
|
||||
}
|
||||
|
||||
project = module.google-cloud-project.project.project_id
|
||||
@@ -810,7 +879,7 @@ resource "google_compute_firewall" "ssh-ipv4" {
|
||||
}
|
||||
|
||||
source_ranges = ["0.0.0.0/0"]
|
||||
target_tags = concat(module.web.target_tags, module.api.target_tags)
|
||||
target_tags = concat(module.web.target_tags, module.api.target_tags, module.domain.target_tags)
|
||||
}
|
||||
|
||||
resource "google_compute_firewall" "ssh-ipv6" {
|
||||
@@ -835,7 +904,7 @@ resource "google_compute_firewall" "ssh-ipv6" {
|
||||
}
|
||||
|
||||
source_ranges = ["::/0"]
|
||||
target_tags = concat(module.web.target_tags, module.api.target_tags)
|
||||
target_tags = concat(module.web.target_tags, module.api.target_tags, module.domain.target_tags)
|
||||
}
|
||||
|
||||
resource "google_compute_firewall" "relays-ssh-ipv4" {
|
||||
@@ -900,6 +969,7 @@ module "ops" {
|
||||
slack_alerts_auth_token = var.slack_alerts_auth_token
|
||||
slack_alerts_channel = var.slack_alerts_channel
|
||||
|
||||
api_host = module.api.host
|
||||
web_host = module.web.host
|
||||
api_host = module.api.host
|
||||
web_host = module.web.host
|
||||
domain_host = module.domain.host
|
||||
}
|
||||
|
||||
@@ -14,12 +14,12 @@ terraform {
|
||||
|
||||
google = {
|
||||
source = "hashicorp/google"
|
||||
version = "~> 5.2"
|
||||
version = "~> 5.20"
|
||||
}
|
||||
|
||||
google-beta = {
|
||||
source = "hashicorp/google-beta"
|
||||
version = "~> 5.2"
|
||||
version = "~> 5.20"
|
||||
}
|
||||
|
||||
tls = {
|
||||
|
||||
38
terraform/modules/elixir-app/dns.tf
Normal file
38
terraform/modules/elixir-app/dns.tf
Normal file
@@ -0,0 +1,38 @@
|
||||
# Create DNS records for the application
|
||||
resource "google_dns_record_set" "application-ipv4" {
|
||||
count = var.application_dns_tld != null ? 1 : 0
|
||||
|
||||
project = var.project_id
|
||||
|
||||
name = "${var.application_dns_tld}."
|
||||
type = "A"
|
||||
ttl = 300
|
||||
|
||||
managed_zone = var.dns_managed_zone_name
|
||||
|
||||
rrdatas = google_compute_global_address.ipv4[*].address
|
||||
|
||||
depends_on = [
|
||||
google_project_service.compute,
|
||||
google_project_service.servicenetworking,
|
||||
]
|
||||
}
|
||||
|
||||
resource "google_dns_record_set" "application-ipv6" {
|
||||
count = var.application_dns_tld != null ? 1 : 0
|
||||
|
||||
project = var.project_id
|
||||
|
||||
name = "${var.application_dns_tld}."
|
||||
type = "AAAA"
|
||||
ttl = 300
|
||||
|
||||
managed_zone = var.dns_managed_zone_name
|
||||
|
||||
rrdatas = google_compute_global_address.ipv6[*].address
|
||||
|
||||
depends_on = [
|
||||
google_project_service.compute,
|
||||
google_project_service.servicenetworking,
|
||||
]
|
||||
}
|
||||
62
terraform/modules/elixir-app/iam.tf
Normal file
62
terraform/modules/elixir-app/iam.tf
Normal file
@@ -0,0 +1,62 @@
|
||||
# Create IAM role for the application instances
|
||||
resource "google_service_account" "application" {
|
||||
project = var.project_id
|
||||
|
||||
account_id = "app-${local.application_name}"
|
||||
display_name = "${local.application_name} app"
|
||||
description = "Service account for ${local.application_name} application instances."
|
||||
}
|
||||
|
||||
## Allow application service account to pull images from the container registry
|
||||
resource "google_project_iam_member" "artifacts" {
|
||||
project = var.project_id
|
||||
|
||||
role = "roles/artifactregistry.reader"
|
||||
|
||||
member = "serviceAccount:${google_service_account.application.email}"
|
||||
}
|
||||
|
||||
## Allow fluentbit to injest logs
|
||||
resource "google_project_iam_member" "logs" {
|
||||
project = var.project_id
|
||||
|
||||
role = "roles/logging.logWriter"
|
||||
|
||||
member = "serviceAccount:${google_service_account.application.email}"
|
||||
}
|
||||
|
||||
## Allow reporting application errors
|
||||
resource "google_project_iam_member" "errors" {
|
||||
project = var.project_id
|
||||
|
||||
role = "roles/errorreporting.writer"
|
||||
|
||||
member = "serviceAccount:${google_service_account.application.email}"
|
||||
}
|
||||
|
||||
## Allow reporting metrics
|
||||
resource "google_project_iam_member" "metrics" {
|
||||
project = var.project_id
|
||||
|
||||
role = "roles/monitoring.metricWriter"
|
||||
|
||||
member = "serviceAccount:${google_service_account.application.email}"
|
||||
}
|
||||
|
||||
## Allow reporting metrics
|
||||
resource "google_project_iam_member" "service_management" {
|
||||
project = var.project_id
|
||||
|
||||
role = "roles/servicemanagement.reporter"
|
||||
|
||||
member = "serviceAccount:${google_service_account.application.email}"
|
||||
}
|
||||
|
||||
## Allow appending traces
|
||||
resource "google_project_iam_member" "cloudtrace" {
|
||||
project = var.project_id
|
||||
|
||||
role = "roles/cloudtrace.agent"
|
||||
|
||||
member = "serviceAccount:${google_service_account.application.email}"
|
||||
}
|
||||
@@ -16,12 +16,14 @@ locals {
|
||||
},
|
||||
{
|
||||
name = "PHOENIX_EXTERNAL_TRUSTED_PROXIES"
|
||||
value = jsonencode([
|
||||
"35.191.0.0/16",
|
||||
"130.211.0.0/22",
|
||||
google_compute_global_address.ipv4.address,
|
||||
google_compute_global_address.ipv6.address
|
||||
])
|
||||
value = jsonencode(concat(
|
||||
[
|
||||
"35.191.0.0/16",
|
||||
"130.211.0.0/22"
|
||||
],
|
||||
google_compute_global_address.ipv4[*].address,
|
||||
google_compute_global_address.ipv6[*].address
|
||||
))
|
||||
},
|
||||
{
|
||||
name = "LOG_LEVEL"
|
||||
@@ -49,16 +51,6 @@ locals {
|
||||
], var.application_environment_variables)
|
||||
|
||||
application_ports_by_name = { for port in var.application_ports : port.name => port }
|
||||
|
||||
google_load_balancer_ip_ranges = [
|
||||
"130.211.0.0/22",
|
||||
"35.191.0.0/16",
|
||||
]
|
||||
|
||||
google_health_check_ip_ranges = [
|
||||
"130.211.0.0/22",
|
||||
"35.191.0.0/16"
|
||||
]
|
||||
}
|
||||
|
||||
# Fetch most recent COS image
|
||||
@@ -67,69 +59,6 @@ data "google_compute_image" "coreos" {
|
||||
project = "cos-cloud"
|
||||
}
|
||||
|
||||
# Create IAM role for the application instances
|
||||
resource "google_service_account" "application" {
|
||||
project = var.project_id
|
||||
|
||||
account_id = "app-${local.application_name}"
|
||||
display_name = "${local.application_name} app"
|
||||
description = "Service account for ${local.application_name} application instances."
|
||||
}
|
||||
|
||||
## Allow application service account to pull images from the container registry
|
||||
resource "google_project_iam_member" "artifacts" {
|
||||
project = var.project_id
|
||||
|
||||
role = "roles/artifactregistry.reader"
|
||||
|
||||
member = "serviceAccount:${google_service_account.application.email}"
|
||||
}
|
||||
|
||||
## Allow fluentbit to injest logs
|
||||
resource "google_project_iam_member" "logs" {
|
||||
project = var.project_id
|
||||
|
||||
role = "roles/logging.logWriter"
|
||||
|
||||
member = "serviceAccount:${google_service_account.application.email}"
|
||||
}
|
||||
|
||||
## Allow reporting application errors
|
||||
resource "google_project_iam_member" "errors" {
|
||||
project = var.project_id
|
||||
|
||||
role = "roles/errorreporting.writer"
|
||||
|
||||
member = "serviceAccount:${google_service_account.application.email}"
|
||||
}
|
||||
|
||||
## Allow reporting metrics
|
||||
resource "google_project_iam_member" "metrics" {
|
||||
project = var.project_id
|
||||
|
||||
role = "roles/monitoring.metricWriter"
|
||||
|
||||
member = "serviceAccount:${google_service_account.application.email}"
|
||||
}
|
||||
|
||||
## Allow reporting metrics
|
||||
resource "google_project_iam_member" "service_management" {
|
||||
project = var.project_id
|
||||
|
||||
role = "roles/servicemanagement.reporter"
|
||||
|
||||
member = "serviceAccount:${google_service_account.application.email}"
|
||||
}
|
||||
|
||||
## Allow appending traces
|
||||
resource "google_project_iam_member" "cloudtrace" {
|
||||
project = var.project_id
|
||||
|
||||
role = "roles/cloudtrace.agent"
|
||||
|
||||
member = "serviceAccount:${google_service_account.application.email}"
|
||||
}
|
||||
|
||||
# Deploy app
|
||||
resource "google_compute_instance_template" "application" {
|
||||
project = var.project_id
|
||||
@@ -358,408 +287,3 @@ resource "google_compute_region_instance_group_manager" "application" {
|
||||
google_compute_instance_template.application
|
||||
]
|
||||
}
|
||||
|
||||
# Define a security policy which allows to filter traffic by IP address,
|
||||
# an edge security policy can also detect and block common types of web attacks
|
||||
resource "google_compute_security_policy" "default" {
|
||||
project = var.project_id
|
||||
|
||||
name = local.application_name
|
||||
|
||||
type = "CLOUD_ARMOR"
|
||||
|
||||
rule {
|
||||
action = "allow"
|
||||
priority = "2147483647"
|
||||
|
||||
match {
|
||||
versioned_expr = "SRC_IPS_V1"
|
||||
|
||||
config {
|
||||
src_ip_ranges = ["*"]
|
||||
}
|
||||
}
|
||||
|
||||
description = "default allow rule"
|
||||
}
|
||||
|
||||
# TODO: Configure more WAF rules
|
||||
|
||||
depends_on = [
|
||||
google_project_service.compute,
|
||||
google_project_service.pubsub,
|
||||
google_project_service.bigquery,
|
||||
google_project_service.container,
|
||||
google_project_service.stackdriver,
|
||||
google_project_service.logging,
|
||||
google_project_service.monitoring,
|
||||
google_project_service.cloudprofiler,
|
||||
google_project_service.cloudtrace,
|
||||
google_project_service.servicenetworking,
|
||||
]
|
||||
}
|
||||
|
||||
# Expose the application ports via HTTP(S) load balancer with a managed SSL certificate and a static IP address
|
||||
resource "google_compute_backend_service" "default" {
|
||||
for_each = local.application_ports_by_name
|
||||
|
||||
project = var.project_id
|
||||
|
||||
name = "${local.application_name}-backend-${each.value.name}"
|
||||
|
||||
load_balancing_scheme = "EXTERNAL"
|
||||
|
||||
port_name = each.value.name
|
||||
protocol = "HTTP"
|
||||
|
||||
timeout_sec = 86400
|
||||
connection_draining_timeout_sec = 120
|
||||
|
||||
enable_cdn = false
|
||||
compression_mode = "DISABLED"
|
||||
|
||||
custom_request_headers = [
|
||||
"X-Geo-Location-Region:{client_region}",
|
||||
"X-Geo-Location-City:{client_city}",
|
||||
"X-Geo-Location-Coordinates:{client_city_lat_long}",
|
||||
]
|
||||
|
||||
custom_response_headers = [
|
||||
"X-Cache-Hit: {cdn_cache_status}"
|
||||
]
|
||||
|
||||
session_affinity = "CLIENT_IP"
|
||||
|
||||
health_checks = try([google_compute_health_check.port[each.key].self_link], null)
|
||||
|
||||
security_policy = google_compute_security_policy.default.self_link
|
||||
|
||||
backend {
|
||||
balancing_mode = "UTILIZATION"
|
||||
capacity_scaler = 1
|
||||
group = google_compute_region_instance_group_manager.application.instance_group
|
||||
|
||||
# Do not send traffic to nodes that have CPU load higher than 80%
|
||||
# max_utilization = 0.8
|
||||
}
|
||||
|
||||
log_config {
|
||||
enable = false
|
||||
sample_rate = "1.0"
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
google_compute_region_instance_group_manager.application,
|
||||
google_compute_health_check.port,
|
||||
]
|
||||
}
|
||||
|
||||
## Create a SSL policy
|
||||
resource "google_compute_ssl_policy" "application" {
|
||||
project = var.project_id
|
||||
|
||||
name = local.application_name
|
||||
|
||||
min_tls_version = "TLS_1_2"
|
||||
profile = "MODERN"
|
||||
|
||||
depends_on = [
|
||||
google_project_service.compute,
|
||||
google_project_service.pubsub,
|
||||
google_project_service.bigquery,
|
||||
google_project_service.container,
|
||||
google_project_service.stackdriver,
|
||||
google_project_service.logging,
|
||||
google_project_service.monitoring,
|
||||
google_project_service.cloudprofiler,
|
||||
google_project_service.cloudtrace,
|
||||
google_project_service.servicenetworking,
|
||||
]
|
||||
}
|
||||
|
||||
## Create a managed SSL certificate
|
||||
resource "google_compute_managed_ssl_certificate" "default" {
|
||||
project = var.project_id
|
||||
|
||||
name = "${local.application_name}-mig-lb-cert"
|
||||
|
||||
type = "MANAGED"
|
||||
|
||||
managed {
|
||||
domains = [
|
||||
var.application_dns_tld,
|
||||
]
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
google_project_service.compute,
|
||||
google_project_service.servicenetworking,
|
||||
]
|
||||
}
|
||||
|
||||
## Create URL map for the application
|
||||
resource "google_compute_url_map" "default" {
|
||||
project = var.project_id
|
||||
|
||||
name = local.application_name
|
||||
default_service = google_compute_backend_service.default["http"].self_link
|
||||
|
||||
depends_on = [
|
||||
google_project_service.compute,
|
||||
google_project_service.servicenetworking,
|
||||
]
|
||||
}
|
||||
|
||||
# Set up HTTP(s) proxies and redirect HTTP to HTTPS
|
||||
resource "google_compute_url_map" "https_redirect" {
|
||||
project = var.project_id
|
||||
|
||||
name = "${local.application_name}-https-redirect"
|
||||
|
||||
default_url_redirect {
|
||||
https_redirect = true
|
||||
redirect_response_code = "MOVED_PERMANENTLY_DEFAULT"
|
||||
strip_query = false
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
google_project_service.compute,
|
||||
google_project_service.servicenetworking,
|
||||
]
|
||||
}
|
||||
|
||||
resource "google_compute_target_http_proxy" "default" {
|
||||
project = var.project_id
|
||||
|
||||
name = "${local.application_name}-http"
|
||||
|
||||
url_map = google_compute_url_map.https_redirect.self_link
|
||||
}
|
||||
|
||||
resource "google_compute_target_https_proxy" "default" {
|
||||
project = var.project_id
|
||||
|
||||
name = "${local.application_name}-https"
|
||||
|
||||
url_map = google_compute_url_map.default.self_link
|
||||
|
||||
ssl_certificates = [google_compute_managed_ssl_certificate.default.self_link]
|
||||
ssl_policy = google_compute_ssl_policy.application.self_link
|
||||
quic_override = "NONE"
|
||||
}
|
||||
|
||||
# Allocate global addresses for the load balancer and set up forwarding rules
|
||||
## IPv4
|
||||
resource "google_compute_global_address" "ipv4" {
|
||||
project = var.project_id
|
||||
|
||||
name = "${local.application_name}-ipv4"
|
||||
|
||||
ip_version = "IPV4"
|
||||
|
||||
depends_on = [
|
||||
google_project_service.compute,
|
||||
google_project_service.servicenetworking,
|
||||
]
|
||||
}
|
||||
|
||||
resource "google_compute_global_forwarding_rule" "http" {
|
||||
project = var.project_id
|
||||
|
||||
name = local.application_name
|
||||
labels = local.application_labels
|
||||
|
||||
target = google_compute_target_http_proxy.default.self_link
|
||||
ip_address = google_compute_global_address.ipv4.address
|
||||
port_range = "80"
|
||||
|
||||
load_balancing_scheme = "EXTERNAL"
|
||||
}
|
||||
|
||||
resource "google_compute_global_forwarding_rule" "https" {
|
||||
project = var.project_id
|
||||
|
||||
name = "${local.application_name}-https"
|
||||
labels = local.application_labels
|
||||
|
||||
target = google_compute_target_https_proxy.default.self_link
|
||||
ip_address = google_compute_global_address.ipv4.address
|
||||
port_range = "443"
|
||||
|
||||
load_balancing_scheme = "EXTERNAL"
|
||||
}
|
||||
|
||||
## IPv6
|
||||
resource "google_compute_global_address" "ipv6" {
|
||||
project = var.project_id
|
||||
|
||||
name = "${local.application_name}-ipv6"
|
||||
|
||||
ip_version = "IPV6"
|
||||
|
||||
depends_on = [
|
||||
google_project_service.compute,
|
||||
google_project_service.servicenetworking,
|
||||
]
|
||||
}
|
||||
|
||||
resource "google_compute_global_forwarding_rule" "http_ipv6" {
|
||||
project = var.project_id
|
||||
|
||||
name = "${local.application_name}-ipv6-http"
|
||||
labels = local.application_labels
|
||||
|
||||
target = google_compute_target_http_proxy.default.self_link
|
||||
ip_address = google_compute_global_address.ipv6.address
|
||||
port_range = "80"
|
||||
|
||||
load_balancing_scheme = "EXTERNAL"
|
||||
}
|
||||
|
||||
resource "google_compute_global_forwarding_rule" "https_ipv6" {
|
||||
project = var.project_id
|
||||
|
||||
name = "${local.application_name}-ipv6-https"
|
||||
labels = local.application_labels
|
||||
|
||||
target = google_compute_target_https_proxy.default.self_link
|
||||
ip_address = google_compute_global_address.ipv6.address
|
||||
port_range = "443"
|
||||
|
||||
load_balancing_scheme = "EXTERNAL"
|
||||
}
|
||||
|
||||
## Open HTTP(S) ports for the load balancer
|
||||
resource "google_compute_firewall" "http" {
|
||||
project = var.project_id
|
||||
|
||||
name = "${local.application_name}-firewall-lb-to-instances-ipv4"
|
||||
network = var.vpc_network
|
||||
|
||||
source_ranges = local.google_load_balancer_ip_ranges
|
||||
target_tags = ["app-${local.application_name}"]
|
||||
|
||||
dynamic "allow" {
|
||||
for_each = var.application_ports
|
||||
|
||||
content {
|
||||
protocol = allow.value.protocol
|
||||
ports = [allow.value.port]
|
||||
}
|
||||
}
|
||||
|
||||
# We also enable UDP to allow QUIC if it's enabled
|
||||
dynamic "allow" {
|
||||
for_each = var.application_ports
|
||||
|
||||
content {
|
||||
protocol = "udp"
|
||||
ports = [allow.value.port]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
## Open HTTP(S) ports for the health checks
|
||||
resource "google_compute_firewall" "http-health-checks" {
|
||||
project = var.project_id
|
||||
|
||||
name = "${local.application_name}-healthcheck"
|
||||
network = var.vpc_network
|
||||
|
||||
source_ranges = local.google_health_check_ip_ranges
|
||||
target_tags = ["app-${local.application_name}"]
|
||||
|
||||
dynamic "allow" {
|
||||
for_each = var.application_ports
|
||||
|
||||
content {
|
||||
protocol = allow.value.protocol
|
||||
ports = [allow.value.port]
|
||||
}
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
google_project_service.compute,
|
||||
google_project_service.servicenetworking,
|
||||
]
|
||||
}
|
||||
|
||||
# Allow outbound traffic
|
||||
resource "google_compute_firewall" "egress-ipv4" {
|
||||
project = var.project_id
|
||||
|
||||
name = "${local.application_name}-egress-ipv4"
|
||||
network = var.vpc_network
|
||||
direction = "EGRESS"
|
||||
|
||||
target_tags = ["app-${local.application_name}"]
|
||||
destination_ranges = ["0.0.0.0/0"]
|
||||
|
||||
allow {
|
||||
protocol = "all"
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
google_project_service.compute,
|
||||
google_project_service.servicenetworking,
|
||||
]
|
||||
}
|
||||
|
||||
resource "google_compute_firewall" "egress-ipv6" {
|
||||
project = var.project_id
|
||||
|
||||
name = "${local.application_name}-egress-ipv6"
|
||||
network = var.vpc_network
|
||||
direction = "EGRESS"
|
||||
|
||||
target_tags = ["app-${local.application_name}"]
|
||||
destination_ranges = ["::/0"]
|
||||
|
||||
allow {
|
||||
protocol = "all"
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
google_project_service.compute,
|
||||
google_project_service.servicenetworking,
|
||||
]
|
||||
}
|
||||
|
||||
# Create DNS records for the application
|
||||
resource "google_dns_record_set" "application-ipv4" {
|
||||
project = var.project_id
|
||||
|
||||
name = "${var.application_dns_tld}."
|
||||
type = "A"
|
||||
ttl = 300
|
||||
|
||||
managed_zone = var.dns_managed_zone_name
|
||||
|
||||
rrdatas = [
|
||||
google_compute_global_address.ipv4.address
|
||||
]
|
||||
|
||||
depends_on = [
|
||||
google_project_service.compute,
|
||||
google_project_service.servicenetworking,
|
||||
]
|
||||
}
|
||||
|
||||
resource "google_dns_record_set" "application-ipv6" {
|
||||
project = var.project_id
|
||||
|
||||
name = "${var.application_dns_tld}."
|
||||
type = "AAAA"
|
||||
ttl = 300
|
||||
|
||||
managed_zone = var.dns_managed_zone_name
|
||||
|
||||
rrdatas = [
|
||||
google_compute_global_address.ipv6.address
|
||||
]
|
||||
|
||||
depends_on = [
|
||||
google_project_service.compute,
|
||||
google_project_service.servicenetworking,
|
||||
]
|
||||
}
|
||||
|
||||
411
terraform/modules/elixir-app/network.tf
Normal file
411
terraform/modules/elixir-app/network.tf
Normal file
@@ -0,0 +1,411 @@
|
||||
locals {
|
||||
google_load_balancer_ip_ranges = [
|
||||
"130.211.0.0/22",
|
||||
"35.191.0.0/16",
|
||||
]
|
||||
|
||||
google_health_check_ip_ranges = [
|
||||
"130.211.0.0/22",
|
||||
"35.191.0.0/16"
|
||||
]
|
||||
|
||||
public_application = var.application_dns_tld != null
|
||||
}
|
||||
|
||||
# Define a security policy which allows to filter traffic by IP address,
|
||||
# an edge security policy can also detect and block common types of web attacks
|
||||
resource "google_compute_security_policy" "default" {
|
||||
count = local.public_application ? 1 : 0
|
||||
|
||||
project = var.project_id
|
||||
|
||||
name = local.application_name
|
||||
|
||||
type = "CLOUD_ARMOR"
|
||||
|
||||
rule {
|
||||
action = "allow"
|
||||
priority = "2147483647"
|
||||
|
||||
match {
|
||||
versioned_expr = "SRC_IPS_V1"
|
||||
|
||||
config {
|
||||
src_ip_ranges = ["*"]
|
||||
}
|
||||
}
|
||||
|
||||
description = "default allow rule"
|
||||
}
|
||||
|
||||
# TODO: Configure more WAF rules
|
||||
|
||||
depends_on = [
|
||||
google_project_service.compute,
|
||||
google_project_service.pubsub,
|
||||
google_project_service.bigquery,
|
||||
google_project_service.container,
|
||||
google_project_service.stackdriver,
|
||||
google_project_service.logging,
|
||||
google_project_service.monitoring,
|
||||
google_project_service.cloudprofiler,
|
||||
google_project_service.cloudtrace,
|
||||
google_project_service.servicenetworking,
|
||||
]
|
||||
}
|
||||
|
||||
# Expose the application ports via HTTP(S) load balancer with a managed SSL certificate and a static IP address
|
||||
resource "google_compute_backend_service" "default" {
|
||||
for_each = local.public_application ? local.application_ports_by_name : {}
|
||||
|
||||
project = var.project_id
|
||||
|
||||
name = "${local.application_name}-backend-${each.value.name}"
|
||||
|
||||
load_balancing_scheme = "EXTERNAL"
|
||||
|
||||
port_name = each.value.name
|
||||
protocol = "HTTP"
|
||||
|
||||
timeout_sec = 86400
|
||||
connection_draining_timeout_sec = 120
|
||||
|
||||
enable_cdn = false
|
||||
compression_mode = "DISABLED"
|
||||
|
||||
custom_request_headers = [
|
||||
"X-Geo-Location-Region:{client_region}",
|
||||
"X-Geo-Location-City:{client_city}",
|
||||
"X-Geo-Location-Coordinates:{client_city_lat_long}",
|
||||
]
|
||||
|
||||
custom_response_headers = [
|
||||
"X-Cache-Hit: {cdn_cache_status}"
|
||||
]
|
||||
|
||||
session_affinity = "CLIENT_IP"
|
||||
|
||||
health_checks = try([google_compute_health_check.port[each.key].self_link], null)
|
||||
|
||||
security_policy = google_compute_security_policy.default[0].self_link
|
||||
|
||||
backend {
|
||||
balancing_mode = "UTILIZATION"
|
||||
capacity_scaler = 1
|
||||
group = google_compute_region_instance_group_manager.application.instance_group
|
||||
|
||||
# Do not send traffic to nodes that have CPU load higher than 80%
|
||||
# max_utilization = 0.8
|
||||
}
|
||||
|
||||
log_config {
|
||||
enable = false
|
||||
sample_rate = "1.0"
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
google_compute_region_instance_group_manager.application,
|
||||
google_compute_health_check.port,
|
||||
]
|
||||
}
|
||||
|
||||
## Create a SSL policy
|
||||
resource "google_compute_ssl_policy" "application" {
|
||||
count = local.public_application ? 1 : 0
|
||||
|
||||
project = var.project_id
|
||||
|
||||
name = local.application_name
|
||||
|
||||
min_tls_version = "TLS_1_2"
|
||||
profile = "MODERN"
|
||||
|
||||
depends_on = [
|
||||
google_project_service.compute,
|
||||
google_project_service.pubsub,
|
||||
google_project_service.bigquery,
|
||||
google_project_service.container,
|
||||
google_project_service.stackdriver,
|
||||
google_project_service.logging,
|
||||
google_project_service.monitoring,
|
||||
google_project_service.cloudprofiler,
|
||||
google_project_service.cloudtrace,
|
||||
google_project_service.servicenetworking,
|
||||
]
|
||||
}
|
||||
|
||||
## Create a managed SSL certificate
|
||||
resource "google_compute_managed_ssl_certificate" "default" {
|
||||
count = local.public_application ? 1 : 0
|
||||
|
||||
project = var.project_id
|
||||
|
||||
name = "${local.application_name}-mig-lb-cert"
|
||||
|
||||
type = "MANAGED"
|
||||
|
||||
managed {
|
||||
domains = [
|
||||
var.application_dns_tld,
|
||||
]
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
google_project_service.compute,
|
||||
google_project_service.servicenetworking,
|
||||
]
|
||||
}
|
||||
|
||||
## Create URL map for the application
|
||||
resource "google_compute_url_map" "default" {
|
||||
count = try(google_compute_backend_service.default["http"], null) != null ? 1 : 0
|
||||
|
||||
project = var.project_id
|
||||
|
||||
name = local.application_name
|
||||
default_service = google_compute_backend_service.default["http"].self_link
|
||||
|
||||
depends_on = [
|
||||
google_project_service.compute,
|
||||
google_project_service.servicenetworking,
|
||||
]
|
||||
}
|
||||
|
||||
# Set up HTTP(s) proxies and redirect HTTP to HTTPS
|
||||
resource "google_compute_url_map" "https_redirect" {
|
||||
count = try(google_compute_backend_service.default["http"], null) != null ? 1 : 0
|
||||
|
||||
project = var.project_id
|
||||
|
||||
name = "${local.application_name}-https-redirect"
|
||||
|
||||
default_url_redirect {
|
||||
https_redirect = true
|
||||
redirect_response_code = "MOVED_PERMANENTLY_DEFAULT"
|
||||
strip_query = false
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
google_project_service.compute,
|
||||
google_project_service.servicenetworking,
|
||||
]
|
||||
}
|
||||
|
||||
resource "google_compute_target_http_proxy" "default" {
|
||||
count = length(google_compute_url_map.https_redirect) > 0 ? 1 : 0
|
||||
|
||||
project = var.project_id
|
||||
|
||||
name = "${local.application_name}-http"
|
||||
|
||||
url_map = google_compute_url_map.https_redirect[0].self_link
|
||||
}
|
||||
|
||||
resource "google_compute_target_https_proxy" "default" {
|
||||
count = local.public_application ? 1 : 0
|
||||
|
||||
project = var.project_id
|
||||
|
||||
name = "${local.application_name}-https"
|
||||
|
||||
url_map = google_compute_url_map.default[0].self_link
|
||||
|
||||
ssl_certificates = google_compute_managed_ssl_certificate.default[*].self_link
|
||||
ssl_policy = google_compute_ssl_policy.application[0].self_link
|
||||
quic_override = "NONE"
|
||||
}
|
||||
|
||||
# Allocate global addresses for the load balancer and set up forwarding rules
|
||||
## IPv4
|
||||
resource "google_compute_global_address" "ipv4" {
|
||||
count = local.public_application ? 1 : 0
|
||||
|
||||
project = var.project_id
|
||||
|
||||
name = "${local.application_name}-ipv4"
|
||||
|
||||
ip_version = "IPV4"
|
||||
|
||||
depends_on = [
|
||||
google_project_service.compute,
|
||||
google_project_service.servicenetworking,
|
||||
]
|
||||
}
|
||||
|
||||
resource "google_compute_global_forwarding_rule" "http" {
|
||||
count = local.public_application ? 1 : 0
|
||||
|
||||
project = var.project_id
|
||||
|
||||
name = local.application_name
|
||||
labels = local.application_labels
|
||||
|
||||
target = google_compute_target_http_proxy.default[0].self_link
|
||||
ip_address = google_compute_global_address.ipv4[0].address
|
||||
port_range = "80"
|
||||
|
||||
load_balancing_scheme = "EXTERNAL"
|
||||
}
|
||||
|
||||
resource "google_compute_global_forwarding_rule" "https" {
|
||||
count = local.public_application ? 1 : 0
|
||||
|
||||
project = var.project_id
|
||||
|
||||
name = "${local.application_name}-https"
|
||||
labels = local.application_labels
|
||||
|
||||
target = google_compute_target_https_proxy.default[0].self_link
|
||||
ip_address = google_compute_global_address.ipv4[0].address
|
||||
port_range = "443"
|
||||
|
||||
load_balancing_scheme = "EXTERNAL"
|
||||
}
|
||||
|
||||
## IPv6
|
||||
resource "google_compute_global_address" "ipv6" {
|
||||
count = local.public_application ? 1 : 0
|
||||
|
||||
project = var.project_id
|
||||
|
||||
name = "${local.application_name}-ipv6"
|
||||
|
||||
ip_version = "IPV6"
|
||||
|
||||
depends_on = [
|
||||
google_project_service.compute,
|
||||
google_project_service.servicenetworking,
|
||||
]
|
||||
}
|
||||
|
||||
resource "google_compute_global_forwarding_rule" "http_ipv6" {
|
||||
count = local.public_application ? 1 : 0
|
||||
|
||||
project = var.project_id
|
||||
|
||||
name = "${local.application_name}-ipv6-http"
|
||||
labels = local.application_labels
|
||||
|
||||
target = google_compute_target_http_proxy.default[0].self_link
|
||||
ip_address = google_compute_global_address.ipv6[0].address
|
||||
port_range = "80"
|
||||
|
||||
load_balancing_scheme = "EXTERNAL"
|
||||
}
|
||||
|
||||
resource "google_compute_global_forwarding_rule" "https_ipv6" {
|
||||
count = local.public_application ? 1 : 0
|
||||
|
||||
project = var.project_id
|
||||
|
||||
name = "${local.application_name}-ipv6-https"
|
||||
labels = local.application_labels
|
||||
|
||||
target = google_compute_target_https_proxy.default[0].self_link
|
||||
ip_address = google_compute_global_address.ipv6[0].address
|
||||
port_range = "443"
|
||||
|
||||
load_balancing_scheme = "EXTERNAL"
|
||||
}
|
||||
|
||||
## Open HTTP ports for the load balancer
|
||||
resource "google_compute_firewall" "http" {
|
||||
count = local.public_application ? 1 : 0
|
||||
|
||||
project = var.project_id
|
||||
|
||||
name = "${local.application_name}-firewall-lb-to-instances-ipv4"
|
||||
network = var.vpc_network
|
||||
|
||||
source_ranges = local.google_load_balancer_ip_ranges
|
||||
target_tags = ["app-${local.application_name}"]
|
||||
|
||||
dynamic "allow" {
|
||||
for_each = var.application_ports
|
||||
|
||||
content {
|
||||
protocol = allow.value.protocol
|
||||
ports = [allow.value.port]
|
||||
}
|
||||
}
|
||||
|
||||
# We also enable UDP to allow QUIC if it's enabled
|
||||
dynamic "allow" {
|
||||
for_each = var.application_ports
|
||||
|
||||
content {
|
||||
protocol = "udp"
|
||||
ports = [allow.value.port]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
## Open HTTP ports for the health checks
|
||||
resource "google_compute_firewall" "http-health-checks" {
|
||||
project = var.project_id
|
||||
|
||||
name = "${local.application_name}-healthcheck"
|
||||
network = var.vpc_network
|
||||
|
||||
source_ranges = local.google_health_check_ip_ranges
|
||||
target_tags = ["app-${local.application_name}"]
|
||||
|
||||
dynamic "allow" {
|
||||
for_each = var.application_ports
|
||||
|
||||
content {
|
||||
protocol = allow.value.protocol
|
||||
ports = [allow.value.port]
|
||||
}
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
google_project_service.compute,
|
||||
google_project_service.servicenetworking,
|
||||
]
|
||||
}
|
||||
|
||||
# Allow outbound traffic
|
||||
resource "google_compute_firewall" "egress-ipv4" {
|
||||
count = local.public_application ? 1 : 0
|
||||
|
||||
project = var.project_id
|
||||
|
||||
name = "${local.application_name}-egress-ipv4"
|
||||
network = var.vpc_network
|
||||
direction = "EGRESS"
|
||||
|
||||
target_tags = ["app-${local.application_name}"]
|
||||
destination_ranges = ["0.0.0.0/0"]
|
||||
|
||||
allow {
|
||||
protocol = "all"
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
google_project_service.compute,
|
||||
google_project_service.servicenetworking,
|
||||
]
|
||||
}
|
||||
|
||||
resource "google_compute_firewall" "egress-ipv6" {
|
||||
count = local.public_application ? 1 : 0
|
||||
|
||||
project = var.project_id
|
||||
|
||||
name = "${local.application_name}-egress-ipv6"
|
||||
network = var.vpc_network
|
||||
direction = "EGRESS"
|
||||
|
||||
target_tags = ["app-${local.application_name}"]
|
||||
destination_ranges = ["::/0"]
|
||||
|
||||
allow {
|
||||
protocol = "all"
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
google_project_service.compute,
|
||||
google_project_service.servicenetworking,
|
||||
]
|
||||
}
|
||||
@@ -222,7 +222,8 @@ variable "application_token_scopes" {
|
||||
|
||||
variable "application_dns_tld" {
|
||||
type = string
|
||||
nullable = false
|
||||
nullable = true
|
||||
default = null
|
||||
|
||||
description = "DNS host which will be used to create DNS records for the application and provision SSL-certificates."
|
||||
}
|
||||
|
||||
@@ -33,6 +33,47 @@ locals {
|
||||
)
|
||||
}
|
||||
|
||||
resource "google_monitoring_uptime_check_config" "domain-https" {
|
||||
project = var.project_id
|
||||
|
||||
display_name = "domain-https"
|
||||
timeout = "60s"
|
||||
|
||||
http_check {
|
||||
port = "443"
|
||||
use_ssl = true
|
||||
validate_ssl = true
|
||||
|
||||
request_method = "GET"
|
||||
path = "/healthz"
|
||||
|
||||
accepted_response_status_codes {
|
||||
status_class = "STATUS_CLASS_2XX"
|
||||
}
|
||||
}
|
||||
|
||||
monitored_resource {
|
||||
type = "uptime_url"
|
||||
|
||||
labels = {
|
||||
project_id = var.project_id
|
||||
host = var.domain_host
|
||||
}
|
||||
}
|
||||
|
||||
content_matchers {
|
||||
content = "\"ok\""
|
||||
matcher = "MATCHES_JSON_PATH"
|
||||
|
||||
json_path_matcher {
|
||||
json_path = "$.status"
|
||||
json_matcher = "EXACT_MATCH"
|
||||
}
|
||||
}
|
||||
|
||||
checker_type = "STATIC_IP_CHECKERS"
|
||||
}
|
||||
|
||||
resource "google_monitoring_uptime_check_config" "api-https" {
|
||||
project = var.project_id
|
||||
|
||||
|
||||
@@ -25,3 +25,7 @@ variable "api_host" {
|
||||
variable "web_host" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "domain_host" {
|
||||
type = string
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user