From 3ec4318d9ed33c17a5c82353d4c5d184908d2e83 Mon Sep 17 00:00:00 2001 From: ghe0 Date: Mon, 26 May 2025 04:11:24 +0300 Subject: [PATCH] network overlay and production surrealdb setup --- README.md | 3 +- create_ssh_config.sh | 30 +++++++ overlay-network/README.md | 17 ++++ overlay-network/create_vms.sh | 14 +++ overlay-network/setup_wg_mesh.sh | 98 +++++++++++++++++++++ overlay-network/vm_configs/template-1.yaml | 10 +++ overlay-network/vm_configs/template-2.yaml | 10 +++ overlay-network/vm_configs/template-3.yaml | 10 +++ overlay-network/vm_configs/template-n.yaml | 10 +++ overlay-network/vm_configs/template-x.yaml | 10 +++ overlay-network/wireguard_full_mesh.png | Bin 0 -> 39780 bytes surrealdb_tikv_prod/README.md | 32 +++++++ surrealdb_tikv_prod/deploy_db.sh | 43 +++++++++ surrealdb_tikv_prod/deploy_nodes.sh | 46 ++++++++++ surrealdb_tikv_prod/prepare_bastion.sh | 59 +++++++++++++ surrealdb_tikv_prod/prod_cluster.yaml | 24 +++++ surrealdb_tikv_prod/staging_cluster.yaml | 24 +++++ surrealdb_tikv_prod/surrealdb.service | 19 ++++ 18 files changed, 458 insertions(+), 1 deletion(-) create mode 100755 create_ssh_config.sh create mode 100644 overlay-network/README.md create mode 100755 overlay-network/create_vms.sh create mode 100755 overlay-network/setup_wg_mesh.sh create mode 100644 overlay-network/vm_configs/template-1.yaml create mode 100644 overlay-network/vm_configs/template-2.yaml create mode 100644 overlay-network/vm_configs/template-3.yaml create mode 100644 overlay-network/vm_configs/template-n.yaml create mode 100644 overlay-network/vm_configs/template-x.yaml create mode 100644 overlay-network/wireguard_full_mesh.png create mode 100644 surrealdb_tikv_prod/README.md create mode 100755 surrealdb_tikv_prod/deploy_db.sh create mode 100755 surrealdb_tikv_prod/deploy_nodes.sh create mode 100644 surrealdb_tikv_prod/prepare_bastion.sh create mode 100644 surrealdb_tikv_prod/prod_cluster.yaml create mode 100644 surrealdb_tikv_prod/staging_cluster.yaml create mode 100644 surrealdb_tikv_prod/surrealdb.service diff --git a/README.md b/README.md index 05dfdea..b392199 100644 --- a/README.md +++ b/README.md @@ -3,5 +3,6 @@ This repository has various deployment examples of real world software to the DeTEE network. The examples currently include: - [Gitea on DeTEE](https://gitea.detee.cloud/general/examples/src/branch/master/gitea/deploy_gitea.sh) - A small bash script that deploys a Gitea server (just like this one) to a VM on DeTEE - [Ansible Postgres](https://gitea.detee.cloud/general/examples/src/branch/master/ansible-postgres) - Deploy a Postgres DB and a read replica via Ansible to two DeTEE VMs. -- [Wireguard Overlay](https://gitea.detee.cloud/general/examples/src/branch/master/wireguard-bastion) - Hide resources behind VPN, by leveraging VM deployments on DeTEE. +- [Wireguard DMZ](https://gitea.detee.cloud/general/examples/src/branch/master/wireguard-bastion) - Hide resources behind WireGuard VPN, by leveraging VM deployments on DeTEE. +- [Overlay Network](https://gitea.detee.cloud/general/examples/src/branch/master/overlay-network) - Automated deployment of an encrypted network overlay (full-mesh between VMs). - [Kubernetes (k3s)](https://gitea.detee.cloud/general/examples/src/branch/master/kubernetes) - Use k3s to deploy 5 Kubernetes nodes to DeTEE VMs, forming a small cluster. diff --git a/create_ssh_config.sh b/create_ssh_config.sh new file mode 100755 index 0000000..838792c --- /dev/null +++ b/create_ssh_config.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# This script will populate ~/.ssh/conf.d/detee.conf with SSH data for all DeTEE VMs. +# After running the script, you will be able to SSH into all VMs by using the hostname + +mkdir -p ~/.ssh/conf.d/ +config="${HOME}/.ssh/conf.d/detee.conf" +echo > "$config" +export FORMAT=YAML + +process_vm() { + vm_id="$1" + local tmp="/tmp/detee_vm_ssh_details" + detee-cli vm ssh $vm_id --just-print > $tmp || return + { + echo Host $(grep 'hostname: ' $tmp | awk '{ print $2 }' ) + echo " User root" + echo " Hostname $(grep 'ip: ' $tmp | awk '{ print $2 }' )" + echo " Port $(grep 'port: ' $tmp | cut -d "'" -f2 )" + echo + } >> $config +} + +detee-cli vm list | grep uuid | awk '{ print $NF}' | + while IFS= read -r vm_id; do + process_vm "$vm_id" +done + +grep 'Include ~/.ssh/conf.d/*.conf' "${HOME}/.ssh/config" > /dev/null 2>&1 || + echo 'Include ~/.ssh/conf.d/*.conf' >> "${HOME}/.ssh/config" diff --git a/overlay-network/README.md b/overlay-network/README.md new file mode 100644 index 0000000..0a08605 --- /dev/null +++ b/overlay-network/README.md @@ -0,0 +1,17 @@ +# Overlay Network + +These scripts allow you to create an overlay network on top of DeTEE VMs. These +VMs do not need a public IP, as the VPN mesh will use the forwarded port. +Every VM in the network will get an IP in the subnet `10.254.254.0/24`. + + +The VMs will be connected in a full-mesh topology, meaning each VM can +communicate with each other VM directly. + +![full mesh](./wireguard_full_mesh.png) + +To create the VMs, run `./create_vms.sh`. + +To deploy the network overlay, run `./setup_wg_mesh.sh`. This will create an +overlay on top of all the VMs created previously using the `./create_vms.sh` +script, assigning IPs in alphabetical order. diff --git a/overlay-network/create_vms.sh b/overlay-network/create_vms.sh new file mode 100755 index 0000000..1037887 --- /dev/null +++ b/overlay-network/create_vms.sh @@ -0,0 +1,14 @@ +#!/bin/bash +script_dir=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +cd $script_dir +set -e +export FORMAT=YAML +mkdir -p tmp/vms + +for vm_config in vm_configs/*; do + vm_name=$(echo $vm_config | cut -d '/' -f2 | cut -d '.' -f1) + detee-cli vm deploy --from-yaml $vm_config > tmp/vms/${vm_name}_install.yaml && + echo "The VM $vm_name got created." & +done + +wait diff --git a/overlay-network/setup_wg_mesh.sh b/overlay-network/setup_wg_mesh.sh new file mode 100755 index 0000000..58697e3 --- /dev/null +++ b/overlay-network/setup_wg_mesh.sh @@ -0,0 +1,98 @@ +#!/bin/bash +script_dir=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +cd $script_dir +set -e +export FORMAT=YAML +mkdir -p tmp/wg +mkdir -p tmp/logs +rm tmp/vms/*inspect.yaml || true +vms=() + +# inspect VMs +for vm_config in $(grep -r uuid: tmp/vms/ | awk '{ print $2}'); do + vm_id=$(echo $vm_config | cut -d '/' -f2 | cut -d '.' -f1) + detee-cli vm inspect $vm_id > tmp/vms/${vm_id}_inspect.yaml + vm_name=$(grep 'hostname: ' tmp/vms/${vm_id}_inspect.yaml | + awk '{ print $2 }') + mv tmp/vms/${vm_id}_inspect.yaml tmp/vms/${vm_name}_inspect.yaml +done + +# define VM object +vm_count=0 +new_vm() { + (( vm_count++ )) || true + local vm_name="$1" + local vm_id="vm$vm_count" + + local vm_install_data="tmp/vms/${vm_name}_install.yaml" + local vm_inspect_data="tmp/vms/${vm_name}_inspect.yaml" + + vm_node_ip=$(grep 'ip: ' $vm_install_data | awk '{ print $2 }') + vm_port=$(grep exposed_ports -A 1 $vm_inspect_data | tail -1 | grep -oE "[0-9]*") + wg_privkey=$(wg genkey) + wg_pubkey=$(echo $wg_privkey | wg pubkey) + + declare -gA "$vm_id" + eval "$vm_id[id]=$vm_count" + eval "$vm_id[name]=$vm_name" + eval "$vm_id[port]=$vm_port" + eval "$vm_id[node_ip]=$vm_node_ip" + eval "$vm_id[private_ip]=10.254.254.$vm_count" + eval "$vm_id[wg_priv]=$wg_privkey" + eval "$vm_id[wg_pub]=$wg_pubkey" + + vms+=("$vm_id") +} + +# loops over all VMs +for vm_install_file in tmp/vms/*_install.yaml; do + vm_name=$(echo $vm_install_file | cut -d '/' -f3 | cut -d '_' -f1) + new_vm $vm_name +done + +# loops over all VMs in array +for main_vm_loop in "${vms[@]}"; do + declare -n main_vm_ref="$main_vm_loop" + wg_file="tmp/wg/${main_vm_ref[name]}.ini" + { + echo "[Interface]" + echo "Address = "${main_vm_ref[private_ip]}" " + echo "PrivateKey = "${main_vm_ref[wg_priv]}" " + echo "ListenPort = 22" + } > ${wg_file} + + ssh="ssh -p ${main_vm_ref[port]} root@${main_vm_ref[node_ip]}" + $ssh sed -i '/10.254.254./d' /etc/hosts + echo ${main_vm_ref[private_ip]} ${main_vm_ref[name]} | $ssh tee -a /etc/hosts > /dev/null + + for inner_vm_loop in "${vms[@]}"; do + declare -n inner_vm_ref="$inner_vm_loop" + [[ "${inner_vm_ref[id]}" == "${main_vm_ref[id]}" ]] && continue + echo ${inner_vm_ref[private_ip]} ${inner_vm_ref[name]} | $ssh tee -a /etc/hosts > /dev/null + { + echo + echo "[Peer]" + echo "PublicKey = ${inner_vm_ref[wg_pub]}" + echo "Endpoint = ${inner_vm_ref[node_ip]}:${inner_vm_ref[port]}" + echo "AllowedIPs = ${inner_vm_ref[private_ip]}" + echo "PersistentKeepalive = 25" + } >> ${wg_file} + done + echo WireGuard config written to ${wg_file} + + $ssh pacman -Syu --noconfirm > tmp/logs/${main_vm_ref[name]}.log 2>&1 + $ssh pacman -S wireguard-tools --needed --noconfirm >> tmp/logs/${main_vm_ref[name]}.log 2>&1 + echo Packages installed for ${main_vm_ref[name]} + + # TODO: enable this if needed, or delete from code + # $ssh sysctl -w net.ipv4.conf.all.forwarding=1 > /dev/null + cat ${wg_file} | $ssh tee /etc/wireguard/brain.conf > /dev/null + { + $ssh wg-quick down brain || true + $ssh wg-quick up brain || true + $ssh wg-quick up brain || true + $ssh systemctl enable wg-quick@brain || true + } >> tmp/logs/${main_vm_ref[name]}.log 2>&1 + + echo WireGuard started on ${main_vm_ref[name]} +done diff --git a/overlay-network/vm_configs/template-1.yaml b/overlay-network/vm_configs/template-1.yaml new file mode 100644 index 0000000..8afd05f --- /dev/null +++ b/overlay-network/vm_configs/template-1.yaml @@ -0,0 +1,10 @@ +hostname: template-1 +hours: 2 +price: 20000 +location: + country: "FR" +ipv4: !PublishPorts [ ] +public_ipv6: false +vcpus: 4 +memory_mb: 8000 +disk_size_gb: 60 diff --git a/overlay-network/vm_configs/template-2.yaml b/overlay-network/vm_configs/template-2.yaml new file mode 100644 index 0000000..cfdfa7d --- /dev/null +++ b/overlay-network/vm_configs/template-2.yaml @@ -0,0 +1,10 @@ +hostname: template-2 +hours: 2 +price: 20000 +location: + country: "GB" +ipv4: !PublishPorts [ ] +public_ipv6: false +vcpus: 4 +memory_mb: 8000 +disk_size_gb: 60 diff --git a/overlay-network/vm_configs/template-3.yaml b/overlay-network/vm_configs/template-3.yaml new file mode 100644 index 0000000..d79625c --- /dev/null +++ b/overlay-network/vm_configs/template-3.yaml @@ -0,0 +1,10 @@ +hostname: template-3 +hours: 2 +price: 20000 +location: + country: "US" +ipv4: !PublishPorts [ ] +public_ipv6: false +vcpus: 4 +memory_mb: 8000 +disk_size_gb: 60 diff --git a/overlay-network/vm_configs/template-n.yaml b/overlay-network/vm_configs/template-n.yaml new file mode 100644 index 0000000..869f0dd --- /dev/null +++ b/overlay-network/vm_configs/template-n.yaml @@ -0,0 +1,10 @@ +hostname: template-n +hours: 2 +price: 20000 +location: + country: "US" +ipv4: !PublishPorts [ ] +public_ipv6: false +vcpus: 2 +memory_mb: 4400 +disk_size_gb: 20 diff --git a/overlay-network/vm_configs/template-x.yaml b/overlay-network/vm_configs/template-x.yaml new file mode 100644 index 0000000..14b5f83 --- /dev/null +++ b/overlay-network/vm_configs/template-x.yaml @@ -0,0 +1,10 @@ +hostname: template-x +hours: 2 +price: 20000 +location: + country: "FR" +ipv4: !PublishPorts [ ] +public_ipv6: false +vcpus: 4 +memory_mb: 8000 +disk_size_gb: 60 diff --git a/overlay-network/wireguard_full_mesh.png b/overlay-network/wireguard_full_mesh.png new file mode 100644 index 0000000000000000000000000000000000000000..adde9a5558f38511282c67cb883de538a2825481 GIT binary patch literal 39780 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ν#{uB1etno9|Q5;5*%2y{lkyvH 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 wN*u@>olBrAo2jt}y=`i=S19# /dev/null && pwd ) +cd $script_dir +set -e + +ssh_command() { + vm_name="$1" + vm_file="tmp/overlay-network/tmp/vms/${vm_name}_install.yaml" + ip="$(grep 'ip: ' $vm_file | awk '{ print $NF }')" + port="$(grep 'port: ' $vm_file | cut -d "'" -f2)" + echo ssh -p $port root@$ip +} +export ssh_command + +ssh_prod_brain1="$(ssh_command prod-brain-1)" +ssh_prod_brain2="$(ssh_command prod-brain-2)" +ssh_prod_brain3="$(ssh_command prod-brain-3)" +ssh_prod_mon="$(ssh_command prod-brain-mon)" +ssh_staging_brain1="$(ssh_command staging-brain-1)" +ssh_staging_brain2="$(ssh_command staging-brain-2)" +ssh_staging_brain3="$(ssh_command staging-brain-3)" +ssh_staging_mon="$(ssh_command staging-brain-mon)" +export ssh_bastion="$(ssh_command bastion-brain)" + +$ssh_bastion 'ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519 -N ""' +bastion_pubkey=$($ssh_bastion cat /root/.ssh/id_ed25519.pub) +echo $bastion_pubkey | $ssh_prod_brain1 tee -a /root/.ssh/authorized_keys +echo $bastion_pubkey | $ssh_prod_brain2 tee -a /root/.ssh/authorized_keys +echo $bastion_pubkey | $ssh_prod_brain3 tee -a /root/.ssh/authorized_keys +echo $bastion_pubkey | $ssh_prod_mon tee -a /root/.ssh/authorized_keys +echo $bastion_pubkey | $ssh_staging_brain1 tee -a /root/.ssh/authorized_keys +echo $bastion_pubkey | $ssh_staging_brain2 tee -a /root/.ssh/authorized_keys +echo $bastion_pubkey | $ssh_staging_brain3 tee -a /root/.ssh/authorized_keys +echo $bastion_pubkey | $ssh_staging_mon tee -a /root/.ssh/authorized_keys + +$ssh_bastion curl -o /root/prepare.sh \ + https://gitea.detee.cloud/general/examples/raw/branch/master/surrealdb_tikv_prod/prepare_bastion.sh +$ssh_bastion curl -o /root/prod_cluster.yaml \ + https://gitea.detee.cloud/general/examples/raw/branch/master/surrealdb_tikv_prod/prod_cluster.yaml +$ssh_bastion curl -o /root/staging_cluster.yaml \ + https://gitea.detee.cloud/general/examples/raw/branch/master/surrealdb_tikv_prod/staging_cluster.yaml +$ssh_bastion chmod +x /root/prepare.sh +$ssh_bastion /root/prepare.sh diff --git a/surrealdb_tikv_prod/deploy_nodes.sh b/surrealdb_tikv_prod/deploy_nodes.sh new file mode 100755 index 0000000..cc7d49d --- /dev/null +++ b/surrealdb_tikv_prod/deploy_nodes.sh @@ -0,0 +1,46 @@ +#!/bin/bash +script_dir=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +cd $script_dir +set -e +export FORMAT=YAML +mkdir -p tmp/ + +rm -rf tmp/overlay-network +cp -r ../overlay-network tmp/ +rm -rf tmp/overlay-network/tmp +cd tmp/overlay-network/vm_configs +find . -maxdepth 1 -type f ! -name 'template-n.yaml' -exec rm -- '{}' + + +setup_vm () { + vm_name="$1" + cp template-n.yaml $vm_name.yaml + sed -i "s/template-n/$vm_name/" $vm_name.yaml + sed -i '/PublishPorts/d' $vm_name.yaml + echo "ipv4: !PublishPorts [ 31337 ]" >> $vm_name.yaml + sed -i '/hours:/d' $vm_name.yaml + echo "hours: 800" >> $vm_name.yaml + country_options=(GB FR CA US) + country=${country_options[RANDOM % ${#country_options[@]}]} + [[ "$vm_name" == "bastion-brain" ]] && country="FR" + sed -i '/location:/d' $vm_name.yaml + sed -i '/country:/d' $vm_name.yaml + echo "location:" >> $vm_name.yaml + echo " country: \"$country\"" >> $vm_name.yaml +} + +setup_vm staging-brain-1 +setup_vm staging-brain-2 +setup_vm staging-brain-3 +setup_vm staging-brain-mon +setup_vm prod-brain-1 +setup_vm prod-brain-2 +setup_vm prod-brain-3 +setup_vm prod-brain-mon +setup_vm bastion-brain +rm template-n.yaml + +cd ../ +echo CREATING VMS! +./create_vms.sh +sleep 10 +./setup_wg_mesh.sh diff --git a/surrealdb_tikv_prod/prepare_bastion.sh b/surrealdb_tikv_prod/prepare_bastion.sh new file mode 100644 index 0000000..4ca645e --- /dev/null +++ b/surrealdb_tikv_prod/prepare_bastion.sh @@ -0,0 +1,59 @@ +#!/bin/bash +curl -sSf https://tiup-mirrors.pingcap.com/install.sh | sh + +LOG_DIR=/root/brain_logs +mkdir -p "$LOG_DIR" + +nodes=( + prod-brain-1 + prod-brain-2 + prod-brain-3 + prod-brain-mon + staging-brain-1 + staging-brain-2 + staging-brain-3 + staging-brain-mon +) + +surreal_pass=$(openssl rand -base64 20 | tr -d '=/+') + +for host in "${nodes[@]}"; do + ssh -o StrictHostKeyChecking=no "$host" -- \ + pacman -S --noconfirm sudo \ + >> "${LOG_DIR}/${host}" 2>&1 + + ssh "$host" iptables -I INPUT 1 \ + -p tcp -s 10.254.254.0/24 \ + -m multiport --dports 20180,9115,9100,20160,2380,2379,8080 \ + -j ACCEPT + + ssh "$host" iptables -I INPUT 1 \ + -p tcp -s 127.0.0.0/8 \ + -m multiport --dports 20180,9115,9100,20160,2380,2379,8080 \ + -j ACCEPT + + ssh "$host" iptables -A INPUT \ + -p tcp \ + -m multiport --dports 20180,9115,9100,20160,2380,2379,8080 \ + -j DROP + + echo $host | grep mon > /dev/null && continue + + curl -sSf https://install.surrealdb.com | ssh $host sh + echo SURREAL_PASS=$surreal_pass | ssh $host tee /opt/surreal_env > /dev/null + ssh $host curl -o /etc/systemd/system/surrealdb.service \ + https://gitea.detee.cloud/general/examples/raw/branch/master/surrealdb_tikv_prod/surrealdb.service + ssh $host systemctl daemon-reload + ssh $host systemctl enable --now surrealdb.service +done + +/root/.tiup/bin/tiup cluster deploy \ + staging-brain v8.5.1 /root/staging_cluster.yaml \ + --user root -i ~/.ssh/id_ed25519 + +/root/.tiup/bin/tiup cluster deploy \ + prod-brain v8.5.1 /root/prod_cluster.yaml \ + --user root -i ~/.ssh/id_ed25519 + +/root/.tiup/bin/tiup cluster start staging-brain --init +/root/.tiup/bin/tiup cluster start prod-brain --init diff --git a/surrealdb_tikv_prod/prod_cluster.yaml b/surrealdb_tikv_prod/prod_cluster.yaml new file mode 100644 index 0000000..77ade6e --- /dev/null +++ b/surrealdb_tikv_prod/prod_cluster.yaml @@ -0,0 +1,24 @@ +global: + user: "brain" + ssh_port: 22 + deploy_dir: "/opt/brain_deployment" + data_dir: "/opt/brain_data" + +pd_servers: + - host: prod-brain-1 + - host: prod-brain-2 + - host: prod-brain-3 + +tikv_servers: + - host: prod-brain-1 + - host: prod-brain-2 + - host: prod-brain-3 + +monitoring_servers: + - host: prod-brain-mon + +grafana_servers: + - host: prod-brain-mon + +alertmanager_servers: + - host: prod-brain-mon diff --git a/surrealdb_tikv_prod/staging_cluster.yaml b/surrealdb_tikv_prod/staging_cluster.yaml new file mode 100644 index 0000000..9fe5563 --- /dev/null +++ b/surrealdb_tikv_prod/staging_cluster.yaml @@ -0,0 +1,24 @@ +global: + user: "brain" + ssh_port: 22 + deploy_dir: "/opt/brain_deployment" + data_dir: "/opt/brain_data" + +pd_servers: + - host: staging-brain-1 + - host: staging-brain-2 + - host: staging-brain-3 + +tikv_servers: + - host: staging-brain-1 + - host: staging-brain-2 + - host: staging-brain-3 + +monitoring_servers: + - host: staging-brain-mon + +grafana_servers: + - host: staging-brain-mon + +alertmanager_servers: + - host: staging-brain-mon diff --git a/surrealdb_tikv_prod/surrealdb.service b/surrealdb_tikv_prod/surrealdb.service new file mode 100644 index 0000000..5efd9b3 --- /dev/null +++ b/surrealdb_tikv_prod/surrealdb.service @@ -0,0 +1,19 @@ +[Unit] +Description=SurrealDB server +After=network-online.target +Wants=network-online.target +Requires=tikv-20160.service +After=tikv-20160.service + +[Service] +Type=simple +EnvironmentFile=/opt/surreal_env +ExecStart=/usr/local/bin/surreal start \ + --user root --pass ${SURREAL_PASS} \ + --bind 0.0.0.0:8080 \ + tikv://127.0.0.1:2379 +Restart=on-failure +RestartSec=15s + +[Install] +WantedBy=multi-user.target