Merge pull request #5 from beardog108/crypto3

Refactor configuration management code
This commit is contained in:
Kevin Froman 2018-02-22 21:19:12 -06:00 committed by GitHub
commit 4f28141de6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 197 additions and 35 deletions

View File

@ -20,7 +20,7 @@
import flask
from flask import request, Response, abort
from multiprocessing import Process
import configparser, sys, random, threading, hmac, hashlib, base64, time, math, os, logger
import sys, random, threading, hmac, hashlib, base64, time, math, os, logger, config
from core import Core
import onionrutils, onionrcrypto
@ -37,31 +37,32 @@ class API:
else:
return True
def __init__(self, config, debug):
def __init__(self, debug):
'''
Initialize the api server, preping variables for later use
This initilization defines all of the API entry points and handlers for the endpoints and errors
This also saves the used host (random localhost IP address) to the data folder in host.txt
'''
if os.path.exists('dev-enabled'):
config.reload()
if config.get('devmode', True):
self._developmentMode = True
logger.set_level(logger.LEVEL_DEBUG)
#logger.warn('DEVELOPMENT MODE ENABLED (THIS IS LESS SECURE!)')
else:
self._developmentMode = False
logger.set_level(logger.LEVEL_INFO)
self.config = config
self.debug = debug
self._privateDelayTime = 3
self._core = Core()
self._crypto = onionrcrypto.OnionrCrypto(self._core)
self._utils = onionrutils.OnionrUtils(self._core)
app = flask.Flask(__name__)
bindPort = int(self.config['CLIENT']['PORT'])
bindPort = int(config.get('CLIENT')['PORT'])
self.bindPort = bindPort
self.clientToken = self.config['CLIENT']['CLIENT HMAC']
self.clientToken = config.get('CLIENT')['CLIENT HMAC']
if not os.environ.get("WERKZEUG_RUN_MAIN") == "true":
logger.debug('Your HMAC token: ' + logger.colors.underline + self.clientToken)

110
onionr/config.py Normal file
View File

@ -0,0 +1,110 @@
'''
Onionr - P2P Microblogging Platform & Social network
This file deals with configuration management.
'''
'''
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
import os, json, logger
_configfile = os.path.abspath('data/config.json')
_config = {}
def get(key, default = None):
'''
Gets the key from configuration, or returns `default`
'''
if is_set(key):
return get_config()[key]
return default
def set(key, value = None, savefile = False):
'''
Sets the key in configuration to `value`
'''
global _config
_config[key] = value
if savefile:
save()
def is_set(key):
return key in get_config() and not get_config()[key] is None
def check():
'''
Checks if the configuration file exists, creates it if not
'''
try:
if not os.path.exists(os.path.dirname(get_config_file())):
os.path.mkdirs(os.path.dirname(get_config_file()))
if not os.path.isfile(get_config_file()):
open(get_config_file(), 'a', encoding="utf8").close()
save()
except:
logger.warn('Failed to check configuration file.')
def save():
'''
Saves the configuration data to the configuration file
'''
check()
try:
with open(get_config_file(), 'w', encoding="utf8") as configfile:
json.dump(get_config(), configfile, indent=2, sort_keys=True)
except:
logger.warn('Failed to write to configuration file.')
def reload():
'''
Reloads the configuration data in memory from the file
'''
check()
try:
with open(get_config_file(), 'r', encoding="utf8") as configfile:
set_config(json.loads(configfile.read()))
except:
logger.warn('Failed to parse configuration file.')
def get_config():
'''
Gets the entire configuration as an array
'''
return _config
def set_config(config):
'''
Sets the configuration to the array in arguments
'''
global _config
_config = config
def get_config_file():
'''
Returns the absolute path to the configuration file
'''
return _configfile
def set_config_file(configfile):
'''
Sets the path to the configuration file
'''
global _configfile
_configfile = os.abs.abspath(configfile)

View File

@ -108,6 +108,21 @@ def get_level():
return _level
def set_file(outputfile):
'''
Set the file to output to, if enabled
'''
global _outputfile
_outputfile = outputfile
def get_file():
'''
Get the file to output to
'''
return _outputfile
def raw(data):
'''
Outputs raw data to console without formatting

View File

@ -20,8 +20,8 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
import sys, os, configparser, base64, random, getpass, shutil, subprocess, requests, time, logger, platform
import api, core, gui
import sys, os, base64, random, getpass, shutil, subprocess, requests, time, platform
import api, core, gui, config, logger
from onionrutils import OnionrUtils
from netcontroller import NetController
@ -39,12 +39,29 @@ class Onionr:
Main Onionr class. This is for the CLI program, and does not handle much of the logic.
In general, external programs and plugins should not use this class.
'''
try:
os.chdir(sys.path[0])
except FileNotFoundError:
pass
if os.path.exists('dev-enabled'):
# Load global configuration data
exists = os.path.exists(config.get_config_file())
config.set_config({'devmode': True, 'log.file': True, 'log.console': True, 'log.outputfile': 'data/output.log', 'log.color': True}) # this is the default config, it will be overwritten if a config file already exists. Else, it saves it
config.reload() # this will read the configuration file into memory
settings = 0b000
if config.get('log.color', True):
settings = settings | logger.USE_ANSI
if config.get('log.console', True):
settings = settings | logger.OUTPUT_TO_CONSOLE
if config.get('log.file', False):
settings = settings | logger.OUTPUT_TO_FILE
logger.set_file(config.get('log.outputfile', 'data/output.log'))
logger.set_settings(settings)
if config.get('devmode', True):
self._developmentMode = True
logger.set_level(logger.LEVEL_DEBUG)
else:
@ -54,7 +71,7 @@ class Onionr:
self.onionrCore = core.Core()
self.onionrUtils = OnionrUtils(self.onionrCore)
# Get configuration and Handle commands
# Handle commands
self.debug = False # Whole application debugging
@ -79,10 +96,8 @@ class Onionr:
self.onionrCore.createAddressDB()
# Get configuration
self.config = configparser.ConfigParser()
if os.path.exists('data/config.ini'):
self.config.read('data/config.ini')
else:
if not exists:
# Generate default config
# Hostname should only be set if different from 127.x.x.x. Important for DNS rebinding attack prevention.
if self.debug:
@ -92,9 +107,7 @@ class Onionr:
randomPort = random.randint(1024, 65535)
if self.onionrUtils.checkPort(randomPort):
break
self.config['CLIENT'] = {'participate': 'true', 'CLIENT HMAC': base64.b64encode(os.urandom(32)).decode('utf-8'), 'PORT': randomPort, 'API VERSION': API_VERSION}
with open('data/config.ini', 'w') as configfile:
self.config.write(configfile)
config.set('CLIENT', {'participate': 'true', 'CLIENT HMAC': base64.b64encode(os.urandom(32)).decode('utf-8'), 'PORT': randomPort, 'API VERSION': API_VERSION}, True)
command = ''
try:
@ -117,13 +130,14 @@ class Onionr:
def getCommands(self):
return {
'help': self.showHelp,
'version': self.version,
'config': self.configure,
'start': self.start,
'stop': self.killDaemon,
'version': self.version,
'stats': self.showStats,
'listpeers': self.listPeers,
'list-peers': self.listPeers,
'stats': self.showStats,
'help': self.showHelp,
'': self.showHelpSuggestion,
'addmsg': self.addMessage,
'addmessage': self.addMessage,
@ -139,6 +153,7 @@ class Onionr:
return {
'help': 'Displays this Onionr help menu',
'version': 'Displays the Onionr version',
'config': 'Configures something and adds it to the file',
'start': 'Starts the Onionr daemon',
'stop': 'Stops the Onionr daemon',
'stats': 'Displays node statistics',
@ -149,6 +164,23 @@ class Onionr:
'gui': 'Opens a graphical interface for Onionr'
}
def configure(self):
'''
Displays something from the configuration file, or sets it
'''
if len(sys.argv) >= 4:
config.reload()
config.set(sys.argv[2], sys.argv[3], True)
logger.debug('Configuration file updated.')
elif len(sys.argv) >= 3:
config.reload()
logger.info(logger.colors.bold + sys.argv[2] + ': ' + logger.colors.reset + str(config.get(sys.argv[2], logger.colors.fg.red + 'Not set.')))
else:
logger.info(logger.colors.bold + 'Get a value: ' + logger.colors.reset + sys.argv[0] + ' ' + sys.argv[1] + ' <key>')
logger.info(logger.colors.bold + 'Set a value: ' + logger.colors.reset + sys.argv[0] + ' ' + sys.argv[1] + ' <key> <value>')
def execute(self, argument):
'''
Executes a command
@ -271,7 +303,7 @@ class Onionr:
if not os.environ.get("WERKZEUG_RUN_MAIN") == "true":
if self._developmentMode:
logger.warn('DEVELOPMENT MODE ENABLED (THIS IS LESS SECURE!)')
net = NetController(self.config['CLIENT']['PORT'])
net = NetController(config.get('CLIENT')['PORT'])
logger.info('Tor is starting...')
if not net.startTor():
sys.exit(1)
@ -280,7 +312,7 @@ class Onionr:
time.sleep(1)
subprocess.Popen(["./communicator.py", "run", str(net.socksPort)])
logger.debug('Started communicator')
api.API(self.config, self.debug)
api.API(self.debug)
return
@ -290,7 +322,7 @@ class Onionr:
'''
logger.warn('Killing the running daemon')
net = NetController(self.config['CLIENT']['PORT'])
net = NetController(config.get('CLIENT')['PORT'])
try:
self.onionrUtils.localCommand('shutdown')
except requests.exceptions.ConnectionError:

View File

@ -18,30 +18,34 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
# Misc functions that do not fit in the main api, but are useful
import getpass, sys, requests, configparser, os, socket, hashlib, logger, sqlite3
import getpass, sys, requests, os, socket, hashlib, logger, sqlite3, config
import nacl.signing, nacl.encoding
if sys.version_info < (3, 6):
try:
import sha3
except ModuleNotFoundError:
logger.fatal('On Python 3 versions prior to 3.6.x, you need the sha3 module')
sys.exit(1)
class OnionrUtils:
'''Various useful functions'''
'''
Various useful function
'''
def __init__(self, coreInstance):
self.fingerprintFile = 'data/own-fingerprint.txt'
self._core = coreInstance
return
def localCommand(self, command):
'''
Send a command to the local http API server, securely. Intended for local clients, DO NOT USE for remote peers.
'''
config = configparser.ConfigParser()
if os.path.exists('data/config.ini'):
config.read('data/config.ini')
else:
return
requests.get('http://' + open('data/host.txt', 'r').read() + ':' + str(config['CLIENT']['PORT']) + '/client/?action=' + command + '&token=' + config['CLIENT']['CLIENT HMAC'])
config.reload()
# TODO: URL encode parameters, just as an extra measure. May not be needed, but should be added regardless.
requests.get('http://' + open('data/host.txt', 'r').read() + ':' + str(config.get('CLIENT')['PORT']) + '/client/?action=' + command + '&token=' + config.get('CLIENT')['CLIENT HMAC'])
return
@ -141,7 +145,7 @@ class OnionrUtils:
retVal = False
return retVal
def validatePubKey(self, key):
'''Validate if a string is a valid base32 encoded Ed25519 key'''
retVal = False
@ -195,5 +199,5 @@ class OnionrUtils:
retVal = False
if not idNoDomain.isalnum():
retVal = False
return retVal
return retVal