Compare commits

9 Commits

Author SHA1 Message Date
c0111f2720 [docs] include README.md into docs 2023-10-31 00:46:57 +01:00
bcaacc1634 [readme] WIP update 2023-10-31 00:46:10 +01:00
abbd1561f2 [docs] add README for triggers and actions 2023-10-31 00:45:44 +01:00
b9b7c0bf3b [docs] change docbuilder markdown output 2023-10-31 00:44:49 +01:00
8898565ff8 [docs] add Examples to each trigger/action 2023-10-31 00:41:59 +01:00
06534e0bbd [readme] init 2023-10-30 23:08:02 +01:00
d4a660383e [docs] rm unused file 2023-10-30 23:00:32 +01:00
e96bbb5f49 [gitignore] update 2023-10-30 23:00:22 +01:00
ad9060c8f6 [gokill] mv main 2023-10-30 23:00:05 +01:00
18 changed files with 329 additions and 82 deletions

2
.gitignore vendored
View File

@@ -4,6 +4,6 @@ result
example.json
go.sum
go.mod
gokill
./gokill
output.md
thoughts.md

90
README.md Normal file
View File

@@ -0,0 +1,90 @@
# gokill
'gokill' is a tool that completes some actions when a certain event occurs.
actions can vary from shuting down the machine to sending mails over erasing data.
actions can be triggert by certain conditions like specific outcomes of unix
comands or not having internet connection.
actions and triggers should be easy to extend and handled like plugins. they
also should be self documenting.
every action and trigger should be testable at anytime as a 'dry-run'.
actions can have a 'stage' defined. the lowest stage is started first,
and only when all actions on that stage are finished next stage is triggered
gokill should run as daemon. config should be read from /etc/somename/config.json
## Config Example
``` json
[ //list of triggers
{
"type": "UsbDisconnect",
"name": "First Trigger",
"options": {
"deviceId": "ata-Samsung_SSD_860_EVO_1TB_S4AALKWJDI102",
"waitTillConnected": true //only trigger when usb drive was actually attached before
}
"actions": [ //list of actions that will be executed when triggered
{
"name": "unixCommand",
"options": {
"command": "shutdown -h now"
},
"stage": 2 // defines the order in which actions are triggered.
},
{
"type": "sendMail",
"options": {
"smtpserver": "domain.org",
"port": 667,
"recipients": [ "mail1@host.org", "mail2@host.org" ],
"message": "kill switch was triggered",
"attachments": [ "/path/atachments" ],
"pubkeys": "/path/to/keys.pub"
},
"stage": 1 //this event is triggered first, then the shutdown
},
]
},
{
"type": "EthernetDisconnect",
"name": "Second Trigger",
"options": {
"interfaceName": "eth0",
}
"actions": [
{
"name": "unixCommand",
"options": {
"command": "env DISPLAY=:0 sudo su -c i3lock someUser"
}
}
]
}
]
```
## actions
- [x] shutdown
- [ ] wipe ram
- [ ] send mail
- [ ] delete data
- [ ] shred area
- [x] random command
- [ ] wordpress post
- [ ] ipfs command
- [ ] [buskill 'triggers'](https://github.com/BusKill/awesome-buskill-triggers)
- [x] [lock-screen](https://github.com/BusKill/buskill-linux/tree/master/triggers)
- [x] shutdown
- [ ] luks header shredder
- [ ] veracrypt self-destruct
## Triggers
- [ ] no internet
- [x] [pull usb stick](https://github.com/deepakjois/gousbdrivedetector/blob/master/usbdrivedetector_linux.go)
- [x] ethernet unplugged
- [ ] power adapter disconnected
- [ ] unix command
- anyOf
- trigger wrapper containing many triggers and fires as soon as one of them
is triggered
- allOf
- [ ] ipfs trigger

View File

@@ -39,7 +39,21 @@ func (p Printer) GetName() string {
}
func (p Printer) GetDescription() string {
return "When triggered prints the configured message to stdout"
return `
Prints a given message to stdout.
This action is mostly used for debugging purposes.
`
}
func (p Printer) GetExample() string {
return `
{
type: "Print",
"options: {
"message": "Hello World!"
}
}
`
}
func (p Printer) GetOptions() []internal.ConfigOption {

View File

@@ -37,7 +37,15 @@ func (p Shutdown) GetName() string {
}
func (p Shutdown) GetDescription() string {
return "When triggered shuts down the machine"
return "Shutsdown the machine by perfoming a ```shutdown -h now```"
}
func (p Shutdown) GetExample() string {
return `
{
"type": "Shutdown",
}
`
}
func (p Shutdown) GetOptions() []internal.ConfigOption {

View File

@@ -40,7 +40,21 @@ func (p TimeOut) GetName() string {
}
func (p TimeOut) GetDescription() string {
return "When triggered waits given duration before continuing with next stage"
return `
Waits given duration in seconds.
This can be used to wait a certain amount of time before continuing to the next Stage
`
}
func (p TimeOut) GetExample() string {
return `
{
"type": "Timeout",
"options": {
"duration": 5
}
}
`
}
func (p TimeOut) GetOptions() []internal.ConfigOption {

View File

@@ -82,7 +82,18 @@ func (p Command) GetName() string {
}
func (p Command) GetDescription() string {
return "When triggered executes given command"
return "Invoces given command using exec."
}
func (p Command) GetExample() string {
return `
{
"type": "Command",
"options": {
"command": "srm /path/to/file"
}
}
`
}
func (p Command) GetOptions() []internal.ConfigOption {

View File

@@ -13,11 +13,19 @@ import (
func getMarkdown(documenter internal.Documenter) string {
var result string
result += fmt.Sprintf("# %v\n%v\n## Options:\n", documenter.GetName(), documenter.GetDescription())
result += fmt.Sprintf("# %v\n%v\n\n", documenter.GetName(), documenter.GetDescription())
result += fmt.Sprintf("*Example:*\n``` json\n%v\n```\n## Options:\n", documenter.GetExample())
for _, opt := range documenter.GetOptions() {
sanitizedDefault := "\"\""
if len(opt.Default) > 0 {
sanitizedDefault = opt.Default
}
result += fmt.Sprintf("### %v\n%v \n\n*Type:* %v \n\n*Default:* ```%v``` \n",
opt.Name, opt.Description, opt.Type, opt.Default)
opt.Name, opt.Description, opt.Type, sanitizedDefault)
}
return result

86
cmd/gokill/gokill.go Normal file
View File

@@ -0,0 +1,86 @@
package main
import (
"encoding/json"
"flag"
"fmt"
"os"
"unknown.com/gokill/actions"
"unknown.com/gokill/internal"
"unknown.com/gokill/triggers"
)
func GetDocumentation() string {
actions := actions.GetDocumenters()
var result string
writeOptions := func(documenters []internal.Documenter) {
for _, act := range documenters {
result += fmt.Sprintf("\n### %v\nDescription: %v \nValues:\n", act.GetName(), act.GetDescription())
for _, opt := range act.GetOptions() {
result += fmt.Sprintf("- Name: **%v**\n\t- Type: %v\n\t- Descr: %v\n\t- Default: %v\n",
opt.Name, opt.Type, opt.Description, opt.Default)
result += "\n\n"
}
}
}
result = "# Available Triggers:\n\n"
writeOptions(triggers.GetDocumenters())
result += "\n\n# Available Actions:\n\n"
writeOptions(actions)
return result
}
func main() {
configFilePath := flag.String("c", "", "path to config file")
showDoc := flag.Bool("d", false, "show doc")
testRun := flag.Bool("t", false, "test run")
flag.Parse()
if *showDoc {
fmt.Print(GetDocumentation())
return
}
if *configFilePath == "" {
fmt.Println("No config file given. Use --help to show usage.")
return
}
actions.TestRun = *testRun
configFile, err := os.ReadFile(*configFilePath)
if err != nil {
fmt.Println("Error loading config file: ", err)
return
}
var f []internal.KillSwitchConfig
err = json.Unmarshal(configFile, &f)
if err != nil {
fmt.Println(err)
return
}
var triggerList []triggers.Trigger
for _, cfg := range f {
trigger, err := triggers.NewTrigger(cfg)
if err != nil {
fmt.Println(err)
return
}
trigger.Listen() //TODO: not block here
triggerList = append(triggerList, trigger)
}
}

View File

@@ -1,4 +1,4 @@
# Summary
- [Introduction](./gokill.md)
- [gokill](./README.md)
@GOKILL_OPTIONS@

View File

@@ -1 +1,23 @@
# Actions
Actions are executed when their parent Trigger got triggered.
They then perform some certain task depending on the specific action.
Those can vary from shutding down the machine, removing a file or running a bash command.
**Some Actions may cause permanent damage to the system. This is intended but should be used with caution.**
Actions can have a ```Stage``` assigned to define in which order they should run.
The lowest stage is executed first and only when finished the next stage is executed.
Actions on the same Stage run concurrently.
Actions have the following syntax:
``` json
{
"type": "SomeAction",
"options": { //each action defines its own options
"firstOption": "someValue",
"Stage": 2 //this (positive) number defines the order of multiple actions
}
}
```
To get a list of all actions and their options from the commandline run ``` gokill -d ```

View File

@@ -1,71 +0,0 @@
# Available Triggers:
### Timeout
Description: Triggers after given duration.
Values:
- **duration**
- Type: int
- Descr: duration in seconds
- Default: 0
# EthernetDisconnect
Description: Triggers if Ethernetcable is disconnected.
Values:
- **waitTillConnected**
- Type: bool
- Descr: Only trigger when device was connected before
- Default: true
- **interfaceName**
- Type: string
- Descr: Name of ethernet adapter
- Default: ""
### UsbDisconnect
Description: Triggers when given usb drive is disconnected
Values:
- **waitTillConnected**
- Type: bool
- Descr: Only trigger when device was connected before
- Default: true
- **deviceId**
- Type: string
- Descr: Name of device under /dev/disk/by-id/
- Default: ""
# Available Actions:
# Print
Description: When triggered prints the configured message to stdout
Values:
- **message**
- Type: string
- Descr: Message that should be printed
- Default: ""
### Timeout
Description: When triggered waits given duration before continuing with next stage
Values:
- **duration**
- Type: int
- Descr: duration in seconds
- Default: 0
# Command
Description: When triggered executes given command
Values:
- **command**
- Type: string
- Descr: command to execute
- Default:
- **args**
- Type: string[]
- Descr: args
- Default:
### Shutdown
Description: When triggered shuts down the machine
Values:

View File

@@ -7,6 +7,7 @@ let
prepareMD = ''
# Copy inputs into the build directory
cp -r --no-preserve=all $inputs/* ./
cp ${../README.md} ./README.md
${docbuilder}/bin/docbuilder --output ./
substituteInPlace ./SUMMARY.md \

View File

@@ -1 +0,0 @@
# Introduction

View File

@@ -1 +1,21 @@
# Triggers
Triggers wait for certain events and execute the actions defined for them.
There are different Triggers for different use cases.
For example ```UsbDisconnect``` is triggered when a certain Usb Drive is unplugged.
If you want your actions to be triggered when an ethernet cable is pulled use ```EthernetDisconnect``` instead.
Triggers have the following syntax:
``` json
{
"type": "SomeTrigger",
"name": "MyFirstTrigger",
"options": { //each trigger defines its own options
"firstOption": 23,
"secondOption": "foo"
},
"actions": [] //list actions that should be executed here
}
```
To get a list of all triggers and their options from the commandline run ```gokill -d```

View File

@@ -36,5 +36,6 @@ type ConfigOption struct {
type Documenter interface {
GetName() string
GetDescription() string
GetExample() string
GetOptions() []ConfigOption
}

View File

@@ -87,6 +87,21 @@ func (p EthernetDisconnect) GetDescription() string {
return "Triggers if Ethernetcable is disconnected."
}
func (p EthernetDisconnect) GetExample() string {
return `
{
"type": "EthernetDisconnect",
"name": "Example Trigger",
"options": {
"interfaceName": "eth0",
"waitTillConnected": true
}
"actions": [
]
}
`
}
func (p EthernetDisconnect) GetOptions() []internal.ConfigOption {
return []internal.ConfigOption{
{"waitTillConnected", "bool", "Only trigger when device was connected before", "true"},

View File

@@ -1,5 +1,4 @@
package triggers
import (
"encoding/json"
"fmt"
@@ -45,7 +44,21 @@ func (p TimeOut) GetName() string {
}
func (p TimeOut) GetDescription() string {
return "Triggers after given duration."
return "Triggers after given duration. Mostly used for debugging."
}
func (p TimeOut) GetExample() string {
return `
{
"type": "Timeout",
"name": "Example Trigger",
"options": {
"duration": 5
}
"actions": [
]
}
`
}
func (p TimeOut) GetOptions() []internal.ConfigOption {

View File

@@ -88,6 +88,22 @@ func (p UsbDisconnect) GetDescription() string {
return "Triggers when given usb drive is disconnected"
}
func (p UsbDisconnect) GetExample() string {
return `
{
"type": "UsbDisconnect",
"name": "Example Trigger",
"options": {
"deviceId": "ata-Samsung_SSD_860_EVO_1TB_S4AALKWJDI102",
"waitTillConnected": true
}
"actions": [
]
}
`
}
func (p UsbDisconnect) GetOptions() []internal.ConfigOption {
return []internal.ConfigOption{
{"waitTillConnected", "bool", "Only trigger when device was connected before", "true"},