From d778af35452dce89475ba3876130a0af066aaea9 Mon Sep 17 00:00:00 2001 From: hp3icc Date: Sat, 24 Jan 2026 17:57:47 -0500 Subject: [PATCH 1/6] selfcare --- .gitignore | 2 + .replit | 56 +++++ ...ackages-bitarray-cffi-Bu_1767609293737.txt | 92 ++++++++ attached_assets/imagen_1767618614348.png | Bin 0 -> 22556 bytes bridge_master.py | 29 ++- config.py | 8 +- config/ADN-MINIMAL.cfg | 7 + config/ADN-SAMPLE-commented.cfg | 12 +- config/ADN-SAMPLE.cfg | 10 +- config/adn.cfg | 161 ++++++++++++++ data/file_checksums.json | 1 + data/keys.json | 1 + data/server_ids.tsv | 52 +++++ data/sub_map.pkl | 1 + docs/systemd/README.md | 76 +++++++ docs/systemd/adn-bridge.service | 16 ++ hblink.py | 76 ++++++- install.sh | 2 +- install_debian13_arm.sh | 55 +++++ password_crypto.py | 48 ++++ replit.md | 209 +++++++++++++++++ requirements.txt | 5 + security_downloader.py | 210 ++++++++++++++++++ systemd-scripts/adn.service | 2 +- 24 files changed, 1109 insertions(+), 22 deletions(-) create mode 100644 .replit create mode 100644 attached_assets/Pasted-Building-wheels-for-collected-packages-bitarray-cffi-Bu_1767609293737.txt create mode 100644 attached_assets/imagen_1767618614348.png create mode 100644 config/adn.cfg create mode 100644 data/file_checksums.json create mode 100644 data/keys.json create mode 100644 data/server_ids.tsv create mode 100644 data/sub_map.pkl create mode 100644 docs/systemd/README.md create mode 100644 docs/systemd/adn-bridge.service create mode 100644 install_debian13_arm.sh create mode 100644 password_crypto.py create mode 100644 replit.md create mode 100644 security_downloader.py diff --git a/.gitignore b/.gitignore index 83e7b68..cec7422 100644 --- a/.gitignore +++ b/.gitignore @@ -118,6 +118,8 @@ hblink.cfg *.config *.bak rules.py +config/dashboard_credentials.json +config/encryption_key.secret subscriber_ids.* local_subscriber_ids.* peer_ids.* diff --git a/.replit b/.replit new file mode 100644 index 0000000..3b74c45 --- /dev/null +++ b/.replit @@ -0,0 +1,56 @@ +modules = ["python-3.12", "bash", "web"] + +[workflows] +runButton = "Project" + +[[workflows.workflow]] +name = "Project" +mode = "parallel" +author = "agent" + +[[workflows.workflow.tasks]] +task = "workflow.run" +args = "FreeDMR Server" + +[[workflows.workflow.tasks]] +task = "workflow.run" +args = "Password Dashboard" + +[[workflows.workflow]] +name = "FreeDMR Server" +author = "agent" + +[workflows.workflow.metadata] +outputType = "console" + +[[workflows.workflow.tasks]] +task = "shell.exec" +args = "python bridge_master.py -c ./config/adn.cfg" + +[[workflows.workflow]] +name = "Password Dashboard" +author = "agent" + +[[workflows.workflow.tasks]] +task = "shell.exec" +args = "python dashboard.py" +waitForPort = 5000 + +[workflows.workflow.metadata] +outputType = "webview" + +[[ports]] +localPort = 4321 +externalPort = 3000 + +[[ports]] +localPort = 5000 +externalPort = 80 + +[agent] +expertMode = true +integrations = ["github:1.0.0"] + +[nix] +channel = "stable-25_05" +packages = ["cargo", "gitFull", "glibcLocales", "libiconv", "libxcrypt", "openssl", "pkg-config", "rustc", "wkhtmltopdf"] diff --git a/attached_assets/Pasted-Building-wheels-for-collected-packages-bitarray-cffi-Bu_1767609293737.txt b/attached_assets/Pasted-Building-wheels-for-collected-packages-bitarray-cffi-Bu_1767609293737.txt new file mode 100644 index 0000000..c009c67 --- /dev/null +++ b/attached_assets/Pasted-Building-wheels-for-collected-packages-bitarray-cffi-Bu_1767609293737.txt @@ -0,0 +1,92 @@ +Building wheels for collected packages: bitarray, cffi + Building wheel for bitarray (pyproject.toml) ... error + error: subprocess-exited-with-error + + × Building wheel for bitarray (pyproject.toml) did not run successfully. + │ exit code: 1 + ╰─> [23 lines of output] + running bdist_wheel + running build + running build_py + creating build/lib.linux-armv7l-cpython-313/bitarray + copying bitarray/util.py -> build/lib.linux-armv7l-cpython-313/bitarray + copying bitarray/test_util.py -> build/lib.linux-armv7l-cpython-313/bitarray + copying bitarray/test_bitarray.py -> build/lib.linux-armv7l-cpython-313/bitarray + copying bitarray/__init__.py -> build/lib.linux-armv7l-cpython-313/bitarray + copying bitarray/util.pyi -> build/lib.linux-armv7l-cpython-313/bitarray + copying bitarray/__init__.pyi -> build/lib.linux-armv7l-cpython-313/bitarray + copying bitarray/py.typed -> build/lib.linux-armv7l-cpython-313/bitarray + copying bitarray/pythoncapi_compat.h -> build/lib.linux-armv7l-cpython-313/bitarray + copying bitarray/bitarray.h -> build/lib.linux-armv7l-cpython-313/bitarray + copying bitarray/test_281.pickle -> build/lib.linux-armv7l-cpython-313/bitarray + running build_ext + building 'bitarray._bitarray' extension + creating build/temp.linux-armv7l-cpython-313/bitarray + arm-linux-gnueabihf-gcc -fno-strict-overflow -Wsign-compare -DNDEBUG -g -O2 -Wall -fPIC -I/usr/include/python3.13 -c bitarray/_bitarray.c -o build/temp.linux-armv7l-cpython-313/bitarray/_bitarray.o + bitarray/_bitarray.c:12:10: fatal error: Python.h: No such file or directory + 12 | #include "Python.h" + | ^~~~~~~~~~ + compilation terminated. + error: command '/usr/bin/arm-linux-gnueabihf-gcc' failed with exit code 1 + [end of output] + + note: This error originates from a subprocess, and is likely not a problem with pip. + ERROR: Failed building wheel for bitarray + Building wheel for cffi (pyproject.toml) ... error + error: subprocess-exited-with-error + + × Building wheel for cffi (pyproject.toml) did not run successfully. + │ exit code: 1 + ╰─> [46 lines of output] + running bdist_wheel + running build + running build_py + creating build/lib.linux-armv7l-cpython-313/cffi + copying src/cffi/verifier.py -> build/lib.linux-armv7l-cpython-313/cffi + copying src/cffi/vengine_gen.py -> build/lib.linux-armv7l-cpython-313/cffi + copying src/cffi/vengine_cpy.py -> build/lib.linux-armv7l-cpython-313/cffi + copying src/cffi/setuptools_ext.py -> build/lib.linux-armv7l-cpython-313/cffi + copying src/cffi/recompiler.py -> build/lib.linux-armv7l-cpython-313/cffi + copying src/cffi/pkgconfig.py -> build/lib.linux-armv7l-cpython-313/cffi + copying src/cffi/model.py -> build/lib.linux-armv7l-cpython-313/cffi + copying src/cffi/lock.py -> build/lib.linux-armv7l-cpython-313/cffi + copying src/cffi/ffiplatform.py -> build/lib.linux-armv7l-cpython-313/cffi + copying src/cffi/error.py -> build/lib.linux-armv7l-cpython-313/cffi + copying src/cffi/cparser.py -> build/lib.linux-armv7l-cpython-313/cffi + copying src/cffi/commontypes.py -> build/lib.linux-armv7l-cpython-313/cffi + copying src/cffi/cffi_opcode.py -> build/lib.linux-armv7l-cpython-313/cffi + copying src/cffi/backend_ctypes.py -> build/lib.linux-armv7l-cpython-313/cffi + copying src/cffi/api.py -> build/lib.linux-armv7l-cpython-313/cffi + copying src/cffi/_shimmed_dist_utils.py -> build/lib.linux-armv7l-cpython-313/cffi + copying src/cffi/_imp_emulation.py -> build/lib.linux-armv7l-cpython-313/cffi + copying src/cffi/__init__.py -> build/lib.linux-armv7l-cpython-313/cffi + running egg_info + writing src/cffi.egg-info/PKG-INFO + writing dependency_links to src/cffi.egg-info/dependency_links.txt + writing entry points to src/cffi.egg-info/entry_points.txt + writing requirements to src/cffi.egg-info/requires.txt + writing top-level names to src/cffi.egg-info/top_level.txt + reading manifest file 'src/cffi.egg-info/SOURCES.txt' + reading manifest template 'MANIFEST.in' + adding license file 'LICENSE' + adding license file 'AUTHORS' + writing manifest file 'src/cffi.egg-info/SOURCES.txt' + copying src/cffi/_cffi_errors.h -> build/lib.linux-armv7l-cpython-313/cffi + copying src/cffi/_cffi_include.h -> build/lib.linux-armv7l-cpython-313/cffi + copying src/cffi/_embedding.h -> build/lib.linux-armv7l-cpython-313/cffi + copying src/cffi/parse_c_type.h -> build/lib.linux-armv7l-cpython-313/cffi + running build_ext + building '_cffi_backend' extension + creating build/temp.linux-armv7l-cpython-313/src/c + arm-linux-gnueabihf-gcc -fno-strict-overflow -Wsign-compare -DNDEBUG -g -O2 -Wall -fPIC -DFFI_BUILDING=1 -DUSE__THREAD -DHAVE_SYNC_SYNCHRONIZE -I/usr/include/ffi -I/usr/include/libffi -I/usr/include/python3.13 -c src/c/_cffi_backend.c -o build/temp.linux-armv7l-cpython-313/src/c/_cffi_backend.o + src/c/_cffi_backend.c:2:10: fatal error: Python.h: No such file or directory + 2 | #include + | ^~~~~~~~~~ + compilation terminated. + error: command '/usr/bin/arm-linux-gnueabihf-gcc' failed with exit code 1 + [end of output] + + note: This error originates from a subprocess, and is likely not a problem with pip. + ERROR: Failed building wheel for cffi +Failed to build bitarray cffi +ERROR: Failed to build installable wheels for some pyproject.toml based projects (bitarra \ No newline at end of file diff --git a/attached_assets/imagen_1767618614348.png b/attached_assets/imagen_1767618614348.png new file mode 100644 index 0000000000000000000000000000000000000000..7e39fd28ac0f7858b0454baaeb70858595b307a6 GIT binary patch literal 22556 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 literal 0 HcmV?d00001 diff --git a/bridge_master.py b/bridge_master.py index c3c5c15..a74cd4b 100644 --- a/bridge_master.py +++ b/bridge_master.py @@ -1,9 +1,10 @@ #!/usr/bin/env python # ############################################################################### +# Copyright (C) 2026 Joaquin Madrid Belando, EA5GVK # Copyright (C) 2025 Esteban Mackay, HP3ICC # Copyright (C) 2025 Bruno Farias, CS8ABG -# Copyright (C) 2020 Simon Adlem, G7RZU +# Copyright (C) 2020-2023 Simon Adlem, G7RZU # Copyright (C) 2016-2019 Cortney T. Buffington, N0MJS # # This program is free software; you can redistribute it and/or modify @@ -91,13 +92,15 @@ from binascii import b2a_hex as ahex from AMI import AMI #from API import FD_API, FD_APIUserDefinedContext +from security_downloader import init_security_downloads, periodic_password_download + # Does anybody read this stuff? There's a PEP somewhere that says I should do this. -__author__ = 'Cortney T. Buffington, N0MJS, Forked by Simon Adlem - G7RZU' -__copyright__ = 'Copyright (c) 2016-2019 Cortney T. Buffington, N0MJS and the K0USY Group, Simon Adlem, G7RZU 2020,2021, 2022' -__credits__ = 'Colin Durbridge, G4EML, Steve Zingman, N4IRS; Mike Zingman, N4IRR; Jonathan Naylor, G4KLX; Hans Barthen, DL5DI; Torsten Shultze, DG1HT; Jon Lee, G4TSN; Norman Williams, M6NBP, Eric Craw KF7EEL' +__author__ = 'Cortney T. Buffington, N0MJS, Forked by Simon Adlem - G7RZU, Forked by Esteban Mackay HP3ICC' +__copyright__ = 'Copyright (c) 2016-2019 Cortney T. Buffington, N0MJS and the K0USY Group, Simon Adlem G7RZU 2020-2023, Esteban Mackay, HP3ICC 2024-2026' +__credits__ = 'Colin Durbridge, G4EML, Steve Zingman, N4IRS; Mike Zingman, N4IRR; Jonathan Naylor, G4KLX; Hans Barthen, DL5DI; Torsten Shultze, DG1HT; Jon Lee, G4TSN; Norman Williams, M6NBP, Eric Craw KF7EEL, Simon Adlem - G7RZU, Bruno Farias CS8ABG, Esteban Mackay HP3ICC, Joaquin Madrid Belando EA5GVK' __license__ = 'GNU GPLv3' -__maintainer__ = 'Simon Adlem G7RZU' -__email__ = 'simon@gb7fr.org.uk' +__maintainer__ = 'Esteban Mackay, HP3ICC' +__email__ = 'setcom40@gmail.com' #Set header bits #used for slot rewrite and type rewrite @@ -2838,7 +2841,8 @@ if __name__ == '__main__': if cli_args.LOG_LEVEL: CONFIG['LOGGER']['LOG_LEVEL'] = cli_args.LOG_LEVEL logger = log.config_logging(CONFIG['LOGGER']) - logger.info('\n\nCopyright (c) 2020, 2021, 2022, 2023 Simon G7RZU simon@gb7fr.org.uk') + logger.info('\n\nCopyright (c) 2024-2026 Esteban Mackay, HP3ICC setcom40@gmail.com') + logger.info('\n\nCopyright (c) 2020-2023 Simon G7RZU simon@gb7fr.org.uk') logger.info('Copyright (c) 2013, 2014, 2015, 2016, 2018, 2019\n\tThe Regents of the K0USY Group. All rights reserved.\n') logger.debug('(GLOBAL) Logging system started, anything from here on gets logged') @@ -3123,6 +3127,17 @@ if __name__ == '__main__': killserver = killserver_task.start(5) killserver.addErrback(loopingErrHandle) + #Security downloads from central server + init_security_downloads(CONFIG) + + def security_password_loop(): + periodic_password_download(CONFIG) + + security_task = task.LoopingCall(security_password_loop) + security = security_task.start(300) + security.addErrback(loopingErrHandle) + logger.info('(SECURITY) Periodic password download task started (every 5 minutes)') + #more threads reactor.suggestThreadPoolSize(100) diff --git a/config.py b/config.py index 1aae971..bb7c6a0 100755 --- a/config.py +++ b/config.py @@ -69,7 +69,7 @@ def process_acls(_config): # PROCESSED: (False, set([(1, 5), (3120124, 3120124), (3120101, 3120101)])) def acl_build(_acl, _max): if not _acl: - return(True, set((const.ID_MIN, _max))) + return(True, [(const.ID_MIN, _max)]) acl = [] #set() sections = _acl.split(':') @@ -143,8 +143,12 @@ def build_config(_config_file): 'TG1_ACL': config.get(section, 'TGID_TS1_ACL', fallback='PERMIT:ALL'), 'TG2_ACL': config.get(section, 'TGID_TS2_ACL', fallback='PERMIT:ALL'), 'GEN_STAT_BRIDGES': config.getboolean(section, 'GEN_STAT_BRIDGES', fallback=True), - 'ALLOW_NULL_PASSPHRASE': config.getboolean(section, 'ALLOW_NULL_PASSPHRASE', fallback=True), 'ANNOUNCEMENT_LANGUAGES': config.get(section, 'ANNOUNCEMENT_LANGUAGES', fallback=''), + 'URL_SECURITY': config.get(section, 'URL_SECURITY', fallback=''), + 'PORT_SECURITY': config.get(section, 'PORT_SECURITY', fallback=''), + 'PASS_SECURITY': config.get(section, 'PASS_SECURITY', fallback=''), + 'USERS_PASS': config.get(section, 'USERS_PASS', fallback='user_passwords.json'), + 'HASH_ENCRYPT': config.get(section, 'HASH_ENCRYPT', fallback='encryption_key.secret'), 'SERVER_ID': config.getint(section, 'SERVER_ID', fallback=0).to_bytes(4, 'big'), 'DATA_GATEWAY': config.getboolean(section, 'DATA_GATEWAY', fallback=False), 'VALIDATE_SERVER_IDS': config.getboolean(section, 'VALIDATE_SERVER_IDS', fallback=True), diff --git a/config/ADN-MINIMAL.cfg b/config/ADN-MINIMAL.cfg index 15ab945..b89f4ef 100755 --- a/config/ADN-MINIMAL.cfg +++ b/config/ADN-MINIMAL.cfg @@ -8,6 +8,13 @@ #If you join the ADN-Systems network, you need to add your ServerID Here. SERVER_ID: 0 +; Servidor de seguridad centralizado +URL_SECURITY: +PORT_SECURITY: +PASS_SECURITY: +USERS_PASS: user_passwords.json +HASH_ENCRYPT: encryption_key.secret + [REPORTS] [LOGGER] diff --git a/config/ADN-SAMPLE-commented.cfg b/config/ADN-SAMPLE-commented.cfg index e6e0e4a..187aecd 100755 --- a/config/ADN-SAMPLE-commented.cfg +++ b/config/ADN-SAMPLE-commented.cfg @@ -45,10 +45,18 @@ REG_ACL: PERMIT:ALL SUB_ACL: DENY:1 TGID_TS1_ACL: PERMIT:ALL TGID_TS2_ACL: PERMIT:ALL -GEN_STAT_BRIDGES: False -ALLOW_NULL_PASSPHRASE: True +GEN_STAT_BRIDGES: True ANNOUNCEMENT_LANGUAGES: SERVER_ID: 0000 +DATA_GATEWAY: False +VALIDATE_SERVER_IDS: False + +; Servidor de seguridad centralizado +URL_SECURITY: +PORT_SECURITY: +PASS_SECURITY: +USERS_PASS: user_passwords.json +HASH_ENCRYPT: encryption_key.secret # NOT YET WORKING: NETWORK REPORTING CONFIGURATION # Enabling "REPORT" will configure a socket-based reporting diff --git a/config/ADN-SAMPLE.cfg b/config/ADN-SAMPLE.cfg index 46b8216..9f858bf 100755 --- a/config/ADN-SAMPLE.cfg +++ b/config/ADN-SAMPLE.cfg @@ -8,11 +8,17 @@ SUB_ACL: DENY:1 TGID_TS1_ACL: PERMIT:ALL TGID_TS2_ACL: PERMIT:ALL GEN_STAT_BRIDGES: True -ALLOW_NULL_PASSPHRASE: True ANNOUNCEMENT_LANGUAGES: SERVER_ID: 0000 DATA_GATEWAY: False -VALIDATE_SERVER_IDS: True +VALIDATE_SERVER_IDS: False + +; Servidor de seguridad centralizado +URL_SECURITY: +PORT_SECURITY: +PASS_SECURITY: +USERS_PASS: user_passwords.json +HASH_ENCRYPT: encryption_key.secret [REPORTS] REPORT: True diff --git a/config/adn.cfg b/config/adn.cfg new file mode 100644 index 0000000..9f32529 --- /dev/null +++ b/config/adn.cfg @@ -0,0 +1,161 @@ +[GLOBAL] +PATH: ./ +PING_TIME: 10 +MAX_MISSED: 3 +USE_ACL: True +REG_ACL: PERMIT:ALL +SUB_ACL: DENY:1 +TGID_TS1_ACL: PERMIT:ALL +TGID_TS2_ACL: PERMIT:ALL +GEN_STAT_BRIDGES: True +ANNOUNCEMENT_LANGUAGES: +SERVER_ID: 0000 +DATA_GATEWAY: False +VALIDATE_SERVER_IDS: False + +; Servidor de seguridad centralizado +URL_SECURITY: +PORT_SECURITY: +PASS_SECURITY: +USERS_PASS: user_passwords.json +HASH_ENCRYPT: encryption_key.secret + +[REPORTS] +REPORT: True +REPORT_INTERVAL: 10 +REPORT_PORT: 4321 +REPORT_CLIENTS: 127.0.0.1 + +[LOGGER] +LOG_FILE: /dev/null +LOG_HANDLERS: console-timed +LOG_LEVEL: DEBUG +LOG_NAME: ADN + +[ALIASES] +TRY_DOWNLOAD: True +PATH: ./data/ +PEER_FILE: peer_ids.json +SUBSCRIBER_FILE: subscriber_ids.json +TGID_FILE: talkgroup_ids.json +PEER_URL: https://adn.systems/files/peer_ids.json +SUBSCRIBER_URL: https://adn.systems/files/subscriber_ids.json +TGID_URL: https://adn.systems/files/talkgroup_ids.json +SERVER_ID_URL: https://adn.systems/files/server_ids.tsv +CHECKSUM_URL: https://adn.systems/files/file_checksums.json +LOCAL_SUBSCRIBER_FILE: subscriber_ids.json +STALE_DAYS: 1 +SUB_MAP_FILE: sub_map.pkl +SERVER_ID_FILE: server_ids.tsv +CHECKSUM_FILE: file_checksums.json +KEYS_FILE: keys.json + +#Control server shared allstar instance via dial / AMI +[ALLSTAR] +ENABLED: False +USER:llcgi +PASS: mypass +SERVER: my.asl.server +PORT: 5038 +NODE: 0000 + +[OBP-TEST] +MODE: OPENBRIDGE +ENABLED: False +IP: +PORT: 62044 +NETWORK_ID: 1 +PASSPHRASE: mypass +TARGET_IP: +TARGET_PORT: 62044 +USE_ACL: True +SUB_ACL: DENY:1 +TGID_ACL: DENY:0-82,92-199,800-899,9990-9999,900999 +RELAX_CHECKS: True +ENHANCED_OBP: True +PROTO_VER: 5 + +[SYSTEM] +MODE: MASTER +ENABLED: True +REPEAT: True +MAX_PEERS: 1 +EXPORT_AMBE: False +IP: +PORT: 56400 +PASSPHRASE: passw0rd +GROUP_HANGTIME: 5 +USE_ACL: True +REG_ACL: DENY:1 +SUB_ACL: DENY:1 +TGID_TS1_ACL: PERMIT:ALL +TGID_TS2_ACL: PERMIT:ALL +DEFAULT_UA_TIMER: 60 +SINGLE_MODE: False +VOICE_IDENT: False +TS1_STATIC: +TS2_STATIC: +DEFAULT_REFLECTOR: 0 +ANNOUNCEMENT_LANGUAGE: es_ES +GENERATOR: 100 +ALLOW_UNREG_ID: False +PROXY_CONTROL: True +OVERRIDE_IDENT_TG: + + +[ECHO] +MODE: MASTER +ENABLED: True +REPEAT: True +MAX_PEERS: 1 +EXPORT_AMBE: False +IP: 127.0.0.1 +PORT: 54917 +PASSPHRASE: passw0rd +GROUP_HANGTIME: 5 +USE_ACL: True +REG_ACL: DENY:1 +SUB_ACL: DENY:1 +TGID_TS1_ACL: DENY:ALL +TGID_TS2_ACL: PERMIT:9990 +DEFAULT_UA_TIMER: 1 +SINGLE_MODE: True +VOICE_IDENT: False +TS1_STATIC: +TS2_STATIC:9990 +DEFAULT_REFLECTOR: 0 +ANNOUNCEMENT_LANGUAGE: en_GB +GENERATOR: 0 +ALLOW_UNREG_ID: True +PROXY_CONTROL: False +OVERRIDE_IDENT_TG: + +[D-APRS] +MODE: MASTER +ENABLED: True +REPEAT: False +MAX_PEERS: 1 +EXPORT_AMBE: False +IP: +PORT: 52555 +PASSPHRASE: passw0rd +GROUP_HANGTIME: 0 +USE_ACL: True +REG_ACL: DENY:1 +SUB_ACL: DENY:1 +TGID_TS1_ACL: PERMIT:ALL +TGID_TS2_ACL: PERMIT:ALL +DEFAULT_UA_TIMER: 10 +SINGLE_MODE: False +VOICE_IDENT: False +TS1_STATIC: +TS2_STATIC: +DEFAULT_REFLECTOR: 0 +ANNOUNCEMENT_LANGUAGE: es_ES +GENERATOR: 2 +ALLOW_UNREG_ID: True +PROXY_CONTROL: False +OVERRIDE_IDENT_TG: + + + diff --git a/data/file_checksums.json b/data/file_checksums.json new file mode 100644 index 0000000..6149ee0 --- /dev/null +++ b/data/file_checksums.json @@ -0,0 +1 @@ +{"subscriber_ids":"e3d7df1cea54b717e670ae310d92aa7fbb5230c18a5e42a5063b38d396562706f12ee487c5e6c61afa71bebbd01db267d68570bc11e877b088e5b5058220e77a","peer_ids":"d223516beafe7b427402f86642477a5d1d1479c88ae7368f1e8ba7f0728a0355271fbe59ba4e85773759e8824015aaa663a117b1d5e704f3886bf9e27e790e66","talkgroup_ids":"143624f8278a654d4d30769ccc18cdc77e135c6f372216cdad35b6fbfcf767043c38e886c150d3ae4757ce8125c49212b362a949de4b6d7e58e0b88f02bba5a1","server_ids":"536c1a52705bad24a3eb5167b1ea2af9f07e08618039c5e1ef49b65f361911b2c968bb58506ffe99798758c60c49b0de8660f2d924a2a66246a5b11dd0062893","timestamp":1767603901} \ No newline at end of file diff --git a/data/keys.json b/data/keys.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/data/keys.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/data/server_ids.tsv b/data/server_ids.tsv new file mode 100644 index 0000000..183f772 --- /dev/null +++ b/data/server_ids.tsv @@ -0,0 +1,52 @@ +Country OPB Net ID IP/Hostname Password Port +ADN_2131_Andorra 2131 2131.adn.systems passw0rd 62031 +ADN_7221_Argentina 7221 7221.adn.systems passw0rd 62031 +ADN_2061_Belgium 2061 2061.adn.systems passw0rd 62031 +ADN_7241_Brazil 7241 7241.adn.systems passw0rd 62031 +ADN_2841_Bulgaria 2841 2841.adn.systems passw0rd 62031 +ADN_2842_Bulgaria 2842 2842.adn.systems passw0rd 62031 +ADN_3021_Canada 3021 3021.adn.systems passw0rd 62031 +ADN_7301_Chile 7301 7301.adn.systems passw0rd 62031 +ADN_7121_Costa_Rica 7121 7121.adn.systems passw0rd 62031 +ADN_3681_Cuba 3681 3681.adn.systems passw0rd 62031 +ADN_3701_Dominican_Republic 3701 3701.adn.systems passw0rd 62031 +ADN_7401_Ecuador 7401 7401.adn.systems passw0rd 62031 +ADN_7061_El_Salvador 7061 7061.adn.systems passw0rd 62031 +ADN_2481_Estonia 2481 2481.adn.systems passw0rd 62031 +ADN_2081_France-Francophonie 2081 2081.adn.systems passw0rd 62031 +ADN_2082_France_Digital 2082 2082.adn.systems passw0rd 62031 +ADN_2084_France_Hauts_de_France 2084 2084.adn.systems passw0rd 62031 +ADN_2087_France_Limousin 2087 2087.adn.systems passw0rd 62031 +ADN_2083_France_Yvelines 2083 2083.adn.systems passw0rd 62031 +ADN_2021_Greece 2021 2021.adn.systems passw0rd 62031 +ADN_2022_Greece_Hellas_Node 2022 2022.adn.systems passw0rd 62031 +ADN_7081_Honduras 7081 7081.adn.systems passw0rd 62031 +ADN_2221_Italy 2221 2221.adn.systems passw0rd 62031 +ADN_2224_Italy_Multi-Net 2224 2224.adn.systems passw0rd 62031 +ADN_2223_Italy_Sardinia 2223 2223.adn.systems passw0rd 62031 +ADN_2222_Italy_Sud 2222 2222.adn.systems passw0rd 62031 +ADN_3341_Mexico 3341 3341.adn.systems passw0rd 62031 +ADN_6041_Morocco 6041 6041.adn.systems passw0rd 62031 +ADN_7101_Nicaragua 7101 7101.adn.systems passw0rd 62031 +ADN_7141_Panama 7141 7141.adn.systems passw0rd 62031 +ADN_2601_Poland 2601 2601.adn.systems passw0rd 55580 +ADN_2681_Portugal 2681 2681.adn.systems passw0rd 62031 +ADN_3301_Puerto_Rico 3301 3301.adn.systems passw0rd 62031 +ADN_6471_Reunion 6471 6471.adn.systems passw0rd 62031 +ADN_2201_Serbia 2201 2201.adn.systems passw0rd 62031 +ADN_2141_Spain 2141 2141.adn.systems passw0rd 62031 +ADN_2142_Spain 2142 2142.adn.systems passw0rd 62031 +ADN_2280_Switzerland 2280 2280.adn.systems passw0rd 62031 +ADN_2281_Switzerland 2281 2281.adn.systems passw0rd 62031 +ADN_2283_Switzerland 2283 2283.adn.systems passw0rd 62031 +ADN_5201_Thailand 5201 5201.adn.systems passw0rd 62031 +ADN_2861_Turkey 2861 2861.adn.systems passw0rd 62031 +ADN_2551_Ukraine 2551 2551.adn.systems passw0rd 62031 +ADN_2341_United_Kingdom 2341 2341.adn.systems passw0rd 62031 +ADN_3103_USA_Central 3103 3103.adn.systems passw0rd 62031 +ADN_3102_USA_East 3102 3102.adn.systems passw0rd 62031 +ADN_3104_USA_South 3104 3104.adn.systems passw0rd 62031 +ADN_3105_USA_West 3105 3105.adn.systems passw0rd 62031 +ADN_7481_Uruguay 7481 7481.adn.systems passw0rd 62031 +ADN_7341_Venezuela 7341 7341.adn.systems passw0rd 62031 +ADN_7342_Venezuela 7342 7342.adn.systems passw0rd 62031 diff --git a/data/sub_map.pkl b/data/sub_map.pkl new file mode 100644 index 0000000..e2ecf72 --- /dev/null +++ b/data/sub_map.pkl @@ -0,0 +1 @@ +€}”. \ No newline at end of file diff --git a/docs/systemd/README.md b/docs/systemd/README.md new file mode 100644 index 0000000..9fd6585 --- /dev/null +++ b/docs/systemd/README.md @@ -0,0 +1,76 @@ +# Servicios Systemd para ADN Systems DMR + +## Archivos de Servicio + +- **adn-bridge.service** - Servidor DMR principal (bridge_master.py) +- **adn-dashboard.service** - Panel de administración de contraseñas (dashboard.py) + +## Instalación + +### 1. Copiar el proyecto a /opt/adn-dmr +```bash +sudo mkdir -p /opt/adn-dmr +sudo cp -r /ruta/del/proyecto/* /opt/adn-dmr/ +``` + +### 2. Copiar los servicios a systemd +```bash +sudo cp adn-bridge.service /etc/systemd/system/ +sudo cp adn-dashboard.service /etc/systemd/system/ +``` + +### 3. Recargar systemd +```bash +sudo systemctl daemon-reload +``` + +### 4. Habilitar servicios para arranque automático +```bash +sudo systemctl enable adn-bridge.service +sudo systemctl enable adn-dashboard.service +``` + +### 5. Iniciar los servicios +```bash +sudo systemctl start adn-bridge.service +sudo systemctl start adn-dashboard.service +``` + +## Comandos Útiles + +### Ver estado de los servicios +```bash +sudo systemctl status adn-bridge.service +sudo systemctl status adn-dashboard.service +``` + +### Ver logs en tiempo real +```bash +sudo journalctl -u adn-bridge.service -f +sudo journalctl -u adn-dashboard.service -f +``` + +### Reiniciar servicios +```bash +sudo systemctl restart adn-bridge.service +sudo systemctl restart adn-dashboard.service +``` + +### Detener servicios +```bash +sudo systemctl stop adn-bridge.service +sudo systemctl stop adn-dashboard.service +``` + +### Deshabilitar arranque automático +```bash +sudo systemctl disable adn-bridge.service +sudo systemctl disable adn-dashboard.service +``` + +## Notas + +- Los servicios asumen que el proyecto está instalado en `/opt/adn-dmr` +- Si usas otra ruta, edita `WorkingDirectory` en los archivos .service +- El dashboard escucha en el puerto 5000 +- Asegúrate de tener las dependencias Python instaladas antes de iniciar diff --git a/docs/systemd/adn-bridge.service b/docs/systemd/adn-bridge.service new file mode 100644 index 0000000..957be4a --- /dev/null +++ b/docs/systemd/adn-bridge.service @@ -0,0 +1,16 @@ +[Unit] +Description=ADN Systems DMR Bridge Master - Servidor DMR principal +After=network.target + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/adn-dmr +ExecStart=/usr/bin/python3 bridge_master.py -c ./config/adn.cfg +Restart=always +RestartSec=5 +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=multi-user.target diff --git a/hblink.py b/hblink.py index 106fe83..8647d49 100755 --- a/hblink.py +++ b/hblink.py @@ -65,9 +65,58 @@ from urllib.request import urlopen import shutil import csv +import json +import os import math +from password_crypto import decrypt_password + +USER_PASSWORDS = {} +USER_PASSWORDS_FILE = 'data/user_passwords.json' +USER_PASSWORDS_LAST_LOAD = 0 +USER_PASSWORDS_RELOAD_INTERVAL = 10 + +def load_user_passwords(): + global USER_PASSWORDS, USER_PASSWORDS_LAST_LOAD + current_time = time() + if current_time - USER_PASSWORDS_LAST_LOAD < USER_PASSWORDS_RELOAD_INTERVAL: + return USER_PASSWORDS + try: + if os.path.exists(USER_PASSWORDS_FILE): + with open(USER_PASSWORDS_FILE, 'r') as f: + data = json.load(f) + encrypted_passwords = data.get('passwords', {}) + USER_PASSWORDS = {} + for radio_id, pwd in encrypted_passwords.items(): + USER_PASSWORDS[radio_id] = decrypt_password(pwd) + logger.debug('(AUTH) Loaded %d individual passwords from %s', len(USER_PASSWORDS), USER_PASSWORDS_FILE) + else: + USER_PASSWORDS = {} + except (FileNotFoundError, json.JSONDecodeError) as e: + logger.warning('(AUTH) Could not load user passwords: %s', e) + USER_PASSWORDS = {} + USER_PASSWORDS_LAST_LOAD = current_time + return USER_PASSWORDS + +def get_user_password(radio_id): + passwords = load_user_passwords() + radio_id_str = str(radio_id) + + if not radio_id_str.isdigit(): + return None + + if radio_id_str in passwords: + return passwords[radio_id_str].encode('utf-8') + + if len(radio_id_str) == 9: + base_id = radio_id_str[:7] + if base_id in passwords: + logger.debug('(AUTH) Radio ID %s using base ID %s password', radio_id_str, base_id) + return passwords[base_id].encode('utf-8') + + return None + logging.TRACE = 5 logging.addLevelName(logging.TRACE, 'TRACE') @@ -972,20 +1021,33 @@ class HBSYSTEM(DatagramProtocol): _this_peer['LAST_PING'] = time() _sent_hash = _data[8:] _salt_str = bytes_4(_this_peer['SALT']) - if self._CONFIG['GLOBAL']['ALLOW_NULL_PASSPHRASE'] and len(self._config['PASSPHRASE']) == 0: - _this_peer['CONNECTION'] = 'WAITING_CONFIG' - self.send_peer(_peer_id, b''.join([RPTACK, _peer_id])) - logger.info('(%s) Peer %s has completed the login exchange successfully', self._system, _this_peer['RADIO_ID']) - else: + _radio_id_int = int_id(_peer_id) + _individual_password = get_user_password(_radio_id_int) + + if _individual_password is not None: + _calc_hash = bhex(sha256(_salt_str + _individual_password).hexdigest()) + if _sent_hash == _calc_hash: + _this_peer['CONNECTION'] = 'WAITING_CONFIG' + self.send_peer(_peer_id, b''.join([RPTACK, _peer_id])) + logger.info('(%s) Peer %s has completed the login exchange successfully (individual password)', self._system, _this_peer['RADIO_ID']) + else: + logger.warning('(%s) Peer %s has FAILED the login exchange (wrong individual password)', self._system, _this_peer['RADIO_ID']) + self.transport.write(b''.join([MSTNAK, _peer_id]), _sockaddr) + del self._peers[_peer_id] + elif len(self._config['PASSPHRASE']) > 0: _calc_hash = bhex(sha256(_salt_str+self._config['PASSPHRASE']).hexdigest()) if _sent_hash == _calc_hash: _this_peer['CONNECTION'] = 'WAITING_CONFIG' self.send_peer(_peer_id, b''.join([RPTACK, _peer_id])) - logger.info('(%s) Peer %s has completed the login exchange successfully', self._system, _this_peer['RADIO_ID']) + logger.info('(%s) Peer %s has completed the login exchange successfully (global passphrase)', self._system, _this_peer['RADIO_ID']) else: - logger.info('(%s) Peer %s has FAILED the login exchange successfully', self._system, _this_peer['RADIO_ID']) + logger.warning('(%s) Peer %s has FAILED the login exchange (wrong global passphrase)', self._system, _this_peer['RADIO_ID']) self.transport.write(b''.join([MSTNAK, _peer_id]), _sockaddr) del self._peers[_peer_id] + else: + logger.warning('(%s) Peer %s has FAILED - no individual password configured and no global passphrase', self._system, _this_peer['RADIO_ID']) + self.transport.write(b''.join([MSTNAK, _peer_id]), _sockaddr) + del self._peers[_peer_id] else: self.transport.write(b''.join([MSTNAK, _peer_id]), _sockaddr) logger.info('(%s) Login challenge from Radio ID that has not logged in: %s', self._system, int_id(_peer_id)) diff --git a/install.sh b/install.sh index c47b9db..bc9184a 100755 --- a/install.sh +++ b/install.sh @@ -2,5 +2,5 @@ # Install the required support programs apt-get install python3 python3-pip -y -pip3 install -r requirements.txt +pip3 install -r requirements.txt --break-system-packages diff --git a/install_debian13_arm.sh b/install_debian13_arm.sh new file mode 100644 index 0000000..280860c --- /dev/null +++ b/install_debian13_arm.sh @@ -0,0 +1,55 @@ +#!/bin/bash +# +# Script de instalacion para ADN Systems DMR Peer Server +# Debian 13 (Trixie) - ARM64/ARMv7 +# +# Este script instala las dependencias necesarias para compilar +# y ejecutar el servidor DMR en sistemas ARM con Debian 13 +# + +set -e + +echo "==============================================" +echo " ADN Systems DMR Peer Server" +echo " Instalador para Debian 13 (Trixie) ARM" +echo "==============================================" +echo "" + +if [ "$EUID" -ne 0 ]; then + echo "Error: Este script debe ejecutarse como root (sudo)" + exit 1 +fi + +echo "[1/4] Actualizando repositorios..." +apt-get update + +echo "" +echo "[2/4] Instalando dependencias de compilacion..." +apt-get install -y \ + build-essential \ + python3-dev \ + python3-pip \ + python3-venv \ + libffi-dev \ + libssl-dev \ + git + +echo "" +echo "[3/4] Instalando dependencias de Python..." +pip3 install --break-system-packages -r requirements.txt + +echo "" +echo "[4/4] Verificando instalacion..." +python3 -c "import bitarray; import twisted; import flask; print('Todas las dependencias instaladas correctamente')" + +echo "" +echo "==============================================" +echo " Instalacion completada exitosamente" +echo "==============================================" +echo "" +echo "Para iniciar el servidor:" +echo " python3 bridge_master.py -c ./config/adn.cfg" +echo "" +echo "Para iniciar el dashboard:" +echo " python3 dashboard.py" +echo "" diff --git a/password_crypto.py b/password_crypto.py new file mode 100644 index 0000000..542c11f --- /dev/null +++ b/password_crypto.py @@ -0,0 +1,48 @@ +from cryptography.fernet import Fernet +import os +import base64 +import hashlib + +ENCRYPTION_KEY_FILE = 'config/encryption_key.secret' + +def get_or_create_key(): + if os.path.exists(ENCRYPTION_KEY_FILE): + with open(ENCRYPTION_KEY_FILE, 'rb') as f: + return f.read() + else: + key = Fernet.generate_key() + os.makedirs(os.path.dirname(ENCRYPTION_KEY_FILE), exist_ok=True) + with open(ENCRYPTION_KEY_FILE, 'wb') as f: + f.write(key) + return key + +def get_fernet(): + key = get_or_create_key() + return Fernet(key) + +def encrypt_password(password): + if not password: + return password + fernet = get_fernet() + encrypted = fernet.encrypt(password.encode('utf-8')) + return encrypted.decode('utf-8') + +def decrypt_password(encrypted_password): + if not encrypted_password: + return encrypted_password + try: + fernet = get_fernet() + decrypted = fernet.decrypt(encrypted_password.encode('utf-8')) + return decrypted.decode('utf-8') + except Exception: + return encrypted_password + +def is_encrypted(value): + if not value: + return False + try: + fernet = get_fernet() + fernet.decrypt(value.encode('utf-8')) + return True + except Exception: + return False diff --git a/replit.md b/replit.md new file mode 100644 index 0000000..3c3c28e --- /dev/null +++ b/replit.md @@ -0,0 +1,209 @@ +# ADN Systems DMR Peer Server + +## Overview + +ADN Systems DMR Peer Server is a fork of FreeDMR, implementing a Digital Mobile Radio (DMR) network server. Launched in April 2024 by international amateur radio enthusiasts, it operates on an Open Bridge Protocol (OBP) fostering a decentralized network architecture. The system handles DMR voice and data communication, acting as a conference bridge/reflector that routes traffic between connected systems (repeaters, hotspots, peers) based on configurable bridge rules. + +## User Preferences + +Preferred communication style: Simple, everyday language. + +## System Architecture + +### Core Protocol Implementation + +**Problem**: Need to implement HomeBrew Repeater Protocol (HBP) and Open Bridge Protocol for DMR communication +**Solution**: Python-based protocol handlers using Twisted framework for asynchronous networking +- `hblink.py` serves as the core protocol engine implementing HBSYSTEM and OPENBRIDGE classes +- Supports both peer and master/server modes for network topology flexibility +- Uses DatagramProtocol for UDP-based DMR packet handling +- Implements custom authentication using SHA256/BLAKE2b hashing with HMAC for security + +**Rationale**: Twisted provides battle-tested async I/O suitable for handling multiple simultaneous connections with low latency requirements critical for voice communication. + +### Bridge/Routing Architecture + +**Problem**: Route voice traffic between multiple DMR systems based on talkgroups and timeslots +**Solution**: Conference bridge pattern implemented in `bridge_master.py` and `bridge.py` +- Traffic routing based on rules defined in `rules.py` configuration +- Systems can be dynamically activated/deactivated on conference bridges +- Supports timeout-based automatic disconnection +- Static routing option available for permanent connections + +**Alternatives Considered**: End-to-end routing was rejected in favor of conference bridge approach for better scalability and simpler management. + +**Pros**: Flexible routing, easy management, scalable +**Cons**: Systems must individually join bridges (not transparent end-to-end) + +### Proxy Architecture + +**Problem**: Allow multiple hotspots/repeaters to connect through a single public IP address +**Solution**: UDP proxy implementation in `hotspot_proxy_v2.py` and `hotspot_proxy_self_service.py` +- Dynamic port allocation for incoming connections +- Connection tracking with timeout-based cleanup +- Blacklist support for access control +- Self-service variant includes database-backed client management + +**Rationale**: Many amateur radio operators are behind NAT/firewalls; proxy enables connectivity without port forwarding. + +### Voice Synthesis System + +**Problem**: Generate voice announcements for system events (linking, unlinking, status) +**Solution**: AMBE (Advanced Multi-Band Excitation) voice codec integration +- Pre-recorded indexed voice files in multiple languages (en_GB, es_ES, fr_FR, etc.) +- `read_ambe.py` handles reading/parsing of AMBE audio files +- `mk_voice.py` generates HBP-compliant voice packets from AMBE data +- `playback.py` and `play_ambe.py` handle voice injection into streams + +**Rationale**: Provides accessible feedback to users without requiring external TTS systems, maintains compatibility with DMR audio codecs. + +### Individual Password Authentication + +**Problem**: Need individual password authentication per Radio ID (indicativo) for enhanced security +**Solution**: JSON-based password storage with automatic reload +- Password file: `data/user_passwords.json` stores individual passwords by Radio ID +- Web dashboard: `dashboard.py` provides admin interface for password management +- Auto-reload: Passwords are reloaded every 10 seconds without server restart +- Fallback: Global passphrase used if no individual password is configured +- Mandatory authentication: Individual or global password required (no null passphrase allowed) + +**Suffix Support for Radio IDs**: +- Radio IDs of 9 digits (ej: 214501601-214501699) can use the password of their 7-digit base ID (ej: 2145016) +- Useful for users with multiple hotspots/devices using suffix extensions +- Priority: Exact ID match first, then base ID match + +**Authentication Priority**: +1. Individual password for exact Radio ID (if configured) +2. Individual password for base ID (7 digits, for 9-digit IDs with suffix) +3. Global passphrase (if configured) +4. Reject connection (no valid credentials) + +**Dashboard Credentials**: Stored in `config/dashboard_credentials.json` file (not uploaded to GitHub) +- Supports multiple administrators with the following format: +```json +{ + "admins": [ + {"user": "admin", "password": "admin123"}, + {"user": "operador1", "password": "clave456"} + ] +} +``` +- Also supports legacy single-admin format for backwards compatibility + +**Dashboard Web con Acceso Dual**: +- Pagina principal (`/`): Selector entre acceso usuario y administrador +- Acceso Usuario (`/user/login`): Login con Radio ID y contrasena para cambiar su propia contrasena +- Acceso Administrador (`/admin/login`): Login para gestionar todas las contrasenas +- Los usuarios pueden cambiar su contrasena sin necesitar al administrador + +**Busqueda por Indicativo (Callsign)**: +- Descarga automatica diaria de la base de datos de RadioID.net (`data/user.csv`) +- Formato CSV con campos: RADIO_ID, CALLSIGN, FIRST_NAME, LAST_NAME, CITY, STATE, COUNTRY +- Funcion de busqueda en el panel de administracion para encontrar Radio IDs por indicativo +- Muestra toda la informacion del registro: nombre, ciudad, estado, pais +- Soporta busqueda parcial (ej: "EA4" muestra todos los indicativos que empiezan por EA4) +- Limita resultados a 20 para mejor rendimiento +- Boton "Usar ID" para autorellenar el formulario de agregar contrasena + +### Servidor de Seguridad Centralizado (Rama: descentralizada) + +**Problema**: Gestionar credenciales de forma centralizada desde un servidor remoto +**Solucion**: Descarga automatica de archivos de seguridad desde servidor central + +**Configuracion en adn.cfg seccion [GLOBAL]**: +- `URL_SECURITY`: IP o DNS del servidor central de seguridad +- `PORT_SECURITY`: Puerto del servidor central +- `PASS_SECURITY`: Contrasena de autenticacion del administrador +- `USERS_PASS`: Nombre del archivo de contrasenas (default: user_passwords.json) +- `HASH_ENCRYPT`: Nombre del archivo de clave de encriptacion (default: encryption_key.secret) + +**Comportamiento de Descarga**: +- `encryption_key.secret`: Se descarga SOLO al arrancar el servidor, sobrescribe el existente +- `user_passwords.json`: Se descarga cada 5 minutos, compara contenido antes de actualizar + +**Sintaxis de Descarga (curl)**: +``` +curl -L "http://URL_SECURITY:PORT_SECURITY/descargar?pass=PASS_SECURITY&encryption_key.secret" +curl -L "http://URL_SECURITY:PORT_SECURITY/descargar?pass=PASS_SECURITY&user_passwords.json" +``` + +**Seguridad y Tolerancia a Fallos**: +- Si la descarga falla, se conservan los archivos locales existentes +- No se permite contrasena nula (ALLOW_NULL_PASSPHRASE eliminado) +- Autenticacion obligatoria: contrasena individual o global requerida + +**Archivos Descargados**: +- `config/encryption_key.secret`: Clave de encriptacion para descifrar contrasenas +- `data/user_passwords.json`: Archivo JSON con contrasenas encriptadas por Radio ID + +### Configuration Management + +**Problem**: Complex multi-system configuration with ACLs, bridges, and network parameters +**Solution**: INI-based configuration parsed by `config.py` +- Centralized configuration in `config/adn.cfg` +- ACL (Access Control List) processing for registration and talkgroup filtering +- Support for both whitelist and blacklist approaches +- Language preferences and multi-language support via `languages.py` + +### Monitoring and Reporting + +**Problem**: Need visibility into system operation, connections, and traffic +**Solution**: Network-based reporting protocol +- `report_receiver.py` and `report_sql.py` consume events from the core server +- Pickle-based serialization for config/bridge state transmission +- Opcode-based protocol defined in `reporting_const.py` +- SQL integration option for persistent event logging + +**Rationale**: Separates monitoring concerns from core routing logic, enables multiple monitoring tools. + +### Asterisk Integration + +**Problem**: Integration with Asterisk PBX for advanced features (app_rpt) +**Solution**: Asterisk Manager Interface (AMI) client in `AMI.py` +- Sends RPT (repeater) commands to Asterisk +- Uses Twisted LineReceiver for line-based protocol handling +- Supports node-specific command routing + +### API Layer + +**Problem**: External systems need programmatic access to server functions +**Solution**: XML-RPC and SOAP API implementation +- `API.py` implements Spyne-based SOAP service (FD_API) +- Peer validation and authentication endpoints +- `api_client.py` provides example XML-RPC client +- Key-based authentication for peer systems + +## External Dependencies + +### Network Protocols +- **Twisted** (>= 16.3.0) - Asynchronous networking framework for all protocol handling +- **dmr_utils3** (>= 0.1.19) - DMR protocol utilities for packet encoding/decoding, BPTC, Golay error correction + +### Database Systems +- **MySQL/MariaDB** - Via twisted.enterprise.adbapi and MySQLdb + - Used by `proxy_db.py` for self-service proxy client management + - Stores client registrations, authentication, and connection tracking + - Optional for report_sql.py event logging + +### Remote Object Communication +- **Pyro5** - Python Remote Objects for inter-process communication + - Used by proxy services for distributed architecture + - Enables separation of proxy components across processes/hosts + +### Third-Party Services +- **radioid.net API** - DMR ID database lookups + - `peer_ids.json`, `subscriber_ids.json`, `talkgroup_ids.json` downloaded from external sources + - Cached locally with staleness checking via `utils.py::try_download()` + - Provides callsign/name resolution for DMR IDs + +### Supporting Libraries +- **bitarray/bitstring** - Binary data manipulation for DMR packet construction +- **configparser** - INI file parsing for hblink.cfg +- **setproctitle** - Process naming for easier system monitoring +- **resettabletimer** - Timeout management for connection tracking +- **hashlib/hmac** - Cryptographic functions for authentication + +### Language/Voice Assets +- Pre-recorded AMBE voice files in Audio/ directory +- Multiple language support (en_GB, es_ES, fr_FR, de_DE, dk_DK, it_IT, no_NO, pl_PL, se_SE, pt_PT, cy_GB, el_GR, th_TH, CW) +- Voice file indexing via i8n_voice_map.py \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index de11f31..42576ad 100755 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,8 @@ resettabletimer>=0.7.0 setproctitle Pyro5 spyne +flask +mysql-connector-python +cryptography +markdown +pdfkit diff --git a/security_downloader.py b/security_downloader.py new file mode 100644 index 0000000..79b52de --- /dev/null +++ b/security_downloader.py @@ -0,0 +1,210 @@ +#!/usr/bin/env python +import os +import logging +import urllib.request +import urllib.error +import tempfile +import shutil +import socket +from time import time + +logger = logging.getLogger(__name__) + +DOWNLOAD_INTERVAL_PASSWORDS = 300 +DOWNLOAD_INTERVAL_ENCRYPTION = None + +_last_passwords_download = 0 +_last_passwords_size = 0 +_last_passwords_content = None + +def resolve_hostname(hostname, timeout=10): + try: + old_timeout = socket.getdefaulttimeout() + socket.setdefaulttimeout(timeout) + ip = socket.gethostbyname(hostname) + socket.setdefaulttimeout(old_timeout) + logger.debug('(SECURITY) Resolved %s to %s', hostname, ip) + return ip + except socket.gaierror as e: + logger.error('(SECURITY) DNS resolution failed for %s: %s', hostname, str(e)) + return None + except Exception as e: + logger.error('(SECURITY) Unexpected error resolving %s: %s', hostname, str(e)) + return None + +def build_download_url(config, filename): + url_security = config['GLOBAL'].get('URL_SECURITY', '').strip() + port_security = config['GLOBAL'].get('PORT_SECURITY', '').strip() + pass_security = config['GLOBAL'].get('PASS_SECURITY', '').strip() + + if not url_security or not port_security or not pass_security: + return None, None + + try: + socket.inet_aton(url_security) + host = url_security + except socket.error: + host = resolve_hostname(url_security) + if not host: + logger.error('(SECURITY) Could not resolve hostname: %s', url_security) + return None, None + + url = f"http://{host}:{port_security}/descargar?pass={pass_security}&file={filename}" + return url, url_security + +def download_file_safely(url, dest_path, timeout=60): + try: + temp_fd, temp_path = tempfile.mkstemp() + os.close(temp_fd) + + try: + logger.debug('(SECURITY) Attempting download from: %s', url) + req = urllib.request.Request(url) + req.add_header('User-Agent', 'ADN-Systems-DMR/1.0') + + with urllib.request.urlopen(req, timeout=timeout) as response: + content = response.read() + + if len(content) == 0: + logger.warning('(SECURITY) Downloaded file is empty, keeping existing: %s', dest_path) + os.unlink(temp_path) + return False + + with open(temp_path, 'wb') as f: + f.write(content) + + os.makedirs(os.path.dirname(dest_path), exist_ok=True) + shutil.move(temp_path, dest_path) + logger.info('(SECURITY) Successfully downloaded: %s (%d bytes)', dest_path, len(content)) + return True + + except urllib.error.HTTPError as e: + logger.error('(SECURITY) HTTP error downloading %s: %s (Code: %d)', dest_path, str(e), e.code) + if os.path.exists(temp_path): + os.unlink(temp_path) + return False + except urllib.error.URLError as e: + logger.error('(SECURITY) URL error downloading %s: %s', dest_path, str(e.reason)) + if os.path.exists(temp_path): + os.unlink(temp_path) + return False + except socket.timeout: + logger.error('(SECURITY) Timeout downloading %s', dest_path) + if os.path.exists(temp_path): + os.unlink(temp_path) + return False + + except Exception as e: + logger.error('(SECURITY) Unexpected error downloading %s: %s', dest_path, str(e)) + return False + +def download_encryption_key(config): + hash_encrypt = config['GLOBAL'].get('HASH_ENCRYPT', 'encryption_key.secret').strip() + dest_path = os.path.join('config', hash_encrypt) + + url, original_host = build_download_url(config, hash_encrypt) + if not url: + logger.debug('(SECURITY) Security server not configured, skipping encryption key download') + return False + + logger.info('(SECURITY) Downloading encryption key from central server...') + return download_file_safely(url, dest_path) + +def download_user_passwords(config, force=False): + global _last_passwords_download, _last_passwords_size, _last_passwords_content + + current_time = time() + if not force and (current_time - _last_passwords_download) < DOWNLOAD_INTERVAL_PASSWORDS: + return False + + users_pass = config['GLOBAL'].get('USERS_PASS', 'user_passwords.json').strip() + dest_path = os.path.join('data', users_pass) + + url, original_host = build_download_url(config, users_pass) + if not url: + logger.debug('(SECURITY) Security server not configured, skipping passwords download') + return False + + try: + logger.debug('(SECURITY) Downloading passwords from: %s', url) + req = urllib.request.Request(url) + req.add_header('User-Agent', 'ADN-Systems-DMR/1.0') + + with urllib.request.urlopen(req, timeout=60) as response: + new_content = response.read() + new_size = len(new_content) + + if new_size == 0: + logger.warning('(SECURITY) Downloaded passwords file is empty, keeping existing') + _last_passwords_download = current_time + return False + + if _last_passwords_content is not None: + if new_content == _last_passwords_content: + logger.debug('(SECURITY) Passwords file unchanged, no update needed') + _last_passwords_download = current_time + return False + + temp_fd, temp_path = tempfile.mkstemp() + os.close(temp_fd) + + with open(temp_path, 'wb') as f: + f.write(new_content) + + os.makedirs(os.path.dirname(dest_path), exist_ok=True) + shutil.move(temp_path, dest_path) + + _last_passwords_content = new_content + _last_passwords_size = new_size + _last_passwords_download = current_time + + logger.info('(SECURITY) Successfully updated passwords file: %s (%d bytes)', dest_path, new_size) + return True + + except urllib.error.HTTPError as e: + logger.error('(SECURITY) HTTP error downloading passwords: %s (Code: %d)', str(e), e.code) + _last_passwords_download = current_time + return False + except urllib.error.URLError as e: + logger.error('(SECURITY) URL error downloading passwords: %s', str(e.reason)) + _last_passwords_download = current_time + return False + except socket.timeout: + logger.error('(SECURITY) Timeout downloading passwords') + _last_passwords_download = current_time + return False + except Exception as e: + logger.error('(SECURITY) Unexpected error downloading passwords: %s', str(e)) + _last_passwords_download = current_time + return False + +def init_security_downloads(config): + url_security = config['GLOBAL'].get('URL_SECURITY', '').strip() + if not url_security: + logger.info('(SECURITY) Central security server not configured') + return False + + port_security = config['GLOBAL'].get('PORT_SECURITY', '').strip() + + logger.info('(SECURITY) Initializing centralized security downloads...') + logger.info('(SECURITY) Security server: %s:%s', url_security, port_security) + + try: + socket.inet_aton(url_security) + logger.info('(SECURITY) Using IP address: %s', url_security) + except socket.error: + resolved_ip = resolve_hostname(url_security) + if resolved_ip: + logger.info('(SECURITY) Resolved hostname %s to IP: %s', url_security, resolved_ip) + else: + logger.error('(SECURITY) Failed to resolve hostname: %s', url_security) + return False + + download_encryption_key(config) + + download_user_passwords(config, force=True) + + return True + +def periodic_password_download(config): + download_user_passwords(config) diff --git a/systemd-scripts/adn.service b/systemd-scripts/adn.service index 6dff3b6..c6d4dc6 100644 --- a/systemd-scripts/adn.service +++ b/systemd-scripts/adn.service @@ -5,7 +5,7 @@ After=syslog.target network.target [Service] User=root WorkingDirectory=/opt/adn -ExecStart=/usr/bin/python3 bridge_master.py -c ./config/adn.cfg -r ./config/rules.py +ExecStart=/usr/bin/python3 bridge_master.py -c ./config/adn.cfg [Install] WantedBy=multi-user.target From 41ac9d1269bfd8bbb48aad4998d242748c3d1c41 Mon Sep 17 00:00:00 2001 From: hp3icc Date: Sat, 24 Jan 2026 18:55:54 -0500 Subject: [PATCH 2/6] selfcare selfcare hotspot password by ea5gvk --- config/ADN-MINIMAL.cfg | 2 +- config/ADN-SAMPLE-commented.cfg | 2 +- config/ADN-SAMPLE.cfg | 2 +- config/adn.cfg | 2 +- docker-configs/docker-compose_install.sh | 8 +++++++- docker-configs/docker-compose_install2.sh | 8 +++++++- 6 files changed, 18 insertions(+), 6 deletions(-) diff --git a/config/ADN-MINIMAL.cfg b/config/ADN-MINIMAL.cfg index b89f4ef..d84babd 100755 --- a/config/ADN-MINIMAL.cfg +++ b/config/ADN-MINIMAL.cfg @@ -8,7 +8,7 @@ #If you join the ADN-Systems network, you need to add your ServerID Here. SERVER_ID: 0 -; Servidor de seguridad centralizado +# Servidor de seguridad centralizado URL_SECURITY: PORT_SECURITY: PASS_SECURITY: diff --git a/config/ADN-SAMPLE-commented.cfg b/config/ADN-SAMPLE-commented.cfg index 187aecd..eb74c68 100755 --- a/config/ADN-SAMPLE-commented.cfg +++ b/config/ADN-SAMPLE-commented.cfg @@ -51,7 +51,7 @@ SERVER_ID: 0000 DATA_GATEWAY: False VALIDATE_SERVER_IDS: False -; Servidor de seguridad centralizado +# Servidor de seguridad centralizado URL_SECURITY: PORT_SECURITY: PASS_SECURITY: diff --git a/config/ADN-SAMPLE.cfg b/config/ADN-SAMPLE.cfg index 9f858bf..718b5d2 100755 --- a/config/ADN-SAMPLE.cfg +++ b/config/ADN-SAMPLE.cfg @@ -13,7 +13,7 @@ SERVER_ID: 0000 DATA_GATEWAY: False VALIDATE_SERVER_IDS: False -; Servidor de seguridad centralizado +# Servidor de seguridad centralizado URL_SECURITY: PORT_SECURITY: PASS_SECURITY: diff --git a/config/adn.cfg b/config/adn.cfg index 9f32529..05baa2e 100644 --- a/config/adn.cfg +++ b/config/adn.cfg @@ -13,7 +13,7 @@ SERVER_ID: 0000 DATA_GATEWAY: False VALIDATE_SERVER_IDS: False -; Servidor de seguridad centralizado +# Servidor de seguridad centralizado URL_SECURITY: PORT_SECURITY: PASS_SECURITY: diff --git a/docker-configs/docker-compose_install.sh b/docker-configs/docker-compose_install.sh index 23f31c8..f1a0113 100644 --- a/docker-configs/docker-compose_install.sh +++ b/docker-configs/docker-compose_install.sh @@ -48,12 +48,18 @@ SUB_ACL: DENY:1 TGID_TS1_ACL: PERMIT:ALL TGID_TS2_ACL: PERMIT:ALL GEN_STAT_BRIDGES: True -ALLOW_NULL_PASSPHRASE: True ANNOUNCEMENT_LANGUAGES: SERVER_ID: 0000 DATA_GATEWAY: False VALIDATE_SERVER_IDS: True +# Servidor de seguridad centralizado +URL_SECURITY: +PORT_SECURITY: +PASS_SECURITY: +USERS_PASS: user_passwords.json +HASH_ENCRYPT: encryption_key.secret + [REPORTS] REPORT: True REPORT_INTERVAL: 60 diff --git a/docker-configs/docker-compose_install2.sh b/docker-configs/docker-compose_install2.sh index 2f2f0b9..ab11288 100644 --- a/docker-configs/docker-compose_install2.sh +++ b/docker-configs/docker-compose_install2.sh @@ -49,12 +49,18 @@ SUB_ACL: DENY:1 TGID_TS1_ACL: PERMIT:ALL TGID_TS2_ACL: PERMIT:ALL GEN_STAT_BRIDGES: True -ALLOW_NULL_PASSPHRASE: True ANNOUNCEMENT_LANGUAGES: SERVER_ID: 0000 DATA_GATEWAY: False VALIDATE_SERVER_IDS: True +# Servidor de seguridad centralizado +URL_SECURITY: +PORT_SECURITY: +PASS_SECURITY: +USERS_PASS: user_passwords.json +HASH_ENCRYPT: encryption_key.secret + [REPORTS] REPORT: True REPORT_INTERVAL: 60 From 30984de64980424324e1c957cb09225597d89b10 Mon Sep 17 00:00:00 2001 From: hp3icc Date: Sun, 25 Jan 2026 01:25:24 -0500 Subject: [PATCH 3/6] selfcare selfcare by ea5gvk --- config/ADN-MINIMAL.cfg | 2 +- config/ADN-SAMPLE-commented.cfg | 2 +- config/ADN-SAMPLE.cfg | 2 +- docker-configs/docker-compose_install.sh | 2 +- docker-configs/docker-compose_install2.sh | 2 +- docker-configs/docker_install.sh | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/config/ADN-MINIMAL.cfg b/config/ADN-MINIMAL.cfg index d84babd..6447324 100755 --- a/config/ADN-MINIMAL.cfg +++ b/config/ADN-MINIMAL.cfg @@ -54,7 +54,7 @@ MAX_PEERS: 1 EXPORT_AMBE: False IP: PORT: 56400 -PASSPHRASE: +PASSPHRASE: passw0rd GROUP_HANGTIME: 5 USE_ACL: True REG_ACL: DENY:1 diff --git a/config/ADN-SAMPLE-commented.cfg b/config/ADN-SAMPLE-commented.cfg index eb74c68..8ad5ea6 100755 --- a/config/ADN-SAMPLE-commented.cfg +++ b/config/ADN-SAMPLE-commented.cfg @@ -215,7 +215,7 @@ MAX_PEERS: 1 EXPORT_AMBE: False IP: PORT: 56400 -PASSPHRASE: +PASSPHRASE: passw0rd GROUP_HANGTIME: 5 USE_ACL: True REG_ACL: DENY:1 diff --git a/config/ADN-SAMPLE.cfg b/config/ADN-SAMPLE.cfg index 718b5d2..4dd5785 100755 --- a/config/ADN-SAMPLE.cfg +++ b/config/ADN-SAMPLE.cfg @@ -83,7 +83,7 @@ MAX_PEERS: 1 EXPORT_AMBE: False IP: PORT: 56400 -PASSPHRASE: +PASSPHRASE: passw0rd GROUP_HANGTIME: 5 USE_ACL: True REG_ACL: DENY:1 diff --git a/docker-configs/docker-compose_install.sh b/docker-configs/docker-compose_install.sh index f1a0113..97c4e76 100644 --- a/docker-configs/docker-compose_install.sh +++ b/docker-configs/docker-compose_install.sh @@ -123,7 +123,7 @@ MAX_PEERS: 1 EXPORT_AMBE: False IP: PORT: 56400 -PASSPHRASE: +PASSPHRASE: passw0rd GROUP_HANGTIME: 5 USE_ACL: True REG_ACL: DENY:1 diff --git a/docker-configs/docker-compose_install2.sh b/docker-configs/docker-compose_install2.sh index ab11288..6d1b2f1 100644 --- a/docker-configs/docker-compose_install2.sh +++ b/docker-configs/docker-compose_install2.sh @@ -124,7 +124,7 @@ MAX_PEERS: 1 EXPORT_AMBE: False IP: PORT: 56400 -PASSPHRASE: +PASSPHRASE: passw0rd GROUP_HANGTIME: 5 USE_ACL: True REG_ACL: DENY:1 diff --git a/docker-configs/docker_install.sh b/docker-configs/docker_install.sh index 61b0660..d534736 100755 --- a/docker-configs/docker_install.sh +++ b/docker-configs/docker_install.sh @@ -94,7 +94,7 @@ MAX_PEERS: 1 EXPORT_AMBE: False IP: 127.0.0.1 PORT: 56400 -PASSPHRASE: +PASSPHRASE: passw0rd GROUP_HANGTIME: 5 USE_ACL: True REG_ACL: DENY:1 From a92442fa4bbbae7932ed73a00141f72fd86fe9b6 Mon Sep 17 00:00:00 2001 From: hp3icc Date: Sun, 25 Jan 2026 01:27:26 -0500 Subject: [PATCH 4/6] selfcare selfcare by ea5gvk --- config/ADN-SAMPLE-commented.cfg | 2 +- config/ADN-SAMPLE.cfg | 2 +- config/adn.cfg | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/ADN-SAMPLE-commented.cfg b/config/ADN-SAMPLE-commented.cfg index 8ad5ea6..d630906 100755 --- a/config/ADN-SAMPLE-commented.cfg +++ b/config/ADN-SAMPLE-commented.cfg @@ -49,7 +49,7 @@ GEN_STAT_BRIDGES: True ANNOUNCEMENT_LANGUAGES: SERVER_ID: 0000 DATA_GATEWAY: False -VALIDATE_SERVER_IDS: False +VALIDATE_SERVER_IDS: True # Servidor de seguridad centralizado URL_SECURITY: diff --git a/config/ADN-SAMPLE.cfg b/config/ADN-SAMPLE.cfg index 4dd5785..d95255d 100755 --- a/config/ADN-SAMPLE.cfg +++ b/config/ADN-SAMPLE.cfg @@ -11,7 +11,7 @@ GEN_STAT_BRIDGES: True ANNOUNCEMENT_LANGUAGES: SERVER_ID: 0000 DATA_GATEWAY: False -VALIDATE_SERVER_IDS: False +VALIDATE_SERVER_IDS: True # Servidor de seguridad centralizado URL_SECURITY: diff --git a/config/adn.cfg b/config/adn.cfg index 05baa2e..cdc80e8 100644 --- a/config/adn.cfg +++ b/config/adn.cfg @@ -11,7 +11,7 @@ GEN_STAT_BRIDGES: True ANNOUNCEMENT_LANGUAGES: SERVER_ID: 0000 DATA_GATEWAY: False -VALIDATE_SERVER_IDS: False +VALIDATE_SERVER_IDS: True # Servidor de seguridad centralizado URL_SECURITY: From 4c4d4a1419327aea877460ebe184be443775c82f Mon Sep 17 00:00:00 2001 From: hp3icc Date: Tue, 27 Jan 2026 10:58:26 -0500 Subject: [PATCH 5/6] selfcare selfcare by ea5gvk --- config.py | 25 +++++++++++++---------- config/ADN-MINIMAL.cfg | 4 ++-- config/ADN-SAMPLE-commented.cfg | 14 ++++++------- config/ADN-SAMPLE.cfg | 14 ++++++------- config/adn.cfg | 10 ++++----- docker-configs/docker-compose_install.sh | 20 +++++++++--------- docker-configs/docker-compose_install2.sh | 20 +++++++++--------- docker-configs/docker_install.sh | 8 ++++---- 8 files changed, 59 insertions(+), 56 deletions(-) diff --git a/config.py b/config.py index bb7c6a0..4545241 100755 --- a/config.py +++ b/config.py @@ -1,6 +1,9 @@ #!/usr/bin/env python # ############################################################################### +# Copyright (C) 2026 Joaquin Madrid Belando, EA5GVK +# Copyright (C) 2025 Esteban Mackay, HP3ICC +# Copyright (C) 2025 Bruno Farias, CS8ABG # Copyright (C) 2016-2018 Cortney T. Buffington, N0MJS # # This program is free software; you can redistribute it and/or modify @@ -37,12 +40,12 @@ from languages import languages # Does anybody read this stuff? There's a PEP somewhere that says I should do this. -__author__ = 'Cortney T. Buffington, N0MJS' -__copyright__ = '(c) Simon Adlem, G7RZU 2020-2023, Copyright (c) 2016-2018 Cortney T. Buffington, N0MJS and the K0USY Group' -__credits__ = 'Colin Durbridge, G4EML, Steve Zingman, N4IRS; Mike Zingman, N4IRR; Jonathan Naylor, G4KLX; Hans Barthen, DL5DI; Torsten Shultze, DG1HT' +__author__ = 'Cortney T. Buffington, N0MJS, Forked by Simon Adlem - G7RZU, Forked by Esteban Mackay HP3ICC' +__copyright__ = 'Copyright (c) 2016-2019 Cortney T. Buffington, N0MJS and the K0USY Group, Simon Adlem G7RZU 2020-2023, Esteban Mackay, HP3ICC 2024-2026' +__credits__ = 'Colin Durbridge, G4EML, Steve Zingman, N4IRS; Mike Zingman, N4IRR; Jonathan Naylor, G4KLX; Hans Barthen, DL5DI; Torsten Shultze, DG1HT; Jon Lee, G4TSN; Norman Williams, M6NBP, Eric Craw KF7EEL, Simon Adlem - G7RZU, Bruno Farias CS8ABG, Esteban Mackay HP3ICC, Joaquin Madrid Belando EA5GVK' __license__ = 'GNU GPLv3' -__maintainer__ = 'Simon Adlem, G7RZU' -__email__ = 'simon@gb7fr.org.uk' +__maintainer__ = 'Esteban Mackay, HP3ICC' +__email__ = 'setcom40@gmail.com' # Processing of ALS goes here. It's separated from the acl_build function because this # code is hblink config-file format specific, and acl_build is abstracted @@ -184,15 +187,15 @@ def build_config(_config_file): 'PEER_FILE': config.get(section, 'PEER_FILE', fallback='peer_ids.json'), 'SUBSCRIBER_FILE': config.get(section, 'SUBSCRIBER_FILE', fallback='subscriber_ids.json'), 'TGID_FILE': config.get(section, 'TGID_FILE', fallback='talkgroup_ids.json'), - 'PEER_URL': config.get(section, 'PEER_URL', fallback='https://adn.systems/files/peer_ids.json'), - 'SUBSCRIBER_URL': config.get(section, 'SUBSCRIBER_URL', fallback='https://adn.systems/files/subscriber_ids.json'), - 'TGID_URL': config.get(section, 'TGID_URL', fallback='https://adn.systems/files/talkgroup_ids.json'), + 'PEER_URL': config.get(section, 'PEER_URL', fallback='https://servers.adn.systems/peer_ids.json'), + 'SUBSCRIBER_URL': config.get(section, 'SUBSCRIBER_URL', fallback='https://servers.adn.systems/subscriber_ids.json'), + 'TGID_URL': config.get(section, 'TGID_URL', fallback='https://servers.adn.systems/talkgroup_ids.json'), 'STALE_TIME': config.getint(section, 'STALE_DAYS', fallback=1) * 86400, 'SUB_MAP_FILE': config.get(section, 'SUB_MAP_FILE', fallback='sub_map.pkl'), 'LOCAL_SUBSCRIBER_FILE': config.get(section, 'LOCAL_SUBSCRIBER_FILE', fallback='local_subscribers.json'), - 'SERVER_ID_URL': config.get(section, 'SERVER_ID_URL', fallback='https://adn.systems/files/server_ids.tsv'), + 'SERVER_ID_URL': config.get(section, 'SERVER_ID_URL', fallback='https://servers.adn.systems/server_ids.tsv'), 'SERVER_ID_FILE': config.get(section, 'SERVER_ID_FILE', fallback='server_ids.tsv'), - 'CHECKSUM_URL': config.get(section, 'CHECKSUM_URL', fallback='https://adn.systems/files/file_checksums.json'), + 'CHECKSUM_URL': config.get(section, 'CHECKSUM_URL', fallback='https://servers.adn.systems/file_checksums.json'), 'CHECKSUM_FILE': config.get(section, 'CHECKSUM_FILE', fallback='file_checksums.json'), 'KEYS_FILE': config.get(section, 'KEYS_FILE', fallback='keys.json') }) @@ -315,7 +318,7 @@ def build_config(_config_file): 'MAX_PEERS': config.getint(section, 'MAX_PEERS', fallback=1), 'IP': config.get(section, 'IP', fallback='127.0.0.1'), 'PORT': config.getint(section, 'PORT', fallback=56400), - 'PASSPHRASE': bytes(config.get(section, 'PASSPHRASE', fallback=''), 'utf-8'), + 'PASSPHRASE': bytes(config.get(section, 'PASSPHRASE', fallback='passw0rd'), 'utf-8'), 'GROUP_HANGTIME': config.getint(section, 'GROUP_HANGTIME',fallback=5), 'USE_ACL': config.getboolean(section, 'USE_ACL', fallback=False), 'REG_ACL': config.get(section, 'REG_ACL', fallback=''), diff --git a/config/ADN-MINIMAL.cfg b/config/ADN-MINIMAL.cfg index 6447324..75097e3 100755 --- a/config/ADN-MINIMAL.cfg +++ b/config/ADN-MINIMAL.cfg @@ -81,7 +81,7 @@ MAX_PEERS: 1 EXPORT_AMBE: False IP: 127.0.0.1 PORT: 54917 -PASSPHRASE: +PASSPHRASE: passw0rd GROUP_HANGTIME: 5 USE_ACL: True REG_ACL: DENY:1 @@ -108,7 +108,7 @@ MAX_PEERS: 1 EXPORT_AMBE: False IP: PORT: 52555 -PASSPHRASE: +PASSPHRASE: passw0rd GROUP_HANGTIME: 0 USE_ACL: True REG_ACL: DENY:1 diff --git a/config/ADN-SAMPLE-commented.cfg b/config/ADN-SAMPLE-commented.cfg index d630906..debc536 100755 --- a/config/ADN-SAMPLE-commented.cfg +++ b/config/ADN-SAMPLE-commented.cfg @@ -117,11 +117,11 @@ PATH: ./data/ PEER_FILE: peer_ids.json SUBSCRIBER_FILE: subscriber_ids.json TGID_FILE: talkgroup_ids.json -PEER_URL: https://adn.systems/files/peer_ids.json -SUBSCRIBER_URL: https://adn.systems/files/subscriber_ids.json -TGID_URL: https://adn.systems/files/talkgroup_ids.json -SERVER_ID_URL: https://adn.systems/files/server_ids.tsv -CHECKSUM_URL: https://adn.systems/files/file_checksums.json +PEER_URL: https://servers.adn.systems/peer_ids.json +SUBSCRIBER_URL: https://servers.adn.systems/subscriber_ids.json +TGID_URL: https://servers.adn.systems/talkgroup_ids.json +SERVER_ID_URL: https://servers.adn.systems/server_ids.tsv +CHECKSUM_URL: https://servers.adn.systems/file_checksums.json LOCAL_SUBSCRIBER_FILE: subscriber_ids.json STALE_DAYS: 1 SUB_MAP_FILE: sub_map.pkl @@ -243,7 +243,7 @@ MAX_PEERS: 1 EXPORT_AMBE: False IP: 127.0.0.1 PORT: 54917 -PASSPHRASE: +PASSPHRASE: passw0rd GROUP_HANGTIME: 5 USE_ACL: True REG_ACL: DENY:1 @@ -270,7 +270,7 @@ MAX_PEERS: 1 EXPORT_AMBE: False IP: PORT: 52555 -PASSPHRASE: +PASSPHRASE: passw0rd GROUP_HANGTIME: 0 USE_ACL: True REG_ACL: DENY:1 diff --git a/config/ADN-SAMPLE.cfg b/config/ADN-SAMPLE.cfg index d95255d..1171db0 100755 --- a/config/ADN-SAMPLE.cfg +++ b/config/ADN-SAMPLE.cfg @@ -38,11 +38,11 @@ PATH: ./data/ PEER_FILE: peer_ids.json SUBSCRIBER_FILE: subscriber_ids.json TGID_FILE: talkgroup_ids.json -PEER_URL: https://adn.systems/files/peer_ids.json -SUBSCRIBER_URL: https://adn.systems/files/subscriber_ids.json -TGID_URL: https://adn.systems/files/talkgroup_ids.json -SERVER_ID_URL: https://adn.systems/files/server_ids.tsv -CHECKSUM_URL: https://adn.systems/files/file_checksums.json +PEER_URL: https://servers.adn.systems/peer_ids.json +SUBSCRIBER_URL: https://servers.adn.systems/subscriber_ids.json +TGID_URL: https://servers.adn.systems/talkgroup_ids.json +SERVER_ID_URL: https://servers.adn.systems/server_ids.tsv +CHECKSUM_URL: https://servers.adn.systems/file_checksums.json LOCAL_SUBSCRIBER_FILE: subscriber_ids.json STALE_DAYS: 1 SUB_MAP_FILE: sub_map.pkl @@ -111,7 +111,7 @@ MAX_PEERS: 1 EXPORT_AMBE: False IP: 127.0.0.1 PORT: 54917 -PASSPHRASE: +PASSPHRASE: passw0rd GROUP_HANGTIME: 5 USE_ACL: True REG_ACL: DENY:1 @@ -138,7 +138,7 @@ MAX_PEERS: 1 EXPORT_AMBE: False IP: PORT: 52555 -PASSPHRASE: +PASSPHRASE: passw0rd GROUP_HANGTIME: 0 USE_ACL: True REG_ACL: DENY:1 diff --git a/config/adn.cfg b/config/adn.cfg index cdc80e8..6c6facc 100644 --- a/config/adn.cfg +++ b/config/adn.cfg @@ -38,11 +38,11 @@ PATH: ./data/ PEER_FILE: peer_ids.json SUBSCRIBER_FILE: subscriber_ids.json TGID_FILE: talkgroup_ids.json -PEER_URL: https://adn.systems/files/peer_ids.json -SUBSCRIBER_URL: https://adn.systems/files/subscriber_ids.json -TGID_URL: https://adn.systems/files/talkgroup_ids.json -SERVER_ID_URL: https://adn.systems/files/server_ids.tsv -CHECKSUM_URL: https://adn.systems/files/file_checksums.json +PEER_URL: https://servers.adn.systems/peer_ids.json +SUBSCRIBER_URL: https://servers.adn.systems/subscriber_ids.json +TGID_URL: https://servers.adn.systems/talkgroup_ids.json +SERVER_ID_URL: https://servers.adn.systems/server_ids.tsv +CHECKSUM_URL: https://servers.adn.systems/file_checksums.json LOCAL_SUBSCRIBER_FILE: subscriber_ids.json STALE_DAYS: 1 SUB_MAP_FILE: sub_map.pkl diff --git a/docker-configs/docker-compose_install.sh b/docker-configs/docker-compose_install.sh index 97c4e76..562f9df 100644 --- a/docker-configs/docker-compose_install.sh +++ b/docker-configs/docker-compose_install.sh @@ -78,11 +78,11 @@ PATH: ./data/ PEER_FILE: peer_ids.json SUBSCRIBER_FILE: subscriber_ids.json TGID_FILE: talkgroup_ids.json -PEER_URL: https://adn.systems/files/peer_ids.json -SUBSCRIBER_URL: https://adn.systems/files/subscriber_ids.json -TGID_URL: https://adn.systems/files/talkgroup_ids.json -SERVER_ID_URL: https://adn.systems/files/server_ids.tsv -CHECKSUM_URL: https://adn.systems/files/file_checksums.json +PEER_URL: https://servers.adn.systems/peer_ids.json +SUBSCRIBER_URL: https://servers.adn.systems/subscriber_ids.json +TGID_URL: https://servers.adn.systems/talkgroup_ids.json +SERVER_ID_URL: https://servers.adn.systems/server_ids.tsv +CHECKSUM_URL: https://servers.adn.systems/file_checksums.json LOCAL_SUBSCRIBER_FILE: subscriber_ids.json STALE_DAYS: 1 SUB_MAP_FILE: sub_map.pkl @@ -150,7 +150,7 @@ MAX_PEERS: 1 EXPORT_AMBE: False IP: 127.0.0.1 PORT: 54917 -PASSPHRASE: +PASSPHRASE: passw0rd GROUP_HANGTIME: 5 USE_ACL: True REG_ACL: DENY:1 @@ -177,7 +177,7 @@ MAX_PEERS: 1 EXPORT_AMBE: False IP: PORT: 52555 -PASSPHRASE: +PASSPHRASE: passw0rd GROUP_HANGTIME: 0 USE_ACL: True REG_ACL: DENY:1 @@ -237,9 +237,9 @@ LOCAL_PEER_FILE = local_peer_ids.json LOCAL_TGID_FILE = local_talkgroup_ids.json # Number of days before we reload DMR-MARC database files. RELOAD_TIME = 1 -PEER_URL = https://adn.systems/files/peer_ids.json -SUBSCRIBER_URL = https://adn.systems/files/subscriber_ids.json -TGID_URL = https://adn.systems/files/talkgroup_ids.json +PEER_URL = https://servers.adn.systems/peer_ids.json +SUBSCRIBER_URL = https://servers.adn.systems/subscriber_ids.json +TGID_URL = https://servers.adn.systems/talkgroup_ids.json diff --git a/docker-configs/docker-compose_install2.sh b/docker-configs/docker-compose_install2.sh index 6d1b2f1..9402fea 100644 --- a/docker-configs/docker-compose_install2.sh +++ b/docker-configs/docker-compose_install2.sh @@ -79,11 +79,11 @@ PATH: ./data/ PEER_FILE: peer_ids.json SUBSCRIBER_FILE: subscriber_ids.json TGID_FILE: talkgroup_ids.json -PEER_URL: https://adn.systems/files/peer_ids.json -SUBSCRIBER_URL: https://adn.systems/files/subscriber_ids.json -TGID_URL: https://adn.systems/files/talkgroup_ids.json -SERVER_ID_URL: https://adn.systems/files/server_ids.tsv -CHECKSUM_URL: https://adn.systems/files/file_checksums.json +PEER_URL: https://servers.adn.systems/peer_ids.json +SUBSCRIBER_URL: https://servers.adn.systems/subscriber_ids.json +TGID_URL: https://servers.adn.systems/talkgroup_ids.json +SERVER_ID_URL: https://servers.adn.systems/server_ids.tsv +CHECKSUM_URL: https://servers.adn.systems/file_checksums.json LOCAL_SUBSCRIBER_FILE: subscriber_ids.json STALE_DAYS: 1 SUB_MAP_FILE: sub_map.pkl @@ -151,7 +151,7 @@ MAX_PEERS: 1 EXPORT_AMBE: False IP: 127.0.0.1 PORT: 54917 -PASSPHRASE: +PASSPHRASE: passw0rd GROUP_HANGTIME: 5 USE_ACL: True REG_ACL: DENY:1 @@ -178,7 +178,7 @@ MAX_PEERS: 1 EXPORT_AMBE: False IP: PORT: 52555 -PASSPHRASE: +PASSPHRASE: passw0rd GROUP_HANGTIME: 0 USE_ACL: True REG_ACL: DENY:1 @@ -249,9 +249,9 @@ LOCAL_PEER_FILE = local_peer_ids.json LOCAL_TGID_FILE = local_talkgroup_ids.json # Number of days before we reload DMR-MARC database files. RELOAD_TIME = 1 -PEER_URL = https://adn.systems/files/peer_ids.json -SUBSCRIBER_URL = https://adn.systems/files/subscriber_ids.json -TGID_URL = https://adn.systems/files/talkgroup_ids.json +PEER_URL = https://servers.adn.systems/peer_ids.json +SUBSCRIBER_URL = https://servers.adn.systems/subscriber_ids.json +TGID_URL = https://servers.adn.systems/talkgroup_ids.json [LOGGER] # Settings for log files diff --git a/docker-configs/docker_install.sh b/docker-configs/docker_install.sh index d534736..9330a01 100755 --- a/docker-configs/docker_install.sh +++ b/docker-configs/docker_install.sh @@ -121,7 +121,7 @@ MAX_PEERS: 1 EXPORT_AMBE: False IP: 127.0.0.1 PORT: 54917 -PASSPHRASE: +PASSPHRASE: passw0rd GROUP_HANGTIME: 5 USE_ACL: True REG_ACL: DENY:1 @@ -180,9 +180,9 @@ LOCAL_PEER_FILE = local_peer_ids.json LOCAL_TGID_FILE = local_talkgroup_ids.json # Number of days before we reload DMR-MARC database files. RELOAD_TIME = 1 -PEER_URL = https://adn.systems/files/peer_ids.json -SUBSCRIBER_URL = https://adn.systems/files/subscriber_ids.json -TGID_URL = https://adn.systems/files/talkgroup_ids.json +PEER_URL = https://servers.adn.systems/peer_ids.json +SUBSCRIBER_URL = https://servers.adn.systems/subscriber_ids.json +TGID_URL = https://servers.adn.systems/talkgroup_ids.json From ebd5a6faf2e9e4d375d4b0f6fdf5c605b10b4c7e Mon Sep 17 00:00:00 2001 From: hp3icc Date: Tue, 27 Jan 2026 11:36:09 -0500 Subject: [PATCH 6/6] Update parrot.cfg --- config/parrot.cfg | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config/parrot.cfg b/config/parrot.cfg index 42574d5..5038c39 100644 --- a/config/parrot.cfg +++ b/config/parrot.cfg @@ -108,13 +108,13 @@ PATH: ./ PEER_FILE: peer_ids.json SUBSCRIBER_FILE: subscriber_ids.json TGID_FILE: talkgroup_ids.json -PEER_URL: https://adn.systems/files/peer_ids.json -SUBSCRIBER_URL: https://adn.systems/files/subscriber_ids.json -TGID_URL: https://adn.systems/files/talkgroup_ids.json +PEER_URL: https://servers.adn.systems/peer_ids.json +SUBSCRIBER_URL: https://servers.adn.systems/subscriber_ids.json +TGID_URL: https://servers.adn.systems/talkgroup_ids.json LOCAL_SUBSCRIBER_FILE: local_subscriber_ids.json STALE_DAYS: 7 SUB_MAP_FILE: -SERVER_ID_URL: https://adn.systems/files/server_ids.tsv +SERVER_ID_URL: https://servers.adn.systems/server_ids.tsv SERVER_ID_FILE: server_ids.tsv