Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Miscellaneous GRPC helpers (API versioning, retry, config, etc...)
Provided classes in this module API help to deal with RPC servers/clients handling.
The RpcServer
class handles the lifecycle of a GRPC server. To initialize, it basically needs:
RpcServiceDescriptor
objectsFolders
instance (usually provided by the RpcCliParser
CLI parsing)RpcCliParser
CLI parsing)Config
or ConfigHolder
instances, which will be this server's static config itemsConfig
or ConfigHolder
instances, which will be this server's user config itemsFalse
)True
)The Folders
class handle the different folders used by the server:
The RpcServiceDescriptor
class describes a given service to be hooked in a server instance. Its attributes are:
The manager class must:
A manager class can also inherit from RpcManager
class, which provides some usefull features:
_load
method, called by the server once all services are alive (typically to perform some internal initializations)_shutdown
method, called by the server once it is shutdown (typically to perform some internal finalization operations + interrupt long-running ones)logger
instance, to be used for all logging inside this manager and dependencieslock
instance, to be used to protect manager inner fields against reentranceclient
instance, initialized to the server own auto-client (see below)folders
instance, initialized to the server Folders
(see above)The RPC server will live its life in its own thread. When the application is about to terminate, it is advised to call the shutdown
method
in order to turn off the RPC server properly. This method can also be called remotely as a RpcServerService servive method.
When the shutdown method is called:
_shutdown
methodrpc-shutdown-grace
configured timeout)method.rpc-shutdown-timeout
value), if this one is >0Note that the code which created the server instance may use the wait_shutdown
method to block until the RPC server is shutdown.
Note that the RPC server instance will automatically serve:
For requests received from the RpcClient
implementation on a given service, the client api version is checked against the server "supported - current"
range for this service:
ResultCode.ERROR_API_CLIENT_TOO_OLD
errorResultCode.ERROR_API_SERVER_TOO_OLD
errorThe RPC server will hook to the USR2 signal for debug purpose (if required). When receiving this signal, following debug information will be dumped in a file name RpcServerDump-YYYYMMDDhhmmss.txt (with dump timestamp) in the logging folder (see below):
An initialized RPC server instance provides an auto_client
attribute, providing an RpcClient
instance pointing on everything
served by this server. Note that the timeout for this client can be configured through the rpc-client-timeout
config item.
Services declared as proxied ones (see RpcServiceDescriptor
above) will be forwarded to the required server/port as soon as this server calls the
proxy_register
method of the proxying server. Note that if this method is not called within the rpc-client-timeout
time, the method call will
end with an ERROR_PROXY_UNREGISTERED
error.
Configuration of proxied services is persisted in the workspace, in order to restore them when the proxying server restarts.
An RpcProxiedManager
abstract class is available for managers who are designed to be proxied. Such managers will automatically register in main proxy
server, with following details:
_proxied_services
method_proxied_version
methodself.proxy_client
attribute can be provided by _proxy_stubs
method_proxy_use_current_host
method needs to return True
When handling proxy servers, if the proxying server is started with events service enabled, it will send:
RPC_PROXY_REGISTER
event on each proxy_register
method successful callRPC_PROXY_FORGET
event on each proxy_forget
method successful callProperties of the events will be inherited from the method input message.
Each manager of the RPC server through its logger
attribute, will have his logs persisted in a <name>/<name>.log rolling file (relative to the
logging folder). The complete logs will also be persisted in the root folder.
Both logging folder and rollover cadency can be configured -- see Configuration chapter below.
The RPC server behavior can be configured through the following static configuration items:
Name | Description | Type | Default |
---|---|---|---|
rpc-max-workers | Maximum parallel RPC worker threads | Positive integer | 30 |
rpc-shutdown-grace | Grace period for pending calls to be terminated on shutdown (seconds) | Positive float | 30 |
rpc-shutdown-timeout | Final timeout before real shutdown (i.e. end of process; seconds) | Positive float | 60 |
rpc-logs-folder | Folder (absolute or workspace-relative) where to store rolling logs | String | "logs" |
rpc-logs-backup | Backup log files to be persisted for each manager on rollover | Integer | 10 |
rpc-logs-interval-unit | Logs rollover interval unit (see TimedRotatingFileHandler documentation) | Custom (see doc) | H |
rpc-logs-interval | Logs rollover interval (see TimedRotatingFileHandler documentation) | Positive integer | 1 |
rpc-main-host | Main RPC server host (to be used by proxied services) | String | "localhost" |
rpc-main-port | Main RPC server port (to be used by proxied services) | Positive integer | 54321 |
rpc-client-timeout | Timeout for RPC client when server is unreachable or proxy not registered yet (seconds) | Positive float | 60 |
rpc-event-retain-timeout | Retain timeout for event queues on interruption (seconds) | Positive integer | 300 |
rpc-event-keepalive-timeout | Timeout for sending keep alive empty events (seconds) | Positive integer | 60 |
import my_package
from my_package.api import MyStatus, MyConfig, MyApiVersion
from my_package.api.my_pb2_grpc import add_MyServiceServicer_to_server, MyServiceServicer, MyServiceStub
from grpc_helper import RpcServer, RpcServiceDescriptor, RpcManager
class MySampleManager(MyServiceServicer, RpcManager):
# Custom implementation for sample service
def update(self, request: MyConfig) -> MyStatus:
# Note that return message *MUST* be explicitely annoted for each implemented method!
return MyStatus()
def start():
# Create an RPC server on port 12345
srv = RpcServer(12345, [RpcServiceDescriptor(my_package, "my", MyApiVersion, MySampleManager(), add_MyServiceServicer_to_server, MyServiceStub)])
# Server is running in its own thread; we need to wait it for shutdown (on Ctrl-C or remote shutdown call)
srv.wait_shutdown()
The RpcClient
class provides an access to client side of a GRPC service. To initialize, it basically needs:
Optional inputs can also be provided:
ResultCode
status (default: true)RpcException
) when receiving non-OK ResultCode
statusOnce created, a client instance provide as many stubs as configured in the service map. Each of this stubs expose the generated methods of the corresponding service. These methods take the following arguments:
from my_package.api import MyStatus, MyApiVersion, Empty
from my_package.api.my_pb2_grpc import MyServiceStub
from grpc_helper import RpcClient
def start():
# Get a client access to my service
c = RpcClient("127.0.0.1", 12345, {"my": (MyServiceStub, MyApiVersion.MY_API_CURRENT)}, name="myclient")
# Use API
s: MyStatus = c.my.list(Empty())
Instances of the Config
class can be provided to initialize list of static and user configuration items known to a RPC server:
The Config
constructor arguments are the same than the public API ConfigItem
ones (see config service).
It also supports an additional custom_validator
one, allowing to provide a validation method when validator
argument is set to CONFIG_VALID_CUSTOM
.
This method takes two arguments:
name
string: may be useful to raise meaningful validation errorsvalue
string: value to be validated. The method shall raise an RpcException
if the value is invalid.Configuration items default value are loaded in the following order:
Note that if the default value fails to be validated with a given item validator (or if it is empty while not allowed), the server will refuse to launch with a thrown relevant exception.
Static items current value will be initialized with the default value. User items are initialized to either the default value, or to the user configured value (thanks to the config service) if it was ever modified. User modified values are persisted in the workspace folder config.json file.
Current value may be programmatically accessed through one of the following property accessors:
item.str_val
: raw string valueitem.int_val
: value converted as an integerConfiguration items may be defined as attributes of a class inheriting from the ConfigHolder
one. Such classes can be provided directly to RpcServer
constructor to initialize configuration items (which is more convenient that referencing items one by one).
A ready-to-use CLI parser is provided through the RpcCliParser
class, allowing to rapidly instantiate an RPC server from CLI options.
Constructor arguments are:
description
string, which will be displayed when using the -h / --help optionversion
string, which will be displayed when using the -V / --version optionThe options defined by this parser are:
Name | Description |
---|---|
--help | displays the program help and exit |
-V / --version | displays the program version and exit |
--system PATH | overrides the default system folder |
--user PATH | overrides the default user folder |
-w / --workspace PATH | defines the workspace folder |
-p / --port PORT | overrides the RPC server default listening port |
-c / --config NAME=VALUE | overrides the default value of a given configuration item |
After parsing:
Folders
object (see above), available in the folders
argument of the namespace.config
argument of the namespace.The following methods are available to tune the parser behavior:
with_rpc_args
: adds the options listed above, and returns the RpcCliParser
instance (for fluent API usage). Arguments are:
default_port
: default RPC listening portdefault_sys
: default system folderdefault_usr
: default user folderdefault_wks
: default workspace folderparse
: will parse input arguments, and return the namespace. Arguments are program command-line ones by default, or may be provided as a string list
(e.g. for testing purpose)Following example shows how to use this parser to create an RpcServer
instance:
from grpc_helper import RpcCliParser, RpcServer
def start():
# Parse arguments to create RPC server instance
args = RpcCliParser("My custom RPC server", "1.0.0").with_rpc_args(12345, "/etc/my_srv", "~/.local/my_srv", "my_srv_wks").parse()
srv = RpcServer(args.port, [], args.folders, args.config)
FAQs
Helpers supercharging grpc features (API versioning, retry, etc...)
We found that grpc-helper demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.