Commit f6120af0 authored by Yury's avatar Yury

new config type

parent 17ebc211
Pipeline #125 failed with stages
in 4 minutes and 44 seconds
stages:
- unittests
- build
- docker_build
# - docker_build
tests:
stage: unittests
......@@ -27,16 +28,15 @@ pypi_build:
- twine upload dist/*
docker_build:
stage: docker_build
tags:
- docker
script:
- docker build --pull -t "$CI_REGISTRY_IMAGE" .
- docker push "$CI_REGISTRY_IMAGE"
only:
- master
before_script:
- echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin $CI_REGISTRY
variables:
DOCKER_HOST: tcp://localhost:2375
\ No newline at end of file
#docker_build:
# stage: docker_build
# image: docker
# tags:
# - docker
# script:
# - docker build --pull -t "$CI_REGISTRY_IMAGE" .
# - docker push "$CI_REGISTRY_IMAGE"
# only:
# - master
# before_script:
# - echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin $CI_REGISTRY
\ No newline at end of file
import logging
from ipaddress import ip_network
from . import protocol
from . import auth
from . import config
from . import dispatcher
from . import engine
from . import socks
......@@ -12,18 +10,18 @@ from .version import __version__
logging.basicConfig(level=logging.INFO)
#
# def validate_config():
# if hasattr(config, "ALLOWED_DESTINATIONS"):
# ips = []
# for ip in config.ALLOWED_DESTINATIONS:
# ips.append(ip_network(ip))
# config.ALLOWED_DESTINATIONS = ips
#
#
# validate_config()
def validate_config():
if hasattr(config, "ALLOWED_DESTINATIONS"):
ips = []
for ip in config.ALLOWED_DESTINATIONS:
ips.append(ip_network(ip))
config.ALLOWED_DESTINATIONS = ips
validate_config()
def start_server(host="0.0.0.0", port=8080):
server = engine.Server(host, port)
def start_server(host, port, config):
server = engine.Server(host, port, config)
server.start()
from saturn import start_server
import sys
import argparse
import os
from saturn.engine import Server
def get_argv(index, default=None):
try:
return sys.argv[index]
except IndexError:
return default
parser = argparse.ArgumentParser(description='Start Saturn SOCKS5 server', usage="python3 -m saturn [options]")
host = get_argv(1, "0.0.0.0")
port = get_argv(2, 8080)
start_server(host, port)
\ No newline at end of file
parser.add_argument('--host', type=str,
help='IP address on which server will run (default: 0.0.0.0)', default='0.0.0.0')
parser.add_argument('--port', type=int,
help='port on which server will run (default: 8080)', default=8080)
parser.add_argument('--config', type=str, help='path to custom config file. Overrides --host and --port values',
default=os.path.dirname(__file__) + "/config.ini")
args = parser.parse_args()
def start_server(host, port, config):
server = Server(host, port, config)
server.start()
start_server(**args.__dict__)
import logging
class Authenticator:
method = 2
have_users = True
def __new__(cls, *args, **kwargs):
if not hasattr(cls, 'instance'):
cls.instance = super().__new__(cls)
return cls.instance
def __init__(self, **data):
self.users = data
def __init__(self, **options):
for option_name, option_value in options.items():
setattr(self, option_name, option_value)
self.users = {}
def import_users(self, userdict):
self.users = userdict
async def authenticate(self, data):
try:
......
class Authenticator:
method = 0
have_users = False
def __new__(cls, *args, **kwargs):
if not hasattr(cls, 'instance'):
......
[General]
host = 0.0.0.0
port = 8080
tcp = True
udp = False
[Authentication]
methods = saturn.auth.dict
[Security]
allowed_destinations = ["0.0.0.0/0"]
[Users]
default_user = 1234567890abcd!
\ No newline at end of file
AUTHENTICATION_METHODS = ["saturn.auth.dict"]
SATURN_AUTH_DICT = {
"USER_TEST": "Test_password"
}
ALLOWED_DESTINATIONS = ["0.0.0.0/0"]
import asyncio
import typing
import os
import logging
from ipaddress import IPv4Address, IPv6Address
from saturn import protocol, config
from configparser import ConfigParser
from saturn import protocol
class Server:
def __init__(self, host: typing.Union[IPv6Address, IPv4Address, str],
port: int,
tcp:bool=True, udp=False, custom_auth=None):
self.host = host
self.port = port
self.tcp = tcp
self.udp = udp
port: int, config_path: str = os.path.dirname(__file__) + "/config.ini",
tcp: bool = True, udp=False):
self.config = ConfigParser()
self.config.read(config_path)
self.host = self.config.get("General", "host", fallback=None) or host if config_path else host
self.port = self.config.get("General", "port", fallback=None) or port if config_path else port
self.tcp = self.config.get("General", "tcp", fallback=None) or tcp if config_path else tcp
self.udp = self.config.get("General", "udp", fallback=None) or udp if config_path else udp
self.auth_methods = []
self.auth_config = config.AUTHENTICATION_METHODS if custom_auth is None else custom_auth
def init_auth_methods(self):
for method in self.auth_config:
m = __import__(method, globals=globals(), fromlist=[""])
self.auth_methods.append(m.Authenticator(**getattr(config, method.upper().replace(".", "_"), {})))
"""
Initiate authentication methods from independent modules
:return:
"""
for method in self.config["Authentication"].get("methods", "").split(","):
m = __import__(method.strip(), globals=globals(), fromlist=[""])
auth_method = m.Authenticator(**getattr(self.config, method.upper().replace(".", "_"), {}))
if m.Authenticator.have_users:
auth_method.import_users(self.config["Users"])
self.auth_methods.append(auth_method)
if not self.auth_methods:
raise Exception("Server have no auth methods. Please fill in AUTHENTICATION_METHODS")
raise Exception("Server have no auth methods. Please fill in [Authentication] methods in config file")
@property
def server_auth_methods(self):
"""
All auth methods IDs as list
:return: list of auth methods IDs
"""
return [x.method for x in self.auth_methods]
async def auth(self, method, *args, **kwargs):
"""
Router to authentication type authenticate() method
:param method: int:
:param args:
:param kwargs:
:return: bool: authenticated or not
"""
for m in self.auth_methods:
if m.method == method:
return await m.authenticate(*args, **kwargs)
......@@ -34,6 +58,11 @@ class Server:
return False
def start(self):
"""
Start SOCKS5 server
:return:
"""
logging.info("Starting Saturn SOCKS5 server")
loop = asyncio.new_event_loop()
self.init_auth_methods()
if self.tcp:
......
......@@ -3,7 +3,7 @@ import socket
from ipaddress import IPv4Address
from ipaddress import ip_network
from saturn import state, config
from saturn import state
from saturn.protocol import TcpClient
from saturn.socks import reply
from .base import SocksRequest
......@@ -16,7 +16,7 @@ class SocksRequestConnect(SocksRequest):
assert not isinstance(self.dispatcher.state, state.Connected)
on_connect = self.dispatcher.loop.create_future()
allowed_to = False
for addr in getattr(config, 'ALLOWED_DESTINATIONS', [ip_network('0.0.0.0/0')]):
for addr in getattr(self.dispatcher.server.config["Security"], 'allowed_destinations', ["0.0.0.0/0"]):
if self.dst_addr in ip_network(addr):
allowed_to = True
break
......
__version__ = "0.7"
\ No newline at end of file
__version__ = "0.9"
\ No newline at end of file
......@@ -3,7 +3,7 @@ from setuptools import setup, find_packages
setup(
name='saturn_proxy_server', # How you named your package folder (MyLib)
packages=find_packages(exclude=['*.tests', '*.tests.*', 'tests.*', 'tests']), # Chose the same as "name"
version='0.7', # Start with a small number and increase it with every change you make
version='0.9', # Start with a small number and increase it with every change you make
license='MIT', # Chose a license from here: https://help.github.com/articles/licensing-a-repository
long_description='''# Socks5 async proxy server
Saturn is a SOCKS5 server based on asyncio protocols
......
import unittest
import saturn
import asyncio
from configparser import ConfigParser
class ServerTests(unittest.TestCase):
......@@ -8,45 +10,74 @@ class ServerTests(unittest.TestCase):
self.assertRaises(TypeError, saturn.engine.Server)
# server = saturn.engine.Server()
class SocksTests(unittest.TestCase):
def test_hello_none_auth(self):
server = saturn.engine.Server('127.0.0.1', 1, custom_auth=["saturn.auth.none"])
with open("tmp_config.ini", "w") as tmp_config:
config = ConfigParser()
config.add_section("Authentication")
config.set("Authentication", "methods", "saturn.auth.none")
config.write(tmp_config)
server = saturn.engine.Server("127.0.0.1", 1, config_path="tmp_config.ini")
server.init_auth_methods()
dispatcher = saturn.dispatcher.Dispatcher(server, None, None)
hello = saturn.socks.SocksHello(dispatcher, b'\x05\x02\x00\x02').reply()
hello = saturn.socks.SocksHello(dispatcher, b"\x05\x02\x00\x02").reply()
self.assertEqual(b"\x05\x00", bytes(hello))
hello = saturn.socks.SocksHello(dispatcher, b'\x05\x01\x02').reply()
self.assertEqual(bytes(hello), b"\x05\xff")
hello = saturn.socks.SocksHello(dispatcher, b"\x05\x01\x02").reply()
self.assertEqual(b"\x05\xff", bytes(hello))
def test_hello_only_password(self):
server = saturn.engine.Server('127.0.0.1', 1, custom_auth=["saturn.auth.dict"])
with open("tmp_config.ini", "w") as tmp_config:
config = ConfigParser()
config.add_section("Authentication")
config.add_section("Users")
config.set("Authentication", "methods", "saturn.auth.dict")
config.write(tmp_config)
server = saturn.engine.Server("127.0.0.1", 1, config_path="tmp_config.ini")
server.init_auth_methods()
dispatcher = saturn.dispatcher.Dispatcher(server, None, None)
hello = saturn.socks.SocksHello(dispatcher, b'\x05\x01\x00').reply()
hello = saturn.socks.SocksHello(dispatcher, b"\x05\x01\x00").reply()
self.assertEqual(bytes(hello), b"\x05\xff")
hello = saturn.socks.SocksHello(dispatcher, b'\x05\x01\x02').reply()
hello = saturn.socks.SocksHello(dispatcher, b"\x05\x01\x02").reply()
self.assertEqual(bytes(hello), b"\x05\x02")
def test_hello_unknown(self):
server = saturn.engine.Server('127.0.0.1', 1, custom_auth=["saturn.auth.dict"])
with open("tmp_config.ini", "w") as tmp_config:
config = ConfigParser()
config.add_section("Authentication")
config.add_section("Users")
config.set("Authentication", "methods", "saturn.auth.dict")
config.write(tmp_config)
server = saturn.engine.Server("127.0.0.1", 1, config_path="tmp_config.ini")
server.init_auth_methods()
dispatcher = saturn.dispatcher.Dispatcher(server, None, None)
hello = saturn.socks.SocksHello(dispatcher, b'\x05\x01\x05').reply()
hello = saturn.socks.SocksHello(dispatcher, b"\x05\x01\x05").reply()
self.assertEqual(bytes(hello), b"\x05\xff")
def test_no_auth_methods(self):
server = saturn.engine.Server('127.0.0.1', 1, custom_auth=[])
with open("tmp_config.ini", "w") as tmp_config:
config = ConfigParser()
config.add_section("Authentication")
config.set("Authentication", "methods", "")
config.write(tmp_config)
server = saturn.engine.Server("127.0.0.1", 1, config_path="tmp_config.ini")
self.assertRaises(Exception, server.init_auth_methods)
def test_SocksAuthenticate(self):
server = saturn.engine.Server('127.0.0.1', 1, custom_auth=["saturn.auth.dict"])
with open("tmp_config.ini", "w") as tmp_config:
config = ConfigParser()
config.add_section("Authentication")
config.add_section("Users")
config.set("Authentication", "methods", "saturn.auth.dict")
config.set("Users", "default_user", "someDefaultPassword")
config.write(tmp_config)
server = saturn.engine.Server("127.0.0.1", 1, config_path="tmp_config.ini")
server.init_auth_methods()
dispatcher = saturn.dispatcher.Dispatcher(server, None, None)
dispatcher.state = saturn.state.WaitingAuthenticationData(2)
login = 'USER_TEST'
password = 'Test_password'
login = "default_user"
password = "someDefaultPassword"
req = b"\x05" + len(login).to_bytes(1, "big") + login.encode() + len(password).to_bytes(1, "big") + password.encode()
self.assertEqual(b"\x01\x00", asyncio.run(saturn.socks.SocksAuthenticate(dispatcher, req).authenticate()))
[Authentication]
methods =
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment