Citadel 0.1.8 (#90)

* Move LND into an app

* Add JWT pubkey module

* Remove old LND dir

* Clean up

* Some cleanups

* WIP: LND app

* Clean up output of ls-installed

* Clean up app system

* Various cleanups

* Fix volume name

* Update dependencies.yml

* Update app-manager

* Fix some minor issues

* Update manager

* Some fixes for the LND app

* Some fixes

* WIP: Caddy

* WIP: More https

* Caddy improvements

* Some more fixes

* Fix caddy port

* Fix for LND app

* Fixes for some apps

* Code cleanups

* Fix entry name

* Fix python

* Update app-manager

* Some Caddy fixes

* Update app-manager

* Fix tor

* Fix Caddy

* Fix caddy

* Minor fix

* Fix

* Fix https

* Update dependencies.yml

* Fix for CLN (#1)

* Update dependencies.yml

* Fix Caddyfile

* Expose IP address to manager

* Update API

* Use API from Docker Hub

* Update dependencies.yml

* Update dependencies.yml

* Update dependencies.yml

* Some fixes

* Minor syntax fix

* How did I even do that?

* Update docker-compose.yml

* Allow restarting Caddy

* Add configure trigger

* Replace configure with a caddy config update

* Update dependencies.yml

* Update Tor

* Update dependencies.yml

* Update dependencies.yml

* Update dependencies.yml

* Latest dashboard

* Move to ghcr.io

* Update 01-run.sh

* Update 01-run.sh

* Update 01-run.sh

* Update dependencies.yml

* Clean up

* Fix mount

* Update mount

* Create .gitkeep

* Dynamic caddy updates

* Update app-cli

* Update dependencies.yml

* Update dependencies.yml

* Remove Lightning logs from debug script

* Update app manager

* Clean up

* Update app-cli

* Citadel 0.1.8

* Remove host gateway
This commit is contained in:
Aaron Dewes 2023-04-16 19:12:12 +00:00 committed by GitHub
parent d09beb6390
commit 8bcb66f0fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 152 additions and 782 deletions

8
.gitignore vendored
View File

@ -15,7 +15,6 @@
.backup
*.pyc
bitcoin/*
lnd/*
tor/*
db/*
statuses/*
@ -33,7 +32,6 @@ docker-compose.override.yml
!db/citadel-seed
db/citadel-seed/*
!db/citadel-seed/.gitkeep
!lnd/.gitkeep
!logs/.gitkeep
!tor/data/.gitkeep
!tor/run/.gitkeep
@ -42,8 +40,6 @@ db/citadel-seed/*
!caddy/.gitkeep
!i2p/.gitkeep
!**/*.license
services/installed.json
services/installed.yml
!**/*.License
events/karen
karen.socket

1
app-data/.gitkeep Normal file
View File

@ -0,0 +1 @@

View File

@ -1,11 +0,0 @@
# SPDX-FileCopyrightText: 2021-2022 Citadel and contributors
#
# SPDX-License-Identifier: GPL-3.0-or-later
# A collection of fully FLOSS app definitions and FLOSS apps for Citadel.
https://github.com/runcitadel/apps 0.1.0
# Some apps modified version of Umbrel apps, and their app definitions aren't FLOSS yet.
# Include them anyway, but as a separate repo.
# Add a # to the line below to disable the repo and only use FLOSS apps.
https://github.com/runcitadel/apps-nonfree v4-stable

View File

@ -7,11 +7,12 @@
import argparse
import json
import os
import time
from lib.manage import (compose, convert_to_upper, createDataDir, deleteData,
download, downloadAll, get_var_safe,
download, downloadAll, downloadNew, get_var_safe,
getAvailableUpdates, getUserData, setInstalled,
setRemoved, update)
setRemoved, update, getAppRegistryEntry)
# Print an error if user is not root
if os.getuid() != 0:
@ -25,10 +26,11 @@ appsDir = os.path.join(nodeRoot, "apps")
appDataDir = os.path.join(nodeRoot, "app-data")
userFile = os.path.join(nodeRoot, "db", "user.json")
legacyScript = os.path.join(nodeRoot, "scripts", "app")
torDataDir = os.path.join(nodeRoot, "tor", "data")
parser = argparse.ArgumentParser(description="Manage apps on your Citadel")
parser.add_argument('action', help='What to do with the app database.', choices=[
"download", "generate", "update", "list-updates", "ls-installed", "install", "uninstall", "stop", "start", "compose", "restart", "get-ip"])
"download", "generate", "update", "list-updates", "ls-installed", "install", "uninstall", "stop", "start", "compose", "restart", "get-ip", "get-implementation"])
parser.add_argument('--verbose', '-v', action='store_true')
parser.add_argument(
'app', help='Optional, the app to perform an action on. (For install, uninstall, stop, start and compose)', nargs='?')
@ -46,6 +48,9 @@ if args.action == "list-updates":
elif args.action == 'download':
downloadAll()
exit(0)
elif args.action == 'download-new':
downloadNew()
exit(0)
elif args.action == 'generate':
update()
exit(0)
@ -64,7 +69,13 @@ elif args.action == 'ls-installed':
with open(userFile, "r") as f:
userData = json.load(f)
if "installedApps" in userData:
print("\n".join(userData["installedApps"]))
with open(os.path.join(appsDir, "virtual-apps.json"), "r") as f:
virtual_apps = json.load(f)
# Print the apps
# Filter out virtual apps (virtual_apps.keys())
for app in userData["installedApps"]:
if app not in virtual_apps.keys():
print(app)
else:
# To match the behavior of the old script, print a newline if there are no apps installed
print("\n")
@ -74,6 +85,29 @@ elif args.action == 'install':
if not args.app:
print("No app provided")
exit(1)
registryEntry = getAppRegistryEntry(args.app)
# If registryEntry is None, fail
if registryEntry is None:
print("App {} does not seem to exist".format(args.app))
exit(1)
if isinstance(registryEntry['hiddenServices'], list):
for entry in registryEntry['hiddenServices']:
if not os.path.exists(os.path.join(torDataDir, entry, "hostname")):
print("Restarting Tor containers...")
try:
os.system("docker restart app-tor app-2-tor app-3-tor")
except:
print("Failed to restart Tor containers")
exit(1)
print("Waiting for Tor containers to restart...")
for i in range(60):
if os.path.exists(os.path.join(torDataDir, entry, "hostname")):
break
time.sleep(1)
else:
print("Tor containers did not restart in time")
exit(1)
update()
with open(os.path.join(appsDir, "virtual-apps.json"), "r") as f:
virtual_apps = json.load(f)
userData = getUserData()
@ -173,7 +207,21 @@ elif args.action == "get-ip":
print("Not an virtual app")
exit(1)
else:
print("Error: Unknown action")
print("See --help for usage")
elif args.action == "get-implementation":
if args.app == "":
print("Missing app")
exit(1)
with open(os.path.join(appsDir, "virtual-apps.json"), "r") as f:
virtual_apps = json.load(f)
userData = getUserData()
implements_service = False
if args.app in virtual_apps:
for implementation in virtual_apps[args.app]:
if "installedApps" in userData and implementation in userData["installedApps"]:
print(implementation)
exit(0)
else:
print("Not an virtual app")
exit(1)
print("Virtual app not found")
exit(1)

View File

@ -2,9 +2,12 @@
#
# SPDX-License-Identifier: GPL-3.0-or-later
version: "3.8"
networks:
default:
name: citadel_main_network
external: true
volumes:
jwt-public-key:
external: true
name: citadel-jwt-public-key

View File

@ -17,7 +17,7 @@ def parse_dotenv(file_path):
value = value.strip('"').strip("'")
envVars[key] = value
else:
print("Error: Invalid line in {}: {}".format(file_path, line))
print("Warning: Invalid line in {}: {}".format(file_path, line))
print("Line should be in the format KEY=VALUE or KEY=\"VALUE\" or KEY='VALUE'")
exit(1)
continue
return envVars

View File

@ -15,8 +15,7 @@ def deriveEntropy(identifier: str):
if os.path.isfile(alternativeSeedFile):
seedFile = alternativeSeedFile
else:
print("No seed file found, exiting...")
exit(1)
raise Exception("No seed file found")
with open(seedFile, "r") as f:
node_seed = f.read().strip()
entropy = subprocess.check_output(

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2021-2022 Citadel and contributors
# SPDX-FileCopyrightText: 2021-2023 Citadel and contributors
#
# SPDX-License-Identifier: GPL-3.0-or-later
@ -19,8 +19,7 @@ from lib.entropy import deriveEntropy
scriptDir = os.path.dirname(os.path.realpath(__file__))
nodeRoot = os.path.join(scriptDir, "..", "..")
appsDir = os.path.join(nodeRoot, "apps")
appSystemDir = os.path.join(nodeRoot, "app-system")
updateIgnore = os.path.join(appsDir, ".updateignore")
appSystemDir = os.path.join(nodeRoot, "app")
appDataDir = os.path.join(nodeRoot, "app-data")
userFile = os.path.join(nodeRoot, "db", "user.json")
with open(os.path.join(nodeRoot, "db", "dependencies.yml"), "r") as file:
@ -28,13 +27,6 @@ with open(os.path.join(nodeRoot, "db", "dependencies.yml"), "r") as file:
dotenv = {}
# Returns a list of every argument after the second one in sys.argv joined into a string by spaces
def getArguments():
arguments = ""
for i in range(3, len(argv)):
arguments += argv[i] + " "
return arguments
def get_var_safe(var_name):
dotenv = parse_dotenv(os.path.join(nodeRoot, ".env"))
if var_name in dotenv:
@ -51,17 +43,6 @@ def get_var(var_name):
print("Error: {} is not defined!".format(var_name))
exit(1)
def getInstalledVirtualApps():
installedApps = []
with open(os.path.join(appsDir, "virtual-apps.json"), "r") as f:
virtual_apps = json.load(f)
userData = getUserData()
for virtual_app in virtual_apps.keys():
for implementation in virtual_apps[virtual_app]:
if "installedApps" in userData and implementation in userData["installedApps"]:
installedApps.append(virtual_app)
return installedApps
# Converts a string to uppercase, also replaces all - with _
def convert_to_upper(string):
return string.upper().replace('-', '_')
@ -71,11 +52,14 @@ def convert_to_upper(string):
def replace_vars(file_content: str):
return re.sub(r'<(.*?)>', lambda m: get_var(convert_to_upper(m.group(1))), file_content)
def update():
os.system("docker run --rm -v {}:/citadel -u 1000:1000 {} /app-cli convert /citadel".format(nodeRoot, dependencies['app-cli']))
print("Generated configuration successfully")
def downloadNew():
os.system("docker run --rm -v {}:/citadel -u 1000:1000 {} /app-cli download-new /citadel".format(nodeRoot, dependencies['app-cli']))
print("Generated configuration successfully")
def downloadAll():
os.system("docker run --rm -v {}:/citadel -u 1000:1000 {} /app-cli download-apps /citadel".format(nodeRoot, dependencies['app-cli']))
print("Generated configuration successfully")
@ -120,11 +104,14 @@ def compose(app, arguments):
"hostname -s 2>/dev/null || echo 'citadel'", shell=True).decode("utf-8").strip() + ".local"
os.environ["APP_HIDDEN_SERVICE"] = subprocess.check_output("cat {} 2>/dev/null || echo 'notyetset.onion'".format(
os.path.join(nodeRoot, "tor", "data", "app-{}/hostname".format(app))), shell=True).decode("utf-8").strip()
os.environ["APP_SEED"] = deriveEntropy("app-{}-seed".format(app))
# Allow more app seeds, with random numbers from 1-5 assigned in a loop
for i in range(1, 6):
os.environ["APP_SEED_{}".format(i)] = deriveEntropy("app-{}-seed{}".format(app, i))
try:
os.environ["APP_SEED"] = deriveEntropy("app-{}-seed".format(app))
# Allow more app seeds, with random numbers from 1-5 assigned in a loop
for i in range(1, 6):
os.environ["APP_SEED_{}".format(i)] = deriveEntropy("app-{}-seed{}".format(app, i))
except: pass
os.environ["APP_DATA_DIR"] = os.path.join(appDataDir, app)
os.environ["CITADEL_APP_DATA"] = appDataDir
# Chown and chmod dataDir to have the owner 1000:1000 and the same permissions as appDir
subprocess.call("chown -R 1000:1000 {}".format(os.path.join(appDataDir, app)), shell=True)
try:
@ -135,10 +122,9 @@ def compose(app, arguments):
subprocess.call("chown -R 33:33 {}".format(os.path.join(appDataDir, app, "data", "nextcloud")), shell=True)
subprocess.call("chmod -R 770 {}".format(os.path.join(appDataDir, app, "data", "nextcloud")), shell=True)
os.environ["BITCOIN_DATA_DIR"] = os.path.join(nodeRoot, "bitcoin")
os.environ["LND_DATA_DIR"] = os.path.join(nodeRoot, "lnd")
os.environ["CITADEL_ROOT"] = nodeRoot
# List all hidden services for an app and put their hostname in the environment
hiddenServices: List[str] = getAppHiddenServices(app)
hiddenServices: List[str] = getAppRegistryEntry(app).get("hiddenServices", [])
for service in hiddenServices:
appHiddenServiceFile = os.path.join(
nodeRoot, "tor", "data", "app-{}-{}/hostname".format(app, service))
@ -196,13 +182,13 @@ def setRemoved(app: str):
with open(userFile, "w") as f:
json.dump(userData, f)
def getAppHiddenServices(app: str):
torDir = os.path.join(nodeRoot, "tor", "data")
# List all subdirectories of torDir which start with app-${APP}-
# but return them without the app-${APP}- prefix
results = []
for subdir in os.listdir(torDir):
if subdir.startswith("app-{}-".format(app)):
results.append(subdir[len("app-{}-".format(app)):])
return results
# Gets the app's registry entry from the registry.json file
# The file is an array of objects, each object is an app's registry entry
# We can filter by the "id" property to get the app's registry entry
def getAppRegistryEntry(app: str):
with open(os.path.join(appsDir, "registry.json")) as f:
registry = json.load(f)
for appRegistryEntry in registry:
if appRegistryEntry["id"] == app:
return appRegistryEntry
return None

View File

@ -1,19 +0,0 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: 2020 Umbrel. https://getumbrel.com
# SPDX-FileCopyrightText: 2021-2022 Citadel and contributors
# SPDX-FileCopyrightText: 2021 https://github.com/o3o3o
#
# SPDX-License-Identifier: GPL-3.0-or-later
set -euo pipefail
CITADEL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/..)"
result=$(docker compose \
--file "${CITADEL_ROOT}/docker-compose.yml" \
--env-file "${CITADEL_ROOT}/.env" \
exec lightning lncli "$@")
# We need to echo with quotes to preserve output formatting
echo "$result"

View File

@ -365,13 +365,8 @@ if [[ "$command" = "configure" ]]; then
fi
if [[ "$2" = "lnd" ]]; then
if $persist; then
edit_file $CITADEL_ROOT/templates/lnd-sample.conf
prompt_apply_config lightning true
else
edit_file $CITADEL_ROOT/lnd/lnd.conf
prompt_apply_config lightning false
fi
edit_file $CITADEL_ROOT/app-data/lnd/lnd.conf
prompt_apply_config lnd-service-1 false
exit
fi

View File

@ -5,6 +5,5 @@
compose: v2.17.2
dashboard: ghcr.io/runcitadel/dashboard:citadel-0.0.10@sha256:54214fbb0e9d7b08771e0c5af912ef1dc4ae1bc1650d8640b74ed01554696beb
manager: harbor.nirvati.org/citadel/api:main@sha256:12d299d8850d830fa5abd0e64c8537dfcbaec662de18376b0d7b01fa59895132
middleware: ghcr.io/runcitadel/middleware:main@sha256:cbd5fd2ab5afe420025c61e276d21c79a004d6148b8dfdd58649adb55907682b
app-cli: harbor.nirvati.org/citadel/app-manager:backports@sha256:e5d54e0559eef96472aca56b9343bfc06f45777283967015dfdbf618d25cadd0
app-cli: harbor.nirvati.org/citadel/app-manager:main@sha256:b841a27f14aa6785a357c4af3632f5fd98dbf2e3b1530e5a6dcd1175fda94f8a
tor: ghcr.io/runcitadel/tor-latest:main@sha256:761948a86f8367238eb61f991cf87094b12a8a772be0eabec00d66164d13075f

View File

@ -76,27 +76,6 @@ services:
networks:
default:
ipv4_address: $BITCOIN_IP
lightning:
container_name: lightning
image: lightninglabs/lnd:v0.15.4-beta@sha256:f5b19812ab7d28faa350838dac4bb88e7fcf9ae905e44d3539be41a97b80ca23
user: 1000:1000
depends_on:
- tor
- bitcoin
volumes:
- ${PWD}/lnd:/data/.lnd
- ${PWD}/walletpassword:/walletpassword
environment:
HOME: /data
restart: on-failure
stop_grace_period: 5m30s
ports:
- 9735:9735
- $LND_REST_PORT:$LND_REST_PORT
- $LND_GRPC_PORT:$LND_GRPC_PORT
networks:
default:
ipv4_address: $LND_IP
dashboard:
container_name: dashboard
image: ghcr.io/runcitadel/dashboard:no-https@sha256:7fc5a5b70496240e6e48a381e8ac3c7978e7343285fda4951c00846580d6216d
@ -118,7 +97,6 @@ services:
- ${PWD}/db:/db
- ${PWD}/events:/events
- ${PWD}/apps:/apps
- ${PWD}/lnd:/lnd:ro
- ${PWD}/statuses:/statuses
- ${PWD}/tor/data:/var/lib/tor/
- jwt-public-key:/jwt-public-key
@ -138,8 +116,6 @@ services:
BITCOIN_RPC_PORT: $BITCOIN_RPC_PORT
BITCOIN_RPC_USER: $BITCOIN_RPC_USER
BITCOIN_RPC_PASSWORD: $BITCOIN_RPC_PASS
LND_CERT_FILE: /lnd/tls.cert
LND_ADMIN_MACAROON_FILE: /lnd/data/chain/bitcoin/${BITCOIN_NETWORK}/admin.macaroon
GITHUB_REPO: runcitadel/core
GITHUB_BRANCH: ${UPDATE_CHANNEL:-"stable"}
VERSION_FILE: /info.json
@ -159,51 +135,6 @@ services:
networks:
default:
ipv4_address: $MANAGER_IP
middleware:
container_name: middleware
image: ghcr.io/runcitadel/middleware:main@sha256:cbd5fd2ab5afe420025c61e276d21c79a004d6148b8dfdd58649adb55907682b
depends_on:
- bitcoin
- lightning
command: sh -c "./wait-for-manager.sh $MANAGER_IP && ././start.sh"
restart: on-failure
volumes:
- ${PWD}/lnd:/lnd
- jwt-public-key:/jwt-public-key
environment:
PORT: '3000'
BITCOIN_HOST: $BITCOIN_IP
RPC_PORT: $BITCOIN_RPC_PORT
RPC_USER: $BITCOIN_RPC_USER
RPC_PASSWORD: $BITCOIN_RPC_PASS
LND_NETWORK: $BITCOIN_NETWORK
LND_HOST: ${LND_IP}
JWT_PUBLIC_KEY_FILE: /jwt-public-key/jwt.pem
DEVICE_HOSTS: ${DEVICE_HOSTS:-"http://citadel.local"}
UNSAFE_REMOVE_CORS_CHECK: true
networks:
default:
ipv4_address: $MIDDLEWARE_IP
neutrino-switcher:
container_name: neutrino-switcher
image: lncm/neutrino-switcher:1.0.5@sha256:3ddf58c5599ba22db8414f2694bfeeba086455d4a19b4955b26c3ae5e967d42a
depends_on:
- bitcoin
- lightning
restart: on-failure
volumes:
- ${PWD}/lnd:/lnd
- ${PWD}/statuses:/statuses
- /var/run/docker.sock:/var/run/docker.sock
environment:
JSONRPCURL: http://${BITCOIN_IP}:${BITCOIN_RPC_PORT}
RPCUSER: $BITCOIN_RPC_USER
RPCPASS: $BITCOIN_RPC_PASS
LND_CONTAINER_NAME: lightning
SLEEPTIME: 3600
networks:
default:
ipv4_address: $NEUTRINO_SWITCHER_IP
i2p:
container_name: i2p
user: 1000:1000

0
events/.gitkeep Normal file
View File

View File

@ -1,7 +1,7 @@
{
"version": "0.1.7",
"name": "Citadel 0.1.7",
"version": "0.1.8",
"name": "Citadel 0.1.8",
"requires": ">=0.1.5",
"isQuickUpdate": false,
"notes": "This update should hopefully fix a few more bugs in Citadel. It also contains a few more changes to prepare the Nirvati release and enables HTTPS for everyone."
"notes": "This update moves LND into an app that can not be uninstalled to make updates in the future easier and to prepare for Nirvati. It also fixes a bug that prevented Tailscale from working properly."
}

View File

@ -1,3 +1,3 @@
# SPDX-FileCopyrightText: 2020 Umbrel. https://getumbrel.com
# SPDX-FileCopyrightText: 2022 Citadel and contributors
#
# SPDX-License-Identifier: GPL-3.0-or-later

4
karen
View File

@ -4,8 +4,8 @@
#
# SPDX-License-Identifier: GPL-3.0-or-later
import socket
import os
import socket
rootDir = os.path.dirname(os.path.abspath(__file__))
os.chdir(rootDir)
@ -27,7 +27,7 @@ while True:
trigger = instructions[1]
instructions.pop(0)
instructions.pop(0)
os.system("events/triggers/{} {}".format(trigger, " ".join(instructions)))
os.system("scripts/triggers/{} {}".format(trigger, " ".join(instructions)))
elif cmd == "exec":
instructions.pop(0)
os.system(" ".join(instructions))

View File

@ -1,20 +0,0 @@
<!--
SPDX-FileCopyrightText: 2020 Umbrel. https://getumbrel.com
SPDX-License-Identifier: GPL-3.0-or-later
-->
# Automatic Encrypted Backups
The backups are encrypted client side before being uploaded over Tor and are padded with random data. Backups are made immediately as soon as the channel state changes. However, Citadel also makes decoy backups at random intervals to prevent timing-analysis attacks.
These features combined ensure that the backup server doesn't learn any sensitive information about the user's Citadel.
- The IP address of user is hidden due to Tor.
- User's channel data are encrypted client side with a key only known to the Citadel device.
- Random interval decoy backups ensure the server can't correlate backup activity with channel state changes on the Lightning network and correlate a backup ID with a channel pubkey.
- Random padding obscures if the backup size has increased/decreased or remains unchanged due to it being a decoy.
Due to the key/id being deterministically derived from the Citadel seed, all that's needed to fully recover an Citadel is the mnemonic seed phrase. Upon recovery the device can automatically regenerate the same backup id/encryption key, request the latest backup from the backup server, decrypt it, and restore the user's settings and Lightning network channel data.
There is currently no way to disable backups or recover from them in the dashboard yet. Both of these features will be introduced in the coming updates.

View File

@ -1,160 +0,0 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: 2020 Umbrel. https://getumbrel.com
# SPDX-FileCopyrightText: 2022 Citadel and contributors. https://runcitadel.space
#
# SPDX-License-Identifier: GPL-3.0-or-later
set -euo pipefail
CITADEL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../..)"
BACKUP_ROOT="${CITADEL_ROOT}/.backup/$RANDOM"
BACKUP_FOLDER_NAME="backup"
BACKUP_FOLDER_PATH="${BACKUP_ROOT}/${BACKUP_FOLDER_NAME}"
BACKUP_FILE="${BACKUP_ROOT}/backup.tar.gz.pgp"
BACKUP_STATUS_FILE="${CITADEL_ROOT}/statuses/backup-status.json"
check_dependencies () {
for cmd in "$@"; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "This script requires \"${cmd}\" to be installed"
exit 1
fi
done
}
check_dependencies openssl tar gpg shuf curl
# Deterministically derives 128 bits of cryptographically secure entropy
derive_entropy () {
identifier="${1}"
citadel_seed=$(cat "${CITADEL_ROOT}/db/citadel-seed/seed") || true
if [[ -z "$citadel_seed" ]] || [[ -z "$identifier" ]]; then
>&2 echo "Missing derivation parameter, this is unsafe, exiting."
exit 1
fi
# We need `sed 's/^.* //'` to trim the "(stdin)= " prefix from some versions of openssl
printf "%s" "${identifier}" | openssl dgst -sha256 -hmac "${citadel_seed}" | sed 's/^.* //'
}
[[ -f "${CITADEL_ROOT}/.env" ]] && source "${CITADEL_ROOT}/.env"
BITCOIN_NETWORK=${BITCOIN_NETWORK:-mainnet}
echo "Deriving keys..."
backup_id=$(derive_entropy "citadel_backup_id")
encryption_key=$(derive_entropy "citadel_backup_encryption_key")
echo "Creating backup..."
if [[ ! -f "${CITADEL_ROOT}/lnd/data/chain/bitcoin/${BITCOIN_NETWORK}/channel.backup" ]]; then
echo "No channel.backup file found, skipping backup..."
exit 1
fi
mkdir -p "${BACKUP_FOLDER_PATH}"
cp --archive "${CITADEL_ROOT}/lnd/data/chain/bitcoin/${BITCOIN_NETWORK}/channel.backup" "${BACKUP_FOLDER_PATH}/channel.backup"
# We want to back up user settings too, however we currently store the encrypted
# mnemonic in this file which is not safe to backup remotely.
# Uncomment this in the future once we've ensured there's no critical data in
# this file.
# cp --archive "${CITADEL_ROOT}/db/user.json" "${BACKUP_FOLDER_PATH}/user.json"
echo "Adding random padding..."
# Up to 10KB of random binary data
# This prevents the server from being able to tell if the backup has increased
# decreased or stayed the sme size. Combined with random interval decoy backups
# this makes a (already very difficult) timing analysis attack to correlate backup
# activity with channel state changes practically impossible.
padding="$(shuf -i 0-10240 -n 1)"
dd if=/dev/urandom bs="${padding}" count=1 > "${BACKUP_FOLDER_PATH}/.padding"
echo "Creating encrypted tarball..."
tar \
--create \
--gzip \
--verbose \
--directory "${BACKUP_FOLDER_PATH}/.." \
"${BACKUP_FOLDER_NAME}" \
| gpg \
--batch \
--symmetric \
--cipher-algo AES256 \
--passphrase "${encryption_key}" \
--output "${BACKUP_FILE}"
# To decrypt:
# cat "${BACKUP_FILE}" | gpg \
# --batch \
# --decrypt \
# --passphrase "${encryption_key}" \
# | tar \
# --extract \
# --verbose \
# --gzip
upload_file() {
local file_to_send="${1}"
local backup_id="${2}"
local upload_data=$(jq --null-input \
--arg name "$backup_id" \
--arg data "$(base64 $file_to_send)" \
'{"name": $name, "data": $data}')
curl -X POST \
"https://account.runcitadel.space/api/upload" \
-d "${upload_data}" \
-H "Content-Type: application/json" \
--socks5 "localhost:${TOR_PROXY_PORT}" \
> /dev/null
}
if [[ $BITCOIN_NETWORK == "testnet" ]]; then
rm -rf "${BACKUP_ROOT}"
cat <<EOF > ${BACKUP_STATUS_FILE}
{"status": "skipped", "timestamp": $(date +%s000)}
EOF
exit
fi
if [[ $BITCOIN_NETWORK == "signet" ]]; then
rm -rf "${BACKUP_ROOT}"
cat <<EOF > ${BACKUP_STATUS_FILE}
{"status": "skipped", "timestamp": $(date +%s000)}
EOF
exit
fi
if [[ $BITCOIN_NETWORK == "regtest" ]]; then
rm -rf "${BACKUP_ROOT}"
cat <<EOF > ${BACKUP_STATUS_FILE}
{"status": "skipped", "timestamp": $(date +%s000)}
EOF
exit
fi
echo "Uploading backup..."
if upload_file "${BACKUP_FILE}" "${backup_id}"; then
status="success"
else
status="failed"
fi
echo
~/citadel-backup-upload-hook "${BACKUP_FILE}" || true
rm -rf "${BACKUP_ROOT}"
# Update status file
cat <<EOF > ${BACKUP_STATUS_FILE}
{"status": "${status}", "timestamp": $(date +%s000)}
EOF
echo "============================="
echo "====== Backup ${status} ======="
echo "============================="
exit 0

View File

@ -1,46 +0,0 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: 2020 Umbrel. https://getumbrel.com
#
# SPDX-License-Identifier: GPL-3.0-or-later
set -euo pipefail
CITADEL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../..)"
MAX_BACKUP_INTERVAL_IN_HOURS="12"
check_if_not_already_running() {
if ps ax | grep $0 | grep -v $$ | grep bash | grep -v grep
then
echo "decoy trigger is already running"
exit 1
fi
}
check_dependencies () {
for cmd in "$@"; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "This script requires \"${cmd}\" to be installed"
exit 1
fi
done
}
check_if_not_already_running
check_dependencies shuf
main () {
while true; do
minutes_in_seconds="60"
hours_in_seconds="$((60 * ${minutes_in_seconds}))"
max_interval="$((${MAX_BACKUP_INTERVAL_IN_HOURS} * ${hours_in_seconds}))"
delay="$(shuf -i 0-${max_interval} -n 1)"
echo "Sleeping for ${delay} seconds..."
sleep $delay
echo "Triggering decoy backup..."
"${CITADEL_ROOT}/scripts/backup/backup"
done
}
main

View File

@ -1,73 +0,0 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: 2020 Umbrel. https://getumbrel.com
#
# SPDX-License-Identifier: GPL-3.0-or-later
check_root () {
if [[ $UID != 0 ]]; then
echo "Error: This script must be run as root."
exit 1
fi
}
check_if_not_already_running() {
if ps ax | grep $0 | grep -v $$ | grep bash | grep -v grep
then
echo "backup monitor is already running"
exit 1
fi
}
check_dependencies () {
for cmd in "$@"; do
if ! command -v $cmd >/dev/null 2>&1; then
echo "This script requires \"${cmd}\" to be installed"
exit 1
fi
done
}
check_root
check_if_not_already_running
check_dependencies fswatch readlink dirname
CITADEL_ROOT="$(dirname $(readlink -f "${BASH_SOURCE[0]}"))/../.."
monitor_file () {
local file_path="${1}"
echo "Monitoring $file_path"
echo
if [[ ! -e "${file_path}" ]]; then
echo "$file_path doesn't exist, waiting for it to be created..."
echo
until [[ -e "${file_path}" ]]; do
sleep 1
done
echo "$file_path created! Triggering backup..."
"${CITADEL_ROOT}/scripts/backup/backup"
fi
fswatch -0 --event Updated $file_path | xargs -0 -n 1 -I {} "${CITADEL_ROOT}/scripts/backup/backup"
}
if [[ ! -d "${CITADEL_ROOT}" ]]; then
echo "Root dir does not exist '$CITADEL_ROOT'"
exit 1
fi
[[ -f "${CITADEL_ROOT}/.env" ]] && source "${CITADEL_ROOT}/.env"
BITCOIN_NETWORK=${BITCOIN_NETWORK:-mainnet}
# Monitor LND channel.backup
monitor_file "${CITADEL_ROOT}/lnd/data/chain/bitcoin/${BITCOIN_NETWORK}/channel.backup" &
# Monitor db/user.json
# We want to back up user settings too, however we currently store the encrypted
# mnemonic in this file which is not safe to backup remotely.
# Uncomment this in the future once we've ensured there's no critical data in
# this file.
# monitor_file "${CITADEL_ROOT}/db/user.json" &

View File

@ -1,77 +0,0 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: 2022 Citadel and contributors. https://runcitadel.space
#
# SPDX-License-Identifier: GPL-3.0-or-later
set -euo pipefail
CITADEL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../..)"
BACKUP_ROOT="${CITADEL_ROOT}/.backup/restore-$RANDOM"
BACKUP_FOLDER_NAME="backup"
BACKUP_FOLDER_PATH="${BACKUP_ROOT}/${BACKUP_FOLDER_NAME}"
check_dependencies () {
for cmd in "$@"; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "This script requires \"${cmd}\" to be installed"
exit 1
fi
done
}
check_dependencies openssl tar gpg curl
[[ -f "${CITADEL_ROOT}/.env" ]] && source "${CITADEL_ROOT}/.env"
BITCOIN_NETWORK=${BITCOIN_NETWORK:-mainnet}
if [[ $BITCOIN_NETWORK == "testnet" ]] || [[ $BITCOIN_NETWORK == "signet" ]] || [[ $BITCOIN_NETWORK == "regtest" ]]; then
echo "Backups can only be restored on mainnet"
exit
fi
# Deterministically derives 128 bits of cryptographically secure entropy
derive_entropy () {
identifier="${1}"
citadel_seed=$(cat "${CITADEL_ROOT}/db/citadel-seed/seed") || true
if [[ -z "$citadel_seed" ]] || [[ -z "$identifier" ]]; then
>&2 echo "Missing derivation parameter, this is unsafe, exiting."
exit 1
fi
# We need `sed 's/^.* //'` to trim the "(stdin)= " prefix from some versions of openssl
printf "%s" "${identifier}" | openssl dgst -sha256 -hmac "${citadel_seed}" | sed 's/^.* //'
}
echo "Deriving keys..."
backup_id=$(derive_entropy "citadel_backup_id")
encryption_key=$(derive_entropy "citadel_backup_encryption_key")
echo "Your node id is: "
echo $(derive_entropy "citadel_backup_id")
# If the first argument is not given, fail
if [[ -z "$1" ]]; then
echo "Usage: $0 <backup-id>"
exit 1
fi
echo "Downloading backup..."
mkdir -p "${BACKUP_ROOT}"
cd "${BACKUP_ROOT}"
curl -X POST https://account.runcitadel.space/api/get-backup-by-id --data "{ \"id\": \"$1\", \"name\": \"$backup_id\" }" -H "Content-Type: application/json" | jq ".data" -r | base64 -d - > channels.encrypted.backup
cat "channels.encrypted.backup" | gpg --batch --decrypt --passphrase "$encryption_key" | tar --extract --verbose --gzip
echo "Restoring backup..."
cd "${BACKUP_FOLDER_PATH}"
cp channel.backup "${CITADEL_ROOT}/channel.backup"
sudo docker exec -it lightning lncli restorechanbackup --multi_file /data/.lnd/channel.backup
echo "Backup restored successfully!"

View File

@ -145,6 +145,7 @@ setup_new_device () {
# Copy Docker data dir to external storage
copy_docker_to_external_storage () {
mkdir -p "${DOCKER_DIR}"
mkdir -p "${EXTERNAL_DOCKER_DIR}"
cp --recursive \
--archive \
@ -285,7 +286,8 @@ main () {
swapon "${SWAP_FILE}"
echo "Checking SD Card root is bind mounted at /sd-root..."
df -h "/sd-root${CITADEL_ROOT}" | grep --quiet "/dev/root"
# Skip this for now
#df -h "/sd-root${CITADEL_ROOT}" | grep --quiet "/dev/root"
if [[ $block_device != nvme* ]]; then
echo "unknown" > "${CITADEL_ROOT}/statuses/external_storage"

84
scripts/configure vendored
View File

@ -174,8 +174,6 @@ if reconfiguring:
I2P_PASSWORD=dotenv['I2P_PASSWORD']
else:
I2P_PASSWORD=generate_password(64)
TOR_PASSWORD=dotenv['TOR_PASSWORD']
TOR_HASHED_PASSWORD=dotenv['TOR_HASHED_PASSWORD']
else:
# Generate RPC credentials
print("Generating auth credentials\n")
@ -183,64 +181,18 @@ else:
BITCOIN_RPC_DETAILS=get_data(BITCOIN_RPC_USER)
BITCOIN_RPC_AUTH=BITCOIN_RPC_DETAILS['auth']
BITCOIN_RPC_PASS=BITCOIN_RPC_DETAILS['password']
# Pull Tor image and generate Tor password
print("Generating Tor password\n")
os.system('docker pull --quiet {}'.format(dependencies["tor"]))
TOR_PASSWORD=get_data('itdoesntmatter')['password']
TOR_HASHED_PASSWORD=os.popen('docker run --rm {} --quiet --hash-password "{}"'.format(dependencies["tor"], TOR_PASSWORD)).read()[:-1]
I2P_PASSWORD=generate_password(64)
BITCOIN_NODE="neutrino"
ALIAS_AND_COLOR=""
ADDITIONAL_BITCOIN_OPTIONS=""
BOLT_DB_OPTIONS=""
CHANNEL_LIMITATIONS=""
BASEFEE = "bitcoin.basefee=0"
EXTERNAL_IP = ""
if os.path.isfile('./tor/data/bitcoin-p2p/hostname'):
EXTERNAL_IP="externalip=" + open('./tor/data/bitcoin-p2p/hostname').read()
if os.path.isfile("./lnd/lnd.conf"):
with open("./lnd/lnd.conf", 'r') as file:
# We generally don't want to allow changing lnd.conf, but we keep as many custom settings as possible
for line in file:
if line.startswith("bitcoin.node="):
BITCOIN_NODE = line.split("=")[1].strip()
if line.startswith("alias="):
ALIAS_AND_COLOR += "\n" + line.strip()
if line.startswith("color="):
ALIAS_AND_COLOR += "\n" + line.strip()
if line.startswith("bitcoin.basefee"):
BASEFEE = line.strip()
if line.startswith("bitcoin.feerate"):
ADDITIONAL_BITCOIN_OPTIONS += "\n" + line.strip()
if line.startswith("minchansize"):
CHANNEL_LIMITATIONS += "\n" + line.strip()
if line.startswith("maxchansize"):
CHANNEL_LIMITATIONS += "\n" + line.strip()
if line.startswith("maxpendingchannels"):
CHANNEL_LIMITATIONS += "\n" + line.strip()
if line.startswith("db.bolt.auto-compact"):
BOLT_DB_OPTIONS += "\n" + line.strip()
if BOLT_DB_OPTIONS != "":
BOLT_DB_OPTIONS = "[bolt]\n" + BOLT_DB_OPTIONS
if CHANNEL_LIMITATIONS == "":
CHANNEL_LIMITATIONS = "maxpendingchannels=3\nminchansize=10000"
NEUTRINO_PEERS=""
if BITCOIN_NETWORK == "mainnet":
BITCOIN_RPC_PORT=8332
BITCOIN_P2P_PORT=8333
elif BITCOIN_NETWORK == "testnet":
BITCOIN_RPC_PORT=18332
BITCOIN_P2P_PORT=18333
NEUTRINO_PEERS='''
[neutrino]
neutrino.addpeer=testnet1-btcd.zaphq.io
neutrino.addpeer=testnet2-btcd.zaphq.io
'''
elif BITCOIN_NETWORK == "signet":
BITCOIN_RPC_PORT=38332
BITCOIN_P2P_PORT=38333
@ -263,10 +215,10 @@ NETWORK_IP="10.21.21.0"
GATEWAY_IP="10.21.21.1"
DASHBOARD_IP="10.21.21.3"
MANAGER_IP="10.21.21.4"
MIDDLEWARE_IP="10.21.21.5"
NEUTRINO_SWITCHER_IP="10.21.21.6"
#MIDDLEWARE_IP="10.21.21.5"
#NEUTRINO_SWITCHER_IP="10.21.21.6"
BITCOIN_IP="10.21.21.7"
LND_IP="10.21.21.8"
#LND_IP="10.21.21.8"
TOR_PROXY_IP="10.21.21.9"
APPS_TOR_IP="10.21.21.10"
APPS_2_TOR_IP="10.21.21.11"
@ -280,8 +232,6 @@ BITCOIN_ZMQ_RAWBLOCK_PORT="28332"
BITCOIN_ZMQ_RAWTX_PORT="28333"
BITCOIN_ZMQ_HASHBLOCK_PORT="28334"
BITCOIN_ZMQ_SEQUENCE_PORT="28335"
LND_GRPC_PORT="10009"
LND_REST_PORT="8080"
TOR_PROXY_PORT="9050"
I2P_SAM_PORT="7656"
TOR_CONTROL_PORT="29051"
@ -297,12 +247,6 @@ DOCKER_EXECUTABLE=subprocess.check_output(["which", "docker"]).decode("utf-8").s
# Get the real path by following symlinks
DOCKER_BINARY=subprocess.check_output(["readlink", "-f", DOCKER_EXECUTABLE]).decode("utf-8").strip()
# Set LND fee URL for neutrino on mainnet
LND_FEE_URL=""
# If the network is mainnet and status_dir/node-status-bitcoind-ready doesn't exist, set the FEE_URL
if BITCOIN_NETWORK == 'mainnet' and BITCOIN_NODE == 'neutrino':
LND_FEE_URL="feeurl=https://nodes.lightning.computer/fees/v1/btc-fee-estimates.json"
# Checks if a variable with the name exists, if not, check if an env var with the name existts
# if neither exists, then exit with an error
def get_var(var_name, other_locals, file_name):
@ -349,7 +293,7 @@ build_template("./templates/torrc-core-sample", "./tor/torrc-core")
build_template("./templates/bitcoin-sample.conf", "./bitcoin/bitcoin.conf")
build_template("./templates/i2p-sample.conf", "./i2p/i2pd.conf")
build_template("./templates/i2p-tunnels-sample.conf", "./i2p/tunnels.conf")
build_template("./templates/lnd-sample.conf", "./lnd/lnd.conf")
MIDDLEWARE_IP="NOT_YET_SET"
build_template("./templates/.env-sample", "./.env")
print("Ensuring Docker Compose is up to date...")
@ -359,7 +303,7 @@ print("Updating core services...")
print()
with open("docker-compose.yml", 'r') as stream:
compose = yaml.safe_load(stream)
for service in ["manager", "middleware", "dashboard"]:
for service in ["manager", "dashboard"]:
compose["services"][service]["image"] = dependencies[service]
for service in ["tor", "app-tor", "app-2-tor", "app-3-tor"]:
compose["services"][service]["image"] = dependencies["tor"]
@ -378,6 +322,24 @@ else:
print("Generating app configuration...\n")
os.system('./scripts/app generate')
# Run ./scripts/app get-implementation lightning to get the implementation
# If it fails, install the LND app and set the implementation to LND
try:
implementation = subprocess.check_output("./scripts/app get-implementation lightning", shell=True).decode("utf-8").strip()
except:
print("Installing LND...\n")
os.system('./scripts/app install lnd')
implementation = "lnd"
# Get APP_<IMPLEMENTATION>_MIDDLEWARE_IP from the .env file
dotenv=parse_dotenv('./.env')
MIDDLEWARE_IP = dotenv["APP_{}_MIDDLEWARE_IP".format(implementation.upper().replace("-", "_"))]
build_template("./templates/.env-sample", "./.env")
print("Updating app configuration...\n")
os.system('./scripts/app generate')
# Touch status_dir/configured
with open(status_dir+'/configured', 'w') as file:
file.write('')

View File

@ -1,6 +1,6 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: 2021-2022 Citadel and contributors
# SPDX-FileCopyrightText: 2021-2023 Citadel and contributors
#
# SPDX-License-Identifier: GPL-3.0-or-later
@ -140,12 +140,6 @@ if [[ "${1}" == "--run" ]]; then
echo "-----------------"
echo
docker compose logs --tail=30 bitcoin
echo
echo "Lightning logs"
echo "--------------"
echo
docker compose logs --tail=30 lightning
echo
echo "Tor logs"

View File

@ -6,7 +6,7 @@
NODE_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/..)"
# IN ${NODE_ROOT}/.env, change the UPDATE_CHANNEL to the desired channel ($1)
# In ${NODE_ROOT}/.env, change the UPDATE_CHANNEL to the desired channel ($1)
# If $1 is not given, fail
if [ -z "$1" ]; then
echo "Usage: $0 <channel>"

View File

@ -28,9 +28,6 @@ check_dependencies () {
# Check system's dependencies
check_dependencies readlink dirname ip docker
# Check karen's dependencies
check_dependencies fswatch
# Check OTA update scripts' dependencies
check_dependencies rsync jq curl
@ -44,7 +41,7 @@ fi
# Configure Citadel if it isn't already configured
if [[ ! -f "${CITADEL_ROOT}/statuses/configured" ]]; then
NGINX_PORT=${NGINX_PORT:-80} NGINX_SSL_PORT=${NGINX_SSL_PORT:-443} NETWORK="${NETWORK:-mainnet}" "${CITADEL_ROOT}/scripts/configure"
CADDY_PORT=${CADDY_PORT:-80} CADDY_HTTPS_PORT=${CADDY_HTTPS_PORT:-443} NETWORK="${NETWORK:-mainnet}" "${CITADEL_ROOT}/scripts/configure"
fi
echo
@ -76,6 +73,7 @@ fi
export DEVICE_HOSTS=$DEVICE_HOSTS
export DEVICE_IP=$DEVICE_IP
export DEVICE_HOSTNAME="${DEVICE_HOSTNAME}.local"
export DEVICE_IP=$DEVICE_IP
# Increase default Docker and Compose timeouts to 240s
# as bitcoin can take a long while to respond

View File

@ -7,3 +7,4 @@
CITADEL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../..)"
"${CITADEL_ROOT}/scripts/app" list-updates
"${CITADEL_ROOT}/scripts/app" download-new

View File

@ -1,6 +1,6 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: 2021-2022 Citadel and contributors
# SPDX-FileCopyrightText: 2021-2023 Citadel and contributors
#
# SPDX-License-Identifier: GPL-3.0-or-later

View File

@ -7,3 +7,5 @@
CITADEL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../..)"
"${CITADEL_ROOT}/scripts/configure"
docker restart caddy

View File

@ -113,6 +113,23 @@ cd "$CITADEL_ROOT"
./scripts/start || true
echo "Installing LND as app"
cat <<EOF > "$CITADEL_ROOT"/statuses/update-status.json
{"state": "installing", "progress": 85, "description": "Installing LND", "updateTo": "$RELEASE"}
EOF
# LND config needs update
./scripts/app uninstall "lnd"
./scripts/configure || true
./scripts/app stop "lnd"
rm -rf "$CITADEL_ROOT"/app-data/lnd/lnd
mv "$CITADEL_ROOT"/lnd "$CITADEL_ROOT"/app-data/lnd/lnd
rm -f "$CITADEL_ROOT"/app-data/lnd/lnd/lnd.conf
./scripts/app start lnd
cat <<EOF > "$CITADEL_ROOT"/statuses/update-status.json
{"state": "success", "progress": 100, "description": "Successfully installed Citadel $RELEASE", "updateTo": ""}
EOF

1
services/installed.yml Normal file
View File

@ -0,0 +1 @@
bitcoin: knots

View File

@ -1,25 +0,0 @@
# SPDX-FileCopyrightText: 2022-2023 Citadel and contributors
#
# SPDX-License-Identifier: GPL-3.0-or-later
lightning:
container_name: lightning
image: lightninglabs/lnd:v0.15.5-beta@sha256:ce94136ccfc48d36f058ae48fdeb53d84dbbb8f972bbae3af98e4fdfb98d641b
user: 1000:1000
depends_on:
- tor
- bitcoin
volumes:
- ${PWD}/lnd:/data/.lnd
- ${PWD}/walletpassword:/walletpassword
environment:
HOME: /data
restart: on-failure
stop_grace_period: 5m30s
ports:
- 9735:9735
- $LND_REST_PORT:$LND_REST_PORT
- $LND_GRPC_PORT:$LND_GRPC_PORT
networks:
default:
ipv4_address: $LND_IP

View File

@ -67,7 +67,6 @@ def setService(name, implementation):
installed = yaml.safe_load(stream)
except FileNotFoundError:
installed = {
"lightning": "lnd",
"bitcoin": "core"
}
installed[name] = implementation
@ -98,7 +97,6 @@ def uninstallService(name):
installed = yaml.safe_load(stream)
except FileNotFoundError:
installed = {
"lightning": "lnd",
"bitcoin": "core"
}
try:
@ -115,7 +113,6 @@ def installServices():
installed = yaml.safe_load(stream)
except FileNotFoundError:
installed = {
"lightning": "lnd",
"bitcoin": "core"
}

4
setenv
View File

@ -7,9 +7,7 @@
CITADEL_ROOT="$(dirname $(readlink -f "${BASH_SOURCE[0]}"))"
alias citadel="${CITADEL_ROOT}/bin/citadel"
alias lncli="docker exec -it lightning lncli"
alias lncli="docker exec -it lnd-service-1 lncli"
alias bitcoin-cli="docker exec -it bitcoin bitcoin-cli"
alias docker-compose="sudo docker compose"
alias docker="sudo docker"
export BOS_DEFAULT_LND_PATH="${CITADEL_ROOT}/lnd"

View File

@ -13,7 +13,6 @@ CADDY_HTTPS_PORT=<caddy-https-port>
DASHBOARD_IP=<dashboard-ip>
MANAGER_IP=<manager-ip>
MIDDLEWARE_IP=<middleware-ip>
NEUTRINO_SWITCHER_IP=<neutrino-switcher-ip>
I2P_IP=<i2p-ip>
I2P_PASSWORD=<i2p-password>
BITCOIN_NETWORK=<bitcoin-network>
@ -27,13 +26,8 @@ BITCOIN_ZMQ_RAWBLOCK_PORT=<bitcoin-zmq-rawblock-port>
BITCOIN_ZMQ_RAWTX_PORT=<bitcoin-zmq-rawtx-port>
BITCOIN_ZMQ_HASHBLOCK_PORT=<bitcoin-zmq-hashblock-port>
BITCOIN_ZMQ_SEQUENCE_PORT=<bitcoin-zmq-sequence-port>
LND_IP=<lnd-ip>
LND_GRPC_PORT=<lnd-grpc-port>
LND_REST_PORT=<lnd-rest-port>
TOR_PROXY_IP=<tor-proxy-ip>
TOR_PROXY_PORT=<tor-proxy-port>
TOR_PASSWORD='<tor-password>'
TOR_HASHED_PASSWORD='<tor-hashed-password>'
APPS_TOR_IP=<apps-tor-ip>
APPS_2_TOR_IP=<apps-2-tor-ip>
APPS_3_TOR_IP=<apps-3-tor-ip>

View File

@ -1,68 +0,0 @@
# SPDX-FileCopyrightText: 2020 Umbrel. https://getumbrel.com
# SPDX-FileCopyrightText: 2021-2022 Citadel and contributors.
#
# SPDX-License-Identifier: GPL-3.0-or-later
# Please note: This file can't be changed, it will be overwritten.
# A few modifications will be kept, including alias, color, channel size limitations and more if you contact us.
[Application Options]
<alias-and-color>
listen=0.0.0.0:9735
rpclisten=0.0.0.0:<lnd-grpc-port>
restlisten=0.0.0.0:<lnd-rest-port>
accept-keysend=true
tlsextraip=<lnd-ip>
tlsextradomain=<device-hostname>.local
tlsautorefresh=1
tlsdisableautofill=1
<lnd-fee-url>
wallet-unlock-password-file=/walletpassword
wallet-unlock-allow-create=true
gc-canceled-invoices-on-startup=true
gc-canceled-invoices-on-the-fly=true
accept-amp=true
<channel-limitations>
[protocol]
; Allow channels larger than 0.16 BTC
protocol.wumbo-channels=true
[Bitcoind]
bitcoind.rpchost=<bitcoin-ip>:<bitcoin-rpc-port>
bitcoind.rpcuser=<bitcoin-rpc-user>
bitcoind.rpcpass=<bitcoin-rpc-pass>
bitcoind.zmqpubrawblock=tcp://<bitcoin-ip>:<bitcoin-zmq-rawblock-port>
bitcoind.zmqpubrawtx=tcp://<bitcoin-ip>:<bitcoin-zmq-rawtx-port>
[Bitcoin]
bitcoin.active=1
bitcoin.<bitcoin-network>=1
# Default to neutrino as the node is
# automatically switched to bitcoind once
# IBD is complete
bitcoin.node=<bitcoin-node>
bitcoin.defaultchanconfs=2
<basefee>
<additional-bitcoin-options>
<neutrino-peers>
; Enable watchtower to watch other nodes
[watchtower]
watchtower.active=1
; activate watchtower client so we can get get other servers
; to make sure no one steals our money
[wtclient]
wtclient.active=1
[tor]
tor.active=1
tor.control=<tor-proxy-ip>:<tor-control-port>
tor.socks=<tor-proxy-ip>:<tor-proxy-port>
tor.targetipaddress=<lnd-ip>
tor.password=<tor-password>
tor.v3=1
<bolt-db-options>

View File

@ -1,44 +0,0 @@
# SPDX-FileCopyrightText: 2020 Umbrel. https://getumbrel.com
# SPDX-FileCopyrightText: 2021-2022 Citadel and contributors
#
# SPDX-License-Identifier: GPL-3.0-or-later
user nginx;
worker_processes 1;
error_log /dev/stdout info;
events {
worker_connections 1024;
}
http {
access_log /dev/stdout;
proxy_read_timeout 600;
default_type application/octet-stream;
server {
listen 80;
location /api/ {
proxy_pass http://<middleware-ip>:3005/;
}
location /api-v2/ {
proxy_pass http://<manager-ip>:3000/;
}
location /i2p/ {
proxy_pass http://<i2p-ip>:7070/;
}
location / {
proxy_pass http://<dashboard-ip>:3004/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
}

View File

@ -4,7 +4,6 @@
# Bind only to "<tor-proxy-ip>" which is the tor IP within the container
SocksPort <tor-proxy-ip>:<tor-proxy-port>
ControlPort <tor-proxy-ip>:<tor-control-port>
# Citadel Core Services
@ -19,13 +18,3 @@ HiddenServicePort <bitcoin-p2p-port> <bitcoin-ip>:<bitcoin-p2p-port>
# Bitcoin Core RPC Hidden Service
HiddenServiceDir /var/lib/tor/bitcoin-rpc
HiddenServicePort <bitcoin-rpc-port> <bitcoin-ip>:<bitcoin-rpc-port>
# LND REST Hidden Service
HiddenServiceDir /var/lib/tor/lnd-rest
HiddenServicePort <lnd-rest-port> <lnd-ip>:<lnd-rest-port>
# LND gRPC Hidden Service
HiddenServiceDir /var/lib/tor/lnd-grpc
HiddenServicePort <lnd-grpc-port> <lnd-ip>:<lnd-grpc-port>
HashedControlPassword <tor-hashed-password>