From 8bcb66f0fa04cc8cec40b9547204e98940ccdade Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Sun, 16 Apr 2023 19:12:12 +0000 Subject: [PATCH] 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 --- .gitignore | 8 +- app-data/.gitkeep | 1 + app-system/sources.list | 11 -- app/app-manager.py | 62 ++++++- {app-system => app}/docker-compose.common.yml | 7 +- app/lib/citadelutils.py | 4 +- app/lib/entropy.py | 3 +- app/lib/manage.py | 62 +++---- bin/lncli | 19 --- {lnd => caddy/data}/.gitkeep | 0 cli/citadel | 9 +- db/dependencies.yml | 3 +- docker-compose.yml | 69 -------- events/.gitkeep | 0 info.json | 6 +- info.json.license | 2 +- karen | 4 +- scripts/backup/README.md | 20 --- scripts/backup/backup | 160 ------------------ scripts/backup/decoy-trigger | 46 ----- scripts/backup/monitor | 73 -------- scripts/backup/restore | 77 --------- scripts/citadel-os/external-storage/mount | 4 +- scripts/configure | 84 +++------ scripts/debug | 8 +- scripts/set-update-channel | 2 +- scripts/start | 6 +- scripts/status/app-updates | 1 + {events => scripts}/triggers/app | 0 {events => scripts}/triggers/app-update | 2 +- {events => scripts}/triggers/backup | 0 .../triggers/caddy-config-update | 2 + {events => scripts}/triggers/change-password | 0 {events => scripts}/triggers/debug | 0 {events => scripts}/triggers/reboot | 0 .../triggers/set-update-channel | 0 {events => scripts}/triggers/shutdown | 0 {events => scripts}/triggers/update | 0 scripts/update/01-run.sh | 17 ++ services/installed.yml | 1 + services/lightning/lnd.yml | 25 --- services/manage.py | 3 - setenv | 4 +- templates/.env-sample | 6 - templates/lnd-sample.conf | 68 -------- templates/nginx-sample.conf | 44 ----- templates/torrc-core-sample | 11 -- 47 files changed, 152 insertions(+), 782 deletions(-) create mode 100644 app-data/.gitkeep delete mode 100644 app-system/sources.list rename {app-system => app}/docker-compose.common.yml (69%) delete mode 100755 bin/lncli rename {lnd => caddy/data}/.gitkeep (100%) create mode 100644 events/.gitkeep delete mode 100644 scripts/backup/README.md delete mode 100755 scripts/backup/backup delete mode 100755 scripts/backup/decoy-trigger delete mode 100755 scripts/backup/monitor delete mode 100755 scripts/backup/restore rename {events => scripts}/triggers/app (100%) rename {events => scripts}/triggers/app-update (96%) rename {events => scripts}/triggers/backup (100%) rename {events => scripts}/triggers/caddy-config-update (91%) rename {events => scripts}/triggers/change-password (100%) rename {events => scripts}/triggers/debug (100%) rename {events => scripts}/triggers/reboot (100%) rename {events => scripts}/triggers/set-update-channel (100%) rename {events => scripts}/triggers/shutdown (100%) rename {events => scripts}/triggers/update (100%) create mode 100644 services/installed.yml delete mode 100644 services/lightning/lnd.yml delete mode 100644 templates/lnd-sample.conf delete mode 100644 templates/nginx-sample.conf diff --git a/.gitignore b/.gitignore index 4560704..edcef9c 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/app-data/.gitkeep b/app-data/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/app-data/.gitkeep @@ -0,0 +1 @@ + diff --git a/app-system/sources.list b/app-system/sources.list deleted file mode 100644 index 8bfb43b..0000000 --- a/app-system/sources.list +++ /dev/null @@ -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 diff --git a/app/app-manager.py b/app/app-manager.py index 472b5b2..bc6bc4f 100755 --- a/app/app-manager.py +++ b/app/app-manager.py @@ -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) diff --git a/app-system/docker-compose.common.yml b/app/docker-compose.common.yml similarity index 69% rename from app-system/docker-compose.common.yml rename to app/docker-compose.common.yml index fca0a8c..2f216b8 100644 --- a/app-system/docker-compose.common.yml +++ b/app/docker-compose.common.yml @@ -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 diff --git a/app/lib/citadelutils.py b/app/lib/citadelutils.py index bba413f..ada7198 100644 --- a/app/lib/citadelutils.py +++ b/app/lib/citadelutils.py @@ -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 diff --git a/app/lib/entropy.py b/app/lib/entropy.py index 3e28f1f..453a760 100644 --- a/app/lib/entropy.py +++ b/app/lib/entropy.py @@ -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( diff --git a/app/lib/manage.py b/app/lib/manage.py index 4adeb68..153c10e 100644 --- a/app/lib/manage.py +++ b/app/lib/manage.py @@ -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 diff --git a/bin/lncli b/bin/lncli deleted file mode 100755 index 67753c0..0000000 --- a/bin/lncli +++ /dev/null @@ -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" diff --git a/lnd/.gitkeep b/caddy/data/.gitkeep similarity index 100% rename from lnd/.gitkeep rename to caddy/data/.gitkeep diff --git a/cli/citadel b/cli/citadel index ca8d37d..697a744 100755 --- a/cli/citadel +++ b/cli/citadel @@ -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 diff --git a/db/dependencies.yml b/db/dependencies.yml index 129b3d7..a44e939 100644 --- a/db/dependencies.yml +++ b/db/dependencies.yml @@ -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 diff --git a/docker-compose.yml b/docker-compose.yml index 9803e93..ba052ce 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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 diff --git a/events/.gitkeep b/events/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/info.json b/info.json index bd595f3..aaec798 100644 --- a/info.json +++ b/info.json @@ -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." } diff --git a/info.json.license b/info.json.license index c8cd663..f75caa7 100644 --- a/info.json.license +++ b/info.json.license @@ -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 \ No newline at end of file diff --git a/karen b/karen index 4d9986e..c7e0278 100755 --- a/karen +++ b/karen @@ -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)) diff --git a/scripts/backup/README.md b/scripts/backup/README.md deleted file mode 100644 index 9a4ef45..0000000 --- a/scripts/backup/README.md +++ /dev/null @@ -1,20 +0,0 @@ - - -# 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. diff --git a/scripts/backup/backup b/scripts/backup/backup deleted file mode 100755 index 035716a..0000000 --- a/scripts/backup/backup +++ /dev/null @@ -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 < ${BACKUP_STATUS_FILE} -{"status": "skipped", "timestamp": $(date +%s000)} -EOF - exit -fi -if [[ $BITCOIN_NETWORK == "signet" ]]; then -rm -rf "${BACKUP_ROOT}" -cat < ${BACKUP_STATUS_FILE} -{"status": "skipped", "timestamp": $(date +%s000)} -EOF - exit -fi -if [[ $BITCOIN_NETWORK == "regtest" ]]; then -rm -rf "${BACKUP_ROOT}" -cat < ${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 < ${BACKUP_STATUS_FILE} -{"status": "${status}", "timestamp": $(date +%s000)} -EOF - -echo "=============================" -echo "====== Backup ${status} =======" -echo "=============================" - -exit 0 diff --git a/scripts/backup/decoy-trigger b/scripts/backup/decoy-trigger deleted file mode 100755 index ddc8918..0000000 --- a/scripts/backup/decoy-trigger +++ /dev/null @@ -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 diff --git a/scripts/backup/monitor b/scripts/backup/monitor deleted file mode 100755 index 6bc499c..0000000 --- a/scripts/backup/monitor +++ /dev/null @@ -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" & diff --git a/scripts/backup/restore b/scripts/backup/restore deleted file mode 100755 index 7075d57..0000000 --- a/scripts/backup/restore +++ /dev/null @@ -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 " - 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!" diff --git a/scripts/citadel-os/external-storage/mount b/scripts/citadel-os/external-storage/mount index 41e4a18..aabcf79 100755 --- a/scripts/citadel-os/external-storage/mount +++ b/scripts/citadel-os/external-storage/mount @@ -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" diff --git a/scripts/configure b/scripts/configure index 33c9152..8d29e63 100755 --- a/scripts/configure +++ b/scripts/configure @@ -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__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('') diff --git a/scripts/debug b/scripts/debug index 01bf2a9..8596410 100755 --- a/scripts/debug +++ b/scripts/debug @@ -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" diff --git a/scripts/set-update-channel b/scripts/set-update-channel index 1495702..32fbccd 100755 --- a/scripts/set-update-channel +++ b/scripts/set-update-channel @@ -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 " diff --git a/scripts/start b/scripts/start index c8341b6..91b04f0 100755 --- a/scripts/start +++ b/scripts/start @@ -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 diff --git a/scripts/status/app-updates b/scripts/status/app-updates index 6cf5f8b..2d66df3 100755 --- a/scripts/status/app-updates +++ b/scripts/status/app-updates @@ -7,3 +7,4 @@ CITADEL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../..)" "${CITADEL_ROOT}/scripts/app" list-updates +"${CITADEL_ROOT}/scripts/app" download-new diff --git a/events/triggers/app b/scripts/triggers/app similarity index 100% rename from events/triggers/app rename to scripts/triggers/app diff --git a/events/triggers/app-update b/scripts/triggers/app-update similarity index 96% rename from events/triggers/app-update rename to scripts/triggers/app-update index f9dc94a..0556b3f 100755 --- a/events/triggers/app-update +++ b/scripts/triggers/app-update @@ -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 diff --git a/events/triggers/backup b/scripts/triggers/backup similarity index 100% rename from events/triggers/backup rename to scripts/triggers/backup diff --git a/events/triggers/caddy-config-update b/scripts/triggers/caddy-config-update similarity index 91% rename from events/triggers/caddy-config-update rename to scripts/triggers/caddy-config-update index 8c68500..0c8fa8d 100755 --- a/events/triggers/caddy-config-update +++ b/scripts/triggers/caddy-config-update @@ -7,3 +7,5 @@ CITADEL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../..)" "${CITADEL_ROOT}/scripts/configure" + +docker restart caddy diff --git a/events/triggers/change-password b/scripts/triggers/change-password similarity index 100% rename from events/triggers/change-password rename to scripts/triggers/change-password diff --git a/events/triggers/debug b/scripts/triggers/debug similarity index 100% rename from events/triggers/debug rename to scripts/triggers/debug diff --git a/events/triggers/reboot b/scripts/triggers/reboot similarity index 100% rename from events/triggers/reboot rename to scripts/triggers/reboot diff --git a/events/triggers/set-update-channel b/scripts/triggers/set-update-channel similarity index 100% rename from events/triggers/set-update-channel rename to scripts/triggers/set-update-channel diff --git a/events/triggers/shutdown b/scripts/triggers/shutdown similarity index 100% rename from events/triggers/shutdown rename to scripts/triggers/shutdown diff --git a/events/triggers/update b/scripts/triggers/update similarity index 100% rename from events/triggers/update rename to scripts/triggers/update diff --git a/scripts/update/01-run.sh b/scripts/update/01-run.sh index 2aac73f..6c72640 100755 --- a/scripts/update/01-run.sh +++ b/scripts/update/01-run.sh @@ -113,6 +113,23 @@ cd "$CITADEL_ROOT" ./scripts/start || true +echo "Installing LND as app" +cat < "$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 < "$CITADEL_ROOT"/statuses/update-status.json {"state": "success", "progress": 100, "description": "Successfully installed Citadel $RELEASE", "updateTo": ""} EOF diff --git a/services/installed.yml b/services/installed.yml new file mode 100644 index 0000000..649007e --- /dev/null +++ b/services/installed.yml @@ -0,0 +1 @@ +bitcoin: knots diff --git a/services/lightning/lnd.yml b/services/lightning/lnd.yml deleted file mode 100644 index c76d5bf..0000000 --- a/services/lightning/lnd.yml +++ /dev/null @@ -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 diff --git a/services/manage.py b/services/manage.py index 4f94d1d..d41007f 100755 --- a/services/manage.py +++ b/services/manage.py @@ -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" } diff --git a/setenv b/setenv index d57fdd4..19dc2d2 100644 --- a/setenv +++ b/setenv @@ -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" diff --git a/templates/.env-sample b/templates/.env-sample index c1068db..6660c16 100644 --- a/templates/.env-sample +++ b/templates/.env-sample @@ -13,7 +13,6 @@ CADDY_HTTPS_PORT= DASHBOARD_IP= MANAGER_IP= MIDDLEWARE_IP= -NEUTRINO_SWITCHER_IP= I2P_IP= I2P_PASSWORD= BITCOIN_NETWORK= @@ -27,13 +26,8 @@ BITCOIN_ZMQ_RAWBLOCK_PORT= BITCOIN_ZMQ_RAWTX_PORT= BITCOIN_ZMQ_HASHBLOCK_PORT= BITCOIN_ZMQ_SEQUENCE_PORT= -LND_IP= -LND_GRPC_PORT= -LND_REST_PORT= TOR_PROXY_IP= TOR_PROXY_PORT= -TOR_PASSWORD='' -TOR_HASHED_PASSWORD='' APPS_TOR_IP= APPS_2_TOR_IP= APPS_3_TOR_IP= diff --git a/templates/lnd-sample.conf b/templates/lnd-sample.conf deleted file mode 100644 index 7f60a12..0000000 --- a/templates/lnd-sample.conf +++ /dev/null @@ -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] - -listen=0.0.0.0:9735 -rpclisten=0.0.0.0: -restlisten=0.0.0.0: -accept-keysend=true -tlsextraip= -tlsextradomain=.local -tlsautorefresh=1 -tlsdisableautofill=1 - -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 - - -[protocol] -; Allow channels larger than 0.16 BTC -protocol.wumbo-channels=true - -[Bitcoind] -bitcoind.rpchost=: -bitcoind.rpcuser= -bitcoind.rpcpass= -bitcoind.zmqpubrawblock=tcp://: -bitcoind.zmqpubrawtx=tcp://: - -[Bitcoin] -bitcoin.active=1 -bitcoin.=1 -# Default to neutrino as the node is -# automatically switched to bitcoind once -# IBD is complete -bitcoin.node= -bitcoin.defaultchanconfs=2 - - - - - -; 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.socks=: -tor.targetipaddress= -tor.password= -tor.v3=1 - - diff --git a/templates/nginx-sample.conf b/templates/nginx-sample.conf deleted file mode 100644 index 7e07890..0000000 --- a/templates/nginx-sample.conf +++ /dev/null @@ -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://:3005/; - } - - location /api-v2/ { - proxy_pass http://:3000/; - } - - location /i2p/ { - proxy_pass http://:7070/; - } - - location / { - proxy_pass http://:3004/; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - } - } -} diff --git a/templates/torrc-core-sample b/templates/torrc-core-sample index 2a3842e..be7b1b7 100644 --- a/templates/torrc-core-sample +++ b/templates/torrc-core-sample @@ -4,7 +4,6 @@ # Bind only to "" which is the tor IP within the container SocksPort : -ControlPort : # Citadel Core Services @@ -19,13 +18,3 @@ HiddenServicePort : # Bitcoin Core RPC Hidden Service HiddenServiceDir /var/lib/tor/bitcoin-rpc HiddenServicePort : - -# LND REST Hidden Service -HiddenServiceDir /var/lib/tor/lnd-rest -HiddenServicePort : - -# LND gRPC Hidden Service -HiddenServiceDir /var/lib/tor/lnd-grpc -HiddenServicePort : - -HashedControlPassword