forked from Deuxfleurs/infrastructure
Compare commits
120 commits
adrien-adm
...
main
Author | SHA1 | Date | |
---|---|---|---|
ab6db28ada | |||
46e29828b1 | |||
a6742bcf53 | |||
653e170fb2 | |||
b449e83870 | |||
b575b2b486 | |||
015c372532 | |||
ec597541c8 | |||
ed82071223 | |||
18610f9a9a | |||
11a2ffa89d | |||
ae91f66fac | |||
145f3a8499 | |||
638f775742 | |||
38a0feffe0 | |||
1e003461bd | |||
2e872eb87f | |||
ef265b87de | |||
64172fc999 | |||
ceae80d87c | |||
0e81c9f23b | |||
39e3ecce64 | |||
51482e16e4 | |||
6c31560c7b | |||
72b41408ef | |||
7dd2aeb63b | |||
a17640d606 | |||
241dd1e175 | |||
d712c08dbc | |||
415075b010 | |||
2021b7d08c | |||
99a4f51166 | |||
653e45f192 | |||
f0ead6efed | |||
f27636dd14 | |||
d7164c7d90 | |||
5b861cd652 | |||
79d68c4aa3 | |||
4cb1dbe663 | |||
d21c010da1 | |||
60ad398c44 | |||
2695a79e8a | |||
1e9a538be9 | |||
c69923f104 | |||
d62f87fa71 | |||
501fbb5553 | |||
b2b26879cb | |||
83745f737a | |||
8cf1b0c3e4 | |||
9701b863fd | |||
1183583fdf | |||
1e5e4af35c | |||
ce36e7e09b | |||
68607d567c | |||
b5137f6665 | |||
3f73721ad5 | |||
0e6aa95754 | |||
306974a163 | |||
9883d85c2a | |||
a1c6c33d73 | |||
1322dae8da | |||
e7329a0202 | |||
b359601d2d | |||
8ce62ddca1 | |||
0b16fd1c08 | |||
41e1a31bb9 | |||
1410f2f8d8 | |||
f74651a0c3 | |||
5ecab67379 | |||
f3dbf47547 | |||
37bea48d45 | |||
89937f2107 | |||
2775eeb0fe | |||
715c3d3a9f | |||
84b26f347d | |||
3baa511fce | |||
00d7106a18 | |||
831ddd3055 | |||
a13a02c45c | |||
453b633268 | |||
a68a1e1da7 | |||
3563fb5994 | |||
7cede37e6d | |||
f229d58467 | |||
87986ff3cf | |||
85eb4d5b82 | |||
59ce079a52 | |||
582882286e | |||
fa75e0012c | |||
e9ba2243e7 | |||
3df786a5f5 | |||
50a09980c5 | |||
f73d8dab93 | |||
c00f0fefe7 | |||
2fc9276be2 | |||
c6819c8d4a | |||
d64fe28143 | |||
783894b60d | |||
854da5b984 | |||
8d178815d6 | |||
2d2e7bb5c6 | |||
ea55c9b12b | |||
3693d9f36b | |||
a4982c6cd6 | |||
7f08d5f324 | |||
2c2ee6c903 | |||
3297135a58 | |||
8846421cc4 | |||
fff6f1db20 | |||
ef2fa848f1 | |||
4cc6a0182c | |||
7113a3ae56 | |||
5df7058c84 | |||
9ce6c7ad6e | |||
0268f63f66 | |||
948a916c2f | |||
289359cedc | |||
627c89b545 | |||
e20b903bc0 | |||
489cc492d5 |
91 changed files with 1265 additions and 986 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -1,6 +1,3 @@
|
|||
[submodule "docker/static/goStatic"]
|
||||
path = app/build/static/goStatic
|
||||
url = https://github.com/PierreZ/goStatic
|
||||
[submodule "docker/blog/quentin.dufour.io"]
|
||||
path = docker/blog-quentin/quentin.dufour.io
|
||||
url = git@gitlab.com:superboum/quentin.dufour.io.git
|
||||
|
|
97
README.md
97
README.md
|
@ -1,31 +1,8 @@
|
|||
deuxfleurs.fr
|
||||
=============
|
||||
|
||||
*Many things are still missing here, including a proper documentation. Please stay nice, it is a volunter project. Feel free to open pull/merge requests to improve it. Thanks.*
|
||||
|
||||
## Our abstraction stack
|
||||
|
||||
We try to build a generic abstraction stack between our different resources (CPU, RAM, disk, etc.) and our services (Chat, Storage, etc.), we develop our own tools when needed:
|
||||
|
||||
* **[garage](https://git.deuxfleurs.fr/Deuxfleurs/garage/):** S3-compatible lightweight object store for self-hosted geo-distributed deployments (we also have a legacy glusterfs cluster)
|
||||
* **[diplonat](https://git.deuxfleurs.fr/Deuxfleurs/diplonat):** network automation (firewalling, upnp igd)
|
||||
* **[bottin](https://git.deuxfleurs.fr/Deuxfleurs/bottin):** authentication and authorization (LDAP protocol, consul backend)
|
||||
* **[guichet](https://git.deuxfleurs.fr/Deuxfleurs/guichet):** a dashboard for our users and administrators
|
||||
* **ansible:** physical node configuration
|
||||
* **nomad:** schedule containers and handle their lifecycle
|
||||
* **consul:** distributed key value store + lock + service discovery
|
||||
* **stolon + postgresql:** distributed relational database
|
||||
* **docker:** package, distribute and isolate applications
|
||||
|
||||
Some services we provide:
|
||||
|
||||
* **Websites:** garage (static) + fediverse blog (plume)
|
||||
* **Chat:** Synapse + Element Web (Matrix protocol)
|
||||
* **Email:** Postfix SMTP + Dovecot IMAP + opendkim DKIM + Sogo webmail (legacy) | Alps webmail (experimental)
|
||||
* **Storage:** Seafile (legacy) | Nextcloud (experimental)
|
||||
* **Visio:** Jitsi
|
||||
|
||||
As a generic abstraction is provided, deploying new services should be easy.
|
||||
**OBSOLETION NOTICE:** We are progressively migrating our stack to NixOS, to replace Ansible. Most of the files present in this repository are outdated or obsolete,
|
||||
the current code for our infrastructure is at: <https://git.deuxfleurs.fr/Deuxfleurs/nixcfg>.
|
||||
|
||||
## I am lost, how this repo works?
|
||||
|
||||
|
@ -42,73 +19,3 @@ To ease the development, we make the choice of a fully integrated environment
|
|||
3. `op_guide`: Guides to explain you operations you can do cluster wide (like configuring postgres)
|
||||
|
||||
|
||||
## Start hacking
|
||||
|
||||
### Deploying/Updating new services is done from your machine
|
||||
|
||||
*The following instructions are provided for ops that already have access to the servers (meaning: their SSH public key is known by the cluster).*
|
||||
|
||||
Deploy Nomad on your machine:
|
||||
|
||||
```bash
|
||||
export NOMAD_VER=1.0.1
|
||||
wget https://releases.hashicorp.com/nomad/${NOMAD_VER}/nomad_${NOMAD_VER}_linux_amd64.zip
|
||||
unzip nomad_${NOMAD_VER}_linux_amd64.zip
|
||||
sudo mv nomad /usr/local/bin
|
||||
rm nomad_${NOMAD_VER}_linux_amd64.zip
|
||||
```
|
||||
|
||||
Deploy Consul on your machine:
|
||||
|
||||
```bash
|
||||
export CONSUL_VER=1.9.0
|
||||
wget https://releases.hashicorp.com/consul/${CONSUL_VER}/consul_${CONSUL_VER}_linux_amd64.zip
|
||||
unzip consul_${CONSUL_VER}_linux_amd64.zip
|
||||
sudo mv consul /usr/local/bin
|
||||
rm consul_${CONSUL_VER}_linux_amd64.zip
|
||||
```
|
||||
|
||||
Create an alias (and put it in your `.bashrc`) to bind APIs on your machine:
|
||||
|
||||
```
|
||||
alias bind_df="ssh \
|
||||
-p110 \
|
||||
-N \
|
||||
-L 1389:bottin2.service.2.cluster.deuxfleurs.fr:389 \
|
||||
-L 4646:127.0.0.1:4646 \
|
||||
-L 5432:psql-proxy.service.2.cluster.deuxfleurs.fr:5432 \
|
||||
-L 8082:traefik-admin.service.2.cluster.deuxfleurs.fr:8082 \
|
||||
-L 8500:127.0.0.1:8500 \
|
||||
<a server from the cluster>"
|
||||
```
|
||||
|
||||
and run:
|
||||
|
||||
bind_df
|
||||
|
||||
Adrien uses `.ssh/config` configuration instead. I works basically the same. Here it goes:
|
||||
|
||||
```
|
||||
# in ~/.ssh/config
|
||||
|
||||
Host deuxfleurs
|
||||
User adrien
|
||||
Hostname deuxfleurs.fr
|
||||
# If you don't use the default ~/.ssh/id_rsa to connect to Deuxfleurs
|
||||
IdentityFile <some_key_path>
|
||||
PubKeyAuthentication yes
|
||||
ForwardAgent No
|
||||
LocalForward 1389 bottin2.service.2.cluster.deuxfleurs.fr:389
|
||||
LocalForward 4646 127.0.0.1:4646
|
||||
LocalForward 5432 psql-proxy.service.2.cluster.deuxfleurs.fr:5432
|
||||
LocalForward 8082 traefik-admin.service.2.cluster.deuxfleurs.fr:8082
|
||||
LocalForward 8500 127.0.0.1:8500
|
||||
```
|
||||
|
||||
Now, to connect, do the following:
|
||||
|
||||
ssh deuxfleurs -N
|
||||
|
||||
## Test cluster
|
||||
|
||||
Configured machines available for testing are listed in the **[`test_cluster` Ansible inventory](./os/config/test_cluster.inventory.yml)**.
|
|
@ -1,22 +0,0 @@
|
|||
FROM golang:buster as builder
|
||||
|
||||
WORKDIR /root
|
||||
RUN git clone https://filippo.io/age && cd age/cmd/age && go build -o age .
|
||||
|
||||
FROM amd64/debian:buster
|
||||
|
||||
COPY --from=builder /root/age/cmd/age/age /usr/local/bin/age
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get -qq -y full-upgrade && \
|
||||
apt-get install -y rsync wget openssh-client postgresql-client && \
|
||||
apt-get clean && \
|
||||
rm -f /var/lib/apt/lists/*_*
|
||||
|
||||
RUN mkdir -p /root/.ssh
|
||||
WORKDIR /root
|
||||
|
||||
COPY do_backup.sh /root/do_backup.sh
|
||||
|
||||
CMD "/root/do_backup.sh"
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -x -e
|
||||
|
||||
cd /root
|
||||
|
||||
chmod 0600 .ssh/id_ed25519
|
||||
|
||||
cat > .ssh/config <<EOF
|
||||
Host backuphost
|
||||
HostName $TARGET_SSH_HOST
|
||||
Port $TARGET_SSH_PORT
|
||||
User $TARGET_SSH_USER
|
||||
EOF
|
||||
|
||||
echo "export sql"
|
||||
export PGPASSWORD=$REPL_PSQL_PWD
|
||||
pg_basebackup \
|
||||
--pgdata=- \
|
||||
--format=tar \
|
||||
--max-rate=1M \
|
||||
--no-slot \
|
||||
--wal-method=none \
|
||||
--gzip \
|
||||
--compress=8 \
|
||||
--checkpoint=spread \
|
||||
--progress \
|
||||
--verbose \
|
||||
--status-interval=10 \
|
||||
--username=$REPL_PSQL_USER \
|
||||
--port=5432 \
|
||||
--host=psql-proxy.service.2.cluster.deuxfleurs.fr | \
|
||||
age -r "$(cat /root/.ssh/id_ed25519.pub)" | \
|
||||
ssh backuphost "cat > $TARGET_SSH_DIR/matrix/db-$(date --iso-8601=minute).gz.age"
|
||||
|
||||
MATRIX_MEDIA="/mnt/glusterfs/chat/matrix/synapse/media"
|
||||
echo "export local_content"
|
||||
tar -vzcf - ${MATRIX_MEDIA} | \
|
||||
age -r "$(cat /root/.ssh/id_ed25519.pub)" | \
|
||||
ssh backuphost "cat > $TARGET_SSH_DIR/matrix/media-$(date --iso-8601=minute).gz.age"
|
1
app/backup/build/backup-psql/.gitignore
vendored
Normal file
1
app/backup/build/backup-psql/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
result
|
8
app/backup/build/backup-psql/README.md
Normal file
8
app/backup/build/backup-psql/README.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
## Build
|
||||
|
||||
```bash
|
||||
docker load < $(nix-build docker.nix)
|
||||
docker push superboum/backup-psql:???
|
||||
```
|
||||
|
||||
|
106
app/backup/build/backup-psql/backup-psql.py
Executable file
106
app/backup/build/backup-psql/backup-psql.py
Executable file
|
@ -0,0 +1,106 @@
|
|||
#!/usr/bin/env python3
|
||||
import shutil,sys,os,datetime,minio,subprocess
|
||||
|
||||
working_directory = "."
|
||||
if 'CACHE_DIR' in os.environ: working_directory = os.environ['CACHE_DIR']
|
||||
required_space_in_bytes = 20 * 1024 * 1024 * 1024
|
||||
bucket = os.environ['AWS_BUCKET']
|
||||
key = os.environ['AWS_ACCESS_KEY_ID']
|
||||
secret = os.environ['AWS_SECRET_ACCESS_KEY']
|
||||
endpoint = os.environ['AWS_ENDPOINT']
|
||||
pubkey = os.environ['CRYPT_PUBLIC_KEY']
|
||||
psql_host = os.environ['PSQL_HOST']
|
||||
psql_user = os.environ['PSQL_USER']
|
||||
s3_prefix = str(datetime.datetime.now())
|
||||
files = [ "backup_manifest", "base.tar.gz", "pg_wal.tar.gz" ]
|
||||
clear_paths = [ os.path.join(working_directory, f) for f in files ]
|
||||
crypt_paths = [ os.path.join(working_directory, f) + ".age" for f in files ]
|
||||
s3_keys = [ s3_prefix + "/" + f for f in files ]
|
||||
|
||||
def abort(msg):
|
||||
for p in clear_paths + crypt_paths:
|
||||
if os.path.exists(p):
|
||||
print(f"Remove {p}")
|
||||
os.remove(p)
|
||||
|
||||
if msg: sys.exit(msg)
|
||||
else: print("success")
|
||||
|
||||
# Check we have enough space on disk
|
||||
if shutil.disk_usage(working_directory).free < required_space_in_bytes:
|
||||
abort(f"Not enough space on disk at path {working_directory} to perform a backup, aborting")
|
||||
|
||||
# Check postgres password is set
|
||||
if 'PGPASSWORD' not in os.environ:
|
||||
abort(f"You must pass postgres' password through the environment variable PGPASSWORD")
|
||||
|
||||
# Check our working directory is empty
|
||||
if len(os.listdir(working_directory)) != 0:
|
||||
abort(f"Working directory {working_directory} is not empty, aborting")
|
||||
|
||||
# Check Minio
|
||||
client = minio.Minio(endpoint, key, secret)
|
||||
if not client.bucket_exists(bucket):
|
||||
abort(f"Bucket {bucket} does not exist or its access is forbidden, aborting")
|
||||
|
||||
# Perform the backup locally
|
||||
try:
|
||||
ret = subprocess.run(["pg_basebackup",
|
||||
f"--host={psql_host}",
|
||||
f"--username={psql_user}",
|
||||
f"--pgdata={working_directory}",
|
||||
f"--format=tar",
|
||||
"--wal-method=stream",
|
||||
"--gzip",
|
||||
"--compress=6",
|
||||
"--progress",
|
||||
"--max-rate=5M",
|
||||
])
|
||||
if ret.returncode != 0:
|
||||
abort(f"pg_basebackup exited, expected return code 0, got {ret.returncode}. aborting")
|
||||
except Exception as e:
|
||||
abort(f"pg_basebackup raised exception {e}. aborting")
|
||||
|
||||
# Check that the expected files are here
|
||||
for p in clear_paths:
|
||||
print(f"Checking that {p} exists locally")
|
||||
if not os.path.exists(p):
|
||||
abort(f"File {p} expected but not found, aborting")
|
||||
|
||||
# Cipher them
|
||||
for c, e in zip(clear_paths, crypt_paths):
|
||||
print(f"Ciphering {c} to {e}")
|
||||
try:
|
||||
ret = subprocess.run(["age", "-r", pubkey, "-o", e, c])
|
||||
if ret.returncode != 0:
|
||||
abort(f"age exit code is {ret}, 0 expected. aborting")
|
||||
except Exception as e:
|
||||
abort(f"aged raised an exception. {e}. aborting")
|
||||
|
||||
# Upload the backup to S3
|
||||
for p, k in zip(crypt_paths, s3_keys):
|
||||
try:
|
||||
print(f"Uploading {p} to {k}")
|
||||
result = client.fput_object(bucket, k, p)
|
||||
print(
|
||||
"created {0} object; etag: {1}, version-id: {2}".format(
|
||||
result.object_name, result.etag, result.version_id,
|
||||
),
|
||||
)
|
||||
except Exception as e:
|
||||
abort(f"Exception {e} occured while upload {p}. aborting")
|
||||
|
||||
# Check that the files have been uploaded
|
||||
for k in s3_keys:
|
||||
try:
|
||||
print(f"Checking that {k} exists remotely")
|
||||
result = client.stat_object(bucket, k)
|
||||
print(
|
||||
"last-modified: {0}, size: {1}".format(
|
||||
result.last_modified, result.size,
|
||||
),
|
||||
)
|
||||
except Exception as e:
|
||||
abort(f"{k} not found on S3. {e}. aborting")
|
||||
|
||||
abort(None)
|
8
app/backup/build/backup-psql/common.nix
Normal file
8
app/backup/build/backup-psql/common.nix
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
pkgsSrc = fetchTarball {
|
||||
# Latest commit on https://github.com/NixOS/nixpkgs/tree/nixos-21.11
|
||||
# As of 2022-04-15
|
||||
url ="https://github.com/NixOS/nixpkgs/archive/2f06b87f64bc06229e05045853e0876666e1b023.tar.gz";
|
||||
sha256 = "sha256:1d7zg96xw4qsqh7c89pgha9wkq3rbi9as3k3d88jlxy2z0ns0cy2";
|
||||
};
|
||||
}
|
37
app/backup/build/backup-psql/default.nix
Normal file
37
app/backup/build/backup-psql/default.nix
Normal file
|
@ -0,0 +1,37 @@
|
|||
let
|
||||
common = import ./common.nix;
|
||||
pkgs = import common.pkgsSrc {};
|
||||
python-with-my-packages = pkgs.python3.withPackages (p: with p; [
|
||||
minio
|
||||
]);
|
||||
in
|
||||
pkgs.stdenv.mkDerivation {
|
||||
name = "backup-psql";
|
||||
src = pkgs.lib.sourceFilesBySuffices ./. [ ".py" ];
|
||||
|
||||
buildInputs = [
|
||||
python-with-my-packages
|
||||
pkgs.age
|
||||
pkgs.postgresql_14
|
||||
];
|
||||
|
||||
buildPhase = ''
|
||||
cat > backup-psql <<EOF
|
||||
#!${pkgs.bash}/bin/bash
|
||||
|
||||
export PYTHONPATH=${python-with-my-packages}/${python-with-my-packages.sitePackages}
|
||||
export PATH=${python-with-my-packages}/bin:${pkgs.age}/bin:${pkgs.postgresql_14}/bin
|
||||
|
||||
${python-with-my-packages}/bin/python3 $out/lib/backup-psql.py
|
||||
EOF
|
||||
|
||||
chmod +x backup-psql
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out/{bin,lib}
|
||||
cp *.py $out/lib/backup-psql.py
|
||||
cp backup-psql $out/bin/backup-psql
|
||||
'';
|
||||
}
|
||||
|
11
app/backup/build/backup-psql/docker.nix
Normal file
11
app/backup/build/backup-psql/docker.nix
Normal file
|
@ -0,0 +1,11 @@
|
|||
let
|
||||
common = import ./common.nix;
|
||||
app = import ./default.nix;
|
||||
pkgs = import common.pkgsSrc {};
|
||||
in
|
||||
pkgs.dockerTools.buildImage {
|
||||
name = "superboum/backup-psql-docker";
|
||||
config = {
|
||||
Cmd = [ "${app}/bin/backup-psql" ];
|
||||
};
|
||||
}
|
171
app/backup/deploy/backup-daily.hcl
Normal file
171
app/backup/deploy/backup-daily.hcl
Normal file
|
@ -0,0 +1,171 @@
|
|||
job "backup_daily" {
|
||||
datacenters = ["dc1"]
|
||||
type = "batch"
|
||||
|
||||
priority = "60"
|
||||
|
||||
periodic {
|
||||
cron = "@daily"
|
||||
// Do not allow overlapping runs.
|
||||
prohibit_overlap = true
|
||||
}
|
||||
|
||||
group "backup-dovecot" {
|
||||
constraint {
|
||||
attribute = "${attr.unique.hostname}"
|
||||
operator = "="
|
||||
value = "digitale"
|
||||
}
|
||||
|
||||
task "main" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "restic/restic:0.12.1"
|
||||
entrypoint = [ "/bin/sh", "-c" ]
|
||||
args = [ "restic backup /mail && restic forget --keep-within 1m1d --keep-within-weekly 3m --keep-within-monthly 1y && restic prune --max-unused 50% --max-repack-size 2G && restic check" ]
|
||||
volumes = [
|
||||
"/mnt/ssd/mail:/mail"
|
||||
]
|
||||
}
|
||||
|
||||
template {
|
||||
data = <<EOH
|
||||
AWS_ACCESS_KEY_ID={{ key "secrets/email/dovecot/backup_aws_access_key_id" }}
|
||||
AWS_SECRET_ACCESS_KEY={{ key "secrets/email/dovecot/backup_aws_secret_access_key" }}
|
||||
RESTIC_REPOSITORY={{ key "secrets/email/dovecot/backup_restic_repository" }}
|
||||
RESTIC_PASSWORD={{ key "secrets/email/dovecot/backup_restic_password" }}
|
||||
EOH
|
||||
|
||||
destination = "secrets/env_vars"
|
||||
env = true
|
||||
}
|
||||
|
||||
resources {
|
||||
cpu = 500
|
||||
memory = 200
|
||||
}
|
||||
|
||||
restart {
|
||||
attempts = 2
|
||||
interval = "30m"
|
||||
delay = "15s"
|
||||
mode = "fail"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
group "backup-plume" {
|
||||
constraint {
|
||||
attribute = "${attr.unique.hostname}"
|
||||
operator = "="
|
||||
value = "digitale"
|
||||
}
|
||||
|
||||
task "main" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "restic/restic:0.12.1"
|
||||
entrypoint = [ "/bin/sh", "-c" ]
|
||||
args = [ "restic backup /plume && restic forget --keep-within 1m1d --keep-within-weekly 3m --keep-within-monthly 1y && restic prune --max-unused 50% --max-repack-size 2G && restic check" ]
|
||||
volumes = [
|
||||
"/mnt/ssd/plume/media:/plume"
|
||||
]
|
||||
}
|
||||
|
||||
template {
|
||||
data = <<EOH
|
||||
AWS_ACCESS_KEY_ID={{ key "secrets/plume/backup_aws_access_key_id" }}
|
||||
AWS_SECRET_ACCESS_KEY={{ key "secrets/plume/backup_aws_secret_access_key" }}
|
||||
RESTIC_REPOSITORY={{ key "secrets/plume/backup_restic_repository" }}
|
||||
RESTIC_PASSWORD={{ key "secrets/plume/backup_restic_password" }}
|
||||
EOH
|
||||
|
||||
destination = "secrets/env_vars"
|
||||
env = true
|
||||
}
|
||||
|
||||
resources {
|
||||
cpu = 500
|
||||
memory = 200
|
||||
}
|
||||
|
||||
restart {
|
||||
attempts = 2
|
||||
interval = "30m"
|
||||
delay = "15s"
|
||||
mode = "fail"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
group "backup-consul" {
|
||||
task "consul-kv-export" {
|
||||
driver = "docker"
|
||||
|
||||
lifecycle {
|
||||
hook = "prestart"
|
||||
sidecar = false
|
||||
}
|
||||
|
||||
config {
|
||||
image = "consul:1.11.2"
|
||||
network_mode = "host"
|
||||
entrypoint = [ "/bin/sh", "-c" ]
|
||||
args = [ "/bin/consul kv export > $NOMAD_ALLOC_DIR/consul.json" ]
|
||||
}
|
||||
|
||||
env {
|
||||
CONSUL_HTTP_ADDR = "http://consul.service.2.cluster.deuxfleurs.fr:8500"
|
||||
}
|
||||
|
||||
resources {
|
||||
cpu = 200
|
||||
memory = 200
|
||||
}
|
||||
|
||||
restart {
|
||||
attempts = 2
|
||||
interval = "30m"
|
||||
delay = "15s"
|
||||
mode = "fail"
|
||||
}
|
||||
}
|
||||
|
||||
task "restic-backup" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "restic/restic:0.12.1"
|
||||
entrypoint = [ "/bin/sh", "-c" ]
|
||||
args = [ "restic backup $NOMAD_ALLOC_DIR/consul.json && restic forget --keep-within 1m1d --keep-within-weekly 3m --keep-within-monthly 1y && restic prune --max-unused 50% --max-repack-size 2G && restic check" ]
|
||||
}
|
||||
|
||||
|
||||
template {
|
||||
data = <<EOH
|
||||
AWS_ACCESS_KEY_ID={{ key "secrets/backup/consul/backup_aws_access_key_id" }}
|
||||
AWS_SECRET_ACCESS_KEY={{ key "secrets/backup/consul/backup_aws_secret_access_key" }}
|
||||
RESTIC_REPOSITORY={{ key "secrets/backup/consul/backup_restic_repository" }}
|
||||
RESTIC_PASSWORD={{ key "secrets/backup/consul/backup_restic_password" }}
|
||||
EOH
|
||||
|
||||
destination = "secrets/env_vars"
|
||||
env = true
|
||||
}
|
||||
|
||||
resources {
|
||||
cpu = 200
|
||||
memory = 200
|
||||
}
|
||||
|
||||
restart {
|
||||
attempts = 2
|
||||
interval = "30m"
|
||||
delay = "15s"
|
||||
mode = "fail"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
job "backup_manual_matrix" {
|
||||
datacenters = ["dc1"]
|
||||
|
||||
type = "batch"
|
||||
|
||||
task "backup-matrix" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "superboum/backup_matrix:4"
|
||||
volumes = [
|
||||
"secrets/id_ed25519:/root/.ssh/id_ed25519",
|
||||
"secrets/id_ed25519.pub:/root/.ssh/id_ed25519.pub",
|
||||
"secrets/known_hosts:/root/.ssh/known_hosts",
|
||||
"/mnt/glusterfs/chat/matrix/synapse/media:/mnt/glusterfs/chat/matrix/synapse/media"
|
||||
]
|
||||
network_mode = "host"
|
||||
}
|
||||
|
||||
env {
|
||||
CONSUL_HTTP_ADDR = "http://consul.service.2.cluster.deuxfleurs.fr:8500"
|
||||
}
|
||||
|
||||
template {
|
||||
data = <<EOH
|
||||
TARGET_SSH_USER={{ key "secrets/backup/target_ssh_user" }}
|
||||
TARGET_SSH_PORT={{ key "secrets/backup/target_ssh_port" }}
|
||||
TARGET_SSH_HOST={{ key "secrets/backup/target_ssh_host" }}
|
||||
TARGET_SSH_DIR={{ key "secrets/backup/target_ssh_dir" }}
|
||||
REPL_PSQL_USER={{ key "secrets/postgres/keeper/pg_repl_username" }}
|
||||
REPL_PSQL_PWD={{ key "secrets/postgres/keeper/pg_repl_pwd" }}
|
||||
EOH
|
||||
|
||||
destination = "secrets/env_vars"
|
||||
env = true
|
||||
}
|
||||
|
||||
template {
|
||||
data = "{{ key \"secrets/backup/id_ed25519\" }}"
|
||||
destination = "secrets/id_ed25519"
|
||||
}
|
||||
template {
|
||||
data = "{{ key \"secrets/backup/id_ed25519.pub\" }}"
|
||||
destination = "secrets/id_ed25519.pub"
|
||||
}
|
||||
template {
|
||||
data = "{{ key \"secrets/backup/target_ssh_fingerprint\" }}"
|
||||
destination = "secrets/known_hosts"
|
||||
}
|
||||
|
||||
resources {
|
||||
memory = 200
|
||||
}
|
||||
|
||||
restart {
|
||||
attempts = 2
|
||||
interval = "30m"
|
||||
delay = "15s"
|
||||
mode = "fail"
|
||||
}
|
||||
}
|
||||
}
|
55
app/backup/deploy/backup-weekly.hcl
Normal file
55
app/backup/deploy/backup-weekly.hcl
Normal file
|
@ -0,0 +1,55 @@
|
|||
job "backup_weekly" {
|
||||
datacenters = ["dc1"]
|
||||
type = "batch"
|
||||
|
||||
priority = "60"
|
||||
|
||||
periodic {
|
||||
cron = "@weekly"
|
||||
// Do not allow overlapping runs.
|
||||
prohibit_overlap = true
|
||||
}
|
||||
|
||||
group "backup-psql" {
|
||||
task "main" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "superboum/backup-psql-docker:gyr3aqgmhs0hxj0j9hkrdmm1m07i8za2"
|
||||
volumes = [
|
||||
// Mount a cache on the hard disk to avoid filling the SSD
|
||||
"/mnt/storage/tmp_bckp_psql:/mnt/cache"
|
||||
]
|
||||
}
|
||||
|
||||
template {
|
||||
data = <<EOH
|
||||
CACHE_DIR=/mnt/cache
|
||||
AWS_BUCKET=backups-pgbasebackup
|
||||
AWS_ENDPOINT=s3.deuxfleurs.shirokumo.net
|
||||
AWS_ACCESS_KEY_ID={{ key "secrets/backup/psql/aws_access_key_id" }}
|
||||
AWS_SECRET_ACCESS_KEY={{ key "secrets/backup/psql/aws_secret_access_key" }}
|
||||
CRYPT_PUBLIC_KEY={{ key "secrets/backup/psql/crypt_public_key" }}
|
||||
PSQL_HOST=psql-proxy.service.2.cluster.deuxfleurs.fr
|
||||
PSQL_USER={{ key "secrets/postgres/keeper/pg_repl_username" }}
|
||||
PGPASSWORD={{ key "secrets/postgres/keeper/pg_repl_pwd" }}
|
||||
EOH
|
||||
|
||||
destination = "secrets/env_vars"
|
||||
env = true
|
||||
}
|
||||
|
||||
resources {
|
||||
cpu = 200
|
||||
memory = 200
|
||||
}
|
||||
|
||||
restart {
|
||||
attempts = 2
|
||||
interval = "30m"
|
||||
delay = "15s"
|
||||
mode = "fail"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
USER Backup AWS access key ID
|
|
@ -0,0 +1 @@
|
|||
USER Backup AWS secret access key
|
1
app/backup/secrets/backup/consul/backup_restic_password
Normal file
1
app/backup/secrets/backup/consul/backup_restic_password
Normal file
|
@ -0,0 +1 @@
|
|||
USER Restic password to encrypt backups
|
|
@ -0,0 +1 @@
|
|||
USER Restic repository, eg. s3:https://s3.garage.tld
|
1
app/backup/secrets/backup/psql/aws_access_key_id
Normal file
1
app/backup/secrets/backup/psql/aws_access_key_id
Normal file
|
@ -0,0 +1 @@
|
|||
USER Minio access key
|
1
app/backup/secrets/backup/psql/aws_secret_access_key
Normal file
1
app/backup/secrets/backup/psql/aws_secret_access_key
Normal file
|
@ -0,0 +1 @@
|
|||
USER Minio secret key
|
1
app/backup/secrets/backup/psql/crypt_private_key
Normal file
1
app/backup/secrets/backup/psql/crypt_private_key
Normal file
|
@ -0,0 +1 @@
|
|||
USER a private key to decript backups from age
|
1
app/backup/secrets/backup/psql/crypt_public_key
Normal file
1
app/backup/secrets/backup/psql/crypt_public_key
Normal file
|
@ -0,0 +1 @@
|
|||
USER A public key to encypt backups with age
|
|
@ -13,14 +13,21 @@ job "bagage" {
|
|||
|
||||
network {
|
||||
port "web_port" { to = 8080 }
|
||||
port "ssh_port" {
|
||||
static = 2222
|
||||
to = 2222
|
||||
}
|
||||
}
|
||||
|
||||
task "server" {
|
||||
driver = "docker"
|
||||
config {
|
||||
image = "superboum/amd64_bagage:v8"
|
||||
readonly_rootfs = true
|
||||
ports = [ "web_port" ]
|
||||
image = "superboum/amd64_bagage:v11"
|
||||
readonly_rootfs = false
|
||||
volumes = [
|
||||
"secrets/id_rsa:/id_rsa"
|
||||
]
|
||||
ports = [ "web_port", "ssh_port" ]
|
||||
}
|
||||
|
||||
env {
|
||||
|
@ -31,19 +38,36 @@ job "bagage" {
|
|||
memory = 500
|
||||
}
|
||||
|
||||
template {
|
||||
data = "{{ key \"secrets/bagage/id_rsa\" }}"
|
||||
destination = "secrets/id_rsa"
|
||||
}
|
||||
|
||||
service {
|
||||
name = "bagage"
|
||||
name = "bagage-ssh"
|
||||
port = "ssh_port"
|
||||
address_mode = "host"
|
||||
tags = [
|
||||
"bagage",
|
||||
"(diplonat (tcp_port 2222))"
|
||||
]
|
||||
}
|
||||
|
||||
service {
|
||||
name = "bagage-webdav"
|
||||
tags = [
|
||||
"bagage",
|
||||
"traefik.enable=true",
|
||||
"traefik.frontend.entryPoints=https,http",
|
||||
"traefik.frontend.rule=Host:bagage.deuxfleurs.fr",
|
||||
"tricot bagage.deuxfleurs.fr",
|
||||
]
|
||||
port = "web_port"
|
||||
address_mode = "host"
|
||||
check {
|
||||
type = "tcp"
|
||||
port = "web_port"
|
||||
address_mode = "host"
|
||||
interval = "60s"
|
||||
timeout = "5s"
|
||||
check_restart {
|
||||
|
|
1
app/bagage/secrets/bagage/id_rsa
Normal file
1
app/bagage/secrets/bagage/id_rsa
Normal file
|
@ -0,0 +1 @@
|
|||
CMD ssh-keygen -q -f >(cat) -N "" <<< y 2>/dev/null 1>&2 ; true
|
|
@ -1,5 +1,5 @@
|
|||
job "core" {
|
||||
datacenters = ["dc1"]
|
||||
datacenters = ["dc1", "neptune"]
|
||||
type = "system"
|
||||
priority = 90
|
||||
|
||||
|
@ -18,7 +18,7 @@ job "core" {
|
|||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "darkgallium/amd64_diplonat:v3"
|
||||
image = "lxpz/amd64_diplonat:3"
|
||||
network_mode = "host"
|
||||
readonly_rootfs = true
|
||||
privileged = true
|
||||
|
@ -33,7 +33,6 @@ job "core" {
|
|||
|
||||
template {
|
||||
data = <<EOH
|
||||
DIPLONAT_PRIVATE_IP={{ env "attr.unique.network.ip-address" }}
|
||||
DIPLONAT_REFRESH_TIME=60
|
||||
DIPLONAT_EXPIRATION_TIME=300
|
||||
DIPLONAT_CONSUL_NODE_NAME={{ env "attr.unique.hostname" }}
|
||||
|
|
2
app/cryptpad/build/README.md
Normal file
2
app/cryptpad/build/README.md
Normal file
|
@ -0,0 +1,2 @@
|
|||
docker load < $(nix-build docker.nix)
|
||||
docker push superboum/cryptpad:???
|
8
app/cryptpad/build/common.nix
Normal file
8
app/cryptpad/build/common.nix
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
pkgsSrc = fetchTarball {
|
||||
# Latest commit on https://github.com/NixOS/nixpkgs/tree/nixos-21.11
|
||||
# As of 2022-04-15
|
||||
url ="https://github.com/NixOS/nixpkgs/archive/2f06b87f64bc06229e05045853e0876666e1b023.tar.gz";
|
||||
sha256 = "sha256:1d7zg96xw4qsqh7c89pgha9wkq3rbi9as3k3d88jlxy2z0ns0cy2";
|
||||
};
|
||||
}
|
10
app/cryptpad/build/docker.nix
Normal file
10
app/cryptpad/build/docker.nix
Normal file
|
@ -0,0 +1,10 @@
|
|||
let
|
||||
common = import ./common.nix;
|
||||
pkgs = import common.pkgsSrc {};
|
||||
in
|
||||
pkgs.dockerTools.buildImage {
|
||||
name = "superboum/cryptpad";
|
||||
config = {
|
||||
Cmd = [ "${pkgs.cryptpad}/bin/cryptpad" ];
|
||||
};
|
||||
}
|
283
app/cryptpad/config/config.js
Normal file
283
app/cryptpad/config/config.js
Normal file
|
@ -0,0 +1,283 @@
|
|||
/* globals module */
|
||||
|
||||
/* DISCLAIMER:
|
||||
|
||||
There are two recommended methods of running a CryptPad instance:
|
||||
|
||||
1. Using a standalone nodejs server without HTTPS (suitable for local development)
|
||||
2. Using NGINX to serve static assets and to handle HTTPS for API server's websocket traffic
|
||||
|
||||
We do not officially recommend or support Apache, Docker, Kubernetes, Traefik, or any other configuration.
|
||||
Support requests for such setups should be directed to their authors.
|
||||
|
||||
If you're having difficulty difficulty configuring your instance
|
||||
we suggest that you join the project's IRC/Matrix channel.
|
||||
|
||||
If you don't have any difficulty configuring your instance and you'd like to
|
||||
support us for the work that went into making it pain-free we are quite happy
|
||||
to accept donations via our opencollective page: https://opencollective.com/cryptpad
|
||||
|
||||
*/
|
||||
module.exports = {
|
||||
/* CryptPad is designed to serve its content over two domains.
|
||||
* Account passwords and cryptographic content is handled on the 'main' domain,
|
||||
* while the user interface is loaded on a 'sandbox' domain
|
||||
* which can only access information which the main domain willingly shares.
|
||||
*
|
||||
* In the event of an XSS vulnerability in the UI (that's bad)
|
||||
* this system prevents attackers from gaining access to your account (that's good).
|
||||
*
|
||||
* Most problems with new instances are related to this system blocking access
|
||||
* because of incorrectly configured sandboxes. If you only see a white screen
|
||||
* when you try to load CryptPad, this is probably the cause.
|
||||
*
|
||||
* PLEASE READ THE FOLLOWING COMMENTS CAREFULLY.
|
||||
*
|
||||
*/
|
||||
|
||||
/* httpUnsafeOrigin is the URL that clients will enter to load your instance.
|
||||
* Any other URL that somehow points to your instance is supposed to be blocked.
|
||||
* The default provided below assumes you are loading CryptPad from a server
|
||||
* which is running on the same machine, using port 3000.
|
||||
*
|
||||
* In a production instance this should be available ONLY over HTTPS
|
||||
* using the default port for HTTPS (443) ie. https://cryptpad.fr
|
||||
* In such a case this should be also handled by NGINX, as documented in
|
||||
* cryptpad/docs/example.nginx.conf (see the $main_domain variable)
|
||||
*
|
||||
*/
|
||||
httpUnsafeOrigin: 'http://localhost:3000',
|
||||
|
||||
/* httpSafeOrigin is the URL that is used for the 'sandbox' described above.
|
||||
* If you're testing or developing with CryptPad on your local machine then
|
||||
* it is appropriate to leave this blank. The default behaviour is to serve
|
||||
* the main domain over port 3000 and to serve the sandbox content over port 3001.
|
||||
*
|
||||
* This is not appropriate in a production environment where invasive networks
|
||||
* may filter traffic going over abnormal ports.
|
||||
* To correctly configure your production instance you must provide a URL
|
||||
* with a different domain (a subdomain is sufficient).
|
||||
* It will be used to load the UI in our 'sandbox' system.
|
||||
*
|
||||
* This value corresponds to the $sandbox_domain variable
|
||||
* in the example nginx file.
|
||||
*
|
||||
* Note that in order for the sandboxing system to be effective
|
||||
* httpSafeOrigin must be different from httpUnsafeOrigin.
|
||||
*
|
||||
* CUSTOMIZE AND UNCOMMENT THIS FOR PRODUCTION INSTALLATIONS.
|
||||
*/
|
||||
// httpSafeOrigin: "https://some-other-domain.xyz",
|
||||
|
||||
/* httpAddress specifies the address on which the nodejs server
|
||||
* should be accessible. By default it will listen on 127.0.0.1
|
||||
* (IPv4 localhost on most systems). If you want it to listen on
|
||||
* all addresses, including IPv6, set this to '::'.
|
||||
*
|
||||
*/
|
||||
httpAddress: '::',
|
||||
|
||||
/* httpPort specifies on which port the nodejs server should listen.
|
||||
* By default it will serve content over port 3000, which is suitable
|
||||
* for both local development and for use with the provided nginx example,
|
||||
* which will proxy websocket traffic to your node server.
|
||||
*
|
||||
*/
|
||||
//httpPort: 3000,
|
||||
|
||||
/* httpSafePort allows you to specify an alternative port from which
|
||||
* the node process should serve sandboxed assets. The default value is
|
||||
* that of your httpPort + 1. You probably don't need to change this.
|
||||
*
|
||||
*/
|
||||
//httpSafePort: 3001,
|
||||
|
||||
/* CryptPad will launch a child process for every core available
|
||||
* in order to perform CPU-intensive tasks in parallel.
|
||||
* Some host environments may have a very large number of cores available
|
||||
* or you may want to limit how much computing power CryptPad can take.
|
||||
* If so, set 'maxWorkers' to a positive integer.
|
||||
*/
|
||||
// maxWorkers: 4,
|
||||
|
||||
/* =====================
|
||||
* Admin
|
||||
* ===================== */
|
||||
|
||||
/*
|
||||
* CryptPad contains an administration panel. Its access is restricted to specific
|
||||
* users using the following list.
|
||||
* To give access to the admin panel to a user account, just add their public signing
|
||||
* key, which can be found on the settings page for registered users.
|
||||
* Entries should be strings separated by a comma.
|
||||
*/
|
||||
/*
|
||||
adminKeys: [
|
||||
//"[cryptpad-user1@my.awesome.website/YZgXQxKR0Rcb6r6CmxHPdAGLVludrAF2lEnkbx1vVOo=]",
|
||||
],
|
||||
*/
|
||||
|
||||
/* =====================
|
||||
* STORAGE
|
||||
* ===================== */
|
||||
|
||||
/* Pads that are not 'pinned' by any registered user can be set to expire
|
||||
* after a configurable number of days of inactivity (default 90 days).
|
||||
* The value can be changed or set to false to remove expiration.
|
||||
* Expired pads can then be removed using a cron job calling the
|
||||
* `evict-inactive.js` script with node
|
||||
*
|
||||
* defaults to 90 days if nothing is provided
|
||||
*/
|
||||
//inactiveTime: 90, // days
|
||||
|
||||
/* CryptPad archives some data instead of deleting it outright.
|
||||
* This archived data still takes up space and so you'll probably still want to
|
||||
* remove these files after a brief period.
|
||||
*
|
||||
* cryptpad/scripts/evict-inactive.js is intended to be run daily
|
||||
* from a crontab or similar scheduling service.
|
||||
*
|
||||
* The intent with this feature is to provide a safety net in case of accidental
|
||||
* deletion. Set this value to the number of days you'd like to retain
|
||||
* archived data before it's removed permanently.
|
||||
*
|
||||
* defaults to 15 days if nothing is provided
|
||||
*/
|
||||
//archiveRetentionTime: 15,
|
||||
|
||||
/* It's possible to configure your instance to remove data
|
||||
* stored on behalf of inactive accounts. Set 'accountRetentionTime'
|
||||
* to the number of days an account can remain idle before its
|
||||
* documents and other account data is removed.
|
||||
*
|
||||
* Leave this value commented out to preserve all data stored
|
||||
* by user accounts regardless of inactivity.
|
||||
*/
|
||||
//accountRetentionTime: 365,
|
||||
|
||||
/* Starting with CryptPad 3.23.0, the server automatically runs
|
||||
* the script responsible for removing inactive data according to
|
||||
* your configured definition of inactivity. Set this value to `true`
|
||||
* if you prefer not to remove inactive data, or if you prefer to
|
||||
* do so manually using `scripts/evict-inactive.js`.
|
||||
*/
|
||||
//disableIntegratedEviction: true,
|
||||
|
||||
|
||||
/* Max Upload Size (bytes)
|
||||
* this sets the maximum size of any one file uploaded to the server.
|
||||
* anything larger than this size will be rejected
|
||||
* defaults to 20MB if no value is provided
|
||||
*/
|
||||
//maxUploadSize: 20 * 1024 * 1024,
|
||||
|
||||
/* Users with premium accounts (those with a plan included in their customLimit)
|
||||
* can benefit from an increased upload size limit. By default they are restricted to the same
|
||||
* upload size as any other registered user.
|
||||
*
|
||||
*/
|
||||
//premiumUploadSize: 100 * 1024 * 1024,
|
||||
|
||||
/* =====================
|
||||
* DATABASE VOLUMES
|
||||
* ===================== */
|
||||
|
||||
/*
|
||||
* CryptPad stores each document in an individual file on your hard drive.
|
||||
* Specify a directory where files should be stored.
|
||||
* It will be created automatically if it does not already exist.
|
||||
*/
|
||||
filePath: './root/tmp/mut/datastore/',
|
||||
|
||||
/* CryptPad offers the ability to archive data for a configurable period
|
||||
* before deleting it, allowing a means of recovering data in the event
|
||||
* that it was deleted accidentally.
|
||||
*
|
||||
* To set the location of this archive directory to a custom value, change
|
||||
* the path below:
|
||||
*/
|
||||
archivePath: './root/tmp/mut/data/archive',
|
||||
|
||||
/* CryptPad allows logged in users to request that particular documents be
|
||||
* stored by the server indefinitely. This is called 'pinning'.
|
||||
* Pin requests are stored in a pin-store. The location of this store is
|
||||
* defined here.
|
||||
*/
|
||||
pinPath: './root/tmp/mut/data/pins',
|
||||
|
||||
/* if you would like the list of scheduled tasks to be stored in
|
||||
a custom location, change the path below:
|
||||
*/
|
||||
taskPath: './root/tmp/mut/data/tasks',
|
||||
|
||||
/* if you would like users' authenticated blocks to be stored in
|
||||
a custom location, change the path below:
|
||||
*/
|
||||
blockPath: './root/tmp/mut/block',
|
||||
|
||||
/* CryptPad allows logged in users to upload encrypted files. Files/blobs
|
||||
* are stored in a 'blob-store'. Set its location here.
|
||||
*/
|
||||
blobPath: './root/tmp/mut/blob',
|
||||
|
||||
/* CryptPad stores incomplete blobs in a 'staging' area until they are
|
||||
* fully uploaded. Set its location here.
|
||||
*/
|
||||
blobStagingPath: './root/tmp/mut/data/blobstage',
|
||||
|
||||
decreePath: './root/tmp/mut/data/decrees',
|
||||
|
||||
/* CryptPad supports logging events directly to the disk in a 'logs' directory
|
||||
* Set its location here, or set it to false (or nothing) if you'd rather not log
|
||||
*/
|
||||
logPath: './root/tmp/mut/data/logs',
|
||||
|
||||
/* =====================
|
||||
* Debugging
|
||||
* ===================== */
|
||||
|
||||
/* CryptPad can log activity to stdout
|
||||
* This may be useful for debugging
|
||||
*/
|
||||
logToStdout: true,
|
||||
|
||||
/* CryptPad can be configured to log more or less
|
||||
* the various settings are listed below by order of importance
|
||||
*
|
||||
* silly, verbose, debug, feedback, info, warn, error
|
||||
*
|
||||
* Choose the least important level of logging you wish to see.
|
||||
* For example, a 'silly' logLevel will display everything,
|
||||
* while 'info' will display 'info', 'warn', and 'error' logs
|
||||
*
|
||||
* This will affect both logging to the console and the disk.
|
||||
*/
|
||||
logLevel: 'debug',
|
||||
|
||||
/* clients can use the /settings/ app to opt out of usage feedback
|
||||
* which informs the server of things like how much each app is being
|
||||
* used, and whether certain clientside features are supported by
|
||||
* the client's browser. The intent is to provide feedback to the admin
|
||||
* such that the service can be improved. Enable this with `true`
|
||||
* and ignore feedback with `false` or by commenting the attribute
|
||||
*
|
||||
* You will need to set your logLevel to include 'feedback'. Set this
|
||||
* to false if you'd like to exclude feedback from your logs.
|
||||
*/
|
||||
logFeedback: false,
|
||||
|
||||
/* CryptPad supports verbose logging
|
||||
* (false by default)
|
||||
*/
|
||||
verbose: true,
|
||||
|
||||
/* Surplus information:
|
||||
*
|
||||
* 'installMethod' is included in server telemetry to voluntarily
|
||||
* indicate how many instances are using unofficial installation methods
|
||||
* such as Docker.
|
||||
*
|
||||
*/
|
||||
installMethod: 'unspecified',
|
||||
};
|
|
@ -4,7 +4,7 @@
|
|||
"consul_host": "http://consul.service.2.cluster.deuxfleurs.fr:8500",
|
||||
"log_level": "debug",
|
||||
"acl": [
|
||||
"*,dc=deuxfleurs,dc=fr::read:*:* !userpassword",
|
||||
"*,dc=deuxfleurs,dc=fr::read:*:* !userpassword !user_secret !alternate_user_secrets !garage_s3_secret_key",
|
||||
"*::read modify:SELF:*",
|
||||
"ANONYMOUS::bind:*,ou=users,dc=deuxfleurs,dc=fr:",
|
||||
"ANONYMOUS::bind:cn=admin,dc=deuxfleurs,dc=fr:",
|
||||
|
@ -20,10 +20,6 @@
|
|||
|
||||
"*:cn=asso_deuxfleurs,ou=groups,dc=deuxfleurs,dc=fr:modifyAdd:cn=email,ou=groups,dc=deuxfleurs,dc=fr:*",
|
||||
"*,ou=invitations,dc=deuxfleurs,dc=fr::modifyAdd:cn=email,ou=groups,dc=deuxfleurs,dc=fr:*",
|
||||
"*:cn=asso_deuxfleurs,ou=groups,dc=deuxfleurs,dc=fr:modifyAdd:cn=seafile,ou=groups,dc=deuxfleurs,dc=fr:*",
|
||||
"*,ou=invitations,dc=deuxfleurs,dc=fr::modifyAdd:cn=seafile,ou=groups,dc=deuxfleurs,dc=fr:*",
|
||||
"*:cn=asso_deuxfleurs,ou=groups,dc=deuxfleurs,dc=fr:modifyAdd:cn=nextcloud,ou=groups,dc=deuxfleurs,dc=fr:*",
|
||||
"*,ou=invitations,dc=deuxfleurs,dc=fr::modifyAdd:cn=seafile,ou=nextcloud,dc=deuxfleurs,dc=fr:*",
|
||||
|
||||
"cn=admin,dc=deuxfleurs,dc=fr::read add modify delete:*:*",
|
||||
"*:cn=admin,ou=groups,dc=deuxfleurs,dc=fr:read add modify delete:*:*"
|
||||
|
|
|
@ -59,6 +59,7 @@ job "directory" {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
group "guichet" {
|
||||
count = 1
|
||||
|
||||
|
@ -69,7 +70,7 @@ job "directory" {
|
|||
task "guichet" {
|
||||
driver = "docker"
|
||||
config {
|
||||
image = "superboum/guichet_amd64:15"
|
||||
image = "dxflrs/guichet:6y7pv4kgfsn02iijj55kf5af0rbksgrn"
|
||||
readonly_rootfs = true
|
||||
ports = [ "web_port" ]
|
||||
volumes = [
|
||||
|
@ -93,6 +94,7 @@ job "directory" {
|
|||
"traefik.enable=true",
|
||||
"traefik.frontend.entryPoints=https,http",
|
||||
"traefik.frontend.rule=Host:guichet.deuxfleurs.fr",
|
||||
"tricot guichet.deuxfleurs.fr",
|
||||
]
|
||||
port = "web_port"
|
||||
address_mode = "host"
|
||||
|
@ -110,5 +112,6 @@ job "directory" {
|
|||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
|
|
@ -7,18 +7,21 @@ services:
|
|||
context: ./im/build/riotweb
|
||||
args:
|
||||
# https://github.com/vector-im/riot-web/releases
|
||||
VERSION: 1.9.0
|
||||
image: superboum/amd64_riotweb:v25
|
||||
VERSION: 1.10.15
|
||||
image: superboum/amd64_riotweb:v30
|
||||
|
||||
synapse:
|
||||
build:
|
||||
context: ./im/build/matrix-synapse
|
||||
args:
|
||||
# https://github.com/matrix-org/synapse/releases
|
||||
VERSION: 1.42.0
|
||||
VERSION: 1.61.1
|
||||
# https://github.com/matrix-org/synapse-s3-storage-provider/commits/main
|
||||
S3_VERSION: 3c3fafd6a2624f05fd396d9e003501bf8bef7b2e
|
||||
image: superboum/amd64_synapse:v47
|
||||
# Update with the latest commit on main each time you update the synapse version
|
||||
# otherwise synapse may fail to launch due to incompatibility issues
|
||||
# see this issue for an example: https://github.com/matrix-org/synapse-s3-storage-provider/issues/64
|
||||
S3_VERSION: ffd3fa477321608e57d27644197e721965e0e858
|
||||
image: superboum/amd64_synapse:v53
|
||||
|
||||
# Email
|
||||
sogo:
|
||||
|
@ -47,16 +50,16 @@ services:
|
|||
context: ./jitsi/build/jitsi-meet
|
||||
args:
|
||||
# https://github.com/jitsi/jitsi-meet
|
||||
MEET_TAG: jitsi-meet_5463
|
||||
image: superboum/amd64_jitsi_meet:v4
|
||||
MEET_TAG: stable/jitsi-meet_6826
|
||||
image: superboum/amd64_jitsi_meet:v5
|
||||
|
||||
jitsi-conference-focus:
|
||||
build:
|
||||
context: ./jitsi/build/jitsi-conference-focus
|
||||
args:
|
||||
# https://github.com/jitsi/jicofo
|
||||
JICOFO_TAG: jitsi-meet_5463
|
||||
image: superboum/amd64_jitsi_conference_focus:v7
|
||||
JICOFO_TAG: stable/jitsi-meet_6826
|
||||
image: superboum/amd64_jitsi_conference_focus:v9
|
||||
|
||||
jitsi-videobridge:
|
||||
build:
|
||||
|
@ -64,23 +67,23 @@ services:
|
|||
args:
|
||||
# https://github.com/jitsi/jitsi-videobridge
|
||||
# note: JVB is not tagged with non-stable tags
|
||||
JVB_TAG: stable/jitsi-meet_5390
|
||||
image: superboum/amd64_jitsi_videobridge:v17
|
||||
JVB_TAG: stable/jitsi-meet_6826
|
||||
image: superboum/amd64_jitsi_videobridge:v20
|
||||
|
||||
jitsi-xmpp:
|
||||
build:
|
||||
context: ./jitsi/build/jitsi-xmpp
|
||||
args:
|
||||
MEET_TAG: jitsi-meet_5463
|
||||
PROSODY_VERSION: 0.11.7-1~buster4
|
||||
image: superboum/amd64_jitsi_xmpp:v9
|
||||
MEET_TAG: stable/jitsi-meet_6826
|
||||
PROSODY_VERSION: 0.11.12-1
|
||||
image: superboum/amd64_jitsi_xmpp:v10
|
||||
|
||||
plume:
|
||||
build:
|
||||
context: ./plume/build/plume
|
||||
args:
|
||||
VERSION: 8c372aa6fcd05083601903d83b0fcb4915585a95
|
||||
image: superboum/plume:v4
|
||||
VERSION: 8709f6cf9f8ff7e3c5ee7ea699ee7c778e92fefc
|
||||
image: superboum/plume:v8
|
||||
|
||||
postfix:
|
||||
build:
|
||||
|
@ -94,16 +97,12 @@ services:
|
|||
build:
|
||||
args:
|
||||
# https://github.com/sorintlab/stolon/releases
|
||||
STOLON_VERSION: 057389f7e484ee1d5c1e1a7020256020e7413c87
|
||||
STOLON_VERSION: 3bb7499f815f77140551eb762b200cf4557f57d3
|
||||
context: ./postgres/build/postgres
|
||||
image: superboum/amd64_postgres:v9
|
||||
image: superboum/amd64_postgres:v11
|
||||
|
||||
backup-consul:
|
||||
build:
|
||||
context: ./backup/build/backup-consul
|
||||
image: lxpz/backup_consul:12
|
||||
|
||||
backup-matrix:
|
||||
build:
|
||||
context: ./backup/build/backup-matrix
|
||||
image: superboum/backup_matrix:4
|
||||
|
|
|
@ -14,7 +14,7 @@ job "drone-ci" {
|
|||
task "drone_server" {
|
||||
driver = "docker"
|
||||
config {
|
||||
image = "drone/drone:2.4.0"
|
||||
image = "drone/drone:2.12.0"
|
||||
ports = [ "web_port" ]
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,7 @@ DRONE_S3_PATH_STYLE=true
|
|||
DRONE_DATABASE_DRIVER=postgres
|
||||
DRONE_DATABASE_DATASOURCE=postgres://{{ key "secrets/drone-ci/db_user" }}:{{ key "secrets/drone-ci/db_pass" }}@psql-proxy.service.2.cluster.deuxfleurs.fr:5432/drone?sslmode=disable
|
||||
DRONE_USER_CREATE=username:lx-admin,admin:true
|
||||
DRONE_REGISTRATION_CLOSED=true
|
||||
DRONE_LOGS_TEXT=true
|
||||
DRONE_LOGS_PRETTY=true
|
||||
DRONE_LOGS_DEBUG=true
|
||||
|
@ -59,6 +60,7 @@ EOH
|
|||
"traefik.enable=true",
|
||||
"traefik.frontend.entryPoints=https,http",
|
||||
"traefik.frontend.rule=Host:drone.deuxfleurs.fr",
|
||||
"tricot drone.deuxfleurs.fr",
|
||||
]
|
||||
port = "web_port"
|
||||
address_mode = "host"
|
||||
|
|
|
@ -26,31 +26,42 @@ curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compo
|
|||
chmod +x /usr/local/bin/docker-compose
|
||||
```
|
||||
|
||||
## Prepare the runner
|
||||
## Install the runner
|
||||
|
||||
*This is our Nix runner version 2, previously we had another way to start Nix runners. This one has a proper way to handle concurrency, require less boilerplate, and should be safer and more idiomatic.*
|
||||
|
||||
Nix folder must be populated before launching any build.
|
||||
|
||||
```bash
|
||||
docker run --rm -it -v /var/lib/drone/nix:/mnt nixpkgs/nix:nixos-21.05 cp -r /nix/{store,var} /mnt/
|
||||
```
|
||||
wget https://git.deuxfleurs.fr/Deuxfleurs/infrastructure/raw/branch/main/app/drone-ci/integration/nix.conf
|
||||
wget https://git.deuxfleurs.fr/Deuxfleurs/infrastructure/raw/branch/main/app/drone-ci/integration/docker-compose.yml
|
||||
|
||||
This folder will grow over time and might need to be garbage collected.
|
||||
As a rule of thumb, after running a full release of Garage, this folder will require 10GB.
|
||||
Consider provisioning it with at least 20GB.
|
||||
|
||||
## Launch the runner
|
||||
|
||||
Because we use a shared nix folder, we set the number of concurrent builds to 1.
|
||||
For more details and customizations, see `docker-compose.yml`.
|
||||
|
||||
```bash
|
||||
DRONE_NAME=lheureduthe DRONE_OWNER=quentin DRONE_SECRET=xxx docker-compose up -d
|
||||
# Edit the docker-compose.yml to adapt its variables to your needs,
|
||||
# especially the capacitiy value and its name.
|
||||
COMPOSE_PROJECT_NAME=drone DRONE_SECRET=xxx docker-compose up -d
|
||||
```
|
||||
|
||||
That's all folks.
|
||||
|
||||
## Check if a given job is built by your runner
|
||||
|
||||
```bash
|
||||
export URL=https://drone.deuxfleurs.fr
|
||||
export REPO=Deuxfleurs/garage
|
||||
export BUILD=1312
|
||||
curl ${URL}/api/repos/${REPO}/builds/${BUILD} \
|
||||
| jq -c '[.stages[] | { name: .name, machine: .machine }]'
|
||||
```
|
||||
|
||||
It will give you the following result:
|
||||
|
||||
```json
|
||||
[{"name":"default","machine":"1686a"},{"name":"release-linux-x86_64","machine":"vimaire"},{"name":"release-linux-i686","machine":"carcajou"},{"name":"release-linux-aarch64","machine":"caribou"},{"name":"release-linux-armv6l","machine":"cariacou"},{"name":"refresh-release-page","machine":null}]
|
||||
```
|
||||
|
||||
## Random note
|
||||
|
||||
*This part might be deprecated!*
|
||||
|
||||
This setup is done mainly to allow nix builds with some cache.
|
||||
To use the cache in Drone, you must set your repository as trusted.
|
||||
The command line tool does not work (it says it successfully set your repository as trusted but it did nothing):
|
||||
|
|
|
@ -1,5 +1,14 @@
|
|||
version: '3.4'
|
||||
services:
|
||||
nix-daemon:
|
||||
image: nixpkgs/nix:nixos-22.05
|
||||
restart: always
|
||||
command: nix-daemon
|
||||
privileged: true
|
||||
volumes:
|
||||
- "nix:/nix"
|
||||
- "./nix.conf:/etc/nix/nix.conf:ro"
|
||||
|
||||
drone-runner:
|
||||
image: drone/drone-runner-docker:latest
|
||||
restart: always
|
||||
|
@ -7,19 +16,30 @@ services:
|
|||
- DRONE_RPC_PROTO=https
|
||||
- DRONE_RPC_HOST=drone.deuxfleurs.fr
|
||||
- DRONE_RPC_SECRET=${DRONE_SECRET}
|
||||
- DRONE_RUNNER_CAPACITY=1
|
||||
- DRONE_RUNNER_CAPACITY=3
|
||||
- DRONE_DEBUG=true
|
||||
- DRONE_LOGS_TRACE=true
|
||||
- DRONE_RPC_DUMP_HTTP=true
|
||||
- DRONE_RPC_DUMP_HTTP_BODY=true
|
||||
- DRONE_RUNNER_NAME=${DRONE_NAME}
|
||||
- DRONE_RUNNER_LABELS=nix:1
|
||||
#- DRONE_RUNNER_VOLUMES=/var/lib/drone/nix:/nix
|
||||
- DRONE_RUNNER_NAME=i_forgot_to_change_my_runner_name
|
||||
- DRONE_RUNNER_LABELS=nix-daemon:1
|
||||
# we should put "nix:/nix:ro but it is not supported by
|
||||
# drone-runner-docker because the dependency envconfig does
|
||||
# not support having two colons (:) in the same stanza.
|
||||
# Without the RO flag (or using docker userns), build isolation
|
||||
# is broken.
|
||||
# https://discourse.drone.io/t/allow-mounting-a-host-volume-as-read-only/10071
|
||||
# https://github.com/kelseyhightower/envconfig/pull/153
|
||||
#
|
||||
# A workaround for isolation is to configure docker with a userns,
|
||||
# so even if the folder is writable to root, it is not to any non
|
||||
# privileged docker daemon ran by drone!
|
||||
- DRONE_RUNNER_VOLUMES=drone_nix:/nix
|
||||
- DRONE_RUNNER_ENVIRON=NIX_REMOTE:daemon
|
||||
ports:
|
||||
- "3000:3000/tcp"
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock"
|
||||
- "/var/lib/drone/nix:/var/lib/drone/nix"
|
||||
|
||||
drone-gc:
|
||||
image: drone/gc:latest
|
||||
|
@ -30,3 +50,5 @@ services:
|
|||
- GC_INTERVAL=10m
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock"
|
||||
volumes:
|
||||
nix:
|
||||
|
|
9
app/drone-ci/integration/nix.conf
Normal file
9
app/drone-ci/integration/nix.conf
Normal file
|
@ -0,0 +1,9 @@
|
|||
substituters = https://cache.nixos.org https://nix.web.deuxfleurs.fr
|
||||
trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= nix.web.deuxfleurs.fr:eTGL6kvaQn6cDR/F9lDYUIP9nCVR/kkshYfLDJf1yKs=
|
||||
max-jobs = auto
|
||||
cores = 0
|
||||
log-lines = 200
|
||||
filter-syscalls = true
|
||||
sandbox = true
|
||||
keep-outputs = true
|
||||
keep-derivations = true
|
|
@ -21,8 +21,9 @@ compatibility_level = 2
|
|||
#===
|
||||
# TLS parameters
|
||||
#===
|
||||
smtpd_tls_cert_file=/etc/ssl/certs/postfix.crt
|
||||
smtpd_tls_key_file=/etc/ssl/private/postfix.key
|
||||
smtpd_tls_cert_file=/etc/ssl/postfix.crt
|
||||
smtpd_tls_key_file=/etc/ssl/postfix.key
|
||||
smtpd_tls_dh1024_param_file=auto
|
||||
smtpd_use_tls=yes
|
||||
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
|
||||
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
|
||||
|
|
|
@ -28,6 +28,12 @@ job "email" {
|
|||
task "server" {
|
||||
driver = "docker"
|
||||
|
||||
constraint {
|
||||
attribute = "${attr.unique.hostname}"
|
||||
operator = "="
|
||||
value = "digitale"
|
||||
}
|
||||
|
||||
config {
|
||||
image = "superboum/amd64_dovecot:v6"
|
||||
readonly_rootfs = false
|
||||
|
@ -38,7 +44,7 @@ job "email" {
|
|||
"secrets/ssl/certs:/etc/ssl/certs",
|
||||
"secrets/ssl/private:/etc/ssl/private",
|
||||
"secrets/conf/:/etc/dovecot/",
|
||||
"/mnt/glusterfs/email/mail:/var/mail/",
|
||||
"/mnt/ssd/mail:/var/mail/",
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -143,12 +149,14 @@ job "email" {
|
|||
|
||||
# ----- secrets ------
|
||||
template {
|
||||
data = "{{ key \"secrets/email/dovecot/dovecot.crt\" }}"
|
||||
# data = "{{ key \"secrets/email/dovecot/dovecot.crt\" }}"
|
||||
data = "{{ with $d := key \"tricot/certs/imap.deuxfleurs.fr\" | parseJSON }}{{ $d.cert_pem }}{{ end }}"
|
||||
destination = "secrets/ssl/certs/dovecot.crt"
|
||||
perms = "400"
|
||||
}
|
||||
template {
|
||||
data = "{{ key \"secrets/email/dovecot/dovecot.key\" }}"
|
||||
# data = "{{ key \"secrets/email/dovecot/dovecot.key\" }}"
|
||||
data = "{{ with $d := key \"tricot/certs/imap.deuxfleurs.fr\" | parseJSON }}{{ $d.key_pem }}{{ end }}"
|
||||
destination = "secrets/ssl/private/dovecot.key"
|
||||
perms = "400"
|
||||
}
|
||||
|
@ -253,8 +261,7 @@ job "email" {
|
|||
command = "postfix"
|
||||
args = [ "start-fg" ]
|
||||
volumes = [
|
||||
"secrets/ssl/certs:/etc/ssl/certs",
|
||||
"secrets/ssl/private:/etc/ssl/private",
|
||||
"secrets/ssl:/etc/ssl",
|
||||
"secrets/postfix:/etc/postfix-conf",
|
||||
"/dev/log:/dev/log"
|
||||
]
|
||||
|
@ -375,14 +382,16 @@ job "email" {
|
|||
|
||||
# --- secrets ---
|
||||
template {
|
||||
data = "{{ key \"secrets/email/postfix/postfix.crt\" }}"
|
||||
destination = "secrets/ssl/certs/postfix.crt"
|
||||
# data = "{{ key \"secrets/email/postfix/postfix.crt\" }}"
|
||||
data = "{{ with $d := key \"tricot/certs/smtp.deuxfleurs.fr\" | parseJSON }}{{ $d.cert_pem }}{{ end }}"
|
||||
destination = "secrets/ssl/postfix.crt"
|
||||
perms = "400"
|
||||
}
|
||||
|
||||
template {
|
||||
data = "{{ key \"secrets/email/postfix/postfix.key\" }}"
|
||||
destination = "secrets/ssl/private/postfix.key"
|
||||
# data = "{{ key \"secrets/email/postfix/postfix.key\" }}"
|
||||
data = "{{ with $d := key \"tricot/certs/smtp.deuxfleurs.fr\" | parseJSON }}{{ $d.key_pem }}{{ end }}"
|
||||
destination = "secrets/ssl/postfix.key"
|
||||
perms = "400"
|
||||
}
|
||||
}
|
||||
|
@ -423,7 +432,8 @@ job "email" {
|
|||
"alps",
|
||||
"traefik.enable=true",
|
||||
"traefik.frontend.entryPoints=https,http",
|
||||
"traefik.frontend.rule=Host:alps.deuxfleurs.fr"
|
||||
"traefik.frontend.rule=Host:alps.deuxfleurs.fr",
|
||||
"tricot alps.deuxfleurs.fr",
|
||||
]
|
||||
check {
|
||||
type = "tcp"
|
||||
|
@ -477,7 +487,9 @@ job "email" {
|
|||
"sogo",
|
||||
"traefik.enable=true",
|
||||
"traefik.frontend.entryPoints=https,http",
|
||||
"traefik.frontend.rule=Host:www.sogo.deuxfleurs.fr,sogo.deuxfleurs.fr;PathPrefix:/"
|
||||
"traefik.frontend.rule=Host:www.sogo.deuxfleurs.fr,sogo.deuxfleurs.fr;PathPrefix:/",
|
||||
"tricot www.sogo.deuxfleurs.fr",
|
||||
"tricot sogo.deuxfleurs.fr",
|
||||
]
|
||||
check {
|
||||
type = "tcp"
|
||||
|
|
1
app/email/secrets/email/dovecot/backup_aws_access_key_id
Normal file
1
app/email/secrets/email/dovecot/backup_aws_access_key_id
Normal file
|
@ -0,0 +1 @@
|
|||
USER AWS Acces Key ID
|
|
@ -0,0 +1 @@
|
|||
USER AWS Secret Access key
|
1
app/email/secrets/email/dovecot/backup_restic_password
Normal file
1
app/email/secrets/email/dovecot/backup_restic_password
Normal file
|
@ -0,0 +1 @@
|
|||
USER Restic backup password to encrypt data
|
1
app/email/secrets/email/dovecot/backup_restic_repository
Normal file
1
app/email/secrets/email/dovecot/backup_restic_repository
Normal file
|
@ -0,0 +1 @@
|
|||
USER Restic Repository URL, check op_guide/backup-minio to see the format
|
60
app/frontend/deploy/frontend-tricot.hcl
Normal file
60
app/frontend/deploy/frontend-tricot.hcl
Normal file
|
@ -0,0 +1,60 @@
|
|||
job "frontend" {
|
||||
datacenters = ["dc1", "neptune"]
|
||||
type = "service"
|
||||
priority = 90
|
||||
|
||||
group "tricot" {
|
||||
network {
|
||||
port "http_port" { static = 80 }
|
||||
port "https_port" { static = 443 }
|
||||
}
|
||||
|
||||
task "server" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "lxpz/amd64_tricot:37"
|
||||
network_mode = "host"
|
||||
readonly_rootfs = true
|
||||
ports = [ "http_port", "https_port" ]
|
||||
}
|
||||
|
||||
resources {
|
||||
cpu = 2000
|
||||
memory = 500
|
||||
}
|
||||
|
||||
restart {
|
||||
interval = "30m"
|
||||
attempts = 2
|
||||
delay = "15s"
|
||||
mode = "delay"
|
||||
}
|
||||
|
||||
template {
|
||||
data = <<EOH
|
||||
TRICOT_NODE_NAME={{ env "attr.unique.hostname" }}
|
||||
TRICOT_LETSENCRYPT_EMAIL=alex@adnab.me
|
||||
TRICOT_ENABLE_COMPRESSION=true
|
||||
RUST_LOG=tricot=debug
|
||||
EOH
|
||||
destination = "secrets/env"
|
||||
env = true
|
||||
}
|
||||
|
||||
service {
|
||||
name = "tricot-http"
|
||||
port = "http_port"
|
||||
tags = [ "(diplonat (tcp_port 80))" ]
|
||||
address_mode = "host"
|
||||
}
|
||||
|
||||
service {
|
||||
name = "tricot-https"
|
||||
port = "https_port"
|
||||
tags = [ "(diplonat (tcp_port 443))" ]
|
||||
address_mode = "host"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,16 +8,17 @@ replication_mode = "3"
|
|||
rpc_bind_addr = "[::]:3901"
|
||||
rpc_secret = "{{ key "secrets/garage/rpc_secret" | trimSpace }}"
|
||||
|
||||
consul_host = "consul.service.2.cluster.deuxfleurs.fr:8500"
|
||||
consul_service_name = "garage-rpc-self-advertised"
|
||||
|
||||
bootstrap_peers = []
|
||||
sled_cache_capacity = 536870912
|
||||
sled_sync_interval_ms = 10000
|
||||
|
||||
[s3_api]
|
||||
s3_region = "garage"
|
||||
api_bind_addr = "[::]:3900"
|
||||
root_domain = ".garage.deuxfleurs.fr"
|
||||
|
||||
[s3_web]
|
||||
bind_addr = "[::]:3902"
|
||||
root_domain = ".web.deuxfleurs.fr"
|
||||
index = "index.html"
|
||||
|
||||
[admin]
|
||||
api_bind_addr = "[::1]:3903"
|
||||
|
|
|
@ -25,7 +25,7 @@ job "garage" {
|
|||
driver = "docker"
|
||||
config {
|
||||
advertise_ipv6_address = true
|
||||
image = "dxflrs/amd64_garage:v0.4-rc2"
|
||||
image = "dxflrs/amd64_garage:v0.7.1"
|
||||
command = "/garage"
|
||||
args = [ "server" ]
|
||||
network_mode = "host"
|
||||
|
@ -45,7 +45,7 @@ job "garage" {
|
|||
}
|
||||
|
||||
resources {
|
||||
memory = 1000
|
||||
memory = 1500
|
||||
cpu = 1000
|
||||
}
|
||||
|
||||
|
@ -55,9 +55,8 @@ job "garage" {
|
|||
service {
|
||||
tags = [
|
||||
"garage_api",
|
||||
"traefik.enable=true",
|
||||
"traefik.frontend.entryPoints=https,http",
|
||||
"traefik.frontend.rule=Host:garage.deuxfleurs.fr"
|
||||
"tricot garage.deuxfleurs.fr",
|
||||
"tricot *.garage.deuxfleurs.fr",
|
||||
]
|
||||
port = 3900
|
||||
address_mode = "driver"
|
||||
|
@ -95,6 +94,32 @@ job "garage" {
|
|||
}
|
||||
}
|
||||
|
||||
service {
|
||||
tags = [
|
||||
"garage-web",
|
||||
"tricot * 1",
|
||||
"tricot-add-header Content-Security-Policy default-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' https://code.jquery.com/; frame-ancestors 'self'",
|
||||
"tricot-add-header Strict-Transport-Security max-age=63072000; includeSubDomains; preload",
|
||||
"tricot-add-header X-Frame-Options SAMEORIGIN",
|
||||
"tricot-add-header X-XSS-Protection 1; mode=block",
|
||||
]
|
||||
port = 3902
|
||||
address_mode = "driver"
|
||||
name = "garage-web"
|
||||
check {
|
||||
type = "tcp"
|
||||
port = 3902
|
||||
address_mode = "driver"
|
||||
interval = "60s"
|
||||
timeout = "5s"
|
||||
check_restart {
|
||||
limit = 3
|
||||
grace = "90s"
|
||||
ignore_warnings = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
restart {
|
||||
interval = "30m"
|
||||
attempts = 10
|
||||
|
|
|
@ -59,7 +59,7 @@ listeners:
|
|||
x_forwarded: false
|
||||
|
||||
resources:
|
||||
- names: [client]
|
||||
- names: [client, federation]
|
||||
compress: true
|
||||
|
||||
- port: 8448
|
||||
|
@ -83,6 +83,7 @@ listeners:
|
|||
# Database configuration
|
||||
database:
|
||||
name: psycopg2
|
||||
allow_unsafe_locale: false
|
||||
args:
|
||||
user: {{ key "secrets/chat/synapse/postgres_user" | trimSpace }}
|
||||
password: {{ key "secrets/chat/synapse/postgres_pwd" | trimSpace }}
|
||||
|
|
|
@ -15,7 +15,7 @@ job "im" {
|
|||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "superboum/amd64_synapse:v47"
|
||||
image = "superboum/amd64_synapse:v53"
|
||||
network_mode = "host"
|
||||
readonly_rootfs = true
|
||||
ports = [ "client_port", "federation_port" ]
|
||||
|
@ -95,11 +95,10 @@ job "im" {
|
|||
address_mode = "host"
|
||||
tags = [
|
||||
"matrix",
|
||||
"traefik.enable=true",
|
||||
"traefik.frontend.entryPoints=https",
|
||||
"traefik.frontend.rule=Host:im.deuxfleurs.fr;PathPrefix:/_matrix,/_synapse",
|
||||
"traefik.frontend.headers.customResponseHeaders=Access-Control-Allow-Origin: *",
|
||||
"traefik.frontend.priority=100"
|
||||
"tricot im.deuxfleurs.fr/_matrix 100",
|
||||
"tricot im.deuxfleurs.fr:443/_matrix 100",
|
||||
"tricot im.deuxfleurs.fr/_synapse 100",
|
||||
"tricot-add-header Access-Control-Allow-Origin *",
|
||||
]
|
||||
check {
|
||||
type = "tcp"
|
||||
|
@ -120,10 +119,8 @@ job "im" {
|
|||
address_mode = "host"
|
||||
tags = [
|
||||
"matrix",
|
||||
"traefik.enable=true",
|
||||
"traefik.frontend.entryPoints=https",
|
||||
"traefik.frontend.rule=Host:deuxfleurs.fr;PathPrefix:/_matrix",
|
||||
"traefik.frontend.priority=100"
|
||||
"tricot deuxfleurs.fr/_matrix 100",
|
||||
"tricot deuxfleurs.fr:443/_matrix 100",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -133,7 +130,7 @@ job "im" {
|
|||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "superboum/amd64_synapse:v47"
|
||||
image = "superboum/amd64_synapse:v53"
|
||||
readonly_rootfs = true
|
||||
command = "/usr/local/bin/matrix-s3-async"
|
||||
work_dir = "/tmp"
|
||||
|
@ -177,7 +174,7 @@ EOH
|
|||
task "server" {
|
||||
driver = "docker"
|
||||
config {
|
||||
image = "superboum/amd64_riotweb:v25"
|
||||
image = "superboum/amd64_riotweb:v30"
|
||||
ports = [ "web_port" ]
|
||||
volumes = [
|
||||
"secrets/config.json:/srv/http/config.json"
|
||||
|
@ -196,10 +193,8 @@ EOH
|
|||
service {
|
||||
tags = [
|
||||
"webstatic",
|
||||
"traefik.enable=true",
|
||||
"traefik.frontend.entryPoints=https",
|
||||
"traefik.frontend.rule=Host:im.deuxfleurs.fr,riot.deuxfleurs.fr;PathPrefix:/",
|
||||
"traefik.frontend.priority=10"
|
||||
"tricot im.deuxfleurs.fr 10",
|
||||
"tricot riot.deuxfleurs.fr 10",
|
||||
]
|
||||
port = "web_port"
|
||||
address_mode = "host"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM debian:buster AS builder
|
||||
FROM debian:bookworm AS builder
|
||||
|
||||
# unzip is required when executing the mvn package command
|
||||
RUN apt-get update && \
|
||||
|
@ -15,7 +15,7 @@ RUN mvn package -DskipTests -Dassembly.skipAssembly=false
|
|||
RUN unzip target/jicofo-1.1-SNAPSHOT-archive.zip && \
|
||||
mv jicofo-1.1-SNAPSHOT /srv/build
|
||||
|
||||
FROM debian:buster
|
||||
FROM debian:bookworm
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y openjdk-11-jre-headless ca-certificates
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
update-ca-certificates -f
|
||||
|
||||
exec java \
|
||||
-Dlog4j2.formatMsgNoLookups=true \
|
||||
-Djdk.tls.ephemeralDHKeySize=2048 \
|
||||
-Djava.util.logging.config.file=/usr/share/jicofo/lib/logging.properties \
|
||||
-Dconfig.file=/etc/jitsi/jicofo.conf \
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
FROM debian:buster AS builder
|
||||
FROM debian:bookworm AS builder
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y curl && \
|
||||
curl -sL https://deb.nodesource.com/setup_14.x | bash - && \
|
||||
curl -sL https://deb.nodesource.com/setup_16.x | bash - && \
|
||||
apt-get install -y git nodejs make git unzip
|
||||
|
||||
ARG MEET_TAG
|
||||
|
@ -12,7 +12,7 @@ WORKDIR jitsi-meet
|
|||
RUN npm install && \
|
||||
make
|
||||
|
||||
FROM debian:buster
|
||||
FROM debian:bookworm
|
||||
|
||||
COPY --from=builder /jitsi-meet /srv/jitsi-meet
|
||||
RUN apt-get update && \
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
From b327e580ab83110cdb52bc1d11687a096b8fc1df Mon Sep 17 00:00:00 2001
|
||||
From: Quentin Dufour <quentin@dufour.io>
|
||||
Date: Mon, 1 Feb 2021 07:16:50 +0100
|
||||
Subject: [PATCH] Disable legacy parameters
|
||||
|
||||
---
|
||||
jvb/src/main/kotlin/org/jitsi/videobridge/Main.kt | 8 --------
|
||||
1 file changed, 8 deletions(-)
|
||||
|
||||
diff --git a/jvb/src/main/kotlin/org/jitsi/videobridge/Main.kt b/jvb/src/main/kotlin/org/jitsi/videobridge/Main.kt
|
||||
index df71f480..8f0ef9a5 100644
|
||||
--- a/jvb/src/main/kotlin/org/jitsi/videobridge/Main.kt
|
||||
+++ b/jvb/src/main/kotlin/org/jitsi/videobridge/Main.kt
|
||||
@@ -62,14 +62,6 @@ fun main(args: Array<String>) {
|
||||
// to be passed.
|
||||
System.setProperty("org.eclipse.jetty.util.log.class", "org.eclipse.jetty.util.log.JavaUtilLog")
|
||||
|
||||
- // Before initializing the application programming interfaces (APIs) of
|
||||
- // Jitsi Videobridge, set any System properties which they use and which
|
||||
- // may be specified by the command-line arguments.
|
||||
- System.setProperty(
|
||||
- Videobridge.REST_API_PNAME,
|
||||
- cmdLine.getOptionValue("--apis").contains(Videobridge.REST_API).toString()
|
||||
- )
|
||||
-
|
||||
// Reload the Typesafe config used by ice4j, because the original was initialized before the new system
|
||||
// properties were set.
|
||||
JitsiConfig.reloadNewConfig()
|
||||
--
|
||||
2.25.1
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
From 01507442620e5a57624c921b508eac7d572440d0 Mon Sep 17 00:00:00 2001
|
||||
From: Quentin Dufour <quentin@deuxfleurs.fr>
|
||||
Date: Tue, 25 Jan 2022 14:46:22 +0100
|
||||
Subject: [PATCH] Remove deprecated argument
|
||||
|
||||
---
|
||||
.../main/kotlin/org/jitsi/videobridge/Main.kt | 17 -----------------
|
||||
1 file changed, 17 deletions(-)
|
||||
|
||||
diff --git a/jvb/src/main/kotlin/org/jitsi/videobridge/Main.kt b/jvb/src/main/kotlin/org/jitsi/videobridge/Main.kt
|
||||
index 4f6cb78..3db00f2 100644
|
||||
--- a/jvb/src/main/kotlin/org/jitsi/videobridge/Main.kt
|
||||
+++ b/jvb/src/main/kotlin/org/jitsi/videobridge/Main.kt
|
||||
@@ -52,23 +52,6 @@ import org.jitsi.videobridge.websocket.singleton as webSocketServiceSingleton
|
||||
fun main(args: Array<String>) {
|
||||
val logger = LoggerImpl("org.jitsi.videobridge.Main")
|
||||
|
||||
- // We only support command line arguments for backward compatibility. The --apis options is the last one supported,
|
||||
- // and it is only used to enable/disable the REST API (XMPP is only controlled through the config files).
|
||||
- // TODO: fully remove support for --apis
|
||||
- CmdLine().apply {
|
||||
- parse(args)
|
||||
- getOptionValue("--apis")?.let {
|
||||
- logger.warn(
|
||||
- "A deprecated command line argument (--apis) is present. Please use the config file to control the " +
|
||||
- "REST API instead (see rest.md). Support for --apis will be removed in a future version."
|
||||
- )
|
||||
- System.setProperty(
|
||||
- Videobridge.REST_API_PNAME,
|
||||
- it.contains(Videobridge.REST_API).toString()
|
||||
- )
|
||||
- }
|
||||
- }
|
||||
-
|
||||
setupMetaconfigLogger()
|
||||
|
||||
setSystemPropertyDefaults()
|
||||
--
|
||||
2.33.1
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
FROM debian:buster AS builder
|
||||
FROM debian:bookworm AS builder
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y git unzip maven openjdk-11-jdk-headless
|
||||
|
@ -8,15 +8,15 @@ RUN git clone --depth 1 --branch ${JVB_TAG} https://github.com/jitsi/jitsi-video
|
|||
|
||||
WORKDIR jitsi-videobridge
|
||||
COPY *.patch .
|
||||
RUN git apply 0001-Disable-legacy-parameters.patch
|
||||
RUN git apply 0001-Remove-deprecated-argument.patch
|
||||
RUN mvn package -DskipTests
|
||||
RUN unzip jvb/target/jitsi-videobridge*.zip && \
|
||||
mv jitsi-videobridge-*-SNAPSHOT build
|
||||
|
||||
FROM debian:buster
|
||||
FROM debian:bookworm
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y openjdk-11-jre-headless curl
|
||||
apt-get install -y openjdk-11-jre-headless curl iproute2
|
||||
|
||||
COPY --from=builder /jitsi-videobridge/build /usr/share/jvb
|
||||
COPY jvb_run /usr/local/bin/jvb_run
|
||||
|
|
|
@ -12,6 +12,7 @@ fi
|
|||
echo "NAT config: ${JITSI_NAT_LOCAL_IP} -> ${JITSI_NAT_PUBLIC_IP}"
|
||||
|
||||
exec java \
|
||||
-Dlog4j2.formatMsgNoLookups=true \
|
||||
-Djdk.tls.ephemeralDHKeySize=2048 \
|
||||
-Djava.util.logging.config.file=/usr/share/jvb/lib/logging.properties \
|
||||
-Dconfig.file=/etc/jitsi/videobridge.conf \
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM debian:buster as builder
|
||||
FROM debian:bookworm as builder
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y git unzip
|
||||
|
@ -6,7 +6,7 @@ RUN apt-get update && \
|
|||
ARG MEET_TAG
|
||||
RUN git clone --depth 1 --branch ${MEET_TAG} https://github.com/jitsi/jitsi-meet/
|
||||
|
||||
FROM debian:buster
|
||||
FROM debian:bookworm
|
||||
|
||||
ARG PROSODY_VERSION
|
||||
RUN apt-get update && \
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# some doc: https://www.nginx.com/resources/wiki/start/topics/examples/full/
|
||||
error_log /dev/stderr;
|
||||
error_log /dev/stderr info;
|
||||
|
||||
events {}
|
||||
|
||||
|
@ -39,8 +39,10 @@ http {
|
|||
|
||||
# inspired by https://raw.githubusercontent.com/jitsi/docker-jitsi-meet/master/web/rootfs/defaults/meet.conf
|
||||
server {
|
||||
listen 0.0.0.0:{{ env "NOMAD_PORT_https_port" }} ssl http2 default_server;
|
||||
listen [::]:{{ env "NOMAD_PORT_https_port" }} ssl http2 default_server;
|
||||
#listen 0.0.0.0:{{ env "NOMAD_PORT_https_port" }} ssl http2 default_server;
|
||||
#listen [::]:{{ env "NOMAD_PORT_https_port" }} ssl http2 default_server;
|
||||
listen 0.0.0.0:{{ env "NOMAD_PORT_https_port" }} default_server;
|
||||
listen [::]:{{ env "NOMAD_PORT_https_port" }} default_server;
|
||||
client_max_body_size 0;
|
||||
server_name _;
|
||||
|
||||
|
@ -48,8 +50,8 @@ http {
|
|||
ssi on;
|
||||
ssi_types application/x-javascript application/javascript;
|
||||
|
||||
ssl_certificate /etc/nginx/jitsi.crt;
|
||||
ssl_certificate_key /etc/nginx/jitsi.key;
|
||||
#ssl_certificate /etc/nginx/jitsi.crt;
|
||||
#ssl_certificate_key /etc/nginx/jitsi.key;
|
||||
root /srv/jitsi-meet;
|
||||
index index.html;
|
||||
error_page 404 /static/404.html;
|
||||
|
@ -90,7 +92,7 @@ http {
|
|||
add_header 'Access-Control-Allow-Origin' '*';
|
||||
proxy_pass http://{{ env "NOMAD_ADDR_bosh_port" }}/http-bind;
|
||||
proxy_set_header X-Forwarded-For \$remote_addr;
|
||||
proxy_set_header Host \$http_host;
|
||||
#proxy_set_header Host \$http_host;
|
||||
}
|
||||
|
||||
# not used yet VVV
|
||||
|
|
|
@ -21,7 +21,7 @@ job "jitsi" {
|
|||
task "xmpp" {
|
||||
driver = "docker"
|
||||
config {
|
||||
image = "superboum/amd64_jitsi_xmpp:v9"
|
||||
image = "superboum/amd64_jitsi_xmpp:v10"
|
||||
ports = [ "bosh_port", "xmpp_port" ]
|
||||
network_mode = "host"
|
||||
volumes = [
|
||||
|
@ -102,7 +102,7 @@ EOF
|
|||
task "front" {
|
||||
driver = "docker"
|
||||
config {
|
||||
image = "superboum/amd64_jitsi_meet:v4"
|
||||
image = "superboum/amd64_jitsi_meet:v5"
|
||||
network_mode = "host"
|
||||
ports = [ "https_port" ]
|
||||
volumes = [
|
||||
|
@ -144,7 +144,8 @@ EOF
|
|||
"traefik.enable=true",
|
||||
"traefik.frontend.entryPoints=https",
|
||||
"traefik.frontend.rule=Host:jitsi.deuxfleurs.fr;PathPrefix:/",
|
||||
"traefik.protocol=https"
|
||||
"traefik.protocol=https",
|
||||
"tricot jitsi.deuxfleurs.fr",
|
||||
]
|
||||
port = "https_port"
|
||||
address_mode = "host"
|
||||
|
@ -166,7 +167,7 @@ EOF
|
|||
task "jicofo" {
|
||||
driver = "docker"
|
||||
config {
|
||||
image = "superboum/amd64_jitsi_conference_focus:v7"
|
||||
image = "superboum/amd64_jitsi_conference_focus:v9"
|
||||
network_mode = "host"
|
||||
volumes = [
|
||||
"secrets/certs/jitsi.crt:/usr/local/share/ca-certificates/jitsi.crt",
|
||||
|
@ -200,7 +201,7 @@ EOF
|
|||
task "videobridge" {
|
||||
driver = "docker"
|
||||
config {
|
||||
image = "superboum/amd64_jitsi_videobridge:v17"
|
||||
image = "superboum/amd64_jitsi_videobridge:v20"
|
||||
network_mode = "host"
|
||||
ports = [ "video_port" ]
|
||||
ulimit {
|
||||
|
|
44
app/matterbridge/config/matterbridge.toml
Normal file
44
app/matterbridge/config/matterbridge.toml
Normal file
|
@ -0,0 +1,44 @@
|
|||
# Bridge accounts on various services
|
||||
|
||||
[rocketchat]
|
||||
[rocketchat.dravedev]
|
||||
Server = "https://rocketchat.drave.quebec:443"
|
||||
Login = "{{ key "secrets/matterbridge/rocketchat.drave.quebec_user" | trimSpace }}"
|
||||
Password = "{{ key "secrets/matterbridge/rocketchat.drave.quebec_pass" | trimSpace }}"
|
||||
PrefixMessagesWithNick=false
|
||||
RemoteNickFormat="{NICK}"
|
||||
|
||||
[matrix]
|
||||
[matrix.deuxfleurs]
|
||||
Server = "https://im.deuxfleurs.fr"
|
||||
Login = "{{ key "secrets/matterbridge/im.deuxfleurs.fr_user" | trimSpace }}"
|
||||
Password = "{{ key "secrets/matterbridge/im.deuxfleurs.fr_pass" | trimSpace }}"
|
||||
PrefixMessagesWithNick=true
|
||||
RemoteNickFormat="<{NICK}> "
|
||||
|
||||
[discord]
|
||||
[discord.la-console]
|
||||
Token = "{{ key "secrets/matterbridge/discord.com_token" | trimSpace }}"
|
||||
Server = "872244032443678730"
|
||||
RemoteNickFormat="{NICK}"
|
||||
PrefixMessagesWithNick=false
|
||||
AutoWebhooks = true
|
||||
|
||||
# Rooms we are bridging
|
||||
|
||||
[[gateway]]
|
||||
name = "rfid"
|
||||
enable = true
|
||||
|
||||
[[gateway.inout]]
|
||||
account = "rocketchat.dravedev"
|
||||
channel = "rfid"
|
||||
|
||||
[[gateway.inout]]
|
||||
account = "matrix.deuxfleurs"
|
||||
channel = "#rfid:deuxfleurs.fr"
|
||||
|
||||
[[gateway.inout]]
|
||||
account = "discord.la-console"
|
||||
channel = "rfid"
|
||||
|
40
app/matterbridge/deploy/matterbridge.hcl
Normal file
40
app/matterbridge/deploy/matterbridge.hcl
Normal file
|
@ -0,0 +1,40 @@
|
|||
job "matterbridge" {
|
||||
datacenters = ["dc1"]
|
||||
type = "service"
|
||||
priority = 90
|
||||
|
||||
constraint {
|
||||
attribute = "${attr.cpu.arch}"
|
||||
value = "amd64"
|
||||
}
|
||||
|
||||
group "main" {
|
||||
count = 1
|
||||
|
||||
task "bridge" {
|
||||
driver = "docker"
|
||||
config {
|
||||
image = "42wim/matterbridge:1.23"
|
||||
readonly_rootfs = true
|
||||
volumes = [
|
||||
"secrets/matterbridge.toml:/etc/matterbridge/matterbridge.toml"
|
||||
]
|
||||
}
|
||||
|
||||
resources {
|
||||
memory = 200
|
||||
}
|
||||
|
||||
template {
|
||||
data = file("../config/matterbridge.toml")
|
||||
destination = "secrets/matterbridge.toml"
|
||||
}
|
||||
|
||||
restart {
|
||||
attempts = 10
|
||||
delay = "30s"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -41,7 +41,8 @@ EOH
|
|||
"platoo",
|
||||
"traefik.enable=true",
|
||||
"traefik.frontend.entryPoints=https",
|
||||
"traefik.frontend.rule=Host:platoo.deuxfleurs.fr;PathPrefix:/"
|
||||
"traefik.frontend.rule=Host:platoo.deuxfleurs.fr;PathPrefix:/",
|
||||
"tricot platoo.deuxfleurs.fr",
|
||||
]
|
||||
port = "web_port"
|
||||
address_mode = "host"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM rust:1.54.0-slim-bullseye as builder
|
||||
FROM rust:1.58.1-slim-bullseye as builder
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y \
|
||||
|
@ -10,6 +10,7 @@ RUN apt-get update && \
|
|||
libpq-dev \
|
||||
gettext \
|
||||
git \
|
||||
python \
|
||||
curl \
|
||||
gcc \
|
||||
make \
|
||||
|
@ -25,11 +26,11 @@ WORKDIR /opt/plume
|
|||
RUN git checkout ${VERSION}
|
||||
|
||||
WORKDIR /opt/plume/script
|
||||
RUN chmod a+x ./wasm-deps.sh && sleep 1 && ./wasm-deps.sh
|
||||
RUN chmod a+x ./wasm-deps.sh && ./wasm-deps.sh
|
||||
|
||||
WORKDIR /opt/plume
|
||||
RUN cargo install wasm-pack
|
||||
RUN chmod a+x ./script/plume-front.sh && sleep 1 && ./script/plume-front.sh
|
||||
RUN chmod a+x ./script/plume-front.sh && ./script/plume-front.sh
|
||||
RUN cargo install --path ./ --force --no-default-features --features postgres
|
||||
RUN cargo install --path plume-cli --force --no-default-features --features postgres
|
||||
RUN cargo clean
|
||||
|
@ -40,13 +41,14 @@ FROM debian:bullseye-slim
|
|||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
ca-certificates \
|
||||
libpq5 \
|
||||
libssl1.1
|
||||
libssl1.1 \
|
||||
rclone \
|
||||
fuse
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=builder /opt/plume /app
|
||||
COPY --from=builder /usr/local/cargo/bin/plm /usr/local/bin/
|
||||
COPY --from=builder /usr/local/cargo/bin/plume /usr/local/bin/
|
||||
COPY plm-start /usr/local/bin/
|
||||
|
||||
CMD ["plm-start"]
|
||||
CMD ["plume"]
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
until plm migration run;
|
||||
do sleep 2;
|
||||
done
|
||||
plm search init
|
||||
plm instance new --domain "$DOMAIN_NAME" --name "$INSTANCE_NAME" --private
|
||||
|
||||
plume
|
|
@ -1,4 +1,4 @@
|
|||
job "plume" {
|
||||
job "plume-blog" {
|
||||
datacenters = ["dc1"]
|
||||
type = "service"
|
||||
|
||||
|
@ -15,16 +15,22 @@ job "plume" {
|
|||
}
|
||||
|
||||
task "plume" {
|
||||
constraint {
|
||||
attribute = "${attr.unique.hostname}"
|
||||
operator = "="
|
||||
value = "digitale"
|
||||
}
|
||||
|
||||
driver = "docker"
|
||||
config {
|
||||
image = "superboum/plume:v4"
|
||||
image = "superboum/plume:v8"
|
||||
network_mode = "host"
|
||||
ports = [ "web_port" ]
|
||||
#command = "cat"
|
||||
#args = [ "/dev/stdout" ]
|
||||
volumes = [
|
||||
"/mnt/glusterfs/plume/media:/app/static/media",
|
||||
"/mnt/glusterfs/plume/search:/app/search_index"
|
||||
"/mnt/ssd/plume/search_index:/app/search_index",
|
||||
"/mnt/ssd/plume/media:/app/static/media"
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -46,6 +52,7 @@ job "plume" {
|
|||
"traefik.enable=true",
|
||||
"traefik.frontend.entryPoints=https,http",
|
||||
"traefik.frontend.rule=Host:plume.deuxfleurs.fr",
|
||||
"tricot plume.deuxfleurs.fr",
|
||||
]
|
||||
port = "web_port"
|
||||
address_mode = "host"
|
||||
|
@ -63,6 +70,12 @@ job "plume" {
|
|||
}
|
||||
}
|
||||
}
|
||||
restart {
|
||||
interval = "30m"
|
||||
attempts = 20
|
||||
delay = "15s"
|
||||
mode = "delay"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
1
app/plume/secrets/plume/backup_aws_access_key_id
Normal file
1
app/plume/secrets/plume/backup_aws_access_key_id
Normal file
|
@ -0,0 +1 @@
|
|||
USER Backup AWS access key ID
|
1
app/plume/secrets/plume/backup_aws_secret_access_key
Normal file
1
app/plume/secrets/plume/backup_aws_secret_access_key
Normal file
|
@ -0,0 +1 @@
|
|||
USER Backup AWS secret access key
|
1
app/plume/secrets/plume/backup_restic_password
Normal file
1
app/plume/secrets/plume/backup_restic_password
Normal file
|
@ -0,0 +1 @@
|
|||
USER Restic password to encrypt backups
|
1
app/plume/secrets/plume/backup_restic_repository
Normal file
1
app/plume/secrets/plume/backup_restic_repository
Normal file
|
@ -0,0 +1 @@
|
|||
USER Restic repository, eg. s3:https://s3.garage.tld
|
|
@ -1,4 +1,4 @@
|
|||
FROM golang:1.13-buster AS builder
|
||||
FROM golang:1.19.0-bullseye AS builder
|
||||
|
||||
ARG STOLON_VERSION
|
||||
WORKDIR /stolon
|
||||
|
@ -9,7 +9,7 @@ COPY 0001-Add-max-rate-to-pg_basebackup.patch .
|
|||
RUN git apply 0001-Add-max-rate-to-pg_basebackup.patch
|
||||
RUN make && chmod +x /stolon/bin/*
|
||||
|
||||
FROM postgres:13.3-buster
|
||||
FROM postgres:14.5-bullseye
|
||||
COPY --from=builder /stolon/bin /usr/local/bin
|
||||
USER postgres
|
||||
ENTRYPOINT []
|
||||
|
|
|
@ -93,6 +93,12 @@ job "postgres13.3" {
|
|||
"--pg-su-password", "${PG_SU_PWD}",
|
||||
"--pg-repl-username", "${PG_REPL_USER}",
|
||||
"--pg-repl-password", "${PG_REPL_PWD}",
|
||||
/*
|
||||
The postgres daemon accepts 0.0.0.0, ::, and * here but not Stolon.
|
||||
Otherwise you will have the following error and your cluster will be broken (no replication)
|
||||
WARN cmd/keeper.go:1979 provided --pg-listen-address "*": is not an ip address but a hostname. This will be advertized to the other components and may have undefined behaviors if resolved differently by other hosts
|
||||
WARN cmd/keeper.go:1984 cannot resolve provided --pg-listen-address "*": lookup *: no such host
|
||||
*/
|
||||
"--pg-listen-address", "${attr.unique.network.ip-address}",
|
||||
"--pg-port", "${NOMAD_PORT_psql_port}",
|
||||
"--pg-bin-path", "/usr/lib/postgresql/13/bin/"
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
python-consul==1.1.0
|
||||
python-ldap==3.3.1
|
||||
python-ldap==3.4.0
|
||||
passlib==1.7.4
|
||||
|
|
15
app/shell.nix
Normal file
15
app/shell.nix
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
pkgs ? import <nixpkgs> {}
|
||||
}:
|
||||
|
||||
with pkgs; mkShell {
|
||||
nativeBuildInputs = [
|
||||
nomad
|
||||
docker-compose
|
||||
python39Packages.pip
|
||||
python39Packages.ldap
|
||||
python39Packages.consul
|
||||
python39Packages.passlib
|
||||
];
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
InsecureSkipVerify = true
|
||||
defaultEntryPoints = ["http", "https"]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.admin]
|
||||
address = ":8082"
|
||||
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
[entryPoints.http.redirect]
|
||||
entryPoint = "https"
|
||||
|
||||
[entryPoints.https]
|
||||
address = ":443"
|
||||
compress = true
|
||||
[entryPoints.https.tls]
|
||||
minVersion = "VersionTLS12"
|
||||
cipherSuites = [
|
||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
|
||||
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
|
||||
]
|
||||
|
||||
[ping]
|
||||
entrypoint = "admin"
|
||||
|
||||
[retry]
|
||||
|
||||
[acme]
|
||||
email = "quentin@dufour.io"
|
||||
storage = "traefik/acme/account"
|
||||
entryPoint = "https"
|
||||
onHostRule = true
|
||||
|
||||
[acme.httpChallenge]
|
||||
entryPoint = "http"
|
||||
|
||||
[api]
|
||||
entryPoint = "admin"
|
||||
dashboard = true
|
||||
|
||||
[consul]
|
||||
endpoint = "172.17.0.1:8500"
|
||||
watch = true
|
||||
prefix = "traefik"
|
||||
|
||||
[consulCatalog]
|
||||
endpoint = "172.17.0.1:8500"
|
||||
prefix = "traefik"
|
||||
domain = "web.deuxfleurs.fr"
|
||||
exposedByDefault = false
|
||||
|
||||
[metrics]
|
||||
[metrics.prometheus]
|
||||
# -- below is for traefik 1.7 see https://doc.traefik.io/traefik/v1.7/configuration/metrics/
|
||||
entryPoint = "admin"
|
|
@ -1,72 +0,0 @@
|
|||
job "frontend" {
|
||||
datacenters = ["dc1"]
|
||||
type = "service"
|
||||
priority = 80
|
||||
|
||||
group "traefik" {
|
||||
|
||||
network {
|
||||
port "http_port" { static = 80 }
|
||||
port "https_port" { static = 443 }
|
||||
port "admin_port" { static = 8082 }
|
||||
}
|
||||
|
||||
task "server" {
|
||||
driver = "docker"
|
||||
|
||||
config {
|
||||
image = "amd64/traefik:1.7.28"
|
||||
readonly_rootfs = true
|
||||
network_mode = "host"
|
||||
volumes = [
|
||||
"secrets/traefik.toml:/etc/traefik/traefik.toml",
|
||||
]
|
||||
ports = [ "http_port", "https_port", "admin_port" ]
|
||||
}
|
||||
|
||||
resources {
|
||||
memory = 265
|
||||
}
|
||||
|
||||
template {
|
||||
data = file("../config/traefik.toml")
|
||||
destination = "secrets/traefik.toml"
|
||||
}
|
||||
|
||||
service {
|
||||
name = "traefik-http"
|
||||
port = "http_port"
|
||||
tags = [ "(diplonat (tcp_port 80))" ]
|
||||
address_mode = "host"
|
||||
}
|
||||
|
||||
service {
|
||||
name = "traefik-https"
|
||||
port = "https_port"
|
||||
tags = [ "(diplonat (tcp_port 443))" ]
|
||||
address_mode = "host"
|
||||
}
|
||||
|
||||
service {
|
||||
name = "traefik-admin"
|
||||
port = "admin_port"
|
||||
address_mode = "host"
|
||||
check {
|
||||
type = "http"
|
||||
protocol = "http"
|
||||
port = 8082
|
||||
address_mode = "driver"
|
||||
path = "/ping"
|
||||
interval = "60s"
|
||||
timeout = "5s"
|
||||
check_restart {
|
||||
limit = 3
|
||||
grace = "90s"
|
||||
ignore_warnings = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
3
op_guide/README.md
Normal file
3
op_guide/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
All documents from our operations guide have been moved to <https://guide.deuxfleurs.fr/operations/> (or in Git repository `guide.deuxfleurs.fr`).
|
||||
|
||||
Tous les documents de notre guide des opérations ont été déplacés sur <https://guide.deuxfleurs.fr/operations/> (ou dans le dépôt Git `guide.deuxfleurs.fr`).
|
|
@ -1,26 +0,0 @@
|
|||
## 1. Create a LDAP user and assign a password for your service
|
||||
|
||||
Go to guichet.deuxfleurs.fr
|
||||
|
||||
1. Everything takes place in `ou=services,ou=users,dc=deuxfleurs,dc=fr`
|
||||
2. Create a new user, like `johny`
|
||||
3. Generate a random password with `openssl rand -base64 32`
|
||||
4. Hash it with `slappasswd`
|
||||
5. Add a `userpassword` entry with the hash
|
||||
|
||||
This step can also be done using the automated tool `secretmgr.py` in the app folder.
|
||||
|
||||
## 2. Connect to postgres with the admin users
|
||||
|
||||
```bash
|
||||
# 1. Launch ssh tunnel given in the README
|
||||
# 2. Make sure you have postregsql client installed locally
|
||||
psql -h localhost -U postgres -W postgres
|
||||
```
|
||||
|
||||
## 3. Create the binded users with LDAP in postgres + the database
|
||||
|
||||
```sql
|
||||
CREATE USER johny;
|
||||
CREATE DATABASE amazingapp OWNER johny;
|
||||
```
|
|
@ -1,72 +0,0 @@
|
|||
Spawn container:
|
||||
|
||||
```bash
|
||||
docker run -t -i superboum/arm32v7_postgres:v6
|
||||
# OR
|
||||
docker run -t -i superboum/amd64_postgres:v1
|
||||
```
|
||||
|
||||
|
||||
Init with:
|
||||
|
||||
```
|
||||
stolonctl \
|
||||
--cluster-name pissenlit \
|
||||
--store-backend=consul \
|
||||
--store-endpoints http://consul.service.2.cluster.deuxfleurs.fr:8500 \
|
||||
init \
|
||||
'{ "initMode": "new", "pgHBA": [ "host all postgres all md5", "host replication replicator all md5", "host all all all ldap ldapserver=bottin.service.2.cluster.deuxfleurs.fr ldapbasedn=\"ou=users,dc=deuxfleurs, dc=fr\" ldapbinddn=\"<bind_dn>\" ldapbindpasswd=\"<bind_pwd>\" ldapsearchattribute=\"cn\"" ] }'
|
||||
|
||||
```
|
||||
|
||||
Then set appropriate permission on host:
|
||||
|
||||
```
|
||||
chown -R 102:102 /mnt/storage/postgres/
|
||||
```
|
||||
|
||||
(102 is the id of the postgres user used in Docker)
|
||||
It might be improved by staying with root, then chmoding in an entrypoint and finally switching to user 102 before executing user's command.
|
||||
Moreover it would enable the usage of the user namespace that shift the UIDs.
|
||||
|
||||
|
||||
|
||||
## Upgrading the cluster
|
||||
|
||||
To retreive the current stolon config:
|
||||
|
||||
```
|
||||
stolonctl spec --cluster-name pissenlit --store-backend consul --store-endpoints http://consul.service.2.cluster.deuxfleurs.fr:8500
|
||||
```
|
||||
|
||||
The important part for the LDAP:
|
||||
|
||||
```
|
||||
{
|
||||
"pgHBA": [
|
||||
"host all postgres all md5",
|
||||
"host replication replicator all md5",
|
||||
"host all all all ldap ldapserver=bottin.service.2.cluster.deuxfleurs.fr ldapbasedn=\"ou=users,dc=deuxfleurs,dc=fr\" ldapbinddn=\"cn=admin,dc=deuxfleurs,dc=fr\" ldapbindpasswd=\"<REDACTED>\" ldapsearchattribute=\"cn\""
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Once a patch is writen:
|
||||
|
||||
```
|
||||
stolonctl --cluster-name pissenlit --store-backend consul --store-endpoints http://consul.service.2.cluster.deuxfleurs.fr:8500 update --patch -f /tmp/patch.json
|
||||
```
|
||||
|
||||
## Log
|
||||
|
||||
- 2020-12-18 Activate pg\_rewind in stolon
|
||||
|
||||
```
|
||||
stolonctl --cluster-name pissenlit --store-backend consul --store-endpoints http://consul.service.2.cluster.deuxfleurs.fr:8500 update --patch '{ "usePgrewind" : true }'
|
||||
```
|
||||
|
||||
- 2021-03-14 Increase proxy timeout to cope with consul latency spikes
|
||||
|
||||
```
|
||||
stolonctl --cluster-name pissenlit --store-backend consul --store-endpoints http://consul.service.2.cluster.deuxfleurs.fr:8500 update --patch '{ "proxyTimeout" : "120s" }'
|
||||
```
|
|
@ -1,60 +0,0 @@
|
|||
# How to setup NextCloud
|
||||
|
||||
## First setup
|
||||
|
||||
It's complicated.
|
||||
|
||||
First, create a service user `nextcloud` and a database `nextcloud` it owns. Also create a Garage access key and bucket `nextcloud` it is allowed to use.
|
||||
|
||||
Fill in the following Consul keys with actual values:
|
||||
|
||||
```
|
||||
secrets/nextcloud/db_user
|
||||
secrets/nextcloud/db_pass
|
||||
secrets/nextcloud/garage_access_key
|
||||
secrets/nextcloud/garage_secret_key
|
||||
```
|
||||
|
||||
Create the following Consul keys with empty values:
|
||||
|
||||
```
|
||||
secrets/nextcloud/instance_id
|
||||
secrets/nextcloud/password_salt
|
||||
secrets/nextcloud/secret
|
||||
```
|
||||
|
||||
Start the nextcloud.hcl nomad service. Enter the container and call `occ maintenance:install` with the correct database parameters as user `www-data`.
|
||||
A possibility: call the admin user `nextcloud` and give it the same password as the `nextcloud` service user.
|
||||
|
||||
Cat the newly generated `config.php` file and copy the instance id, password salt, and secret from there to Consul
|
||||
(they were generated by the install script and we want to keep them).
|
||||
|
||||
Restart the Nextcloud Nomad server.
|
||||
|
||||
You should now be able to log in to Nextcloud using the admin user (`nextcloud` if you called it that).
|
||||
|
||||
Go to the apps settings and enable desired apps.
|
||||
|
||||
## Configure LDAP login
|
||||
|
||||
LDAP login has to be configured from the admin interface. First, enable the LDAP authentification application.
|
||||
|
||||
Go to settings > LDAP/AD integration. Enter the following parameters:
|
||||
|
||||
- ldap server: `bottin2.service.2.cluster.deuxfleurs.fr`
|
||||
- bind user: `cn=nextcloud,ou=services,ou=users,dc=deuxfleurs,dc=fr`
|
||||
- bind password: password of the nextcloud service user
|
||||
- base DN for users: `ou=users,dc=deuxfleurs,dc=fr`
|
||||
- check "manually enter LDAP filters"
|
||||
- in the users tab, edit LDAP query and set it to `(&(|(objectclass=inetOrgPerson))(|(memberof=cn=nextcloud,ou=groups,dc=deuxfleurs,dc=fr)))`
|
||||
- in the login attributes tab, edit LDAP query and set it to `(&(&(|(objectclass=inetOrgPerson))(|(memberof=cn=nextcloud,ou=groups,dc=deuxfleurs,dc=fr)))(|(|(mailPrimaryAddress=%uid)(mail=%uid))(|(cn=%uid))))`
|
||||
- in the groups tab, edit the LDAP query and set it to `(|(objectclass=groupOfNames))`
|
||||
- in the advanced tab, enter the "directory setting" section and check/modify the following:
|
||||
- user display name field: `displayname`
|
||||
- base user tree: `ou=users,dc=deuxfleurs,dc=fr`
|
||||
- user search attribute: `cn`
|
||||
- groupe display name field: `displayname`
|
||||
- **base group tree**: `ou=groups,dc=deuxfleurs,dc=fr`
|
||||
- group search attribute: `cn`
|
||||
|
||||
That should be it. Go to the login attributes tab and enter a username (which should have been added to the nextcloud group) to check that nextcloud is able to find it and allows it for login.
|
|
@ -1,20 +0,0 @@
|
|||
## Creating a new Plume user
|
||||
|
||||
1. Bind nomad on your machine with SSH (check the README file at the root of this repo)
|
||||
2. Go to http://127.0.0.1:4646
|
||||
3. Select `plume` -> click `exec` button (top right)
|
||||
4. Select `plume` on the left panel
|
||||
5. Press `enter` to get a bash shell
|
||||
6. Run:
|
||||
|
||||
```bash
|
||||
plm users new \
|
||||
--username alice \
|
||||
--display-name Alice \
|
||||
--bio Just an internet user \
|
||||
--email alice@example.com \
|
||||
--password s3cr3t
|
||||
```
|
||||
|
||||
That's all folks, now you can use your new account at https://plume.deuxfleurs.fr
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
Le 20 janvier free a changé mon IP, un peu comme partout en France.
|
||||
Ça concerne l'IPv4 et le préfixe IPv6.
|
||||
Ici le bon vieux Bortzmoinsbien qui tweet : https://twitter.com/bortzmeyer/status/1351434290916155394
|
||||
|
||||
Max a update tout de suite l'IPv4 mais avec un TTL de 4h le temps de propagation est grand.
|
||||
J'ai réduit les entrées sur les IP à 300 secondes, soit 5 minutes, le minimum chez Gandi, à voir si c'est une bonne idée.
|
||||
Reste à update les IPv6, moins critiques pour le front facing mais utilisées pour le signaling en interne...
|
||||
|
||||
## Le fameux signaling
|
||||
Ça pose un gros problème avec Nomad (et en moindre mesure avec Consul).
|
||||
En effet, Nomad utilise l'IPv6 pour communiquer, il faut donc changer les IPs de tous les noeuds.
|
||||
Problème ! On peut pas faire la migration au fur et à mesure car, changeant d'IP, les noeuds ne seront plus en mesure de communiquer.
|
||||
On n'a pas envie de supprimer le cluster et d'en créer un nouveau car ça voudrait dire tout redéployer ce qui est long également (tous les fichiers HCL pour Nomad, tout le KV pour consul).
|
||||
On ne peut pas non plus la faire à la bourrin en stoppant tous les cluster, changer son IP, puis redémarrer.
|
||||
Enfin si, Consul accepte mais pas Nomad, qui lui va chercher à communiquer avec les anciennes IP et n'arrivera jamais à un consensus.
|
||||
|
||||
Au passage j'en ai profité pour changer le nom des noeuds car la dernière fois, Nomad n'avait PAS DU TOUT apprécié qu'un noeud ayant le même nom change d'IP. Ceci dit, si on utilise de facto le `peers.json` c'est peut être pas problématique. À tester.
|
||||
|
||||
Du coup, après moult réflexions, la silver bullet c'est la fonction outage recovery de nomad (consul l'a aussi au besoin).
|
||||
Elle est ici : https://learn.hashicorp.com/tutorials/consul/recovery-outage
|
||||
En gros, il faut arrêter tous les nodes.
|
||||
Ensuite créer un fichier à ce path : `/var/lib/nomad/server/raft/peers.json`
|
||||
Ne vous laissez pas perturber par le fichier `peers.info` à côté, il ne faut pas le toucher.
|
||||
Après la grande question c'est de savoir si le cluster est en Raft v2 ou Raft v3.
|
||||
Bon ben nous on était en Raft v2. Si vous vous trompez, au redémarrage Nomad va crasher avec une sale erreur :
|
||||
|
||||
```
|
||||
nomad: failed to start Raft: error="recovery failed to parse peers.json: json: cannot unmarshal string into Go value of type raft.configEntry"
|
||||
```
|
||||
|
||||
(je me suis trompé bien sûr).
|
||||
Voilà, après il ne vous reste plus qu'à redémarrer et suivre les logs, cherchez bien la ligne où il dit qu'il a trouvé le peers.json.
|
||||
|
||||
## Les trucs à pas oublier
|
||||
|
||||
- Reconfigurer le backend KV de traefik (à voir à utiliser des DNS plutôt du coup)
|
||||
- Reconfigurer l'IPv4 publique annoncée à Jitsi
|
||||
|
||||
## Ce qui reste à faire
|
||||
|
||||
- Mettre à jour les entrées DNS IPv6, ce qui devrait créer :
|
||||
- digitale.machine.deuxfleurs.fr
|
||||
- datura.machine.deuxfleurs.fr
|
||||
- drosera.machine.deuxfleurs.fr
|
||||
- Mettre à jour l'instance garage sur io
|
|
@ -1,15 +0,0 @@
|
|||
```
|
||||
curl http://127.0.0.1:8500/v1/kv/traefik/acme/account/object?raw > traefik.gzip
|
||||
gunzip -c traefik.gzip > traefik.json
|
||||
cat traefik.json | jq '.DomainsCertificate.Certs[] | .Certificate.Domain, .Domains.Main'
|
||||
# "alps.deuxfleurs.fr"
|
||||
# "alps.deuxfleurs.fr"
|
||||
# "cloud.deuxfleurs.fr"
|
||||
# "cloud.deuxfleurs.fr"
|
||||
# chaque NDD doit apparaitre 2x à la suite sinon fix comme suit
|
||||
cat traefik.json | jq > traefik-new.json
|
||||
vim traefik-new.json
|
||||
# enlever les certifs corrompus, traefik les renouvellera automatiquement au démarrage
|
||||
gzip -c traefik-new.json > traefik-new.gzip
|
||||
curl --request PUT --data-binary @traefik-new.gzip http://127.0.0.1:8500/v1/kv/traefik/acme/account/object
|
||||
```
|
|
@ -1,93 +0,0 @@
|
|||
How to update Matrix?
|
||||
=====================
|
||||
|
||||
## 1. Build the new containers
|
||||
|
||||
Often, I update Riot Web and Synapse at the same time.
|
||||
|
||||
|
||||
* Open `app/docker-compose.yml` and locate `riot` (the Element Web service) and `synapse` (the Matrix Synapse server). There are two things you need to do for each service:
|
||||
|
||||
* Set the `VERSION` argument to the target service version (e.g. `1.26.0` for Synapse). This argument is then used to template the Dockerfile.
|
||||
|
||||
The `VERSION` value should match a github release, the link to the corresponding release page is put as a comment next to the variable in the compose file;
|
||||
|
||||
* Tag the image with a new incremented version tag. For example: `superboum/amd64_riotweb:v17` will become `superboum/amd64_riotweb:v18`.
|
||||
|
||||
We use the docker hub to store our images. So, if you are not `superboum` you must change the name with your own handle, eg. `john/amd64_riotweb:v18`. This requires that you registered an account (named `john`) on https://hub.docker.com.
|
||||
|
||||
|
||||
So, from now we expect you have:
|
||||
|
||||
* changed the `VERSION` value and `image` name/tag of `riot`
|
||||
* changed the `VERSION` value and `image` name/tag of `synapse`
|
||||
|
||||
From the `/app` folder, you can now simply build and push the new images:
|
||||
|
||||
```bash
|
||||
docker-compose build riot synapse
|
||||
```
|
||||
|
||||
And then send them to the docker hub:
|
||||
|
||||
```
|
||||
docker-compose push riot synapse
|
||||
```
|
||||
|
||||
Don't forget to commit and push your changes before doing anything else!
|
||||
|
||||
## 2. Deploy the new containers
|
||||
|
||||
Now, we will edit the deployment file `app/im/deploy/im.hcl`.
|
||||
|
||||
Find where the image is defined in the file, for example Element-web will look like that:
|
||||
|
||||
|
||||
```hcl
|
||||
group "riotweb" {
|
||||
count = 1
|
||||
|
||||
task "server" {
|
||||
driver = "docker"
|
||||
config {
|
||||
image = "superboum/amd64_riotweb:v17"
|
||||
port_map {
|
||||
web_port = 8043
|
||||
}
|
||||
```
|
||||
|
||||
And replace the `image =` entry with its new version created above.
|
||||
Do the same thing for the `synapse` service.
|
||||
|
||||
Now, you need a way to access the cluster to deploy this file.
|
||||
To do this, you must bind nomad on your machine through a SSH tunnel.
|
||||
Check the end of [the parent `README.md`](../README.md) to do it.
|
||||
If you have access to the Nomad web UI when entering http://127.0.0.1:4646
|
||||
you are ready to go.
|
||||
|
||||
You must have installed the Nomad command line tool on your machine (also explained in [the parent `README.md`](../README.md)).
|
||||
|
||||
Now, on your machine and from the `app/im/deploy` folder, you must be able to run:
|
||||
|
||||
```
|
||||
nomad plan im.hcl
|
||||
```
|
||||
|
||||
Check that the proposed diff corresponds to what you have in mind.
|
||||
If it seems OK, just copy paste the `nomad job run ... im.hcl` command proposed as part of the output of the `nomad plan` command.
|
||||
|
||||
From now, it will take around ~2 minutes to deploy the new images.
|
||||
You can follow the deployment from the Nomad UI.
|
||||
Bear in mind that, once the deployment is done on Nomad, you may still need to wait some minutes that Traefik refreshes its configuration.
|
||||
|
||||
If everythings worked as intended, you can commit and push your deployment file.
|
||||
|
||||
If something went wrong, you must rollback your deployment.
|
||||
|
||||
1. First, find a working deployment with [nomad job history](https://www.nomadproject.io/docs/commands/job/history)
|
||||
2. Revert to this deployment with [nomad job revert](https://www.nomadproject.io/docs/commands/job/revert)
|
||||
|
||||
Now, if the deployment failed, you should probably investigate what went wrong offline.
|
||||
I built a test stack with docker-compose in `app/<service>/integration` that should help you out (for now, test suites are only written for plume and jitsi).
|
||||
|
||||
|
|
@ -4,9 +4,12 @@
|
|||
|
||||
For each machine, **one by one** do:
|
||||
- Check that cluster is healthy
|
||||
- Check gluster
|
||||
- `sudo gluster peer status`
|
||||
- `sudo gluster volume status all` (check Online Col, only `Y` must appear)
|
||||
- Check garage
|
||||
- check that all nodes are online `docker exec -ti xxx /garage status`
|
||||
- check that tables are in sync `docker exec -ti 63a4d7ecd795 /garage repair --yes tables`
|
||||
- check garage logs
|
||||
- no unknown errors or resync should be in progress
|
||||
- the following line must appear `INFO garage_util::background > Worker exited: Repair worker`
|
||||
- Check that Nomad is healthy
|
||||
- `nomad server members`
|
||||
- `nomad node status`
|
||||
|
@ -17,5 +20,5 @@ For each machine, **one by one** do:
|
|||
- Run `nomad node drain -enable -force -self`
|
||||
- Reboot
|
||||
- Run `nomad node drain -self -disable`
|
||||
- Check that cluster is healthy
|
||||
- Check that cluster is healthy (basically the whole first point)
|
||||
|
||||
|
|
|
@ -14,6 +14,12 @@
|
|||
- role: network
|
||||
tags: net
|
||||
|
||||
# UNSAFE!! This section configures glusterfs. Once done, don't run it ever again as it may break stuff.
|
||||
# - role: storage
|
||||
# tags: sto
|
||||
- hosts: extra_nodes
|
||||
serial: 1
|
||||
roles:
|
||||
- role: common
|
||||
tags: base
|
||||
- role: users
|
||||
tags: account
|
||||
- role: network
|
||||
tags: net
|
||||
|
|
|
@ -7,7 +7,7 @@ cluster_nodes:
|
|||
ipv4: 192.168.0.2
|
||||
gatewayv4: 192.168.0.254
|
||||
ipv6: 2a01:e0a:260:b5b0::2
|
||||
gatewayv6: 2a01:e34:ec5c:dbe0::1
|
||||
gatewayv6: 2a01:e0a:260:b5b0::1
|
||||
interface: eno1
|
||||
dns_1: 212.27.40.240
|
||||
dns_2: 212.27.40.241
|
||||
|
@ -39,6 +39,8 @@ cluster_nodes:
|
|||
dns_2: 212.27.40.241
|
||||
ansible_python_interpreter: python3
|
||||
|
||||
extra_nodes:
|
||||
hosts:
|
||||
io:
|
||||
ansible_host: io.machine.deuxfleurs.fr
|
||||
ansible_port: 22
|
||||
|
@ -46,7 +48,7 @@ cluster_nodes:
|
|||
ipv4: 192.168.1.57
|
||||
gatewayv4: 192.168.1.1
|
||||
ipv6: 2a01:e0a:5e4:1d0::57
|
||||
gatewayv6: 2a01:e0a:5e4:1d0::1
|
||||
gatewayv6: 2a01:e0a:5e4:1d0::2
|
||||
interface: enp2s0
|
||||
dns_1: 9.9.9.10
|
||||
dns_2: 149.112.112.10
|
||||
|
|
|
@ -43,6 +43,15 @@
|
|||
- ethtool
|
||||
- pciutils
|
||||
- pv
|
||||
- zstd
|
||||
- miniupnpc
|
||||
- rsync
|
||||
- ncdu
|
||||
- smartmontools
|
||||
- ioping
|
||||
- lm-sensors
|
||||
- netcat
|
||||
- sysstat
|
||||
state: present
|
||||
|
||||
- name: "Passwordless sudo"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
- name: "Set consul version"
|
||||
set_fact:
|
||||
consul_version: 1.9.1
|
||||
consul_version: 1.11.4
|
||||
|
||||
- name: "Download and install Consul for x86_64"
|
||||
unarchive:
|
||||
|
|
|
@ -10,12 +10,12 @@
|
|||
-A INPUT -p tcp --dport 22 -j ACCEPT
|
||||
|
||||
# Diplonat needs everything open to communicate with IGD with the router
|
||||
-A INPUT -s 192.168.1.254 -j ACCEPT
|
||||
-A INPUT -s 192.168.0.254 -j ACCEPT
|
||||
|
||||
# Cluster
|
||||
{% for selected_host in groups['cluster_nodes'] %}
|
||||
-A INPUT -s {{ hostvars[selected_host]['ipv4'] }} -j ACCEPT
|
||||
{% endfor %}
|
||||
-A INPUT -s 192.168.0.2 -j ACCEPT
|
||||
-A INPUT -s 192.168.0.3 -j ACCEPT
|
||||
-A INPUT -s 192.168.0.4 -j ACCEPT
|
||||
|
||||
# Local
|
||||
-A INPUT -i docker0 -j ACCEPT
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
-A INPUT -p tcp --dport 22 -j ACCEPT
|
||||
|
||||
# Cluster
|
||||
{% for selected_host in groups['cluster_nodes'] %}
|
||||
-A INPUT -s {{ hostvars[selected_host]['ipv6'] }} -j ACCEPT
|
||||
{% endfor %}
|
||||
-A INPUT -s 2a01:e0a:260:b5b0::2 -j ACCEPT
|
||||
-A INPUT -s 2a01:e0a:260:b5b0::3 -j ACCEPT
|
||||
-A INPUT -s 2a01:e0a:260:b5b0::4 -j ACCEPT
|
||||
|
||||
# Local
|
||||
-A INPUT -i docker0 -j ACCEPT
|
||||
|
@ -27,7 +27,7 @@
|
|||
|
||||
# Who is part of our trusted net?
|
||||
# Max@Bruxelles
|
||||
-A DEUXFLEURS-TRUSTED-NET -s 2a02:1811:3606:4800::0/64 -j DEUXFLEURS-TRUSTED-PORT
|
||||
-A DEUXFLEURS-TRUSTED-NET -s 2a02:1811:3612:b300::0/64 -j DEUXFLEURS-TRUSTED-PORT
|
||||
# Max@Suresnes
|
||||
-A DEUXFLEURS-TRUSTED-NET -s 2a01:e0a:183:7be2::0/64 -j DEUXFLEURS-TRUSTED-PORT
|
||||
# Max@OVH
|
||||
|
@ -36,10 +36,20 @@
|
|||
-A DEUXFLEURS-TRUSTED-NET -s 2a01:e0a:5e4:1d0::0/64 -j DEUXFLEURS-TRUSTED-PORT
|
||||
# ADRN@Gandi
|
||||
-A DEUXFLEURS-TRUSTED-NET -s 2001:4b98:dc0:41:216:3eff:fe9b:1afb/128 -j DEUXFLEURS-TRUSTED-PORT
|
||||
# ADRN@Lille
|
||||
-A DEUXFLEURS-TRUSTED-NET -s 2a01:e0a:e4:2dd0::0/64 -j DEUXFLEURS-TRUSTED-PORT
|
||||
# Quentin@Rennes
|
||||
-A DEUXFLEURS-TRUSTED-NET -s 2a01:e35:2fdc:dbe0::0/64 -j DEUXFLEURS-TRUSTED-PORT
|
||||
# Erwan@Rennes
|
||||
-A DEUXFLEURS-TRUSTED-NET -s 2a01:e0a:260:b5b0::0/64 -j DEUXFLEURS-TRUSTED-PORT
|
||||
# LX@Orsay
|
||||
-A DEUXFLEURS-TRUSTED-NET -s 2a06:a004:3025:1::0/64 -j DEUXFLEURS-TRUSTED-PORT
|
||||
-A DEUXFLEURS-TRUSTED-NET -s 2a06:a003:515d:1::0/64 -j DEUXFLEURS-TRUSTED-PORT
|
||||
-A DEUXFLEURS-TRUSTED-NET -s 2001:910:1204:1::0/64 -j DEUXFLEURS-TRUSTED-PORT
|
||||
# Zorun@Nantes
|
||||
-A DEUXFLEURS-TRUSTED-NET -s 2a00:5881:4008::/56 -j DEUXFLEURS-TRUSTED-PORT
|
||||
# Quentin@Lyon
|
||||
-A DEUXFLEURS-TRUSTED-NET -s 2a01:e0a:28f:5e60::/64 -j DEUXFLEURS-TRUSTED-PORT
|
||||
# Source address is not trusted
|
||||
-A DEUXFLEURS-TRUSTED-NET -j RETURN
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
- name: "Set nomad version"
|
||||
set_fact:
|
||||
nomad_version: 1.0.2
|
||||
nomad_version: 1.2.6
|
||||
|
||||
- name: "Download and install Nomad for x86_64"
|
||||
unarchive:
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
---
|
||||
- name: umount gluster
|
||||
shell: umount --force --lazy /mnt/glusterfs ; true
|
|
@ -1,72 +0,0 @@
|
|||
- name: "Add GlusterFS Repo Key"
|
||||
apt_key:
|
||||
url: https://download.gluster.org/pub/gluster/glusterfs/5/rsa.pub
|
||||
state: present
|
||||
|
||||
- name: "Add GlusterFS official repository"
|
||||
apt_repository:
|
||||
repo: "deb [arch=amd64] https://download.gluster.org/pub/gluster/glusterfs/5/LATEST/Debian/buster/amd64/apt buster main"
|
||||
state: present
|
||||
filename: gluster
|
||||
|
||||
- name: "Install GlusterFS"
|
||||
apt:
|
||||
name:
|
||||
- glusterfs-server
|
||||
- glusterfs-client
|
||||
state: present
|
||||
|
||||
- name: "Ensure Gluster Daemon started and enabled"
|
||||
service:
|
||||
name: glusterd
|
||||
enabled: yes
|
||||
state: started
|
||||
|
||||
- name: "Create directory for GlusterFS bricks"
|
||||
file: path=/mnt/storage/glusterfs/brick1 recurse=yes state=directory
|
||||
|
||||
- name: "Create GlusterFS volumes"
|
||||
gluster_volume:
|
||||
state: present
|
||||
name: donnees
|
||||
bricks: /mnt/storage/glusterfs/brick1/g1
|
||||
#rebalance: yes
|
||||
redundancies: 1
|
||||
disperses: 3
|
||||
#replicas: 3
|
||||
force: yes
|
||||
options:
|
||||
client.event-threads: "8"
|
||||
server.event-threads: "8"
|
||||
performance.stat-prefetch: "on"
|
||||
nfs.disable: "on"
|
||||
features.cache-invalidation: "on"
|
||||
performance.client-io-threads: "on"
|
||||
config.transport: tcp
|
||||
performance.quick-read: "on"
|
||||
performance.io-cache: "on"
|
||||
nfs.export-volumes: "off"
|
||||
cluster.lookup-optimize: "on"
|
||||
|
||||
cluster: "{% for selected_host in groups['cluster_nodes'] %}{{ hostvars[selected_host]['ipv4'] }}{{ ',' if not loop.last else '' }}{% endfor %}"
|
||||
run_once: true
|
||||
|
||||
- name: "Create mountpoint"
|
||||
file: path=/mnt/glusterfs recurse=yes state=directory
|
||||
|
||||
- name: "Flush handlers (umount glusterfs and restart ganesha)"
|
||||
meta: flush_handlers
|
||||
|
||||
- name: "Add fstab entry"
|
||||
tags: gluster-fstab
|
||||
mount:
|
||||
path: /mnt/glusterfs
|
||||
src: "{{ ipv4 }}:/donnees"
|
||||
fstype: glusterfs
|
||||
opts: "defaults,_netdev,noauto,x-systemd.automount"
|
||||
state: present
|
||||
|
||||
- name: Mount everything
|
||||
command: mount -a
|
||||
args:
|
||||
warn: no
|
|
@ -25,7 +25,7 @@ active_users:
|
|||
- 'florian-key2.pub'
|
||||
|
||||
- username: 'adrien'
|
||||
is_admin: false
|
||||
is_admin: true
|
||||
ssh_keys:
|
||||
- 'adrien-key1.pub'
|
||||
|
||||
|
|
Loading…
Reference in a new issue