2025-02-06 14:42:06 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"crypto/tls"
|
|
|
|
|
"crypto/x509"
|
|
|
|
|
"flag"
|
|
|
|
|
"fmt"
|
|
|
|
|
"log"
|
2025-04-01 20:28:14 +02:00
|
|
|
"os"
|
2025-02-06 14:42:06 +01:00
|
|
|
|
|
|
|
|
"github.com/chzyer/readline"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Default server address
|
|
|
|
|
var defaultServerAddr = "127.0.0.1:8080"
|
|
|
|
|
|
2025-04-06 09:36:31 +02:00
|
|
|
// Path to certificates
|
2025-07-11 14:06:11 +02:00
|
|
|
var clientCertPath = "certificates/client.crt"
|
|
|
|
|
var clientKeyPath = "certificates/client.key"
|
|
|
|
|
var caCertPath = "certificates/ca.crt"
|
2025-02-06 14:42:06 +01:00
|
|
|
|
2025-04-06 10:39:09 +02:00
|
|
|
// Configure readline interface
|
|
|
|
|
func setupReadline() (*readline.Instance, error) {
|
|
|
|
|
rlConfig := &readline.Config{
|
|
|
|
|
Prompt: "\033[31mSigma >\033[0m ", // Colored prompt
|
|
|
|
|
HistoryFile: "/tmp/operator_history.txt",
|
|
|
|
|
AutoComplete: getCompleter(),
|
|
|
|
|
InterruptPrompt: "^C",
|
2025-02-06 14:42:06 +01:00
|
|
|
DisableAutoSaveHistory: false,
|
|
|
|
|
HistorySearchFold: true,
|
2025-04-06 10:39:09 +02:00
|
|
|
UniqueEditLine: true,
|
2025-02-06 14:42:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rl, err := readline.NewEx(rlConfig)
|
|
|
|
|
if err != nil {
|
2025-04-06 10:39:09 +02:00
|
|
|
return nil, fmt.Errorf("error initializing readline: %v", err)
|
2025-02-06 14:42:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rl.SetVimMode(false)
|
2025-04-06 10:39:09 +02:00
|
|
|
return rl, nil
|
|
|
|
|
}
|
2025-02-06 14:42:06 +01:00
|
|
|
|
2025-08-03 18:57:10 +02:00
|
|
|
// Prepares the TLS configuration for secure connection
|
|
|
|
|
func setupTLSConfig() (*tls.Config, error) {
|
|
|
|
|
// Load CA certificate
|
|
|
|
|
caCert, err := os.ReadFile(caCertPath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to read CA certificate: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
caCertPool := x509.NewCertPool()
|
|
|
|
|
if !caCertPool.AppendCertsFromPEM(caCert) {
|
|
|
|
|
return nil, fmt.Errorf("failed to append CA certificate to pool")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Load client certificate and key
|
|
|
|
|
clientCert, err := tls.LoadX509KeyPair(clientCertPath, clientKeyPath)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("failed to load client certificate and key: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Configure TLS
|
|
|
|
|
return &tls.Config{
|
|
|
|
|
Certificates: []tls.Certificate{clientCert},
|
|
|
|
|
RootCAs: caCertPool,
|
|
|
|
|
InsecureSkipVerify: false, // Ensure server's certificate is validated
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Establishes a secure connection to the server
|
|
|
|
|
func connectToServer(serverAddr string, tlsConfig *tls.Config) (*tls.Conn, error) {
|
|
|
|
|
conn, err := tls.Dial("tcp", serverAddr, tlsConfig)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("error connecting to server: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Println("Connected to server")
|
|
|
|
|
return conn, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Pretty simple authentication of user with the server
|
|
|
|
|
func authenticateUser(conn *tls.Conn, userID, password string) error {
|
|
|
|
|
fmt.Println("Starting authentication")
|
|
|
|
|
|
|
|
|
|
// Send authentication message
|
|
|
|
|
authMessage := fmt.Sprintf("AUTH:%s:%s", userID, password)
|
|
|
|
|
_, err := conn.Write([]byte(authMessage))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("error sending credentials: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Receive server response
|
|
|
|
|
buffer := make([]byte, 4096)
|
|
|
|
|
n, err := conn.Read(buffer)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("error reading from server: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
response := string(buffer[:n])
|
|
|
|
|
if response == "AUTH_OK" {
|
|
|
|
|
fmt.Println("Auth successful. You can now issue commands.")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return fmt.Errorf("authentication failed")
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-06 10:39:09 +02:00
|
|
|
func main() {
|
|
|
|
|
// Define flags for username, password and address of server
|
|
|
|
|
operatorPassword := flag.String("p", "", "Password")
|
|
|
|
|
operatorID := flag.String("u", "", "Username")
|
|
|
|
|
serverAddr := flag.String("ip", defaultServerAddr, "IP and port of server")
|
|
|
|
|
flag.Parse()
|
2025-04-06 09:36:31 +02:00
|
|
|
|
2025-04-06 10:39:09 +02:00
|
|
|
// Check if password and username are provided
|
|
|
|
|
if *operatorPassword == "" || *operatorID == "" {
|
|
|
|
|
fmt.Println("Usage: client.exe -u <username> -p <password> -ip <ip:port>")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
fmt.Println("Client's username and password provided")
|
2025-04-06 09:36:31 +02:00
|
|
|
|
2025-04-06 10:39:09 +02:00
|
|
|
// Setup TLS
|
|
|
|
|
tlsConfig, err := setupTLSConfig()
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("TLS setup failed: %v", err)
|
|
|
|
|
}
|
2025-02-06 14:42:06 +01:00
|
|
|
|
2025-04-06 10:39:09 +02:00
|
|
|
// Connect to server
|
|
|
|
|
conn, err := connectToServer(*serverAddr, tlsConfig)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("Server connection failed: %v", err)
|
2025-02-06 14:42:06 +01:00
|
|
|
}
|
2025-04-06 10:39:09 +02:00
|
|
|
defer conn.Close()
|
2025-04-06 09:36:31 +02:00
|
|
|
|
2025-04-06 10:39:09 +02:00
|
|
|
// Authenticate
|
|
|
|
|
err = authenticateUser(conn, *operatorID, *operatorPassword)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("Authentication failed: %v", err)
|
2025-04-06 09:36:31 +02:00
|
|
|
}
|
|
|
|
|
|
2025-04-06 10:39:09 +02:00
|
|
|
// Setup readline
|
|
|
|
|
rl, err := setupReadline()
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("Readline setup failed: %v", err)
|
2025-04-06 09:36:31 +02:00
|
|
|
}
|
2025-04-06 10:39:09 +02:00
|
|
|
defer rl.Close()
|
|
|
|
|
|
|
|
|
|
// Create exit channel
|
|
|
|
|
exitChan := make(chan struct{})
|
2025-08-03 20:11:41 +02:00
|
|
|
doneChan := make(chan struct{}) // new: signals graceful exit
|
2025-04-06 10:39:09 +02:00
|
|
|
|
|
|
|
|
// Start server message listener
|
2025-08-03 20:11:41 +02:00
|
|
|
startServerListener(conn, rl, exitChan, doneChan)
|
2025-04-06 10:39:09 +02:00
|
|
|
|
|
|
|
|
// Run main command loop
|
2025-08-03 20:11:41 +02:00
|
|
|
runCommandLoop(conn, rl, exitChan, doneChan)
|
2025-04-06 09:36:31 +02:00
|
|
|
}
|