blob: 0687408883747a306243335f5a0dc7bf818c1a9c [file] [log] [blame]
local kube = import "../../../kube/kube.libsonnet";
local postgres = import "../../../kube/postgres.libsonnet";
local redis = import "../../../kube/redis.libsonnet";
{
local top = self,
local cfg = top.cfg,
cfg:: {
namespace: error "cfg.namespace must be set",
# Domain as seen in the fediverse.
localDomain: error "cfg.localDomain must be set",
# Domain where the web interface is running. If different,
# localDomain's real server must be configured to forward
# /.well-known/webfinger to webDomain.
webDomain: cfg.localDomain,
images: {
mastodon: "tootsuite/mastodon:v4.1.9@sha256:525032827b5438c47670f44194e4adaed9f2c46f39c28cb37e9feb54b93b9ebf",
},
passwords: {
# generate however you like
postgres: error "cfg.secrets.postgres must be set",
# generate however you like
redis: error "cfg.secrets.redis must be set",
},
smtp: {
user: "mastodon",
from: "mastodon-noreply@hackerspace.pl",
# from mail server
password: error "cfg.smtp.password must be set",
},
secrets: {
# generate with podman run --rm -it tootsuite/mastodon:v4.0.2 bundle exec rake secret
keyBase: error "cfg.secrets.keyBase must be set",
# generate with podman run --rm -it tootsuite/mastodon:v4.0.2 bundle exec rake secret
otp: error "cfg.secrets.otp must be set",
# generate with podman run --rm -it tootsuite/mastodon:v4.0.2 bundle exec rake mastodon:webpush:generate_vapid_key
vapid: {
private: error "cfg.secrets.vapid.private must be set",
public: error "cfg.secrets.vapid.public must be set",
}
},
oidc: {
clientID: error "cfg.oidc.clientID must be set",
clientSecret: error "cfg.oidc.clientSecret must be set",
},
objectStorage: {
bucket: error "cfg.objectStorage.bucket must be set",
accessKeyId: error "cfg.objectStorage.accessKeyId must be set",
secretAccessKey: error "cfg.objectStorage.secretAccessKey must be set",
},
scaling: {
web: 1,
sidekiq: 1,
},
},
// Unified env var based config used for {web, streaming, sidekiq}.
// Sample available in https://github.com/mastodon/mastodon/blob/main/.env.production.sample
env:: {
LOCAL_DOMAIN: cfg.localDomain,
WEB_DOMAIN: cfg.webDomain,
// REDIS_PASS is not used directly by the apps, it's just used to embed
// a secret fragment into REDIS_URL.
REDIS_PASS: kube.SecretKeyRef(top.config, "redis-pass"),
REDIS_URL: "redis://:$(REDIS_PASS)@%s" % [top.redis.svc.host_colon_port],
DB_HOST: top.postgres.svc.host,
DB_PORT: top.postgres.svc.port,
DB_USER: top.postgres.cfg.username,
DB_NAME: top.postgres.cfg.database,
DB_PASS: top.postgres.cfg.password,
ES_ENABLED: "false",
SECRET_KEY_BASE: kube.SecretKeyRef(top.config, "secret-key-base"),
OTP_SECRET: kube.SecretKeyRef(top.config, "otp-secret"),
VAPID_PRIVATE_KEY: kube.SecretKeyRef(top.config, "vapid-private"),
VAPID_PUBLIC_KEY: kube.SecretKeyRef(top.config, "vapid-public"),
SMTP_SERVER: "mail.hackerspace.pl",
SMTP_PORT: "587",
SMTP_LOGIN: "mastodon",
SMTP_PASSWORD: kube.SecretKeyRef(top.config, "smtp-password"),
SMTP_FROM_ADDRESS: "mastodon-noreply@hackerspace.pl",
S3_ENABLED: "true",
S3_BUCKET: cfg.objectStorage.bucket,
AWS_ACCESS_KEY_ID: kube.SecretKeyRef(top.config, "object-access-key-id"),
AWS_SECRET_ACCESS_KEY: kube.SecretKeyRef(top.config, "object-secret-access-key"),
S3_HOSTNAME: "object.ceph-waw3.hswaw.net",
S3_ENDPOINT: "https://object.ceph-waw3.hswaw.net",
IP_RETENTION_PERIOD: "31556952",
SESSION_RETENTION_PERIOD: "31556952",
OIDC_ENABLED: "true",
OIDC_DISPLAY_NAME: "Use Warsaw Hackerspace SSO",
OIDC_ISSUER: "https://sso.hackerspace.pl",
OIDC_DISCOVERY: "false",
OIDC_SCOPE: "openid,profile:read",
OIDC_UID_FIELD: "uid",
OIDC_CLIENT_ID: cfg.oidc.clientId,
OIDC_REDIRECT_URI: "https://%s/auth/auth/openid_connect/callback" % [cfg.webDomain],
OIDC_SECURITY_ASSUME_EMAIL_IS_VERIFIED: "true",
OIDC_CLIENT_SECRET: kube.SecretKeyRef(top.config, "oidc-client-secret"),
OIDC_AUTH_ENDPOINT: "https://sso.hackerspace.pl/oauth/authorize",
OIDC_TOKEN_ENDPOINT: "https://sso.hackerspace.pl/oauth/token",
OIDC_USER_INFO_ENDPOINT: "https://sso.hackerspace.pl/api/1/userinfo",
OIDC_JWKS_URI: "https://sso.hackerspace.pl/.well-known/jwks.json",
},
local ns = kube.Namespace(cfg.namespace),
namespace:: ns, // used by dumper.jsonnet
# there used to be a nonversioned postgres (10.4) here
# at time of writing it exists in prod, scaled down to 0, to preserve the PVC
postgres: ns.Contain(postgres) {
cfg+: {
version: "13.9",
appName: "mastodon",
database: "mastodon",
username: "mastodon",
prefix: "waw3-",
password: kube.SecretKeyRef(top.config, "postgres-pass"),
storageClassName: "waw-hdd-redundant-3",
storageSize: "100Gi",
opts: { wal_level: "logical" },
versionedNames: true,
},
},
redis: ns.Contain(redis) {
cfg+: {
appName: "mastodon",
storageClassName: "waw-hdd-redundant-3",
prefix: "waw3-",
password: kube.SecretKeyRef(top.config, "redis-pass"),
},
},
web: ns.Contain(kube.Deployment("web")) {
spec+: {
minReadySeconds: 10,
replicas: cfg.scaling.web,
template+: {
spec+: {
initContainers_: {
migrate: kube.Container("migrate") {
image: cfg.images.mastodon,
env_: top.env {
//That's confusing one - all the random "how to mastodon in docker" tutorials
//say you need to set it. However, with this set, the web dashboard was sad
//about unfinished migrations.
//I can't obviously tell if we'd ever want this to be enabled though.
//Leaving it commented out here for now.
//SKIP_POST_DEPLOYMENT_MIGRATIONS: "true",
},
command: [
"bundle", "exec",
"rails", "db:migrate",
],
},
},
containers_: {
default: kube.Container("default") {
image: cfg.images.mastodon,
env_: top.env,
command: [
"bundle", "exec",
"rails", "s", "-p", "3000",
],
ports_: {
web: { containerPort: 3000 },
},
readinessProbe: {
httpGet: {
path: "/health",
port: "web",
},
failureThreshold: 10,
periodSeconds: 5,
},
resources: {
requests: {
cpu: "250m",
memory: "1024M",
},
limits: {
cpu: "1",
memory: "1024M",
},
},
},
},
},
},
},
},
sidekiq: ns.Contain(kube.Deployment("sidekiq")) {
spec+: {
replicas: cfg.scaling.sidekiq,
minReadySeconds: 10,
template+: {
spec+: {
containers_: {
default: kube.Container("default") {
image: cfg.images.mastodon,
env_: top.env,
command: [
"bundle", "exec",
"sidekiq",
],
resources: {
requests: {
cpu: "250m",
memory: "1024M",
},
limits: {
cpu: "1",
memory: "1024M",
},
},
},
},
},
},
},
},
streaming: ns.Contain(kube.Deployment("streaming")) {
spec+: {
minReadySeconds: 10,
template+: {
spec+: {
containers_: {
default: kube.Container("default") {
image: cfg.images.mastodon,
env_: top.env {
"STREAMING_CLUSTER_NUM": "1",
},
command: [
"node", "./streaming",
],
ports_: {
web: { containerPort: 4000 },
},
readinessProbe: {
httpGet: {
path: "/api/v1/streaming/health",
port: "web",
},
failureThreshold: 1,
periodSeconds: 5,
},
resources: {
requests: {
cpu: "250m",
memory: "1024M",
},
limits: {
cpu: "1",
memory: "1024M",
},
},
},
},
},
},
},
},
svcWeb: ns.Contain(kube.Service("web")) {
target:: top.web,
},
svcStreaming: ns.Contain(kube.Service("streaming")) {
target:: top.streaming,
},
ingress: ns.Contain(kube.Ingress("mastodon")) {
// TODO(https://issues.hackerspace.pl/issues/74): mastodon's docs say we should enable CSP. Figure it out.
metadata+: {
annotations+: {
"kubernetes.io/tls-acme": "true",
"cert-manager.io/cluster-issuer": "letsencrypt-prod",
"nginx.ingress.kubernetes.io/proxy-body-size": "0",
},
},
spec+: {
tls: [
{
hosts: [cfg.webDomain],
secretName: "mastodon-ingress-tls",
},
],
rules: [
{
host: cfg.webDomain,
http: {
paths: [
{ path: "/", backend: top.svcWeb.name_port },
{ path: "/api/v1/streaming", backend: top.svcStreaming.name_port },
],
},
},
],
},
},
config: ns.Contain(kube.Secret("config")) {
data_: {
"postgres-pass": cfg.passwords.postgres,
"redis-pass": cfg.passwords.redis,
"secret-key-base": cfg.secrets.keyBase,
"otp-secret": cfg.secrets.otp,
"vapid-private": cfg.secrets.vapid.private,
"vapid-public": cfg.secrets.vapid.public,
"smtp-password": cfg.smtp.password,
"object-access-key-id": cfg.objectStorage.accessKeyId,
"object-secret-access-key": cfg.objectStorage.secretAccessKey,
"oidc-client-secret": cfg.oidc.clientSecret,
},
},
}