diff --git a/changelog.d/5-internal/federation-v1 b/changelog.d/5-internal/federation-v1 new file mode 100644 index 00000000000..01960024d5d --- /dev/null +++ b/changelog.d/5-internal/federation-v1 @@ -0,0 +1 @@ +Add federation-v1 environment for testing compatibility of the federation API with version 1 diff --git a/charts/integration/templates/configmap.yaml b/charts/integration/templates/configmap.yaml index ca2d49f9bec..42297c017c6 100644 --- a/charts/integration/templates/configmap.yaml +++ b/charts/integration/templates/configmap.yaml @@ -164,4 +164,44 @@ data: stern: host: stern.wire-federation-v0.svc.cluster.local port: 8080 + + federation-v1: + originDomain: federation-test-helper.wire-federation-v1.svc.cluster.local + brig: + host: brig.wire-federation-v1.svc.cluster.local + port: 8080 + cannon: + host: cannon.wire-federation-v1.svc.cluster.local + port: 8080 + cargohold: + host: cargohold.wire-federation-v1.svc.cluster.local + port: 8080 + federatorInternal: + host: federator.wire-federation-v1.svc.cluster.local + port: 8080 + federatorExternal: + host: federator.wire-federation-v1.svc.cluster.local + port: 8081 + galley: + host: galley.wire-federation-v1.svc.cluster.local + port: 8080 + gundeck: + host: gundeck.wire-federation-v1.svc.cluster.local + port: 8080 + nginz: + host: nginz-integration-http.wire-federation-v1.svc.cluster.local + port: 8080 + spar: + host: spar.wire-federation-v1.svc.cluster.local + port: 8080 + proxy: + host: proxy.wire-federation-v1.svc.cluster.local + port: 8080 + backgroundWorker: + host: backgroundWorker.wire-federation-v1.svc.cluster.local + port: 8080 + stern: + host: stern.wire-federation-v1.svc.cluster.local + port: 8080 + integrationTestHostName: integration-headless.{{ .Release.Namespace }}.svc.cluster.local diff --git a/charts/integration/templates/integration-integration.yaml b/charts/integration/templates/integration-integration.yaml index 56dbf2bf8e7..3fe4284dc5b 100644 --- a/charts/integration/templates/integration-integration.yaml +++ b/charts/integration/templates/integration-integration.yaml @@ -328,3 +328,7 @@ spec: key: uploadXmlAwsSecretAccessKey {{- end }} {{- end }} + - name: ENABLE_FEDERATION_V0 + value: "1" + - name: ENABLE_FEDERATION_V1 + value: "1" diff --git a/deploy/dockerephemeral/coredns-config/db.example.com b/deploy/dockerephemeral/coredns-config/db.example.com index a458686bca7..46df3527882 100644 --- a/deploy/dockerephemeral/coredns-config/db.example.com +++ b/deploy/dockerephemeral/coredns-config/db.example.com @@ -18,3 +18,4 @@ _wire-server-federator._tcp.d1 IN SRV 0 0 10443 localhost. _wire-server-federator._tcp.d2 IN SRV 0 0 11443 localhost. _wire-server-federator._tcp.d3 IN SRV 0 0 12443 localhost. _wire-server-federator._tcp.federation-v0 IN SRV 0 0 21443 localhost. +_wire-server-federator._tcp.federation-v1 IN SRV 0 0 22443 localhost. diff --git a/deploy/dockerephemeral/docker/elasticmq.conf b/deploy/dockerephemeral/docker/elasticmq.conf index 7cd41d7317e..77cdd11e4b4 100644 --- a/deploy/dockerephemeral/docker/elasticmq.conf +++ b/deploy/dockerephemeral/docker/elasticmq.conf @@ -43,6 +43,7 @@ queues { integration-brig-events4 = ${queues.default-queue-template} integration-brig-events5 = ${queues.default-queue-template} integration-brig-events-federation-v0 = ${queues.default-queue-template} + integration-brig-events-federation-v1 = ${queues.default-queue-template} integration-brig-events-internal = ${queues.default-queue-template} integration-brig-events-internal2 = ${queues.default-queue-template} @@ -50,6 +51,7 @@ queues { integration-brig-events-internal4 = ${queues.default-queue-template} integration-brig-events-internal5 = ${queues.default-queue-template} integration-brig-events-internal-federation-v0 = ${queues.default-queue-template} + integration-brig-events-internal-federation-v1 = ${queues.default-queue-template} "integration-user-events.fifo" = ${queues.fifo-queue-template} "integration-user-events2.fifo" = ${queues.fifo-queue-template} @@ -57,6 +59,7 @@ queues { "integration-user-events4.fifo" = ${queues.fifo-queue-template} "integration-user-events5.fifo" = ${queues.fifo-queue-template} "integration-user-events-federation-v0.fifo" = ${queues.fifo-queue-template} + "integration-user-events-federation-v1.fifo" = ${queues.fifo-queue-template} integration-gundeck-events = ${queues.default-queue-template} integration-gundeck-events2 = ${queues.default-queue-template} @@ -64,6 +67,7 @@ queues { integration-gundeck-events4 = ${queues.default-queue-template} integration-gundeck-events5 = ${queues.default-queue-template} integration-gundeck-events-federation-v0 = ${queues.default-queue-template} + integration-gundeck-events-federation-v1 = ${queues.default-queue-template} "integration-team-events.fifo" = ${queues.fifo-queue-template} "integration-team-events2.fifo" = ${queues.fifo-queue-template} @@ -71,10 +75,11 @@ queues { "integration-team-events4.fifo" = ${queues.fifo-queue-template} "integration-team-events5.fifo" = ${queues.fifo-queue-template} "integration-team-events-federation-v0.fifo" = ${queues.fifo-queue-template} + "integration-team-events-federation-v1.fifo" = ${queues.fifo-queue-template} } # Region and accountId which will be included in resource ids aws { region = eu-west-1 accountId = 000000000000 -} \ No newline at end of file +} diff --git a/deploy/dockerephemeral/federation-v0.yaml b/deploy/dockerephemeral/federation-v0.yaml index 28e50750273..d34be166d50 100644 --- a/deploy/dockerephemeral/federation-v0.yaml +++ b/deploy/dockerephemeral/federation-v0.yaml @@ -2,8 +2,11 @@ networks: demo_wire: external: false + coredns: + external: false + services: - brig_schema: + brig_schema-v0: container_name: brig-schema-federation-v0 image: quay.io/wire/brig-schema:4.38.51 command: --host cassandra --keyspace brig_test_federation_v0 --replication-factor 1 @@ -15,7 +18,7 @@ services: condition: on-failure networks: - demo_wire - brig: + brig-v0: container_name: brig-federation-v0 image: quay.io/wire/brig:4.38.0-mandarin.14 volumes: @@ -27,7 +30,7 @@ services: healthcheck: &haskell_health_check test: "curl --fail localhost:8080/i/status" depends_on: - brig_schema: + brig_schema-v0: condition: service_completed_successfully aws_cli: condition: service_completed_successfully @@ -41,7 +44,7 @@ services: - RABBITMQ_USERNAME=${RABBITMQ_USERNAME} - RABBITMQ_PASSWORD=${RABBITMQ_PASSWORD} - galley_schema: + galley_schema-v0: container_name: galley-schema-federation-v0 image: quay.io/wire/galley-schema:4.38.51 command: --host cassandra --keyspace galley_test_federation_v0 --replication-factor 1 @@ -54,7 +57,7 @@ services: networks: - demo_wire - galley: + galley-v0: container_name: galley-federation-v0 image: quay.io/wire/galley:4.38.0-mandarin.14 volumes: @@ -65,7 +68,7 @@ services: - '127.0.0.1:21085:8080' healthcheck: *haskell_health_check depends_on: - galley_schema: + galley_schema-v0: condition: service_completed_successfully aws_cli: condition: service_completed_successfully @@ -79,7 +82,7 @@ services: - RABBITMQ_USERNAME=${RABBITMQ_USERNAME} - RABBITMQ_PASSWORD=${RABBITMQ_PASSWORD} - cargohold: + cargohold-v0: container_name: cargohold-federation-v0 image: quay.io/wire/cargohold:4.38.0-mandarin.14 volumes: @@ -98,7 +101,7 @@ services: - AWS_ACCESS_KEY_ID=dummykey - AWS_SECRET_ACCESS_KEY=dummysecret - gundeck_schema: + gundeck_schema-v0: container_name: gundeck-schema-federation-v0 image: quay.io/wire/gundeck-schema:4.38.51 command: --host cassandra --keyspace gundeck_test_federation_v0 --replication-factor 1 @@ -111,7 +114,7 @@ services: networks: - demo_wire - gundeck: + gundeck-v0: container_name: gundeck-federation-v0 image: quay.io/wire/gundeck:4.38.0-mandarin.14 volumes: @@ -122,11 +125,11 @@ services: - '127.0.0.1:21086:8080' healthcheck: *haskell_health_check depends_on: - gundeck_schema: + gundeck_schema-v0: condition: service_completed_successfully aws_cli: condition: service_completed_successfully - redis: + redis-v0: condition: service_started environment: @@ -134,7 +137,7 @@ services: - AWS_ACCESS_KEY_ID=dummykey - AWS_SECRET_ACCESS_KEY=dummysecret - spar_schema: + spar_schema-v0: container_name: spar-schema-federation-v0 image: quay.io/wire/spar-schema:4.38.51 command: --host cassandra --keyspace spar_test_federation_v0 --replication-factor 1 @@ -147,7 +150,7 @@ services: networks: - demo_wire - spar: + spar-v0: container_name: spar-federation-v0 image: quay.io/wire/spar:4.38.0-mandarin.14 volumes: @@ -158,10 +161,10 @@ services: - '127.0.0.1:21088:8080' healthcheck: *haskell_health_check depends_on: - spar_schema: + spar_schema-v0: condition: service_completed_successfully - cannon: + cannon-v0: container_name: cannon-federation-v0 image: quay.io/wire/cannon:4.38.0-mandarin.14 volumes: @@ -172,7 +175,7 @@ services: - '127.0.0.1:21083:8080' healthcheck: *haskell_health_check - federator: + federator-v0: container_name: federator-federation-v0 image: quay.io/wire/federator:4.38.0-mandarin.14 volumes: @@ -189,10 +192,10 @@ services: healthcheck: test: "true" depends_on: - coredns-federation: + coredns-federation-v0: condition: service_started - background_worker: + background_worker-v0: container_name: background-worker-federation-v0 image: quay.io/wire/background-worker:4.38.0-mandarin.14 volumes: @@ -201,6 +204,7 @@ services: - demo_wire ports: - '127.0.0.1:21089:8080' + healthcheck: *haskell_health_check depends_on: init_vhosts: condition: service_completed_successfully @@ -208,7 +212,7 @@ services: - RABBITMQ_USERNAME=${RABBITMQ_USERNAME} - RABBITMQ_PASSWORD=${RABBITMQ_PASSWORD} - proxy: + proxy-v0: container_name: proxy-federation-v0 image: quay.io/wire/proxy:4.38.0-mandarin.14 volumes: @@ -219,7 +223,7 @@ services: - '127.0.0.1:21087:8080' healthcheck: *haskell_health_check - nginz: + nginz-v0: container_name: nginz-federation-v0 image: quay.io/wire/nginz:4.38.0-mandarin.14 volumes: @@ -230,26 +234,28 @@ services: - '127.0.0.1:21080:8080' - '127.0.0.1:21443:8443' depends_on: - brig: + brig-v0: + condition: service_healthy + galley-v0: condition: service_healthy - galley: + gundeck-v0: condition: service_healthy - gundeck: + cargohold-v0: condition: service_healthy - cargohold: + cannon-v0: condition: service_healthy - cannon: + spar-v0: condition: service_healthy - spar: + federator-v0: condition: service_healthy - federator: + proxy-v0: condition: service_healthy - proxy: + background_worker-v0: condition: service_healthy # We have to run a separate redis instance for each version of wire-server we # want. This is because gundeck just assumes the whole redis is for itself - redis: + redis-v0: container_name: redis-federation-v0 image: redis:6.0-alpine networks: @@ -257,7 +263,7 @@ services: # This coredns serves slightly different SRV records, so federator running in # a docker container can talk to federator running on the host. - coredns-federation: + coredns-federation-v0: image: docker.io/coredns/coredns:1.8.4 volumes: - ./federation-v0/coredns-config:/coredns-config diff --git a/deploy/dockerephemeral/federation-v0/coredns-config/db.example.com b/deploy/dockerephemeral/federation-v0/coredns-config/db.example.com index 448d8b5f594..407f4770916 100644 --- a/deploy/dockerephemeral/federation-v0/coredns-config/db.example.com +++ b/deploy/dockerephemeral/federation-v0/coredns-config/db.example.com @@ -17,4 +17,5 @@ _wire-server-federator._tcp.b IN SRV 0 0 9443 host.docker.internal. _wire-server-federator._tcp.d1 IN SRV 0 0 10443 host.docker.internal. _wire-server-federator._tcp.d2 IN SRV 0 0 11443 host.docker.internal. _wire-server-federator._tcp.d3 IN SRV 0 0 12443 host.docker.internal. -_wire-server-federator._tcp.v0 IN SRV 0 0 21443 host.docker.internal. +_wire-server-federator._tcp.federation-v0 IN SRV 0 0 21443 host.docker.internal. +_wire-server-federator._tcp.federation-v1 IN SRV 0 0 22443 host.docker.internal. diff --git a/deploy/dockerephemeral/federation-v0/nginz/upstreams b/deploy/dockerephemeral/federation-v0/nginz/upstreams index a3e6afada32..adfdafa6eec 100644 --- a/deploy/dockerephemeral/federation-v0/nginz/upstreams +++ b/deploy/dockerephemeral/federation-v0/nginz/upstreams @@ -1,38 +1,38 @@ upstream cargohold { least_conn; keepalive 32; - server cargohold:8080 max_fails=3 weight=1; + server cargohold-v0:8080 max_fails=3 weight=1; } upstream gundeck { least_conn; keepalive 32; - server gundeck:8080 max_fails=3 weight=1; + server gundeck-v0:8080 max_fails=3 weight=1; } upstream cannon { least_conn; keepalive 32; - server cannon:8080 max_fails=3 weight=1; + server cannon-v0:8080 max_fails=3 weight=1; } upstream galley { least_conn; keepalive 32; - server galley:8080 max_fails=3 weight=1; + server galley-v0:8080 max_fails=3 weight=1; } upstream proxy { least_conn; keepalive 32; - server proxy:8080 max_fails=3 weight=1; + server proxy-v0:8080 max_fails=3 weight=1; } upstream brig { least_conn; keepalive 32; - server brig:8080 max_fails=3 weight=1; + server brig-v0:8080 max_fails=3 weight=1; } upstream spar { least_conn; keepalive 32; - server spar:8080 max_fails=3 weight=1; + server spar-v0:8080 max_fails=3 weight=1; } upstream federator_external { - server federator:8081 max_fails=3 weight=1; + server federator-v0:8081 max_fails=3 weight=1; } diff --git a/deploy/dockerephemeral/federation-v1.yaml b/deploy/dockerephemeral/federation-v1.yaml new file mode 100644 index 00000000000..fc151a37dd0 --- /dev/null +++ b/deploy/dockerephemeral/federation-v1.yaml @@ -0,0 +1,274 @@ +networks: + demo_wire: + external: false + + coredns: + external: false + +services: + brig_schema-v1: + container_name: brig-schema-federation-v1 + image: quay.io/wire/brig-schema:4.42.0-pre.27 + command: --host cassandra --keyspace brig_test_federation_v1 --replication-factor 1 + depends_on: + cassandra: + condition: service_healthy + deploy: + restart_policy: + condition: on-failure + networks: + - demo_wire + + brig-v1: + container_name: brig-federation-v1 + image: quay.io/wire/brig:4.42.0-pre.27 + volumes: + - ./federation-v1:/etc/wire/brig/conf + networks: + - demo_wire + ports: + - '127.0.0.1:22082:8080' + healthcheck: &haskell_health_check + test: "curl --fail localhost:8080/i/status" + depends_on: + brig_schema-v1: + condition: service_completed_successfully + aws_cli: + condition: service_completed_successfully + init_vhosts: + condition: service_completed_successfully + environment: + - AWS_REGION=eu-west-1 + - AWS_ACCESS_KEY_ID=dummykey + - AWS_SECRET_ACCESS_KEY=dummysecret + - RABBITMQ_USERNAME=${RABBITMQ_USERNAME} + - RABBITMQ_PASSWORD=${RABBITMQ_PASSWORD} + + galley_schema-v1: + container_name: galley-schema-federation-v1 + image: quay.io/wire/galley-schema:4.42.0-pre.27 + command: --host cassandra --keyspace galley_test_federation_v1 --replication-factor 1 + depends_on: + cassandra: + condition: service_healthy + deploy: + restart_policy: + condition: on-failure + networks: + - demo_wire + + galley-v1: + container_name: galley-federation-v1 + image: quay.io/wire/galley:4.42.0-pre.27 + volumes: + - ./federation-v1:/etc/wire/galley/conf + networks: + - demo_wire + ports: + - '127.0.0.1:22085:8080' + healthcheck: *haskell_health_check + depends_on: + galley_schema-v1: + condition: service_completed_successfully + aws_cli: + condition: service_completed_successfully + init_vhosts: + condition: service_completed_successfully + environment: + - AWS_REGION=eu-west-1 + - AWS_ACCESS_KEY_ID=dummykey + - AWS_SECRET_ACCESS_KEY=dummysecret + - RABBITMQ_USERNAME=${RABBITMQ_USERNAME} + - RABBITMQ_PASSWORD=${RABBITMQ_PASSWORD} + + cargohold-v1: + container_name: cargohold-federation-v1 + image: quay.io/wire/cargohold:4.42.0-pre.27 + volumes: + - ./federation-v1:/etc/wire/cargohold/conf + networks: + - demo_wire + ports: + - '127.0.0.1:22084:8080' + healthcheck: *haskell_health_check + depends_on: + aws_cli: + condition: service_completed_successfully + environment: + - AWS_REGION=eu-west-1 + - AWS_ACCESS_KEY_ID=dummykey + - AWS_SECRET_ACCESS_KEY=dummysecret + + gundeck_schema-v1: + container_name: gundeck-schema-federation-v1 + image: quay.io/wire/gundeck-schema:4.42.0-pre.27 + command: --host cassandra --keyspace gundeck_test_federation_v1 --replication-factor 1 + depends_on: + cassandra: + condition: service_healthy + deploy: + restart_policy: + condition: on-failure + networks: + - demo_wire + + gundeck-v1: + container_name: gundeck-federation-v1 + image: quay.io/wire/gundeck:4.42.0-pre.27 + volumes: + - ./federation-v1:/etc/wire/gundeck/conf + networks: + - demo_wire + ports: + - '127.0.0.1:22086:8080' + healthcheck: *haskell_health_check + depends_on: + gundeck_schema-v1: + condition: service_completed_successfully + aws_cli: + condition: service_completed_successfully + redis-v1: + condition: service_started + environment: + - AWS_REGION=eu-west-1 + - AWS_ACCESS_KEY_ID=dummykey + - AWS_SECRET_ACCESS_KEY=dummysecret + + spar_schema-v1: + container_name: spar-schema-federation-v1 + image: quay.io/wire/spar-schema:4.42.0-pre.27 + command: --host cassandra --keyspace spar_test_federation_v1 --replication-factor 1 + depends_on: + cassandra: + condition: service_healthy + deploy: + restart_policy: + condition: on-failure + networks: + - demo_wire + + spar-v1: + container_name: spar-federation-v1 + image: quay.io/wire/spar:4.42.0-pre.27 + volumes: + - ./federation-v1:/etc/wire/spar/conf + networks: + - demo_wire + ports: + - '127.0.0.1:22088:8080' + healthcheck: *haskell_health_check + depends_on: + spar_schema-v1: + condition: service_completed_successfully + + cannon-v1: + container_name: cannon-federation-v1 + image: quay.io/wire/cannon:4.42.0-pre.27 + volumes: + - ./federation-v1:/etc/wire/cannon/conf + networks: + - demo_wire + ports: + - '127.0.0.1:22083:8080' + healthcheck: *haskell_health_check + + federator-v1: + container_name: federator-federation-v1 + image: quay.io/wire/federator:4.42.0-pre.27 + volumes: + - ./federation-v1:/etc/wire/federator/conf + networks: + - demo_wire + - coredns + extra_hosts: + - "host.docker.internal.:host-gateway" + ports: + - '127.0.0.1:22097:8080' + - '127.0.0.1:22098:8081' + # healthcheck: *haskell_health_check + healthcheck: + test: "true" + depends_on: + coredns-federation-v1: + condition: service_started + + background_worker-v1: + container_name: background-worker-federation-v1 + image: quay.io/wire/background-worker:4.42.0-pre.27 + volumes: + - ./federation-v1:/etc/wire/background-worker/conf + networks: + - demo_wire + ports: + - '127.0.0.1:22089:8080' + healthcheck: *haskell_health_check + depends_on: + init_vhosts: + condition: service_completed_successfully + environment: + - RABBITMQ_USERNAME=${RABBITMQ_USERNAME} + - RABBITMQ_PASSWORD=${RABBITMQ_PASSWORD} + + proxy-v1: + container_name: proxy-federation-v1 + image: quay.io/wire/proxy:4.42.0-pre.27 + volumes: + - ./federation-v1:/etc/wire/proxy/conf + networks: + - demo_wire + ports: + - '127.0.0.1:22087:8080' + healthcheck: *haskell_health_check + + nginz-v1: + container_name: nginz-federation-v1 + image: quay.io/wire/nginz:4.42.0-pre.27 + volumes: + - ./federation-v1:/etc/wire/ + networks: + - demo_wire + ports: + - '127.0.0.1:22080:8080' + - '127.0.0.1:22443:8443' + depends_on: + brig-v1: + condition: service_healthy + galley-v1: + condition: service_healthy + gundeck-v1: + condition: service_healthy + cargohold-v1: + condition: service_healthy + cannon-v1: + condition: service_healthy + spar-v1: + condition: service_healthy + federator-v1: + condition: service_healthy + proxy-v1: + condition: service_healthy + background_worker-v1: + condition: service_healthy + + # We have to run a separate redis instance for each version of wire-server we + # want. This is because gundeck just assumes the whole redis is for itself + redis-v1: + container_name: redis-federation-v1 + image: redis:6.0-alpine + networks: + - demo_wire + + + # This coredns serves slightly different SRV records, so federator running in + # a docker container can talk to federator running on the host. + coredns-federation-v1: + image: docker.io/coredns/coredns:1.8.4 + volumes: + - ./federation-v1/coredns-config:/coredns-config + entrypoint: + - /coredns + - -conf + - /coredns-config/Corefile + networks: + coredns: + ipv4_address: 172.20.1.4 diff --git a/deploy/dockerephemeral/federation-v1/background-worker.yaml b/deploy/dockerephemeral/federation-v1/background-worker.yaml new file mode 100644 index 00000000000..d70699f7f4e --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/background-worker.yaml @@ -0,0 +1,28 @@ +logLevel: Debug + +backgroundWorker: + host: 0.0.0.0 + port: 8080 + +federatorInternal: + host: federator-federation-v1 + port: 8080 + +galley: + host: galley-federation-v1 + port: 8080 + +brig: + host: brig-federation-v1 + port: 8080 + +rabbitmq: + host: rabbitmq + port: 5672 + vHost: federation-v1 + adminPort: 15672 + +backendNotificationPusher: + pushBackoffMinWait: 1000 + pushBackoffMaxWait: 1000000 + remotesRefreshInterval: 5000000 diff --git a/deploy/dockerephemeral/federation-v1/brig.yaml b/deploy/dockerephemeral/federation-v1/brig.yaml new file mode 100644 index 00000000000..62a6c8c2a8f --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/brig.yaml @@ -0,0 +1,218 @@ +brig: + host: 0.0.0.0 + port: 8080 + +cassandra: + endpoint: + host: demo_wire_cassandra + port: 9042 + keyspace: brig_test_federation_v1 + # filterNodesByDatacentre: datacenter1 + +elasticsearch: + url: http://nginz-federation-v1:9201 + index: directory_test + +rabbitmq: + host: rabbitmq + port: 5672 + vHost: federation-v1 + +cargohold: + host: cargohold-federation-v1 + port: 8080 + +galley: + host: galley-federation-v1 + port: 8080 + +gundeck: + host: gundeck-federation-v1 + port: 8080 + +federatorInternal: + host: federator-federation-v1 + port: 8080 + +multiSFT: false + +# You can set up local SQS/Dynamo running e.g. `../../deploy/dockerephemeral/run.sh` +aws: + userJournalQueue: integration-user-events-federation-v1.fifo + # ^ Comment this out if you don't want to journal user events + prekeyTable: integration-brig-prekeys-federation-v1 + sqsEndpoint: http://fake_sqs:4568 # https://sqs.eu-west-1.amazonaws.com + # dynamoDBEndpoint: http://localhost:4567 # https://dynamodb.eu-west-1.amazonaws.com + +# Uncomment to use the randomPrekey allocation strategy instead of dynamoDB +randomPrekeys: true + +# Uncomment this if you want STOMP. +# +# stomp: +# stompHost: localhost +# stompPort: 61613 +# stompTls: false + +# TODO: possibly move 'userJournalQueue' to the top level as well +internalEvents: + queueType: sqs + queueName: integration-brig-events-internal-federation-v1 + # queueType: stomp + # queueName: /queue/integration-brig-events-internal + +emailSMS: + # You can either use SES directly (in which case, ensure a feedback queue is configured) + # or you can use SMTP directly (blacklisting of email/phone must be otherwise handled by + # the operator). + email: + sesQueue: integration-brig-events-federation-v1 + sesEndpoint: http://ses:4569 # https://email.eu-west-1.amazonaws.com + # If you prefer to use SMTP directly, uncomment the following lines + # and set the correct credentials. + # NOTE: In case a user tries to supply config values for both SES and SMTP, + # SES takes precedence and gets used instead + # smtpEndpoint: + # host: localhost + # port: 2500 + # smtpCredentials: + # username: + # password: test/resources/smtp-secret.txt + # smtpConnType: plain + # ^ NOTE: blacklisting of emails (processing of bounces and complaints) is only done + # automatically IF sesQueue/sesEndpoint are used. If SMTP is used directly, the + # operator must handle these notifications "manually" (there are internal endpoints) + # that may be used for this + + general: + templateDir: /usr/share/wire/templates + emailSender: backend-integration@wire.com + smsSender: "+123456789" # or MG123456789... (twilio alphanumeric sender id) + templateBranding: + brand: Wire + brandUrl: https://wire.com + brandLabelUrl: wire.com # This is the text in the label for the above URL + brandLogoUrl: https://wire.com/p/img/email/logo-email-black.png + brandService: Wire Service Provider + copyright: © WIRE SWISS GmbH + misuse: misuse@wire.com + legal: https://wire.com/legal/ + forgot: https://wire.com/forgot/ + support: https://support.wire.com/ + user: + activationUrl: http://127.0.0.1:8080/activate?key=${key}&code=${code} + smsActivationUrl: http://127.0.0.1:8080/v/${code} + passwordResetUrl: http://127.0.0.1:8080/password-reset/${key}?code=${code} + invitationUrl: http://127.0.0.1:8080/register?invitation_code=${code} + deletionUrl: http://127.0.0.1:8080/users/delete?key=${key}&code=${code} + + provider: + homeUrl: https://provider.localhost/ + providerActivationUrl: http://127.0.0.1:8080/provider/activate?key=${key}&code=${code} + approvalUrl: http://127.0.0.1:8080/provider/approve?key=${key}&code=${code} + approvalTo: success@simulator.amazonses.com + providerPwResetUrl: http://127.0.0.1:8080/provider/password-reset?key=${key}&code=${code} + + team: + tInvitationUrl: http://127.0.0.1:8080/register?team=${team}&team_code=${code} + tActivationUrl: http://127.0.0.1:8080/register?team=${team}&team_code=${code} + tCreatorWelcomeUrl: http://127.0.0.1:8080/creator-welcome-website + tMemberWelcomeUrl: http://127.0.0.1:8080/member-welcome-website + +zauth: + privateKeys: /etc/wire/brig/conf/zauth-privkeys.txt + publicKeys: /etc/wire/brig/conf/zauth-pubkeys.txt + authSettings: + keyIndex: 1 + userTokenTimeout: 120 + sessionTokenTimeout: 20 + accessTokenTimeout: 30 + providerTokenTimeout: 60 + legalHoldUserTokenTimeout: 120 + legalHoldAccessTokenTimeout: 30 + +turn: + serversSource: dns # files | dns + baseDomain: example.com + discoveryIntervalSeconds: 100 + + # This should be the same secret as used by the TURN servers + secret: /etc/wire/brig/conf/turn-secret.txt + configTTL: 3600 + tokenTTL: 21600 + +optSettings: + setActivationTimeout: 10 + setVerificationTimeout: 10 + setTeamInvitationTimeout: 10 + setExpiredUserCleanupTimeout: 1 + setTwilio: /etc/wire/brig/conf/twilio-credentials.yaml + setNexmo: /etc/wire/brig/conf/nexmo-credentials.yaml + # setStomp: test/resources/stomp-credentials.yaml + setUserMaxConnections: 16 + setCookieInsecure: true + setUserCookieRenewAge: 2 + setUserCookieLimit: 5 + setUserCookieThrottle: + stdDev: 5 + retryAfter: 3 + setLimitFailedLogins: + timeout: 5 # seconds. if you reach the limit, how long do you have to wait to try again. + retryLimit: 5 # how many times can you have a failed login in that timeframe. + setSuspendInactiveUsers: # if this is omitted: never suspend inactive users. + suspendTimeout: 10 + setRichInfoLimit: 5000 # should be in sync with Spar + setDefaultUserLocale: en + setMaxTeamSize: 32 + setMaxConvSize: 16 + setEmailVisibility: visible_to_self + setPropertyMaxKeyLen: 1024 + setPropertyMaxValueLen: 4096 + setDeleteThrottleMillis: 0 + setSqsThrottleMillis: 1000 + setRestrictUserCreation: false + # setSearchSameTeamOnly: false + # ^ NOTE: this filters out search results for team users, + # i.e., if you are a team user the search endpoints will + # return only users part of the same team. For name search, + # this is slightly more inefficient as it requires 2 extra DB lookups + # setUserMaxPermClients: 7 + # ^ You can limit the max number of permanent clients that a user is allowed + # to register, per account. The default value is '7' if the option is unset. + + # Federation domain is used to qualify local IDs and handles, + # e.g. 0c4d8944-70fa-480e-a8b7-9d929862d18c@wire.com and somehandle@wire.com. + # It should also match the SRV DNS records under which other wire-server installations can find this backend: + # _wire-server-federator._tcp. + # Once set, DO NOT change it: if you do, existing users may have a broken experience and/or stop working + # Remember to keep it the same in Galley. + setFederationDomain: federation-v1.example.com + setFeatureFlags: # see #RefConfigOptions in `/docs/reference` + setFederationDomainConfigsUpdateFreq: 1 + setFederationStrategy: allowAll + setFederationDomainConfigs: + - domain: example.com + search_policy: full_search + set2FACodeGenerationDelaySecs: 5 + setDisabledAPIVersions: [] + setNonceTtlSecs: 5 + setDpopMaxSkewSecs: 1 + setDpopTokenExpirationTimeSecs: 300 # 5 minutes + setPublicKeyBundle: /etc/wire/brig/conf/jwt-ed25519-bundle.pem + setEnableMLS: true + # To only allow specific email address domains to register, uncomment and update the setting below + # setAllowlistEmailDomains: + # - wire.com + # To only allow specific phone number prefixes to register uncomment and update the settings below + # setAllowlistPhonePrefixes: + # - "+1555555" + # needs to be kept in sync with services/nginz/integration-test/resources/oauth/ed25519_public.jwk + setOAuthJwkKeyPair: /etc/wire/brig/conf/oauth-ed25519.jwk + setOAuthAuthCodeExpirationTimeSecs: 3 # 3 secs + setOAuthAccessTokenExpirationTimeSecs: 3 # 3 secs + setOAuthEnabled: true + setOAuthRefreshTokenExpirationTimeSecs: 14515200 # 24 weeks + setOAuthMaxActiveRefreshTokens: 10 + +logLevel: Warn +logNetStrings: false diff --git a/deploy/dockerephemeral/federation-v1/cannon.yaml b/deploy/dockerephemeral/federation-v1/cannon.yaml new file mode 100644 index 00000000000..c091ea67ed2 --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/cannon.yaml @@ -0,0 +1,26 @@ +# Example yaml-formatted configuration for cannon used in integration tests + +# cannon can be started with a config file (e.g. ./dist/cannon -c cannon.yaml.example) + +cannon: + host: 0.0.0.0 + port: 8080 + + # Each cannon instance advertises its own location (ip or dns name) to gundeck. + # Either externalHost or externalHostFile must be set (externalHost takes precedence if both are defined) + # externalHostFile expects a file with a single line containing the IP or dns name of this instance of cannon + externalHost: cannon-federation-v1 + #externalHostFile: /etc/wire/cannon/cannon-host.txt + +gundeck: + host: gundeck-federation-v1 + port: 8080 + +drainOpts: + gracePeriodSeconds: 1 + millisecondsBetweenBatches: 500 + minBatchSize: 5 + +disabledAPIVersions: [] +logLevel: Warn +logNetStrings: false diff --git a/deploy/dockerephemeral/federation-v1/cargohold.yaml b/deploy/dockerephemeral/federation-v1/cargohold.yaml new file mode 100644 index 00000000000..4913082900e --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/cargohold.yaml @@ -0,0 +1,33 @@ +brig: + host: brig-federation-v1 + port: 8080 + +cargohold: + host: 0.0.0.0 + port: 8080 + +federator: + host: federator-federation-v1 + port: 8080 + +aws: + s3Bucket: dummy-bucket-federation-v1 # <-- insert-bucket-name-here + s3Endpoint: http://fake_s3:4570 # https://s3-eu-west-1.amazonaws.com:443 + # s3DownloadEndpoint: http://fake-s3:4570 + # ^ When not using a real S3 service, we may need to use a different, + # publicly accessible endpoint for downloading assets. + # + # If you want to use cloudfront for asset downloads + # cloudFront: + # domain: + # keyPairId: + # privateKey: cf-pk.pem + +settings: + maxTotalBytes: 27262976 + downloadLinkTTL: 300 # Seconds + federationDomain: example.com + disabledAPIVersions: [] + +logLevel: Warn +logNetStrings: false diff --git a/deploy/dockerephemeral/federation-v1/coredns-config/Corefile b/deploy/dockerephemeral/federation-v1/coredns-config/Corefile new file mode 100644 index 00000000000..7bf495f2e89 --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/coredns-config/Corefile @@ -0,0 +1,4 @@ +example.com { + file /coredns-config/db.example.com + log +} \ No newline at end of file diff --git a/deploy/dockerephemeral/federation-v1/coredns-config/db.example.com b/deploy/dockerephemeral/federation-v1/coredns-config/db.example.com new file mode 100644 index 00000000000..407f4770916 --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/coredns-config/db.example.com @@ -0,0 +1,21 @@ +$ORIGIN example.com. +@ 3600 IN SOA sns.dns.icann.org. noc.dns.icann.org. ( + 2017042745 ; serial + 7200 ; refresh (2 hours) + 3600 ; retry (1 hour) + 1209600 ; expire (2 weeks) + 3600 ; minimum (1 hour) + ) + + 3600 IN NS a.iana-servers.net. + 3600 IN NS b.iana-servers.net. + +www IN A 127.0.0.1 + IN AAAA ::1 +_wire-server-federator._tcp IN SRV 0 0 8443 host.docker.internal. +_wire-server-federator._tcp.b IN SRV 0 0 9443 host.docker.internal. +_wire-server-federator._tcp.d1 IN SRV 0 0 10443 host.docker.internal. +_wire-server-federator._tcp.d2 IN SRV 0 0 11443 host.docker.internal. +_wire-server-federator._tcp.d3 IN SRV 0 0 12443 host.docker.internal. +_wire-server-federator._tcp.federation-v0 IN SRV 0 0 21443 host.docker.internal. +_wire-server-federator._tcp.federation-v1 IN SRV 0 0 22443 host.docker.internal. diff --git a/deploy/dockerephemeral/federation-v1/federator.yaml b/deploy/dockerephemeral/federation-v1/federator.yaml new file mode 100644 index 00000000000..1973a0540be --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/federator.yaml @@ -0,0 +1,29 @@ +federatorInternal: + host: 0.0.0.0 + port: 8080 +federatorExternal: + host: 0.0.0.0 + port: 8081 +brig: + host: brig-federation-v1 + port: 8080 +cargohold: + host: cargohold-federation-v1 + port: 8080 +galley: + host: galley-federation-v1 + port: 8080 + +logLevel: Warn +logNetStrings: false + +optSettings: + # Filepath to one or more PEM-encoded server certificates to use as a trust + # store when making requests to remote backends + remoteCAStore: "/etc/wire/federator/conf/integration-ca.pem" + useSystemCAStore: false + clientCertificate: "/etc/wire/federator/conf/integration-leaf.pem" + clientPrivateKey: "/etc/wire/federator/conf/integration-leaf-key.pem" + tcpConnectionTimeout: 5000000 + dnsHost: 172.20.1.3 + dnsPort: 53 diff --git a/deploy/dockerephemeral/federation-v1/galley.yaml b/deploy/dockerephemeral/federation-v1/galley.yaml new file mode 100644 index 00000000000..f272536260c --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/galley.yaml @@ -0,0 +1,107 @@ +galley: + host: 0.0.0.0 + port: 8080 + +cassandra: + endpoint: + host: demo_wire_cassandra + port: 9042 + keyspace: galley_test_federation_v1 + # filterNodesByDatacentre: datacenter1 + +brig: + host: brig-federation-v1 + port: 8080 + +gundeck: + host: gundeck-federation-v1 + port: 8080 + +spar: + host: spar-federation-v1 + port: 8080 + +federator: + host: federator-federation-v1 + port: 8080 + +rabbitmq: + host: rabbitmq + port: 5672 + vHost: federation-v1 + +settings: + httpPoolSize: 128 + maxTeamSize: 32 + maxFanoutSize: 18 + exposeInvitationURLsTeamAllowlist: [] + maxConvSize: 16 + intraListing: false + conversationCodeURI: https://account.wire.com/conversation-join/ + concurrentDeletionEvents: 1024 + deleteConvThrottleMillis: 0 + # Federation domain is used to qualify local IDs and handles, + # e.g. 0c4d8944-70fa-480e-a8b7-9d929862d18c@wire.com and somehandle@wire.com. + # It should also match the SRV DNS records under which other wire-server installations can find this backend: + # _wire-server-federator._tcp. + # Once set, DO NOT change it: if you do, existing users may have a broken experience and/or stop working + # Remember to keep it the same in Brig + federationDomain: federation-v1.example.com + mlsPrivateKeyPaths: + removal: + ed25519: /etc/wire/galley/conf/mls-private-key-ed25519.pem + guestLinkTTLSeconds: 604800 + disabledAPIVersions: [] + + featureFlags: # see #RefConfigOptions in `/docs/reference` + sso: disabled-by-default + legalhold: whitelist-teams-and-implicit-consent + teamSearchVisibility: disabled-by-default + appLock: + defaults: + status: enabled + config: + enforceAppLock: false + inactivityTimeoutSecs: 60 + classifiedDomains: + status: enabled + config: + domains: ["example.com"] + fileSharing: + defaults: + status: enabled + lockStatus: unlocked + conferenceCalling: + defaults: + status: enabled + outlookCalIntegration: + defaults: + status: disabled + lockStatus: locked + mlsE2EId: + defaults: + status: disabled + config: + verificationExpiration: 86400 + acmeDiscoveryUrl: null + lockStatus: unlocked + mlsMigration: + defaults: + status: enabled + config: + startTime: "2029-05-16T10:11:12.123Z" + finaliseRegardlessAfter: "2029-10-17T00:00:00.000Z" + usersThreshold: 100 + clientsThreshold: 50 + lockStatus: locked + limitedEventFanout: + defaults: + status: disabled + +logLevel: Warn +logNetStrings: false + +journal: # if set, journals; if not set, disables journaling + queueName: integration-team-events-federation-v1.fifo + endpoint: http://demo_wire_sqs:4568 # https://sqs.eu-west-1.amazonaws.com + region: eu-west-1 diff --git a/deploy/dockerephemeral/federation-v1/gundeck.yaml b/deploy/dockerephemeral/federation-v1/gundeck.yaml new file mode 100644 index 00000000000..6722f2e0b90 --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/gundeck.yaml @@ -0,0 +1,46 @@ +gundeck: + host: 0.0.0.0 + port: 8080 + +brig: + host: brig-federation-v1 + port: 8080 + +cassandra: + endpoint: + host: demo_wire_cassandra + port: 9042 + keyspace: gundeck_test_federation_v1 + # filterNodesByDatacentre: datacenter1 + +redis: + host: redis-federation-v1 + port: 6379 + connectionMode: master + +# redisAdditionalWrite: +# host: 127.0.0.1 +# port: 6379 +# connectionMode: master + +aws: + queueName: integration-gundeck-events-federation-v1 + region: eu-west-1 + account: "123456789012" # Default account nr used by localstack + arnEnv: integration + sqsEndpoint: http://demo_wire_sqs:4568 # https://sqs.eu-west-1.amazonaws.com + snsEndpoint: http://demo_wire_sns:4575 # https://sns.eu-west-1.amazonaws.com + +settings: + httpPoolSize: 1024 + notificationTTL: 24192200 + bulkPush: true + perNativePushConcurrency: 32 + sqsThrottleMillis: 1000 + maxConcurrentNativePushes: + hard: 30 # more than this number of threads will not be allowed + soft: 10 # more than this number of threads will be warned about + disabledAPIVersions: [] + +logLevel: Warn +logNetStrings: false diff --git a/deploy/dockerephemeral/federation-v1/integration-ca.pem b/deploy/dockerephemeral/federation-v1/integration-ca.pem new file mode 100644 index 00000000000..304fc892245 --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/integration-ca.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDEzCCAfugAwIBAgIUQ35aUV70pJjvDTbfgFUj5YmchHQwDQYJKoZIhvcNAQEL +BQAwGTEXMBUGA1UEAwwOY2EuZXhhbXBsZS5jb20wHhcNMjQwNjE3MTMxNTMxWhcN +MzQwNjE1MTMxNTMxWjAZMRcwFQYDVQQDDA5jYS5leGFtcGxlLmNvbTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAJQlUOLNmd7Ll7iskcSnsv9xcx/+TnMw +qtqkK17w54/Kto+NJJAkD1L+X5EkSPZ7FDKqt2bGfoETWGnlpH/zsUTUpchlf6Jf +w6TJOejQer5FQNLCtQSnOIchlAFKzFxhGSvcOrRWiBAPjTVIkv9eiCNXcJ5PE9Sk +8+bmn2ztz7LVHcv46PmT/+ihRxKJ01T5CsXWPUHOZQRfGvKZmyGf+iTBuhcxMPYC +nXb7/M3rYCQXL8FQZiaqbIVMqNRpMBVkAqU3l2JnSrlNIjIh6Nqowjog8QYGuIz6 +fxwWkw6EU5ZBwHIr2rOakCnQoKeXVqBJdWZNRMX1Vtqeh7O9zDoW4/0CAwEAAaNT +MFEwHQYDVR0OBBYEFHNgZ4nZQoNKnb0AnDkefTXxxYDqMB8GA1UdIwQYMBaAFHNg +Z4nZQoNKnb0AnDkefTXxxYDqMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBAIuLuyF7m1SP6PBu29jXnfGtaGi7j0jlqfcAysn7VmAU3StgWvSatlAl +AO6MIasjSQ+ygAbfIQW6W2Wc/U+NLQq5fRVi1cnmlxH5OULOFeQZCVyux8Maq0fT +jj4mmsz62b/iiA4tyS5r+foY4v1u2siSViBJSbfYbMp/VggIimt26RNV2u/ZV6Kf +UrOxazMx1yyuqARiqoA3VOMV8Byv8SEIiteWUSYni6u7xOT4gucPORhbM1HOSQ/S +CVq95x4FeKQnbEMykHI+bpBdkoadMVtrjCbskU49mOrvl/pli9V44R8KK6C1Nv3E +VLLcoOctdw90aT3sIjaXBcZtDTE6p6g= +-----END CERTIFICATE----- diff --git a/deploy/dockerephemeral/federation-v1/integration-leaf-key.pem b/deploy/dockerephemeral/federation-v1/integration-leaf-key.pem new file mode 100644 index 00000000000..1e7a83068de --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/integration-leaf-key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCZjOHeUnlauuxD +WgrRnh3hj5Fs+uh9vyddMX8rSWJIbWFw4QuYzYKY8CQa3MBb6qK1uUwoJ0W1w47I +RgA5VLvGxI+T1wX8E5vljVgfT3CAXHKRB88NrT8A1urQnWpzlq5sNerL6dqgBrjG +QBmFF7NxrvjGgerC2D8+srWfpQ6Jbl9by8c3JDu+T79PM+pW9ycUgdF1AJQBTz9K +zNQ7ZTlBQvJG8WhTMKioJgQsE60oEXD0C8M5yKBBb7DrqkeZInXqCw2y7DZLWzog +D+jgoAD5/9sk3d/gGNqDibzjjwMiJnH/IqBTkZsQ9OdZZPfx5v/p062hQBlM656P +2jMpJ1xxAgMBAAECggEAS3NBjWgDP4T4EUROaqACWNKeB+nmkdt68T0gGtoNVD+D +EN9UPnpFQPdHFngAgWnzF858UIKzq1Pzdg+HjqRHPK1bS67tvua3xP1GHuR/CGPk +28T1hefqPHRen7GqHDAfdwarYBWCGv4Sjz/yCkcSIrtyfMBb5fAya5GO02pckUSK +19sl7XhkPtHJVirRkjQL29R2TCpkNNpQMjkuYLk7mox+6pNTbxgbk0cnT3eGj1pV +mlPqpwzC5GevRziE/VE/WXFLChY+8KB4fDLRqWnyvabDvQ4coaXgzwbdScJyM5hX ++Dxdfni/P2m7xAZXUyfBsr0VUzqUkJfK3WWvvAGTDQKBgQDNi3RUEjVnU/MN4aDz +iZB2VYGfo/K69xTPNEbLQWs1F4ZMpHVtUVXzTfx/xG9ug989ijEm6ncL9OsnhThn +UldSz2ojSJUxLmhgCHZGYHT72v/9rEqfT9JisWpIj44KXufUHCcl3Cozj1ae3EUp +NVhN1HphB2LsCIJvLYfLIGdBNwKBgQC/PhHQMm/MQe4pOHAbdzDrRZWdG2KSRVxp +9mmJ/aT8LOp7BDjq+Dkct6a56JGqlOTeJirMTTmCKiOiTInuB9S+K7kWJJiYg9g4 +UCiuMU+40Px/1Z4/uxRj3DSdGLXG7S6kPeADx9f9BUNpAytGqOnSnfbDiDVvQVbp +0N0+nIXDlwKBgQC2uZOXrXxGOE4pd/ySpCeF2yvZ1HDTnxWjwlBxHt4Em74rYkR2 +A0mKezjOCL4bHCaYWcKqWuOsAHYQcxEaYQv6NSOg7ESdLSlivgMPO26j+yN5yvGn +wNlCHYBjsyLNu2MSoFh5AsmNfo69uQnOwXqX7h1BJsTdGg+CcJJ4lHzWbwKBgQCD +/CRzGbwKrh3eGPNWIUaDuTxudy3qYTBMeSGReJpa5+zUBa/6imFwLldEyvttTOE/ +Z/v1j/52lPqO0mAHBSSQMsDERXGDIMsi4j+RKLsqhCEfYKCcv1JtMNam7RzXM24T +MBjgwxWPrAg/+03ssDrffuGFRQYLyH5hVCK9SW0P9QKBgQDJ1ZSto+RWxv/uOKNr +7FYeQoKpMb2IvNvnGlnYHC8KS9qRq6wUE+FtuKcdLBQP4M9Cgq71VD/dsawrhEw7 +1rAYk3OqmHxBOU5Dcb152NxYHEf53pfEfWc0x4AEVe+Jzynj2EYixRKNWwODNTEx +LKJOYd0CuWywxg6d9G7A7XbgWQ== +-----END PRIVATE KEY----- diff --git a/deploy/dockerephemeral/federation-v1/integration-leaf.pem b/deploy/dockerephemeral/federation-v1/integration-leaf.pem new file mode 100644 index 00000000000..635d332de70 --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/integration-leaf.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgIBADANBgkqhkiG9w0BAQsFADAZMRcwFQYDVQQDDA5jYS5l +eGFtcGxlLmNvbTAeFw0yNDA2MTcxMzE1MzFaFw0yNDA3MTcxMzE1MzFaMAAwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCZjOHeUnlauuxDWgrRnh3hj5Fs ++uh9vyddMX8rSWJIbWFw4QuYzYKY8CQa3MBb6qK1uUwoJ0W1w47IRgA5VLvGxI+T +1wX8E5vljVgfT3CAXHKRB88NrT8A1urQnWpzlq5sNerL6dqgBrjGQBmFF7NxrvjG +gerC2D8+srWfpQ6Jbl9by8c3JDu+T79PM+pW9ycUgdF1AJQBTz9KzNQ7ZTlBQvJG +8WhTMKioJgQsE60oEXD0C8M5yKBBb7DrqkeZInXqCw2y7DZLWzogD+jgoAD5/9sk +3d/gGNqDibzjjwMiJnH/IqBTkZsQ9OdZZPfx5v/p062hQBlM656P2jMpJ1xxAgMB +AAGjgawwgakwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMEgGA1UdEQEB +/wQ+MDyCGSouaW50ZWdyYXRpb24uZXhhbXBsZS5jb22CFGhvc3QuZG9ja2VyLmlu +dGVybmFsgglsb2NhbGhvc3QwHQYDVR0OBBYEFPowAfmLPCmdCMdSxQjsR6UQSoyH +MB8GA1UdIwQYMBaAFHNgZ4nZQoNKnb0AnDkefTXxxYDqMA0GCSqGSIb3DQEBCwUA +A4IBAQCMJwbLzUsrkQkgdGKVi/Mb5XAAV0sfkwZch1Fx0vhJI072cZSow5A2ZUHa +LScFNTPmilPKEr6MS4xIKtRQaMHInbfxSsyNViKhpzkSOKoAiJjIJ2xPKFPnbTDI +uV74nxxyf9q/p3SLQfJFk7fxbvNeLqg5bYSrMeklHj4bpMJ9fybS8/mZVc8AkTFK +fsXSu9CW1B3GF+jP3E2GrFF3Zh9MgvWjMlSYg4ljPf5FoMCUq6GmQ17hQeJFvb5h +Jqk6TcgUrp082bcVlPW17XzFwVe3n6uzvWMtwI62EztVUj98+YkBiFL3i4+OQwAU +/noc22fq20OyJtCPJY4FIK7xUcgD +-----END CERTIFICATE----- diff --git a/deploy/dockerephemeral/federation-v1/jwt-ed25519-bundle.pem b/deploy/dockerephemeral/federation-v1/jwt-ed25519-bundle.pem new file mode 100644 index 00000000000..afbd4dfb0ec --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/jwt-ed25519-bundle.pem @@ -0,0 +1,6 @@ +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEIFANnxZLNE4p+GDzWzR3wm/v8x/0bxZYkCyke1aTRucX +-----END PRIVATE KEY----- +-----BEGIN PUBLIC KEY----- +MCowBQYDK2VwAyEACPvhIdimF20tOPjbb+fXJrwS2RKDp7686T90AZ0+Th8= +-----END PUBLIC KEY----- diff --git a/deploy/dockerephemeral/federation-v1/mls-private-key-ed25519.pem b/deploy/dockerephemeral/federation-v1/mls-private-key-ed25519.pem new file mode 100644 index 00000000000..182df6f5a7d --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/mls-private-key-ed25519.pem @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEIKqoSUVW579Aw8Nz47CRwArSigl/25jg0suQmg6mOwdy +-----END PRIVATE KEY----- diff --git a/deploy/dockerephemeral/federation-v1/nexmo-credentials.yaml b/deploy/dockerephemeral/federation-v1/nexmo-credentials.yaml new file mode 100644 index 00000000000..1f83517f2ee --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/nexmo-credentials.yaml @@ -0,0 +1,2 @@ +key: "dummy" +secret: "dummy" diff --git a/deploy/dockerephemeral/federation-v1/nginz/conf/README.md b/deploy/dockerephemeral/federation-v1/nginz/conf/README.md new file mode 100644 index 00000000000..8e614e99d1b --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/nginz/conf/README.md @@ -0,0 +1,7 @@ +# How to regenerate certificates in this directory + +Run from this directory: + +```bash +../../../../../hack/bin/gen-certs.sh +``` diff --git a/deploy/dockerephemeral/federation-v1/nginz/conf/common_response.conf b/deploy/dockerephemeral/federation-v1/nginz/conf/common_response.conf new file mode 100644 index 00000000000..1b8a947f437 --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/nginz/conf/common_response.conf @@ -0,0 +1,38 @@ + # remove access_token from logs, see 'Note sanitized_request'. + set $sanitized_request $request; + if ($sanitized_request ~ (.*)access_token=[^&]*(.*)) { + set $sanitized_request $1access_token=****$2; + } + + # Should be overriden when using websockets + proxy_set_header Connection ""; + proxy_set_header Z-Type $zauth_type; + proxy_set_header Z-User $zauth_user; + proxy_set_header Z-Client $zauth_client; + proxy_set_header Z-Connection $zauth_connection; + proxy_set_header Z-Provider $zauth_provider; + proxy_set_header Z-Bot $zauth_bot; + proxy_set_header Z-Conversation $zauth_conversation; + proxy_set_header Request-Id $request_id; + + # NOTE: This should only be used on endpoints where credentials are needed + more_set_headers 'Access-Control-Allow-Credentials: true'; + # NOTE: This allows all origins, you may want to tune this value + more_set_headers 'Access-Control-Allow-Origin: $http_origin'; + more_set_headers 'Access-Control-Expose-Headers: Request-Id, Location'; + more_set_headers 'Request-Id: $request_id'; + more_set_headers 'Strict-Transport-Security: max-age=31536000; preload'; + + if ($request_method = 'OPTIONS') { + add_header 'Access-Control-Allow-Methods' "GET, POST, PUT, DELETE, OPTIONS"; + add_header 'Access-Control-Allow-Headers' "$http_access_control_request_headers, DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type"; + add_header 'Content-Type' 'text/plain; charset=UTF-8'; + add_header 'Content-Length' 0; + return 204; + } + + + proxy_http_version 1.1; + + # NOTE: You may want to tune this + client_max_body_size 64M; diff --git a/deploy/dockerephemeral/federation-v1/nginz/conf/common_response_no_zauth.conf b/deploy/dockerephemeral/federation-v1/nginz/conf/common_response_no_zauth.conf new file mode 100644 index 00000000000..4277ede8c0f --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/nginz/conf/common_response_no_zauth.conf @@ -0,0 +1,2 @@ + zauth off; + include common_response.conf; diff --git a/deploy/dockerephemeral/federation-v1/nginz/conf/common_response_with_zauth.conf b/deploy/dockerephemeral/federation-v1/nginz/conf/common_response_with_zauth.conf new file mode 100644 index 00000000000..699dd263b31 --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/nginz/conf/common_response_with_zauth.conf @@ -0,0 +1,3 @@ + include common_response.conf; + proxy_set_header Authorization ""; + proxy_set_header Z-Host $host; diff --git a/deploy/dockerephemeral/federation-v1/nginz/conf/integration.conf b/deploy/dockerephemeral/federation-v1/nginz/conf/integration.conf new file mode 100644 index 00000000000..12c49ccfe88 --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/nginz/conf/integration.conf @@ -0,0 +1,19 @@ +# plain TCP/http listening for integration tests only. +listen 8080; +listen 8081; + +# for nginx-without-tls, we need to use a separate port for http2 traffic, +# as nginx cannot handle unencrypted http1 and http2 traffic on the same +# port. +# This port is only used for trying out nginx http2 forwarding without TLS locally and should not +# be ported to any production nginz config. +listen 8090 http2; + +######## TLS/SSL block start ############## +# +# Most integration tests simply use the http ports 8080 and 8081 +# But to also test tls forwarding, this port can be used. +# This applies only locally, as for kubernetes (helm chart) based deployments, +# TLS is terminated at the ingress level, not at nginz level +listen 8443 ssl http2; +listen [::]:8443 ssl http2; diff --git a/deploy/dockerephemeral/federation-v1/nginz/conf/nginx.conf b/deploy/dockerephemeral/federation-v1/nginz/conf/nginx.conf new file mode 100644 index 00000000000..43f8c68b306 --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/nginz/conf/nginx.conf @@ -0,0 +1,518 @@ +worker_processes 4; +worker_rlimit_nofile 1024; +include pid.conf; # for easy overriding + +# nb. start up errors (eg. misconfiguration) may still end up in /$(LOG_PATH)/error.log +error_log stderr warn; + +events { + worker_connections 1024; + multi_accept off; +} + +http { + # + # Some temporary paths (by default, will use the `prefix` path given when starting nginx) + # + + client_body_temp_path /tmp; + fastcgi_temp_path /tmp; + proxy_temp_path /tmp; + scgi_temp_path /tmp; + uwsgi_temp_path /tmp; + + # + # Sockets + # + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + + # + # Timeouts + # + + client_body_timeout 60; + client_header_timeout 60; + keepalive_timeout 75; + send_timeout 60; + + ignore_invalid_headers off; + + types_hash_max_size 2048; + + server_names_hash_bucket_size 64; + server_name_in_redirect off; + + large_client_header_buffers 4 8k; + + # + # Security + # + + server_tokens off; + + # + # Logging + # + # Note sanitized_request: + # We allow passing access_token as query parameter for e.g. websockets + # However we do not want to log access tokens. + # + + log_format custom_zeta '$remote_addr - $remote_user [$time_local] "$sanitized_request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for" - $connection $request_time $upstream_response_time $upstream_cache_status $zauth_user $zauth_connection $request_id $proxy_protocol_addr'; + access_log /dev/stdout custom_zeta; + + # + # Monitoring + # + vhost_traffic_status_zone; + + # + # Gzip + # + + gzip on; + gzip_disable msie6; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_buffers 16 8k; + gzip_http_version 1.1; + gzip_min_length 1024; + gzip_types 'text/plain text/css application/json text/xml'; + + # + # Proxied Upstream Services + # + + include ../upstreams; + + # + # Mapping for websocket connections + # + + map $http_upgrade $connection_upgrade { + websocket upgrade; + default ''; + } + + # + # Locations + # + + server { + # elastic search does not support running http and https listeners + # at the same time. so our instance only runs https, but + # federation-v1 only supports http. this proxy rule helps with + # that. + # + # see also: git grep -Hn 'elasticsearch:' ../../brig.yaml + listen 9201; + + zauth_keystore /etc/wire/zauth-pubkeys.txt; + zauth_acl /etc/wire/nginz/conf/zauth_acl.txt; + + location "" { + zauth off; + + proxy_pass https://demo_wire_elasticsearch:9200; + proxy_set_header Authorization "Basic ZWxhc3RpYzpjaGFuZ2VtZQ=="; + } + } + + server { + include integration.conf; + + # self-signed certificates generated using wire-server/hack/bin/gen-certs.sh + ssl_certificate /etc/wire/integration-leaf.pem; + ssl_certificate_key /etc/wire/integration-leaf-key.pem; + + ssl_verify_client on; + ssl_client_certificate /etc/wire/integration-ca.pem; + ######## TLS/SSL block end ############## + + zauth_keystore /etc/wire/zauth-pubkeys.txt; + zauth_acl /etc/wire/nginz/conf/zauth_acl.txt; + # needs to be kept in sync with services/brig/test/resources/oauth/ed25519.jwk + oauth_pub_key /etc/wire/oauth-ed25519_public.jwk; + + location /status { + set $sanitized_request $request; + zauth off; + return 200; + } + + location /i/status { + set $sanitized_request $request; + zauth off; + return 200; + } + + location /vts { + set $sanitized_request $request; + zauth off; + vhost_traffic_status_display; + vhost_traffic_status_display_format html; + } + + # + # Service Routing + # + + # Federator endpoints: expose the federatorExternal port (Inward service) + location /federation { + set $sanitized_request $request; + zauth off; + + proxy_set_header "X-SSL-Certificate" $ssl_client_escaped_cert; + proxy_pass http://federator_external; + + # FUTUREWORK(federation): are any other settings + # (e.g. timeouts, body size, buffers, headers,...) + # useful/recommended/important-for-security?) + } + + # Brig Endpoints + # + ## brig unauthenticated endpoints + + location ~* ^(/v[0-9]+)?/api/swagger-ui { + include common_response_no_zauth.conf; + proxy_pass http://brig; + } + + location ~* ^(/v[0-9]+)?/api/swagger.json { + include common_response_no_zauth.conf; + proxy_pass http://brig; + } + + location ~* ^(/v[0-9]+)?/api-internal/swagger-ui { + include common_response_no_zauth.conf; + proxy_pass http://brig; + } + + location ~* ^(/v[0-9]+)?/api-internal/swagger.json { + include common_response_no_zauth.conf; + proxy_pass http://brig; + } + + location /register { + include common_response_no_zauth.conf; + proxy_pass http://brig; + } + + location /access { + include common_response_no_zauth.conf; + proxy_pass http://brig; + } + + location /activate { + include common_response_no_zauth.conf; + proxy_pass http://brig; + } + + location /login { + include common_response_no_zauth.conf; + proxy_pass http://brig; + } + + location ~* ^(/v[0-9]+)?/teams/invitations/([^/]*)$ { + include common_response_no_zauth.conf; + proxy_pass http://brig; + } + + location /verification-code/send { + include common_response_no_zauth.conf; + proxy_pass http://brig; + } + + ## brig authenticated endpoints + + location ~* ^(/v[0-9]+)?/self$ { + include common_response_with_zauth.conf; + oauth_scope self; + proxy_pass http://brig; + } + + location /users { + include common_response_with_zauth.conf; + proxy_pass http://brig; + } + + location /list-users { + include common_response_with_zauth.conf; + proxy_pass http://brig; + } + + location /search { + include common_response_with_zauth.conf; + proxy_pass http://brig; + } + + location /list-connections { + include common_response_with_zauth.conf; + proxy_pass http://brig; + } + + location ~* ^/teams/([^/]+)/search$ { + include common_response_with_zauth.conf; + proxy_pass http://brig; + } + + location /connections { + include common_response_with_zauth.conf; + proxy_pass http://brig; + } + + location ~* ^(/v[0-9]+)?/clients { + include common_response_with_zauth.conf; + proxy_pass http://brig; + } + + location ~* ^(/v[0-9]+)?/mls/key-packages { + include common_response_with_zauth.conf; + proxy_pass http://brig; + } + + location /properties { + include common_response_with_zauth.conf; + proxy_pass http://brig; + } + + location /calls/config { + include common_response_with_zauth.conf; + proxy_pass http://brig; + } + + location ~* ^/teams/([^/]*)/size$ { + include common_response_with_zauth.conf; + proxy_pass http://brig; + } + + location ~* ^(/v[0-9]+)?/system/settings/unauthorized$ { + include common_response_no_zauth.conf; + proxy_pass http://brig; + } + + location ~* ^(/v[0-9]+)?/system/settings$ { + include common_response_with_zauth.conf; + proxy_pass http://brig; + } + + location ~* ^/oauth/clients/([^/]*)$ { + include common_response_with_zauth.conf; + proxy_pass http://brig; + } + + location ~* ^/oauth/authorization/codes$ { + include common_response_with_zauth.conf; + proxy_pass http://brig; + } + + location /oauth/token { + include common_response_no_zauth.conf; + proxy_pass http://brig; + } + + location /oauth/revoke { + include common_response_no_zauth.conf; + proxy_pass http://brig; + } + + location /oauth/applications { + include common_response_with_zauth.conf; + proxy_pass http://brig; + } + + # Cargohold Endpoints + + location ~* ^(/v[0-9]+)?/assets { + include common_response_with_zauth.conf; + proxy_pass http://cargohold; + } + + location /assets { + include common_response_with_zauth.conf; + proxy_pass http://cargohold; + } + + location /bot/assets { + include common_response_with_zauth.conf; + proxy_pass http://cargohold; + } + + location /provider/assets { + include common_response_with_zauth.conf; + proxy_pass http://cargohold; + } + + # Galley Endpoints + + location ~* ^(/v[0-9]+)?/legalhold/conversations/(.*)$ { + include common_response_with_zauth.conf; + proxy_pass http://galley; + } + + location ~* ^(/v[0-9]+)?/conversations$ { + include common_response_with_zauth.conf; + oauth_scope conversations; + proxy_pass http://galley; + } + + location ~* ^(/v[0-9]+)?/conversations/([^/]*)/code { + include common_response_with_zauth.conf; + oauth_scope conversations_code; + proxy_pass http://galley; + } + + location ~* ^(/v[0-9]+)?/conversations.* { + include common_response_with_zauth.conf; + proxy_pass http://galley; + } + + location ~* ^/conversations/([^/]*)/otr/messages { + include common_response_with_zauth.conf; + proxy_pass http://galley; + } + + location ~* ^/conversations/([^/]*)/([^/]*)/proteus/messages { + include common_response_with_zauth.conf; + proxy_pass http://galley; + } + + location /broadcast { + include common_response_with_zauth.conf; + proxy_pass http://galley; + } + + location /bot/conversation { + include common_response_with_zauth.conf; + proxy_pass http://galley; + } + + location /bot/messages { + include common_response_with_zauth.conf; + proxy_pass http://galley; + } + + location ~* ^/teams$ { + include common_response_with_zauth.conf; + proxy_pass http://galley; + } + + location ~* ^/teams/([^/]*)$ { + include common_response_with_zauth.conf; + proxy_pass http://galley; + } + + location ~* ^/teams/([^/]*)/members(.*) { + include common_response_with_zauth.conf; + proxy_pass http://galley; + } + + location ~* ^/teams/([^/]*)/conversations(.*) { + include common_response_with_zauth.conf; + proxy_pass http://galley; + } + + location ~* ^/teams/([^/]*)/features { + include common_response_with_zauth.conf; + proxy_pass http://galley; + } + + location ~* ^/teams/([^/]*)/features/([^/]*) { + include common_response_with_zauth.conf; + proxy_pass http://galley; + } + + location ~* ^(/v[0-9]+)?/feature-configs$ { + include common_response_with_zauth.conf; + oauth_scope feature_configs; + proxy_pass http://galley; + } + + location ~* ^(/v[0-9]+)?/feature-configs(.*) { + include common_response_with_zauth.conf; + proxy_pass http://galley; + } + + location ~* ^/teams/([^/]*)/legalhold(.*) { + include common_response_with_zauth.conf; + proxy_pass http://galley; + } + + location ~* ^/teams/([^/]*)/members/csv$ { + include common_response_with_zauth.conf; + proxy_pass http://galley; + } + + location /mls/welcome { + include common_response_with_zauth.conf; + proxy_pass http://galley; + } + + location /mls/messages { + include common_response_with_zauth.conf; + proxy_pass http://galley; + } + + location ~* ^(/v[0-9]+)?/mls/commit-bundles { + include common_response_with_zauth.conf; + proxy_pass http://galley; + } + + location ~* ^(/v[0-9]+)?/mls/public-keys { + include common_response_with_zauth.conf; + proxy_pass http://galley; + } + + # Gundeck Endpoints + + location /push { + include common_response_with_zauth.conf; + proxy_pass http://gundeck; + } + + location /presences { + include common_response_with_zauth.conf; + proxy_pass http://gundeck; + } + + location ~* ^(/v[0-9]+)?/notifications$ { + include common_response_with_zauth.conf; + proxy_pass http://gundeck; + } + + # Proxy Endpoints + + location /proxy { + include common_response_with_zauth.conf; + proxy_pass http://proxy; + } + + # Cannon Endpoints + + location /await { + include common_response_with_zauth.conf; + proxy_pass http://cannon; + + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_read_timeout 1h; + } + + # Spar Endpoints + + location /sso { + include common_response_no_zauth.conf; + proxy_pass http://spar; + } + + location /identity-providers { + include common_response_with_zauth.conf; + proxy_pass http://spar; + } + } +} diff --git a/deploy/dockerephemeral/federation-v1/nginz/conf/pid.conf b/deploy/dockerephemeral/federation-v1/nginz/conf/pid.conf new file mode 100644 index 00000000000..e722aa5ae23 --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/nginz/conf/pid.conf @@ -0,0 +1 @@ +pid /tmp/nginz.pid; diff --git a/deploy/dockerephemeral/federation-v1/nginz/conf/zauth_acl.txt b/deploy/dockerephemeral/federation-v1/nginz/conf/zauth_acl.txt new file mode 100644 index 00000000000..3b644bf3d98 --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/nginz/conf/zauth_acl.txt @@ -0,0 +1,15 @@ +a (blacklist (regex "(/v[0-9]+)?/provider(/.*)?") + (regex "(/v[0-9]+)?/bot(/.*)?") + (regex "(/v[0-9]+)?/i/.*")) + +b (whitelist (regex "(/v[0-9]+)?/bot(/.*)?")) + +p (whitelist (regex "(/v[0-9]+)?/provider(/.*)?")) + +# LegalHold Access Tokens +# FUTUREWORK: remove /legalhold/conversations/ when support for v1 dropped +la (whitelist (regex "(/v[0-9]+)?/notifications") + (regex "(/v[0-9]+)?/assets/v3/.*") + (regex "(/v[0-9]+)?/users(/.*)?") + (regex "(/v[0-9]+)?/legalhold/conversations/[^/]+") + (regex "(/v[0-9]+)?/conversations/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$")) diff --git a/deploy/dockerephemeral/federation-v1/nginz/upstreams b/deploy/dockerephemeral/federation-v1/nginz/upstreams new file mode 100644 index 00000000000..cbf7b353fe1 --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/nginz/upstreams @@ -0,0 +1,38 @@ +upstream cargohold { + least_conn; + keepalive 32; + server cargohold-v1:8080 max_fails=3 weight=1; +} +upstream gundeck { + least_conn; + keepalive 32; + server gundeck-v1:8080 max_fails=3 weight=1; +} +upstream cannon { + least_conn; + keepalive 32; + server cannon-v1:8080 max_fails=3 weight=1; +} +upstream galley { + least_conn; + keepalive 32; + server galley-v1:8080 max_fails=3 weight=1; +} +upstream proxy { + least_conn; + keepalive 32; + server proxy-v1:8080 max_fails=3 weight=1; +} +upstream brig { + least_conn; + keepalive 32; + server brig-v1:8080 max_fails=3 weight=1; +} +upstream spar { + least_conn; + keepalive 32; + server spar-v1:8080 max_fails=3 weight=1; +} +upstream federator_external { + server federator-v1:8081 max_fails=3 weight=1; +} diff --git a/deploy/dockerephemeral/federation-v1/oauth-ed25519.jwk b/deploy/dockerephemeral/federation-v1/oauth-ed25519.jwk new file mode 100644 index 00000000000..c00a8270aa4 --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/oauth-ed25519.jwk @@ -0,0 +1 @@ +{"kty":"OKP","crv":"Ed25519","x":"mhP-NgFw3ifIXGZqJVB0kemt9L3BtD5P8q4Gah4Iklc","d":"R8-pV2-sPN7dykV8HFJ73S64F3kMHTNnJiSN8UdWk_o"} diff --git a/deploy/dockerephemeral/federation-v1/proxy.config b/deploy/dockerephemeral/federation-v1/proxy.config new file mode 100644 index 00000000000..d2225ca26c9 --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/proxy.config @@ -0,0 +1,8 @@ +secrets { + youtube = "my-youtube-secret" + googlemaps = "my-googlemaps-secret" + soundcloud = "my-soundcloud-secret" + giphy = "my-giphy-secret" + # Base64 encoded client ID and secret: `Bearer id:secret`: + spotify = "my-spotify-secret" +} diff --git a/deploy/dockerephemeral/federation-v1/proxy.yaml b/deploy/dockerephemeral/federation-v1/proxy.yaml new file mode 100644 index 00000000000..43a117e6a20 --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/proxy.yaml @@ -0,0 +1,20 @@ +# Example yaml-formatted configuration for proxy +# proxy can be started with a config file (e.g. ./dist/proxy -c proxy.yaml.example) + +host: 0.0.0.0 +port: 8080 + +# number of connections for the http pool +httpPoolSize: 1000 + +# maximum number of incoming connections +maxConns: 5000 + +# File containing upstream secrets. +secretsConfig: /etc/wire/proxy/conf/proxy.config + +disabledAPIVersions: [] + +# Logging settings +logLevel: Info +logNetStrings: false diff --git a/deploy/dockerephemeral/federation-v1/spar.yaml b/deploy/dockerephemeral/federation-v1/spar.yaml new file mode 100644 index 00000000000..e111292bc03 --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/spar.yaml @@ -0,0 +1,44 @@ +saml: + version: SAML2.0 + logLevel: Warn + + spHost: 0.0.0.0 + spPort: 8080 + # TODO: change these + spAppUri: http://localhost:8080/ + spSsoUri: http://localhost:8080/sso + + contacts: + - type: ContactBilling + company: evil corp. + givenName: Dr. + surname: Girlfriend + email: email:president@evil.corp + +brig: + host: brig-federation-v1 + port: 8080 + +galley: + host: galley-federation-v1 + port: 8080 + +cassandra: + endpoint: + host: demo_wire_cassandra + port: 9042 + keyspace: spar_test_federation_v1 + filterNodesByDatacentre: datacenter1 + +# Wire/AWS specific, optional +# discoUrl: "https://" + +disabledAPIVersions: [] + +maxttlAuthreq: 5 # seconds. don't set this too large, it is also the run time of one TTL test. +maxttlAuthresp: 7200 # seconds. do not set this to 1h or less, as that is what the mock idp wants. + +maxScimTokens: 2 # Token limit {#RefScimToken} +richInfoLimit: 5000 # should be in sync with Brig + +logNetStrings: False # log using netstrings encoding (see http://cr.yp.to/proto/netstrings.txt) diff --git a/deploy/dockerephemeral/federation-v1/turn-secret.txt b/deploy/dockerephemeral/federation-v1/turn-secret.txt new file mode 100644 index 00000000000..5e558cab2cc --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/turn-secret.txt @@ -0,0 +1 @@ +xMtZyTpu=Leb?YKCoq#BXQR:gG^UrE83dNWzFJ2VcD \ No newline at end of file diff --git a/deploy/dockerephemeral/federation-v1/twilio-credentials.yaml b/deploy/dockerephemeral/federation-v1/twilio-credentials.yaml new file mode 100644 index 00000000000..d64e0ec4f23 --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/twilio-credentials.yaml @@ -0,0 +1,2 @@ +sid: "dummy" +token: "dummy" diff --git a/deploy/dockerephemeral/federation-v1/zauth-privkeys.txt b/deploy/dockerephemeral/federation-v1/zauth-privkeys.txt new file mode 100644 index 00000000000..7b6d17ed984 --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/zauth-privkeys.txt @@ -0,0 +1,4 @@ +qjIAZtKrpXInwyqgM7JCZ3QeK9B4JGBYAv1_63YjTtgDylLfTTpdwvDYSy32is13biThD03QZAUOhBO042Odrw== +dNLsH_oIA6hJCyw-AwokLz3AukHNghlP3H-pW5Ao1Wy06OI2MGgBwRnvjgfI2l1mgCLPJQflUR-7DsYO0p6zoQ== +drShe2GnggBy-VAW1gdE6myf4UAFcN1ZdixCO8NRuYLv_TO-xNQzRj-8RfemJ4R6Oz-R5KTfP6Oj_Tj0qezDTw== +tZWlAKOCe5-vlQl0TbECvxeIptEBGRrnGSiej-olAFe-46gXpFkWTas2Ci84VUWyhWzRJj4rtBmyJkAm-TMvwQ== diff --git a/deploy/dockerephemeral/federation-v1/zauth-pubkeys.txt b/deploy/dockerephemeral/federation-v1/zauth-pubkeys.txt new file mode 100644 index 00000000000..661fcfc71ba --- /dev/null +++ b/deploy/dockerephemeral/federation-v1/zauth-pubkeys.txt @@ -0,0 +1,4 @@ +A8pS3006XcLw2Est9orNd24k4Q9N0GQFDoQTtONjna8= +tOjiNjBoAcEZ744HyNpdZoAizyUH5VEfuw7GDtKes6E= +7_0zvsTUM0Y_vEX3pieEejs_keSk3z-jo_049Knsw08= +vuOoF6RZFk2rNgovOFVFsoVs0SY-K7QZsiZAJvkzL8E= diff --git a/deploy/dockerephemeral/init_vhosts.sh b/deploy/dockerephemeral/init_vhosts.sh index 688d635e0a5..93746557fa8 100755 --- a/deploy/dockerephemeral/init_vhosts.sh +++ b/deploy/dockerephemeral/init_vhosts.sh @@ -16,5 +16,6 @@ create_vhost d1.example.com create_vhost d2.example.com create_vhost d3.example.com create_vhost federation-v0 +create_vhost federation-v1 echo 'RabbitMQ resources created successfully!' diff --git a/deploy/dockerephemeral/run.sh b/deploy/dockerephemeral/run.sh index 8d9a98cc8be..f6455dacf5c 100755 --- a/deploy/dockerephemeral/run.sh +++ b/deploy/dockerephemeral/run.sh @@ -1,17 +1,34 @@ #!/usr/bin/env bash -set -xe +set -e # run.sh should work no matter what is the current directory SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" DOCKER_FILE="$SCRIPT_DIR/docker-compose.yaml" +FED_VERSIONS=(0 1) + +opts=( "--file" "$DOCKER_FILE" ) +for v in "${FED_VERSIONS[@]}"; do + var="ENABLE_FEDERATION_V$v" + if [[ "${!var}" == 1 ]]; then + opts+=( "--file" "$SCRIPT_DIR/federation-v$v.yaml" ) + fi +done + +dc() { + docker-compose "${opts[@]}" "$@" +} cleanup () { - docker-compose --file "$DOCKER_FILE" --file "$SCRIPT_DIR/federation-v0.yaml" down + dc down } -docker-compose --file "$DOCKER_FILE" --file "$SCRIPT_DIR/federation-v0.yaml" up -d -trap cleanup EXIT -echo "All Services started successfully, press Ctrl+C to stop them" -# Wait for something to kill this -while true; do sleep 100000000; done +if [ -z "$1" ]; then + dc up -d + trap cleanup EXIT + echo "All Services started successfully, press Ctrl+C to stop them" + # Wait for something to kill this + sleep infinity +else + dc "$@" +fi diff --git a/hack/helm_vars/wire-server/values.yaml.gotmpl b/hack/helm_vars/wire-server/values.yaml.gotmpl index 452b3864685..5ca735fe38e 100644 --- a/hack/helm_vars/wire-server/values.yaml.gotmpl +++ b/hack/helm_vars/wire-server/values.yaml.gotmpl @@ -459,6 +459,7 @@ federator: config: optSettings: useSystemCAStore: false + logLevel: Debug tests: {{- if .Values.uploadXml }} config: diff --git a/integration/Setup.hs b/integration/Setup.hs index a9c6110e00b..71b172a7e29 100644 --- a/integration/Setup.hs +++ b/integration/Setup.hs @@ -191,10 +191,11 @@ testHooks hooks = [ "module RunAllTests where", "import Testlib.PTest", "import Prelude", + "import Control.Monad.Trans.Writer", unlines (map ("import qualified " <>) modules), - "allTests :: [Test]", - "allTests =", - " " <> intercalate " <>\n " (map (\(m, n, s, f) -> "mkTests " <> unwords [show m, show n, show s, show f, m <> "." <> n]) tests) + "mkAllTests :: IO [Test]", + "mkAllTests = execWriterT $ do", + unlines (map (\(m, n, s, f) -> " yieldTests " <> unwords [show m, show n, show s, show f, m <> "." <> n]) tests) ] ) pure () diff --git a/integration/integration.cabal b/integration/integration.cabal index 0c4c2f93b6c..75e68530583 100644 --- a/integration/integration.cabal +++ b/integration/integration.cabal @@ -170,6 +170,7 @@ library Testlib.Run Testlib.RunServices Testlib.Types + Testlib.VersionedFed Testlib.XML build-depends: diff --git a/integration/test/SetupHelpers.hs b/integration/test/SetupHelpers.hs index 63e4c61b786..73a7bfe692f 100644 --- a/integration/test/SetupHelpers.hs +++ b/integration/test/SetupHelpers.hs @@ -227,14 +227,15 @@ withFederatingBackendsAllowDynamic k = do -- | Create two users on different domains such that the one-to-one -- conversation, once finalised, will be hosted on the backend given by the --- input domain. -createOne2OneConversation :: (HasCallStack) => Domain -> App (Value, Value, Value) -createOne2OneConversation owningDomain = do +-- first domain. +createOne2OneConversation :: + (HasCallStack, MakesValue domain1, MakesValue domain2) => + domain1 -> + domain2 -> + App (Value, Value, Value) +createOne2OneConversation owningDomain otherDomain = do owningUser <- randomUser owningDomain def domainName <- owningUser %. "qualified_id.domain" - let otherDomain = case owningDomain of - OwnDomain -> OtherDomain - OtherDomain -> OwnDomain let go = do otherUser <- randomUser otherDomain def otherUserId <- otherUser %. "qualified_id" diff --git a/integration/test/Test/Connection.hs b/integration/test/Test/Connection.hs index d12feb41f01..c0468db6a39 100644 --- a/integration/test/Test/Connection.hs +++ b/integration/test/Test/Connection.hs @@ -22,11 +22,15 @@ import API.Galley import Notifications import SetupHelpers import Testlib.Prelude +import Testlib.VersionedFed import UnliftIO.Async (forConcurrently_) -testConnectWithRemoteUser :: (HasCallStack) => Domain -> App () +testConnectWithRemoteUser :: (HasCallStack) => OneOf Domain AnyFedDomain -> App () testConnectWithRemoteUser owningDomain = do - (alice, bob, one2oneId) <- createOne2OneConversation owningDomain + let otherDomain = case owningDomain of + OneOfA OwnDomain -> OtherDomain + _ -> OwnDomain + (alice, bob, one2oneId) <- createOne2OneConversation owningDomain otherDomain aliceId <- alice %. "qualified_id" getConversation alice one2oneId `bindResponse` \resp -> do resp.status `shouldMatchInt` 200 @@ -79,20 +83,21 @@ testRemoteUserGetsDeleted = do pure charlie - charlieUnconnected <- do - randomUser OtherDomain def + charlieUnconnected <- randomUser OtherDomain def - forConcurrently_ [charliePending, charlieConnected, charlieBlocked, charlieUnconnected] \charlie -> do - deleteUser charlie + forConcurrently_ + [charliePending, charlieConnected, charlieBlocked, charlieUnconnected] + \charlie -> do + deleteUser charlie - -- charlie is on their local backend, so asking should be instant - getConnection charlie alice `bindResponse` \resp -> - resp.status `shouldMatchInt` 404 + -- charlie is on their local backend, so asking should be instant + getConnection charlie alice `bindResponse` \resp -> + resp.status `shouldMatchInt` 404 - -- for alice, charlie is on the remote backend, so the status change - -- may not be instant - getConnection alice charlie `waitForResponse` \resp -> - resp.status `shouldMatchInt` 404 + -- for alice, charlie is on the remote backend, so the status change + -- may not be instant + getConnection alice charlie `waitForResponse` \resp -> + resp.status `shouldMatchInt` 404 testInternalGetConStatusesAll :: (HasCallStack) => App () testInternalGetConStatusesAll = @@ -149,9 +154,11 @@ assertConnectionStatus userFrom userTo connStatus = resp.status `shouldMatchInt` 200 resp.json %. "status" `shouldMatch` connStatus -testConnectFromIgnored :: (HasCallStack) => App () -testConnectFromIgnored = do - [alice, bob] <- forM [OwnDomain, OtherDomain] $ flip randomUser def +testConnectFromIgnored :: (HasCallStack) => StaticDomain -> App () +testConnectFromIgnored domain = do + alice <- randomUser OwnDomain def + bob <- randomUser domain def + void $ postConnection bob alice >>= getBody 201 -- set up an initial "ignored" state on Alice's side assertConnectionStatus alice bob "pending" @@ -168,9 +175,11 @@ testConnectFromIgnored = do resp.status `shouldMatchInt` 200 resp.json %. "status" `shouldMatch` "accepted" -testSentFromIgnored :: (HasCallStack) => App () -testSentFromIgnored = do - [alice, bob] <- forM [OwnDomain, OtherDomain] $ flip randomUser def +testSentFromIgnored :: (HasCallStack) => StaticDomain -> App () +testSentFromIgnored domain = do + alice <- randomUser OwnDomain def + bob <- randomUser domain def + -- set up an initial "ignored" state void $ postConnection bob alice >>= getBody 201 void $ putConnection alice bob "ignored" >>= getBody 200 @@ -185,9 +194,9 @@ testSentFromIgnored = do void $ putConnection alice bob "accepted" >>= getBody 200 assertConnectionStatus alice bob "sent" -testConnectFromBlocked :: (HasCallStack) => App () -testConnectFromBlocked = do - (alice, bob, one2oneId) <- createOne2OneConversation OwnDomain +testConnectFromBlocked :: (HasCallStack) => StaticDomain -> App () +testConnectFromBlocked domain = do + (alice, bob, one2oneId) <- createOne2OneConversation OwnDomain domain bobId <- bob %. "qualified_id" -- set up an initial "blocked" state @@ -211,9 +220,11 @@ testConnectFromBlocked = do qIds <- for others (%. "qualified_id") qIds `shouldMatchSet` [bobId] -testSentFromBlocked :: (HasCallStack) => App () -testSentFromBlocked = do - [alice, bob] <- forM [OwnDomain, OtherDomain] $ flip randomUser def +testSentFromBlocked :: (HasCallStack) => StaticDomain -> App () +testSentFromBlocked domain = do + alice <- randomUser OwnDomain def + bob <- randomUser domain def + -- set up an initial "blocked" state void $ postConnection bob alice >>= getBody 201 void $ putConnection alice bob "blocked" >>= getBody 200 @@ -228,9 +239,10 @@ testSentFromBlocked = do void $ putConnection alice bob "accepted" >>= getBody 200 assertConnectionStatus alice bob "sent" -testCancel :: (HasCallStack) => App () -testCancel = do - [alice, bob] <- forM [OwnDomain, OtherDomain] $ flip randomUser def +testCancel :: (HasCallStack) => StaticDomain -> App () +testCancel domain = do + alice <- randomUser OwnDomain def + bob <- randomUser domain def void $ postConnection alice bob >>= getBody 201 assertConnectionStatus alice bob "sent" @@ -238,16 +250,16 @@ testCancel = do void $ putConnection alice bob "cancelled" >>= getBody 200 assertConnectionStatus alice bob "cancelled" -testConnectionLimits :: (HasCallStack) => App () -testConnectionLimits = do +testConnectionLimits :: (HasCallStack) => StaticDomain -> App () +testConnectionLimits domain = do let connectionLimit = 16 alice <- randomUser OwnDomain def [charlie1, charlie2, charlie3, charlie4] <- replicateM 4 do - randomUser OtherDomain def + randomUser domain def -- connect to connectionLimit - 1 many users (charlie5 : _) <- replicateM (connectionLimit - 1) do - charlie <- randomUser OtherDomain def + charlie <- randomUser domain def postConnection alice charlie `bindResponse` \resp -> resp.status `shouldMatchInt` 201 pure charlie diff --git a/integration/test/Test/Conversation.hs b/integration/test/Test/Conversation.hs index f44eb9eea2f..844fd6e295e 100644 --- a/integration/test/Test/Conversation.hs +++ b/integration/test/Test/Conversation.hs @@ -35,6 +35,7 @@ import SetupHelpers hiding (deleteUser) import Testlib.One2One (generateRemoteAndConvIdWithDomain) import Testlib.Prelude import Testlib.ResourcePool +import Testlib.VersionedFed testDynamicBackendsFullyConnectedWhenAllowAll :: (HasCallStack) => App () testDynamicBackendsFullyConnectedWhenAllowAll = do @@ -113,10 +114,10 @@ testDynamicBackendsNotFullyConnected = do resp.json %. "status" `shouldMatch` "non-fully-connected" resp.json %. "not_connected" `shouldMatchSet` [domainB, domainC] -testFederationStatus :: (HasCallStack) => App () -testFederationStatus = do +testFederationStatus :: (HasCallStack) => StaticDomain -> App () +testFederationStatus domain = do uid <- randomUser OwnDomain def {BrigI.team = True} - federatingRemoteDomain <- asString OtherDomain + federatingRemoteDomain <- asString domain let invalidDomain = "c.example.com" -- Does not have any srv records bindResponse (getFederationStatus uid []) @@ -261,12 +262,12 @@ testAddMemberV1 domain = do users <- resp.json %. "data.users" >>= asList traverse (%. "qualified_id") users `shouldMatchSet` [bobId] -testConvWithUnreachableRemoteUsers :: (HasCallStack) => App () -testConvWithUnreachableRemoteUsers = do +testConvWithUnreachableRemoteUsers :: (HasCallStack) => StaticDomain -> App () +testConvWithUnreachableRemoteUsers domain = do ([alice, alex, bob, charlie, dylan], domains) <- startDynamicBackends [def, def] $ \domains -> do own <- make OwnDomain & asString - other <- make OtherDomain & asString + other <- make domain & asString users@(alice : others) <- createUsers $ [own, own, other] <> domains forM_ others $ connectTwoUsers alice pure (users, domains) @@ -280,11 +281,11 @@ testConvWithUnreachableRemoteUsers = do regConvs <- filterM (\c -> (==) <$> (c %. "type" & asInt) <*> pure 0) convs regConvs `shouldMatch` ([] :: [Value]) -testAddUserWithUnreachableRemoteUsers :: (HasCallStack) => App () -testAddUserWithUnreachableRemoteUsers = do +testAddUserWithUnreachableRemoteUsers :: (HasCallStack) => StaticDomain -> App () +testAddUserWithUnreachableRemoteUsers domain = do resourcePool <- asks resourcePool own <- make OwnDomain & asString - other <- make OtherDomain & asString + other <- make domain & asString runCodensity (acquireResources 1 resourcePool) $ \[cDom] -> do ([alex, bobId, bradId, chrisId], conv) <- runCodensity (startDynamicBackend cDom mempty) $ \_ -> do [alice, alex, bob, brad, charlie, chris] <- @@ -302,7 +303,7 @@ testAddUserWithUnreachableRemoteUsers = do runCodensity (startDynamicBackend cDom mempty) $ \_ -> void $ addMembers alex conv def {users = [bobId]} >>= getBody 200 - -- even though backend C is unreachable, we know B/OtherDomain and C + -- even though backend C is unreachable, we know B/domain and C -- federate because Bob joined when C was reachable, hence it is OK to add -- brad from B to the conversation. void $ addMembers alex conv def {users = [bradId]} >>= getBody 200 @@ -312,13 +313,13 @@ testAddUserWithUnreachableRemoteUsers = do resp.status `shouldMatchInt` 533 resp.jsonBody %. "unreachable_backends" `shouldMatchSet` [cDom.berDomain] -testAddUnreachableUserFromFederatingBackend :: (HasCallStack) => App () -testAddUnreachableUserFromFederatingBackend = do +testAddUnreachableUserFromFederatingBackend :: (HasCallStack) => StaticDomain -> App () +testAddUnreachableUserFromFederatingBackend domain = do resourcePool <- asks resourcePool runCodensity (acquireResources 1 resourcePool) $ \[cDom] -> do (alice, chadId, conv) <- runCodensity (startDynamicBackend cDom mempty) $ \_ -> do ownDomain <- make OwnDomain & asString - otherDomain <- make OtherDomain & asString + otherDomain <- make domain & asString [alice, bob, charlie, chad] <- createAndConnectUsers [ownDomain, otherDomain, cDom.berDomain, cDom.berDomain] @@ -355,11 +356,11 @@ testAddUnreachable = do -- need to be reachable so we can check that the graph for those domains is fully connected. resp.json %. "unreachable_backends" `shouldMatchSet` [charlieDomain, dylanDomain] -testGetOneOnOneConvInStatusSentFromRemote :: App () -testGetOneOnOneConvInStatusSentFromRemote = do +testGetOneOnOneConvInStatusSentFromRemote :: (HasCallStack) => StaticDomain -> App () +testGetOneOnOneConvInStatusSentFromRemote domain = do d1User <- randomUser OwnDomain def let shouldBeLocal = True - (d2Usr, d2ConvId) <- generateRemoteAndConvIdWithDomain OtherDomain (not shouldBeLocal) d1User + (d2Usr, d2ConvId) <- generateRemoteAndConvIdWithDomain domain (not shouldBeLocal) d1User bindResponse (postConnection d1User d2Usr) $ \r -> do r.status `shouldMatchInt` 201 r.json %. "status" `shouldMatch` "sent" @@ -373,8 +374,8 @@ testGetOneOnOneConvInStatusSentFromRemote = do resp <- getConversation d1User d2ConvId resp.status `shouldMatchInt` 200 -testAddingUserNonFullyConnectedFederation :: (HasCallStack) => App () -testAddingUserNonFullyConnectedFederation = do +testAddingUserNonFullyConnectedFederation :: (HasCallStack) => StaticDomain -> App () +testAddingUserNonFullyConnectedFederation domain = do let overrides = def { brigCfg = @@ -382,7 +383,7 @@ testAddingUserNonFullyConnectedFederation = do } startDynamicBackends [overrides] $ \[dynBackend] -> do own <- asString OwnDomain - other <- asString OtherDomain + other <- asString domain -- Ensure that dynamic backend only federates with own domain, but not other -- domain. @@ -484,10 +485,12 @@ testAddUserWhenOtherBackendOffline = do bindResponse (addMembers alice conv def {users = [alex]}) $ \resp -> do resp.status `shouldMatchInt` 200 -testSynchroniseUserRemovalNotification :: (HasCallStack) => App () -testSynchroniseUserRemovalNotification = do +testSynchroniseUserRemovalNotification :: (HasCallStack) => StaticDomain -> App () +testSynchroniseUserRemovalNotification domain = do resourcePool <- asks resourcePool - [alice, bob] <- createAndConnectUsers [OwnDomain, OtherDomain] + ownDomain <- make OwnDomain + otherDomain <- make domain + [alice, bob] <- createAndConnectUsers [ownDomain, otherDomain] runCodensity (acquireResources 1 resourcePool) $ \[dynBackend] -> do (conv, charlie, client) <- runCodensity (startDynamicBackend dynBackend mempty) $ \_ -> do @@ -851,10 +854,10 @@ testGuestLinksExpired = do bindResponse (getJoinCodeConv tm k v) $ \resp -> do resp.status `shouldMatchInt` 404 -testConversationWithFedV0 :: (HasCallStack) => App () -testConversationWithFedV0 = do +testConversationWithLegacyFed :: (HasCallStack) => AnyFedDomain -> App () +testConversationWithLegacyFed domain = do alice <- randomUser OwnDomain def - bob <- randomUser FedV0Domain def + bob <- randomUser domain def withAPIVersion 4 $ connectTwoUsers alice bob conv <- diff --git a/integration/test/Test/Demo.hs b/integration/test/Test/Demo.hs index 85f67354f3c..376e5fd3258 100644 --- a/integration/test/Test/Demo.hs +++ b/integration/test/Test/Demo.hs @@ -10,6 +10,7 @@ import qualified API.Nginz as Nginz import GHC.Stack import SetupHelpers import Testlib.Prelude +import Testlib.VersionedFed -- | Deleting unknown clients should fail with 404. testDeleteUnknownClient :: (HasCallStack) => App () @@ -194,15 +195,15 @@ testUnrace = do -} retryT $ True `shouldMatch` True -testFedV0Instance :: (HasCallStack) => App () -testFedV0Instance = do - res <- BrigP.getAPIVersion FedV0Domain >>= getJSON 200 - res %. "domain" `shouldMatch` FedV0Domain +testLegacyFedInstance :: (HasCallStack) => AnyFedDomain -> App () +testLegacyFedInstance domain = do + res <- BrigP.getAPIVersion domain >>= getJSON 200 + res %. "domain" `shouldMatch` domain -testFedV0Federation :: (HasCallStack) => App () -testFedV0Federation = do +testLegacyFedFederation :: (HasCallStack) => AnyFedDomain -> App () +testLegacyFedFederation domain = do alice <- randomUser OwnDomain def - bob <- randomUser FedV0Domain def + bob <- randomUser domain def bob' <- BrigP.getUser alice bob >>= getJSON 200 bob' %. "qualified_id" `shouldMatch` (bob %. "qualified_id") diff --git a/integration/test/Test/MLS/One2One.hs b/integration/test/Test/MLS/One2One.hs index 338cae3a7e4..515a82683d0 100644 --- a/integration/test/Test/MLS/One2One.hs +++ b/integration/test/Test/MLS/One2One.hs @@ -215,11 +215,12 @@ data One2OneScenario One2OneScenarioRemoteConv instance TestCases One2OneScenario where - testCases = - [ MkTestCase "[domain=own]" One2OneScenarioLocal, - MkTestCase "[domain=other;conv=own]" One2OneScenarioLocalConv, - MkTestCase "[domain=other;conv=other]" One2OneScenarioRemoteConv - ] + mkTestCases = + pure + [ MkTestCase "[domain=own]" One2OneScenarioLocal, + MkTestCase "[domain=other;conv=own]" One2OneScenarioLocalConv, + MkTestCase "[domain=other;conv=other]" One2OneScenarioRemoteConv + ] one2OneScenarioUserDomain :: One2OneScenario -> Domain one2OneScenarioUserDomain One2OneScenarioLocal = OwnDomain diff --git a/integration/test/Test/User.hs b/integration/test/Test/User.hs index 183a391d779..7002de72e49 100644 --- a/integration/test/Test/User.hs +++ b/integration/test/Test/User.hs @@ -11,8 +11,9 @@ import qualified Data.UUID as UUID import qualified Data.UUID.V4 as UUID import SetupHelpers import Testlib.Prelude +import Testlib.VersionedFed -testSupportedProtocols :: (HasCallStack) => Domain -> App () +testSupportedProtocols :: (HasCallStack) => OneOf Domain AnyFedDomain -> App () testSupportedProtocols bobDomain = do alice <- randomUser OwnDomain def alice %. "supported_protocols" `shouldMatchSet` ["proteus"] diff --git a/integration/test/Test/Version.hs b/integration/test/Test/Version.hs index abd59a49958..df0a7ab731c 100644 --- a/integration/test/Test/Version.hs +++ b/integration/test/Test/Version.hs @@ -9,22 +9,24 @@ newtype Versioned' = Versioned' Versioned -- | This instance is used to generate tests for some of the versions. (Not checking all of them for time efficiency reasons) instance TestCases Versioned' where - testCases = - [ MkTestCase "[version=unversioned]" (Versioned' Unversioned), - MkTestCase "[version=versioned]" (Versioned' Versioned), - MkTestCase "[version=v1]" (Versioned' (ExplicitVersion 1)), - MkTestCase "[version=v3]" (Versioned' (ExplicitVersion 3)), - MkTestCase "[version=v6]" (Versioned' (ExplicitVersion 6)) - ] + mkTestCases = + pure + [ MkTestCase "[version=unversioned]" (Versioned' Unversioned), + MkTestCase "[version=versioned]" (Versioned' Versioned), + MkTestCase "[version=v1]" (Versioned' (ExplicitVersion 1)), + MkTestCase "[version=v3]" (Versioned' (ExplicitVersion 3)), + MkTestCase "[version=v6]" (Versioned' (ExplicitVersion 6)) + ] -- | Used to test endpoints that have changed after version 5 data Version5 = Version5 | NoVersion5 instance TestCases Version5 where - testCases = - [ MkTestCase "[version=versioned]" NoVersion5, - MkTestCase "[version=v5]" Version5 - ] + mkTestCases = + pure + [ MkTestCase "[version=versioned]" NoVersion5, + MkTestCase "[version=v5]" Version5 + ] withVersion5 :: Version5 -> App a -> App a withVersion5 Version5 = withAPIVersion 5 diff --git a/integration/test/Testlib/App.hs b/integration/test/Testlib/App.hs index 38188f9a67e..2eecee9d686 100644 --- a/integration/test/Testlib/App.hs +++ b/integration/test/Testlib/App.hs @@ -63,11 +63,6 @@ instance MakesValue Domain where make OwnDomain = asks (String . T.pack . (.domain1)) make OtherDomain = asks (String . T.pack . (.domain2)) -data FedDomain = FedV0Domain - -instance MakesValue FedDomain where - make FedV0Domain = asks (String . T.pack . (.federationV0Domain)) - -- | Run an action, `recoverAll`ing with exponential backoff (min step 8ms, total timeout -- ~15s). Search this package for examples how to use it. -- diff --git a/integration/test/Testlib/Env.hs b/integration/test/Testlib/Env.hs index 336383deb72..42ae9ea25f3 100644 --- a/integration/test/Testlib/Env.hs +++ b/integration/test/Testlib/Env.hs @@ -88,7 +88,8 @@ mkGlobalEnv cfgFile = do Map.fromList $ [ (intConfig.backendOne.originDomain, intConfig.backendOne.beServiceMap), (intConfig.backendTwo.originDomain, intConfig.backendTwo.beServiceMap), - (intConfig.federationV0.originDomain, intConfig.federationV0.beServiceMap) + (intConfig.federationV0.originDomain, intConfig.federationV0.beServiceMap), + (intConfig.federationV1.originDomain, intConfig.federationV1.beServiceMap) ] <> [(berDomain resource, resourceServiceMap resource) | resource <- resources] tempDir <- Codensity $ withSystemTempDirectory "test" @@ -102,6 +103,7 @@ mkGlobalEnv cfgFile = do gDomain2 = intConfig.backendTwo.originDomain, gIntegrationTestHostName = intConfig.integrationTestHostName, gFederationV0Domain = intConfig.federationV0.originDomain, + gFederationV1Domain = intConfig.federationV1.originDomain, gDynamicDomains = (.domain) <$> Map.elems intConfig.dynamicBackends, gDefaultAPIVersion = 6, gManager = manager, @@ -141,8 +143,15 @@ mkEnv ge = do domain2 = gDomain2 ge, integrationTestHostName = gIntegrationTestHostName ge, federationV0Domain = gFederationV0Domain ge, + federationV1Domain = gFederationV1Domain ge, dynamicDomains = gDynamicDomains ge, defaultAPIVersion = gDefaultAPIVersion ge, + -- hardcode version 5 for fed 0 backend + apiVersionByDomain = + Map.fromList + [ (gFederationV0Domain ge, 5), + (gFederationV1Domain ge, 6) + ], manager = gManager ge, servicesCwdBase = gServicesCwdBase ge, removalKeyPaths = gRemovalKeyPaths ge, diff --git a/integration/test/Testlib/HTTP.hs b/integration/test/Testlib/HTTP.hs index d155a45c46f..153a8008c79 100644 --- a/integration/test/Testlib/HTTP.hs +++ b/integration/test/Testlib/HTTP.hs @@ -11,6 +11,7 @@ import qualified Data.CaseInsensitive as CI import Data.Function import Data.List import Data.List.Split (splitOn) +import qualified Data.Map as Map import Data.Maybe import Data.String import Data.String.Conversions (cs) @@ -130,15 +131,20 @@ data Versioned = Versioned | Unversioned | ExplicitVersion Int -- OwnDomain ...`. rawBaseRequest :: (HasCallStack, MakesValue domain) => domain -> Service -> Versioned -> String -> App HTTP.Request rawBaseRequest domain service versioned path = do + domainV <- objDomain domain + pathSegsPrefix <- case versioned of Versioned -> do - v <- asks (.defaultAPIVersion) + d <- asString domainV + versionMap <- asks (.apiVersionByDomain) + v <- case Map.lookup d versionMap of + Nothing -> asks (.defaultAPIVersion) + Just v -> pure v pure ["v" <> show v] Unversioned -> pure [] ExplicitVersion v -> do pure ["v" <> show v] - domainV <- objDomain domain serviceMap <- getServiceMap domainV liftIO . HTTP.parseRequest $ diff --git a/integration/test/Testlib/PTest.hs b/integration/test/Testlib/PTest.hs index 037cb276260..850960b2d2c 100644 --- a/integration/test/Testlib/PTest.hs +++ b/integration/test/Testlib/PTest.hs @@ -1,44 +1,45 @@ module Testlib.PTest where +import Control.Monad.Trans.Class +import Control.Monad.Trans.Writer import Data.Bifunctor (bimap) import Data.Char (toLower) import Data.Functor ((<&>)) import Data.Kind import Data.Proxy +import Data.Traversable import GHC.Generics import GHC.TypeLits import Testlib.Env +import Testlib.JSON import Testlib.Types import Prelude type Test = (String, String, String, String, App ()) +yieldTests :: (HasTests x) => String -> String -> String -> String -> x -> WriterT [Test] IO () +yieldTests m n s f x = do + t <- lift (mkTests m n s f x) + tell t + class HasTests x where - mkTests :: String -> String -> String -> String -> x -> [Test] + mkTests :: String -> String -> String -> String -> x -> IO [Test] instance HasTests (App ()) where - mkTests m n s f x = [(m, n, s, f, x)] + mkTests m n s f x = pure [(m, n, s, f, x)] instance (HasTests x, TestCases a) => HasTests (a -> x) where - mkTests m n s f x = - flip foldMap (testCases @a) \tc -> + mkTests m n s f x = do + tcs <- mkTestCases @a + fmap concat $ for tcs $ \tc -> mkTests m (n <> tc.testCaseName) s f (x tc.testCase) data TestCase a = MkTestCase {testCaseName :: String, testCase :: a} - deriving stock (Eq, Ord, Show, Generic) + deriving stock (Eq, Ord, Show, Generic, Functor, Foldable, Traversable) -- | enumerate all members of a bounded enum type --- --- >>> testCases @Bool --- [MkTestCase {testCaseName = "[bool=false]", testCase = False},MkTestCase {testCaseName = "[bool=true]", testCase = True}] --- >>> testCases @Domain --- [MkTestCase {testCaseName = "[domain=owndomain]", testCase = OwnDomain},MkTestCase {testCaseName = "[domain=otherdomain]", testCase = OtherDomain}] --- >>> testCases @Ciphersuite --- [MkTestCase {testCaseName = "[suite=0x0001]", testCase = Ciphersuite {code = "0x0001"}},MkTestCase {testCaseName = "[suite=0xf031]", testCase = Ciphersuite {code = "0xf031"}}] --- >>> testCases @(Tagged "foo" Bool) --- [MkTestCase {testCaseName = "[foo=false]", testCase = MkTagged {unTagged = False}},MkTestCase {testCaseName = "[foo=true]", testCase = MkTagged {unTagged = True}}] class TestCases a where - testCases :: [TestCase a] + mkTestCases :: IO [TestCase a] type Tagged :: Symbol -> Type -> Type newtype Tagged s a = MkTagged {unTagged :: a} @@ -52,21 +53,20 @@ pattern TaggedBool a = MkTagged a {-# COMPLETE TaggedBool #-} -- | only works for outer-most use of `Tagged` (not: `Maybe (Tagged "bla" Bool)`) --- --- >>> testCases @(Tagged "bla" Bool) instance (GEnum (Rep a), KnownSymbol s, Generic a) => TestCases (Tagged s a) where - testCases = - uni @(Rep a) <&> \case - -- replace the toplevel - (Left _ : ls, tc) -> - MkTestCase - { testCaseName = foldr mkName "" (Left (symbolVal @s Proxy) : ls), - testCase = MkTagged $ to tc - } - _ -> error "tagged test cases: impossible" + mkTestCases = + pure $ + uni @(Rep a) <&> \case + -- replace the toplevel + (Left _ : ls, tc) -> + MkTestCase + { testCaseName = foldr mkName "" (Left (symbolVal @s Proxy) : ls), + testCase = MkTagged $ to tc + } + _ -> error "tagged test cases: impossible" instance TestCases Ciphersuite where - testCases = do + mkTestCases = pure $ do suite <- allCiphersuites pure $ MkTestCase @@ -75,20 +75,22 @@ instance TestCases Ciphersuite where } instance TestCases CredentialType where - testCases = - [ MkTestCase "[ctype=basic]" BasicCredentialType, - MkTestCase "[ctype=x509]" X509CredentialType - ] + mkTestCases = + pure + [ MkTestCase "[ctype=basic]" BasicCredentialType, + MkTestCase "[ctype=x509]" X509CredentialType + ] -- | a default instance, normally we don't do such things but this is more convenient in -- the test suite as you don't have to derive anything instance {-# OVERLAPPABLE #-} (Generic a, GEnum (Rep a)) => TestCases a where - testCases = - uni @(Rep a) <&> \(tcn, tc) -> - MkTestCase - { testCaseName = foldr mkName "" tcn, - testCase = to tc - } + mkTestCases = + pure $ + uni @(Rep a) <&> \(tcn, tc) -> + MkTestCase + { testCaseName = foldr mkName "" tcn, + testCase = to tc + } {-# INLINE [1] mkName #-} mkName :: Either String String -> String -> String @@ -118,3 +120,15 @@ instance GEnum U1 where instance (GEnum (Rep k), Generic k) => GEnum (K1 r k) where uni = fmap (K1 . to) <$> uni @(Rep k) + +data OneOf a b = OneOfA a | OneOfB b + +instance (MakesValue a, MakesValue b) => MakesValue (OneOf a b) where + make (OneOfA a) = make a + make (OneOfB b) = make b + +instance (TestCases a, TestCases b) => TestCases (OneOf a b) where + mkTestCases = do + as <- fmap (map (fmap OneOfA)) mkTestCases + bs <- fmap (map (fmap OneOfB)) mkTestCases + pure $ as <> bs diff --git a/integration/test/Testlib/Run.hs b/integration/test/Testlib/Run.hs index 431530c91e0..6500b6f71e6 100644 --- a/integration/test/Testlib/Run.hs +++ b/integration/test/Testlib/Run.hs @@ -99,6 +99,7 @@ main = do let f = testFilter opts cfg = opts.configFile + allTests <- mkAllTests let tests = filter (\(qname, _, _, _) -> f qname) . sortOn (\(qname, _, _, _) -> qname) diff --git a/integration/test/Testlib/Types.hs b/integration/test/Testlib/Types.hs index e77e8b0a457..2ebec043a86 100644 --- a/integration/test/Testlib/Types.hs +++ b/integration/test/Testlib/Types.hs @@ -105,6 +105,7 @@ data GlobalEnv = GlobalEnv gDomain2 :: String, gIntegrationTestHostName :: String, gFederationV0Domain :: String, + gFederationV1Domain :: String, gDynamicDomains :: [String], gDefaultAPIVersion :: Int, gManager :: HTTP.Manager, @@ -120,6 +121,7 @@ data IntegrationConfig = IntegrationConfig { backendOne :: BackendConfig, backendTwo :: BackendConfig, federationV0 :: BackendConfig, + federationV1 :: BackendConfig, integrationTestHostName :: String, dynamicBackends :: Map String DynamicBackendConfig, rabbitmq :: RabbitMQConfig, @@ -134,6 +136,7 @@ instance FromJSON IntegrationConfig where <$> parseJSON (Object o) <*> o .: fromString "backendTwo" <*> o .: fromString "federation-v0" + <*> o .: fromString "federation-v1" <*> o .: fromString "integrationTestHostName" <*> o .: fromString "dynamicBackends" <*> o .: fromString "rabbitmq" @@ -201,8 +204,10 @@ data Env = Env domain2 :: String, integrationTestHostName :: String, federationV0Domain :: String, + federationV1Domain :: String, dynamicDomains :: [String], defaultAPIVersion :: Int, + apiVersionByDomain :: Map String Int, manager :: HTTP.Manager, servicesCwdBase :: Maybe FilePath, -- | paths to removal keys by signature scheme diff --git a/integration/test/Testlib/VersionedFed.hs b/integration/test/Testlib/VersionedFed.hs new file mode 100644 index 00000000000..1dcadc2bffb --- /dev/null +++ b/integration/test/Testlib/VersionedFed.hs @@ -0,0 +1,65 @@ +module Testlib.VersionedFed where + +import Control.Monad.Reader +import Data.Proxy +import qualified Data.Text as T +import GHC.TypeLits +import System.Environment +import Testlib.PTest +import Testlib.Prelude + +data FedDomain n = FedDomain + +instance MakesValue (FedDomain 0) where + make FedDomain = asks (String . T.pack . (.federationV0Domain)) + +instance MakesValue (FedDomain 1) where + make FedDomain = asks (String . T.pack . (.federationV1Domain)) + +instance (KnownNat n) => TestCases (FedDomain n) where + mkTestCases = + map (fmap (const FedDomain)) + <$> mkFedTestCase "" (natVal (Proxy @n)) + +mkFedTestCase :: String -> Integer -> IO [TestCase Integer] +mkFedTestCase name n = do + v <- lookupEnv $ "ENABLE_FEDERATION_V" <> show n + if v == Just "1" + then pure [MkTestCase name n] + else pure [] + +data AnyFedDomain = AnyFedDomain Integer + +instance MakesValue AnyFedDomain where + make (AnyFedDomain 0) = asks (String . T.pack . (.federationV0Domain)) + make (AnyFedDomain 1) = asks (String . T.pack . (.federationV1Domain)) + make (AnyFedDomain _) = error "invalid federation version" + +instance TestCases AnyFedDomain where + mkTestCases = + map (fmap AnyFedDomain) + . concat + <$> traverse + (uncurry mkFedTestCase) + [("[domain=fed-v" <> show v <> "]", v) | v <- [0, 1]] + +-- | This can be used as an argument for parametrised tests. It will be bound +-- to at least 'OtherDomain', and optionally to legacy federated domains, +-- according to the values of the corresponding environment variables +-- (@ENABLE_FEDERATION_V0@ and similar). +data StaticDomain = StaticDomain | StaticFedDomain Integer + deriving (Eq) + +instance MakesValue StaticDomain where + make StaticDomain = make OtherDomain + make (StaticFedDomain n) = make (AnyFedDomain n) + +instance TestCases StaticDomain where + mkTestCases = do + feds <- + map (fmap StaticFedDomain) + . concat + <$> traverse + (uncurry mkFedTestCase) + [("[domain=fed-v" <> show v <> "]", v) | v <- [0, 1]] + pure $ [MkTestCase "[domain=other]" StaticDomain] <> feds diff --git a/services/integration.yaml b/services/integration.yaml index b33259f873a..201b99ce025 100644 --- a/services/integration.yaml +++ b/services/integration.yaml @@ -183,4 +183,43 @@ federation-v0: host: 127.0.0.1 port: 21091 +federation-v1: + originDomain: federation-v1.example.com + brig: + host: 127.0.0.1 + port: 22082 + cannon: + host: 127.0.0.1 + port: 22083 + cargohold: + host: 127.0.0.1 + port: 22084 + federatorInternal: + host: 127.0.0.1 + port: 22097 + federatorExternal: + host: 127.0.0.1 + port: 22098 + galley: + host: 127.0.0.1 + port: 22085 + gundeck: + host: 127.0.0.1 + port: 22086 + nginz: + host: 127.0.0.1 + port: 22080 + spar: + host: 127.0.0.1 + port: 22088 + proxy: + host: 127.0.0.1 + port: 22087 + backgroundWorker: + host: 127.0.0.1 + port: 22089 + stern: + host: 127.0.0.1 + port: 22091 + integrationTestHostName: "localhost"