Compare commits
12 Commits
0.0.2
...
79aa8136c5
Author | SHA1 | Date | |
---|---|---|---|
79aa8136c5 | |||
344d874c02 | |||
1ffadf5c86 | |||
e95b4972da | |||
a0d118b987 | |||
e9aefaf8d6 | |||
beed9726e3 | |||
e85b644e82 | |||
2c43affac9 | |||
fbf1758ccb | |||
ffb4b7681f | |||
7f2e3c0ed9 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,4 +2,4 @@ hyp.secret
|
||||
*.exe
|
||||
hypd/hypd
|
||||
hyp/hyp
|
||||
hypd/server/hyp_bpf_bpfe*
|
||||
hypd/hypdconfig.json
|
@ -25,14 +25,13 @@ Most port-knocking implementations are susceptible to replay attacks, a network
|
||||
|
||||
hyp supports a clock skew of up to 30 seconds between client and server.
|
||||
|
||||
### TBD: Protection Against Sweeping Attacks
|
||||
### Protection Against Sweeping Attacks
|
||||
|
||||
~~hyp protects against sweeping attacks where an adversary modulates over the entire port range multiple times by ensuring the authentic knock sequence is strict and ordered correctly. If the first port is guessed, but the next pack arrives and is the incorrect second port in the sequence, the progress gets reset.~~
|
||||
hyp protects against sweeping attacks where an adversary modulates over the entire port range multiple times by ensuring the authentic knock sequence is strict and ordered correctly. If the first port is guessed, but the next pack arrives and is the incorrect second port in the sequence, the progress gets reset. In addition to this, the correct authentic knock sequence must be entered within 5 seconds of the start of the sequence.
|
||||
|
||||
### Known Weaknesses
|
||||
|
||||
* Lossy networks can result in the knock sequence failing
|
||||
* Networks with latency > 500ms can result in the knock sequence failing if packets arrive out of order
|
||||
|
||||
### References
|
||||
|
||||
|
@ -38,6 +38,14 @@ Example usage:
|
||||
panic(fmt.Errorf("failed to parse command flag 'secret': %w", err))
|
||||
}
|
||||
|
||||
maxJitter, err := cmd.Flags().GetInt("maxjitter")
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to parse command flag 'maxjitter': %w", err))
|
||||
}
|
||||
if maxJitter < 1 || maxJitter > 1500 {
|
||||
panic(fmt.Errorf("maxjitter must be value between 1 and 1500"))
|
||||
}
|
||||
|
||||
secretBytes, err := os.ReadFile(secretFilePath)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to read file 'hyp.secret': %v", err)
|
||||
@ -50,12 +58,12 @@ Example usage:
|
||||
}
|
||||
|
||||
// Transmit
|
||||
fmt.Println("Transmitting knock sequence:", ports)
|
||||
for _, port := range ports {
|
||||
fmt.Printf("knock | %s:%d\n", args[0], port)
|
||||
conn, _ := net.Dial("udp", fmt.Sprintf("%s:%d", args[0], port))
|
||||
conn.Write([]byte{0})
|
||||
conn.Close()
|
||||
time.Sleep(time.Millisecond * 200) // TBD: Make this configurable with flag (maxJitter)
|
||||
time.Sleep(time.Millisecond * time.Duration(maxJitter)) // TBD: Make this configurable with flag (maxJitter)
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -64,4 +72,5 @@ func init() {
|
||||
rootCmd.AddCommand(knockCmd)
|
||||
|
||||
knockCmd.PersistentFlags().String("secret", "hyp.secret", "Path to the file containing the hyp secret.")
|
||||
knockCmd.PersistentFlags().Int("maxjitter", 200, "Specifies the time in milliseconds between knock sequence transmissions.")
|
||||
}
|
||||
|
34
hypd/cmd/defaultconfig.go
Normal file
34
hypd/cmd/defaultconfig.go
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
Copyright © 2024 Steven Polley <himself@stevenpolley.net>
|
||||
*/
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"deadbeef.codes/steven/hyp/hypd/configuration"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// defaultconfigCmd represents the defaultconfig command
|
||||
var defaultconfigCmd = &cobra.Command{
|
||||
Use: "defaultconfig",
|
||||
Short: "Prints the default configuration to stdout",
|
||||
Long: `The default configuration is used if one is not set. The default configuration
|
||||
can be used as a reference to build your own.
|
||||
|
||||
hypd generate defaultconfig | tee hypdconfig.json`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
config := configuration.DefaultConfig()
|
||||
b, err := json.MarshalIndent(config, "", " ")
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to marshal default configuration to json (this should never happen): %v", err))
|
||||
}
|
||||
fmt.Println(string(b))
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
generateCmd.AddCommand(defaultconfigCmd)
|
||||
}
|
@ -19,7 +19,7 @@ server and to clients.
|
||||
|
||||
Example:
|
||||
|
||||
hypd generatesecret > hyp.secret`,
|
||||
hypd generate secret > hyp.secret`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
sharedSecret, err := otphyp.GenerateSecret()
|
||||
if err != nil {
|
||||
@ -31,14 +31,4 @@ hypd generatesecret > hyp.secret`,
|
||||
|
||||
func init() {
|
||||
generateCmd.AddCommand(secretCmd)
|
||||
|
||||
// Here you will define your flags and configuration settings.
|
||||
|
||||
// Cobra supports Persistent Flags which will work for this command
|
||||
// and all subcommands, e.g.:
|
||||
// secretCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||
|
||||
// Cobra supports local flags which will only run when this command
|
||||
// is called directly, e.g.:
|
||||
// secretCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||
}
|
||||
|
@ -5,57 +5,57 @@ package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/user"
|
||||
|
||||
"deadbeef.codes/steven/hyp/hypd/configuration"
|
||||
"deadbeef.codes/steven/hyp/hypd/server"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// serverCmd represents the server command
|
||||
var serverCmd = &cobra.Command{
|
||||
Use: "server <NIC>",
|
||||
Use: "server <configFilePath>",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Short: "Runs hyp in server mode",
|
||||
Long: `Runs the hyp server and begins capture on the NIC specified
|
||||
Long: `Runs the hyp server and begins watching for authentic knock sequences.
|
||||
|
||||
Before running this command, you must first have a configuration file. You can
|
||||
generate a configuration file with: hypd generate defaultconfig > hypdconfig.json
|
||||
|
||||
You should then edit the config file to meet your needs.
|
||||
|
||||
In addition to a config file you will need to generate pre-shared keys:
|
||||
mkdir -p ./secrets
|
||||
hypd generate secret > secrets/mykey.secret
|
||||
|
||||
Example Usage:
|
||||
|
||||
# Linux - capture enp0s0
|
||||
hyp server enp0s0
|
||||
|
||||
# Linux - capture eth0
|
||||
hyp server eth0
|
||||
|
||||
# Windows - get-netadapter | where {$_.Name -eq “Ethernet”} | Select-Object -Property DeviceName
|
||||
hyp.exe server "\\Device\\NPF_{A6F067DE-C2DC-4B4E-9C74-BE649C4C0F03}"
|
||||
# Use config file in local directory
|
||||
hypd server hypdconfig.json
|
||||
|
||||
# Use config file in /etc/hyp/
|
||||
hypd server /etc/hyp/hypdconfig.json
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := server.PacketServer(args[0])
|
||||
currentUser, err := user.Current()
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("could not determine current user: %w", err))
|
||||
}
|
||||
if currentUser.Username != "root" {
|
||||
fmt.Println("WARNING: It's recommended you run this as root, but will proceed anyways...")
|
||||
}
|
||||
|
||||
hypdConfiguration, err := configuration.LoadConfiguration(args[0])
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to start packet server: %w", err))
|
||||
}
|
||||
err = server.PacketServer(hypdConfiguration)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to start packet server: %w", err))
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(serverCmd)
|
||||
/*
|
||||
viper.SetConfigName("hypconfig")
|
||||
viper.SetConfigType("yaml")
|
||||
viper.AddConfigPath("/etc/hyp/")
|
||||
viper.AddConfigPath("$HOME/.hyp")
|
||||
viper.AddConfigPath(".")
|
||||
viper.SetDefault("RefreshInterval", 7200)
|
||||
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
||||
// Config file not found
|
||||
// TBD: Implement
|
||||
} else {
|
||||
// Config file was found, but another error was produced
|
||||
panic(fmt.Errorf("failed reading existing config file: %w", err))
|
||||
}
|
||||
}*/
|
||||
|
||||
}
|
||||
|
68
hypd/configuration/configuration.go
Normal file
68
hypd/configuration/configuration.go
Normal file
@ -0,0 +1,68 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
type HypdConfiguration struct {
|
||||
NetworkInterface string `json:"networkInterface"`
|
||||
PreSharedKeyDirectory string `json:"preSharedKeyDirectory"` // hypd will load all *.secret files from this directory
|
||||
SuccessAction string `json:"successAction"` // The action to take
|
||||
TimeoutSeconds int `json:"timeoutSeconds"` // If > 0, once a knock sequence has been successful this value will count down and when it reaches 0, it will perform the TimeoutAction on the client.
|
||||
TimeoutAction string `json:"timeoutAction"` // The action to take after TimeoutSeconds has elapsed. only applicable if TimeoutSeconds is > 0
|
||||
|
||||
}
|
||||
|
||||
// LoadConfiguration opens and parses the configuration file into a HypdConfiguration struct
|
||||
// If a configFilePath is not specified, it will search in common locations
|
||||
func LoadConfiguration(configFilePath string) (*HypdConfiguration, error) {
|
||||
if configFilePath == "" {
|
||||
commonLocations := []string{"hypdconfig.json",
|
||||
"~/.hypdconfig.json",
|
||||
"~/.config/hyp/hypdConfig.json",
|
||||
"/etc/hyp/hypdConfig.json",
|
||||
"/usr/local/etc/hyp/hypdConfig.json",
|
||||
}
|
||||
|
||||
for _, loc := range commonLocations {
|
||||
if _, err := os.Stat(loc); err == nil {
|
||||
configFilePath = loc
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
// if it's still not found after checking common locations, load default config
|
||||
if configFilePath == "" {
|
||||
fmt.Println("no configuration file found. You can generate one with ./hypd generate defaultconfig | tee hypdconfig.json")
|
||||
return DefaultConfig(), nil
|
||||
}
|
||||
|
||||
// Otherwise if a config is specified, try to load it and error if it fails.
|
||||
// I think it's better to error here if a config was intended and failed
|
||||
// rather than failing back to default
|
||||
|
||||
b, err := os.ReadFile(configFilePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read config file '%s': %w", configFilePath, err)
|
||||
}
|
||||
|
||||
hypdConfiguration := &HypdConfiguration{}
|
||||
err = json.Unmarshal(b, hypdConfiguration)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal config file json to HypdConfiguration (is the config file malformed?): %w", err)
|
||||
}
|
||||
|
||||
return hypdConfiguration, nil
|
||||
}
|
||||
|
||||
func DefaultConfig() *HypdConfiguration {
|
||||
return &HypdConfiguration{
|
||||
NetworkInterface: "enp0s3",
|
||||
PreSharedKeyDirectory: "./secrets/",
|
||||
SuccessAction: "iptables -A INPUT -p tcp -s %s --dport 22 -j ACCEPT",
|
||||
TimeoutSeconds: 1440,
|
||||
TimeoutAction: "iptables -D INPUT -p tcp -s %s --dport 22 -j ACCEPT",
|
||||
}
|
||||
}
|
14
hypd/examples/openwrt-wireguard/closewireguard.sh
Normal file
14
hypd/examples/openwrt-wireguard/closewireguard.sh
Normal file
@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "Usage: $0 <srcip>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Can't use dots in rule name, so swap for underscores
|
||||
rulename="hypd_${1//./_}_wireguard"
|
||||
|
||||
# Configure the rule in OpenWRT's uci interface
|
||||
uci delete firewall.$rulename
|
||||
uci commit firewall
|
||||
service firewall restart
|
7
hypd/examples/openwrt-wireguard/hypdconfig.json
Normal file
7
hypd/examples/openwrt-wireguard/hypdconfig.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"networkInterface": "enp0s3",
|
||||
"preSharedKeyDirectory": "./secrets/",
|
||||
"successAction": "./examples/openwrt-wireguard/openwireguard.sh %s",
|
||||
"timeoutSeconds": 1440,
|
||||
"timeoutAction": "./examples/openwrt-wireguard/closewireguard.sh %s"
|
||||
}
|
24
hypd/examples/openwrt-wireguard/openwireguard.sh
Normal file
24
hypd/examples/openwrt-wireguard/openwireguard.sh
Normal file
@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "Usage: $0 <srcip>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Can't use dots in rule name, so swap for underscores
|
||||
# example: 10.69.69.100 changes to hypd_10_69_69_100_wireguard
|
||||
rulename="hypd_${1//./_}_wireguard"
|
||||
|
||||
# Configure the rule in OpenWRT's uci interface
|
||||
uci set firewall.$rulename=redirect
|
||||
uci set firewall.$rulename.dest=lan
|
||||
uci set firewall.$rulename.target=DNAT
|
||||
uci set firewall.$rulename.name=$rulename
|
||||
uci set firewall.$rulename.src=wan
|
||||
uci set firewall.$rulename.src_dport=51820
|
||||
uci set firewall.$rulename.dest_ip=10.0.100.1
|
||||
uci set firewall.$rulename.dest_port=51820
|
||||
uci set firewall.$rulename.src_ip=$1
|
||||
uci add_list firewall.$rulename.proto=udp
|
||||
uci commit firewall
|
||||
service firewall restart
|
30
hypd/server/README.md
Normal file
30
hypd/server/README.md
Normal file
@ -0,0 +1,30 @@
|
||||
# hypd server
|
||||
|
||||
hypd is the port knocking daemon which runs on an edge device connecting to an untrusted network. Leveraging eBPF's XDP hook point, it extracts header information directly and sends to userspace the specific information required. This method is faster than alternative methods such as using libpcap.
|
||||
|
||||
### eBPF
|
||||
|
||||
The hyp_bpf.c program can be recompiled using go generate.
|
||||
|
||||
```bash
|
||||
# Debian: sudo apt install git clang linux-headers-amd64 libbpf-dev
|
||||
go generate .
|
||||
```
|
||||
|
||||
### Generating vmlinux.h
|
||||
|
||||
vmlinux.h is included in hyp_bpf.c and can be regenerated with bpftool.
|
||||
|
||||
```bash
|
||||
# Debian: sudo apt install bpftool
|
||||
sudo bpftool btf dump file /sys/kernel/btf/vmlinux format c > ../headers/vmlinux.h
|
||||
```
|
||||
|
||||
### Building hypd
|
||||
|
||||
hypd has no CGO dependencies and so can run on musl systems as well.
|
||||
|
||||
```bash
|
||||
# To ensure it can run on systems don't use CGO
|
||||
CGO_ENABLED=0 go build .
|
||||
```
|
@ -7,12 +7,8 @@ Copyright © 2024 Steven Polley <himself@stevenpolley.net>
|
||||
#include "bpf_endian.h"
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
|
||||
char __license[] SEC("license") = "BSD";
|
||||
|
||||
#define ETH_P_IP 0x0800
|
||||
#define IP_FRAGMENTED 65343
|
||||
|
||||
// representation of knock data that gets sent to userspace
|
||||
struct knock_data {
|
||||
__u32 srcip; // 4 bytes
|
||||
@ -44,7 +40,6 @@ int xdp_prog_func(struct xdp_md *ctx) {
|
||||
|
||||
// parse ethernet header
|
||||
struct ethhdr *eth = data;
|
||||
|
||||
if ((void *)eth + sizeof(*eth) <= data_end) {
|
||||
// parse IP header
|
||||
struct iphdr *ip = data + sizeof(*eth);
|
||||
|
125
hypd/server/hyp_bpf_bpfeb.go
Normal file
125
hypd/server/hyp_bpf_bpfeb.go
Normal file
@ -0,0 +1,125 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build mips || mips64 || ppc64 || s390x
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
type hyp_bpfKnockData struct {
|
||||
Srcip uint32
|
||||
Dstport uint16
|
||||
Pad uint16
|
||||
}
|
||||
|
||||
// loadHyp_bpf returns the embedded CollectionSpec for hyp_bpf.
|
||||
func loadHyp_bpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_Hyp_bpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load hyp_bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadHyp_bpfObjects loads hyp_bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *hyp_bpfObjects
|
||||
// *hyp_bpfPrograms
|
||||
// *hyp_bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadHyp_bpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadHyp_bpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// hyp_bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type hyp_bpfSpecs struct {
|
||||
hyp_bpfProgramSpecs
|
||||
hyp_bpfMapSpecs
|
||||
}
|
||||
|
||||
// hyp_bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type hyp_bpfProgramSpecs struct {
|
||||
XdpProgFunc *ebpf.ProgramSpec `ebpf:"xdp_prog_func"`
|
||||
}
|
||||
|
||||
// hyp_bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type hyp_bpfMapSpecs struct {
|
||||
Rb *ebpf.MapSpec `ebpf:"rb"`
|
||||
}
|
||||
|
||||
// hyp_bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadHyp_bpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type hyp_bpfObjects struct {
|
||||
hyp_bpfPrograms
|
||||
hyp_bpfMaps
|
||||
}
|
||||
|
||||
func (o *hyp_bpfObjects) Close() error {
|
||||
return _Hyp_bpfClose(
|
||||
&o.hyp_bpfPrograms,
|
||||
&o.hyp_bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// hyp_bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadHyp_bpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type hyp_bpfMaps struct {
|
||||
Rb *ebpf.Map `ebpf:"rb"`
|
||||
}
|
||||
|
||||
func (m *hyp_bpfMaps) Close() error {
|
||||
return _Hyp_bpfClose(
|
||||
m.Rb,
|
||||
)
|
||||
}
|
||||
|
||||
// hyp_bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadHyp_bpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type hyp_bpfPrograms struct {
|
||||
XdpProgFunc *ebpf.Program `ebpf:"xdp_prog_func"`
|
||||
}
|
||||
|
||||
func (p *hyp_bpfPrograms) Close() error {
|
||||
return _Hyp_bpfClose(
|
||||
p.XdpProgFunc,
|
||||
)
|
||||
}
|
||||
|
||||
func _Hyp_bpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//
|
||||
//go:embed hyp_bpf_bpfeb.o
|
||||
var _Hyp_bpfBytes []byte
|
BIN
hypd/server/hyp_bpf_bpfeb.o
Normal file
BIN
hypd/server/hyp_bpf_bpfeb.o
Normal file
Binary file not shown.
125
hypd/server/hyp_bpf_bpfel.go
Normal file
125
hypd/server/hyp_bpf_bpfel.go
Normal file
@ -0,0 +1,125 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build 386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
type hyp_bpfKnockData struct {
|
||||
Srcip uint32
|
||||
Dstport uint16
|
||||
Pad uint16
|
||||
}
|
||||
|
||||
// loadHyp_bpf returns the embedded CollectionSpec for hyp_bpf.
|
||||
func loadHyp_bpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_Hyp_bpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load hyp_bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadHyp_bpfObjects loads hyp_bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *hyp_bpfObjects
|
||||
// *hyp_bpfPrograms
|
||||
// *hyp_bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadHyp_bpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadHyp_bpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// hyp_bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type hyp_bpfSpecs struct {
|
||||
hyp_bpfProgramSpecs
|
||||
hyp_bpfMapSpecs
|
||||
}
|
||||
|
||||
// hyp_bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type hyp_bpfProgramSpecs struct {
|
||||
XdpProgFunc *ebpf.ProgramSpec `ebpf:"xdp_prog_func"`
|
||||
}
|
||||
|
||||
// hyp_bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type hyp_bpfMapSpecs struct {
|
||||
Rb *ebpf.MapSpec `ebpf:"rb"`
|
||||
}
|
||||
|
||||
// hyp_bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadHyp_bpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type hyp_bpfObjects struct {
|
||||
hyp_bpfPrograms
|
||||
hyp_bpfMaps
|
||||
}
|
||||
|
||||
func (o *hyp_bpfObjects) Close() error {
|
||||
return _Hyp_bpfClose(
|
||||
&o.hyp_bpfPrograms,
|
||||
&o.hyp_bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// hyp_bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadHyp_bpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type hyp_bpfMaps struct {
|
||||
Rb *ebpf.Map `ebpf:"rb"`
|
||||
}
|
||||
|
||||
func (m *hyp_bpfMaps) Close() error {
|
||||
return _Hyp_bpfClose(
|
||||
m.Rb,
|
||||
)
|
||||
}
|
||||
|
||||
// hyp_bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadHyp_bpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type hyp_bpfPrograms struct {
|
||||
XdpProgFunc *ebpf.Program `ebpf:"xdp_prog_func"`
|
||||
}
|
||||
|
||||
func (p *hyp_bpfPrograms) Close() error {
|
||||
return _Hyp_bpfClose(
|
||||
p.XdpProgFunc,
|
||||
)
|
||||
}
|
||||
|
||||
func _Hyp_bpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//
|
||||
//go:embed hyp_bpf_bpfel.o
|
||||
var _Hyp_bpfBytes []byte
|
BIN
hypd/server/hyp_bpf_bpfel.o
Normal file
BIN
hypd/server/hyp_bpf_bpfel.o
Normal file
Binary file not shown.
@ -15,6 +15,7 @@ import (
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"deadbeef.codes/steven/hyp/hypd/configuration"
|
||||
"deadbeef.codes/steven/hyp/otphyp"
|
||||
"github.com/cilium/ebpf/link"
|
||||
"github.com/cilium/ebpf/ringbuf"
|
||||
@ -48,11 +49,10 @@ var (
|
||||
// PacketServer is the main function when operating in server mode
|
||||
// it sets up the pcap on the capture device and starts a goroutine
|
||||
// to rotate the knock sequence
|
||||
func PacketServer(captureDevice string) error {
|
||||
|
||||
iface, err := net.InterfaceByName(captureDevice)
|
||||
func PacketServer(config *configuration.HypdConfiguration) error {
|
||||
iface, err := net.InterfaceByName(config.NetworkInterface)
|
||||
if err != nil {
|
||||
log.Fatalf("lookup network iface %q: %v", captureDevice, err)
|
||||
log.Fatalf("lookup network iface %q: %v", config.NetworkInterface, err)
|
||||
}
|
||||
|
||||
secretBytes, err := os.ReadFile("hyp.secret")
|
||||
@ -147,8 +147,6 @@ func handleKnock(knockEvent hyp_bpfKnockData) {
|
||||
}
|
||||
|
||||
// if it's wrong, reset progress
|
||||
// TBD: vulnerable to sweep attack - this won't be triggered if a wrong packet doesn't match BPF filter
|
||||
// TBD: make the sweep attack fix on by default, but configurable to be off to allow for limited BPF filter for extremely low overhead as compromise.
|
||||
if knockEvent.Dstport != client.Sequence[client.Progress] {
|
||||
delete(clients, knockEvent.Srcip)
|
||||
fmt.Printf("port '%d' is in sequence, but came at unexpected order - resetting progress", knockEvent.Dstport)
|
||||
|
Reference in New Issue
Block a user