Compare commits

2 Commits

Author SHA1 Message Date
20120785bd [nix] add devShell 2023-10-30 20:00:43 +01:00
5af4c963ea [actions] handle errors via channel 2023-10-30 19:59:21 +01:00
20 changed files with 8 additions and 405 deletions

3
.gitignore vendored
View File

@@ -1,9 +1,8 @@
*.qcow2
.envrc
result
example.json
go.sum
go.mod
./gokill
gokill
output.md
thoughts.md

View File

@@ -1,90 +0,0 @@
# 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,21 +39,7 @@ func (p Printer) GetName() string {
}
func (p Printer) GetDescription() string {
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!"
}
}
`
return "When triggered prints the configured message to stdout"
}
func (p Printer) GetOptions() []internal.ConfigOption {

View File

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

View File

@@ -40,21 +40,7 @@ func (p TimeOut) GetName() string {
}
func (p TimeOut) GetDescription() string {
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
}
}
`
return "When triggered waits given duration before continuing with next stage"
}
func (p TimeOut) GetOptions() []internal.ConfigOption {

View File

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

View File

@@ -1,102 +0,0 @@
package main
import (
"fmt"
"strings"
"os"
"flag"
"unknown.com/gokill/actions"
"unknown.com/gokill/triggers"
"unknown.com/gokill/internal"
)
func getMarkdown(documenter internal.Documenter) string {
var result string
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, sanitizedDefault)
}
return result
}
func writeToFile(path string, documenter internal.Documenter) error {
fileName := fmt.Sprintf("%s/%s.md", path, documenter.GetName())
f, err := os.Create(fileName)
if err != nil {
fmt.Println(err)
return err
}
defer f.Close()
_, err = f.WriteString(getMarkdown(documenter))
if err != nil {
fmt.Println(err)
return err
}
return nil
}
func writeDocumentersToFiles(destination string) {
writeFolder := func(typeName string, documenters []internal.Documenter) {
path := fmt.Sprintf("%s/%s", destination, typeName)
_ = os.Mkdir(path, os.ModePerm)
for _, documenter := range documenters {
writeToFile(path, documenter)
}
}
actions := actions.GetDocumenters()
writeFolder("actions", actions)
triggers := triggers.GetDocumenters()
writeFolder("triggers", triggers)
}
func printDocumentersSummary() {
result := fmt.Sprintf("- [Triggers](triggers/README.md)\n")
for _, trigger := range triggers.GetDocumenters() {
result += fmt.Sprintf("\t- [%s](triggers/%s.md)\n", trigger.GetName(), trigger.GetName())
}
result += fmt.Sprintf("- [Actions](actions/README.md)\n")
for _, action := range actions.GetDocumenters() {
result += fmt.Sprintf("\t- [%s](actions/%s.md)\n", action.GetName(), action.GetName())
}
fmt.Print(result)
}
func main() {
outputPath := flag.String("output", "", "path where docs/ shoud be created")
flag.Parse()
if *outputPath == "" {
printDocumentersSummary()
return
}
if len(*outputPath) > 1 {
*outputPath = strings.TrimSuffix(*outputPath, "/")
}
writeDocumentersToFiles(*outputPath)
}

1
docs/.gitignore vendored
View File

@@ -1 +0,0 @@
book/*

View File

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

View File

@@ -1,23 +0,0 @@
# 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,10 +0,0 @@
[book]
authors = []
language = "en"
multilingual = false
src = "."
title = "gokill docs"
[output.html.fold]
enable = true
level = 0

View File

@@ -1,33 +0,0 @@
{ pkgs, lib, self, ... }:
with lib;
let
docbuilder = self.packages.x86_64-linux.gokill-docbuilder;
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 \
--replace "@GOKILL_OPTIONS@" "$(${docbuilder}/bin/docbuilder)"
cat ./SUMMARY.md
'';
in
pkgs.stdenv.mkDerivation {
name = "gokill-docs";
phases = [ "buildPhase" ];
buildInputs = [ pkgs.mdbook ];
inputs = sourceFilesBySuffices ./. [ ".md" ".toml" ];
buildPhase = ''
dest=$out/share/doc
mkdir -p $dest
${prepareMD}
mdbook build
cp -r ./book/* $dest
'';
}

View File

@@ -1,21 +0,0 @@
# 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

@@ -28,20 +28,6 @@
'';
};
packages.x86_64-linux.gokill-docbuilder = nixpkgs.legacyPackages.x86_64-linux.buildGoModule rec {
pname = "docbuilder";
version = "1.0";
vendorHash = null;
buildFLags = "-o . $dest/cmd/gokill/docbuilder";
src = ./.;
postInstall = ''
'';
};
packages.x86_64-linux.docs = pkgs.callPackage (import ./docs/default.nix) { self = self; };
packages.x86_64-linux.default = self.packages.x86_64-linux.gokill;
nixosModules.gokill = { config, lib, pkgs, ... }:
@@ -154,11 +140,5 @@
${self.packages."x86_64-linux".testVm}/bin/run-nixos-vm
'');
};
apps.x86_64-linux.docs = {
type = "app";
program = builtins.toString (nixpkgs.legacyPackages."x86_64-linux".writeScript "docs" ''
${pkgs.python3}/bin/python3 -m http.server --directory ${self.packages."x86_64-linux".docs}/share/doc'');
};
};
}

2
go.mod
View File

@@ -1,3 +1,3 @@
module unknown.com/gokill
go 1.21.3
go 1.20

View File

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

View File

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

View File

@@ -88,22 +88,6 @@ 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"},