OpenC2-org / openc2-yuuki

OpenC2 proxy
MIT License
1 stars 2 forks source link

AdamTheAnalyst: Added Config File, Added Multiple Profiles, Fixed Break Bug in Shell.py #2

Closed AdamTheAnalyst closed 8 years ago

AdamTheAnalyst commented 8 years ago

I have added the following which should enable me to get up and running with profile creation:

Config File

Found in yuuki.conf, allows users to define the location of profiles and network settings without passing them as args. This config then gets loaded into the flask app context so can be accessed by profiles if necessary in the future (IP's of routers etc) using "from flask import current_app as app".

I have stored this in the config dict app.config["yuuki"] rather than using flask's traditional "app.config.from_envvar" to load it into Flask's default config. This means that the user can't define profile names that would interfere with Flask's operation. If you don't like this approach and want to use Flask's inbuilt method then I am happy to change it.

Break In Shell.py

Just to catch ^C's, it wasn't exiting before.

Multiple Profile Loading

Profiles are now defined in the config file under the "[profiles]" section, they are then loaded in to a list which gets passed to the Dispatcher's constructor and loaded in the same way. master.

I am happy to change anything you don't see as working, just let me know. After this I should be able to continue as we talked about, building more profiles for vendor technologies without tampering with your core functionality.

Cheers

Adam

AdamTheAnalyst commented 8 years ago

No problem, I will get these rolled in tomorrow morning BST.

Thanks for the feedback.

AdamTheAnalyst commented 8 years ago

I have added the above fixes. The only thing I am unsure on is whether you want the port flag to over rule the config file.

I will look to address profile collisions at some-point, as all profiles should execute if they share an action, but I am unsure how to handle responses for partial failures (one succeeds and one fails etc). I'll play around with some options in my fork. If you have any ideas on this, send them me in a comment and I will get them built.

jtcbrule commented 8 years ago

Thanks for the updated pull request.

I think the convention in most projects is that command line switches should override external configuration files; but it's a low priority. I'll probably change that when I get a chance to refactor the code a bit.

It's not clear to me that what you described is the 'correct' behavior for profile collisions. Certainly, the following situation should be supported (and currently is not - I've been meaning to fix this):

profile1.py

@action(target="targetA")
def deny(target, actuator, modifier):
    do_something();

profile2.py

@action(target="targetB")
def deny(target, actuator, modifier):
    do_something_else();

I don't consider this a collision, because they have different targets. But if the same action is defined in two files, for the same targets:

profile1.py

@action(target="targetA")
def deny(target, actuator, modifier):
    return do_something();

profile2.py

@action(target="targetA")
def deny(target, actuator, modifier):
    return do_something_else();

I don't think both do_something() and do_something_else() should be called. It's not obvious at all how to handle the return values and it breaks the multimethod... 'metaphor', I guess. My instinct is that the last defined @action() with the same target and actuator types should simply replace earlier ones, probably issuing a warning on redefinition.


I can see a case for the following:

profile1.py

@action(target="targetA", actuator="device1")
def deny(target, actuator, modifier):
    return do_something();

profile2.py

@action(target="targetA", actuator="device2")
def deny(target, actuator, modifier):
    return do_something_else();

And having something like the command:

{"action": "deny", "target": {"type": "targetA"}, "actuator": {"type": "openc2:all"}}

Trigger all of the actuators for that target. (Still not sure what to do with the return values.) Would that satisfy your use case?

AdamTheAnalyst commented 8 years ago

That satisfies my use case. The return would be a RESPONSE object to the original command acknowledging its receipt and successful parsing, then a separate RESPONSE message would be sent for each device to the upstream orchestrator that references the original command. This keeps to the example in section 5 of the Language Description Document.