Commit 32c6932c authored by Kevin Lyda's avatar Kevin Lyda
Browse files

Add syslog for login failures

parent 638d4f30
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@ import (
	"encoding/base64"
	"errors"
	"fmt"
	"log/syslog"
	"os"
	"strings"
	"time"
@@ -20,9 +21,27 @@ import (
	"golang.org/x/crypto/ssh"
)

// logAuthFailure logs a shibboleth failure to the auth syslog facility
// so that tools like CrowdSec can rate-limit abusive IPs.
func logAuthFailure(clientIP, fingerprint string) {
	sl, err := syslog.New(syslog.LOG_AUTHPRIV|syslog.LOG_WARNING, "bulletin")
	if err != nil {
		return
	}
	defer sl.Close()
	_ = sl.Warning(fmt.Sprintf("Failed shibboleth from %s (key %s)", clientIP, fingerprint))
}

// Run executes the onboarding flow. fingerprint is the SHA256
// fingerprint of the connecting key. pubkeyStr is "type:base64".
func Run(fingerprint, pubkeyStr string) int {
	// Parse client IP from SSH_CONNECTION for auth logging.
	clientIP := "unknown"
	if conn := os.Getenv("SSH_CONNECTION"); conn != "" {
		if parts := strings.Fields(conn); len(parts) >= 1 {
			clientIP = parts[0]
		}
	}
	// Open DB.
	store, err := storage.Open()
	if err != nil {
@@ -61,6 +80,7 @@ func Run(fingerprint, pubkeyStr string) int {
				// Rate limit: increasing delay on wrong answers.
				delay := time.Duration(i+1) * 2 * time.Second
				time.Sleep(delay)
				logAuthFailure(clientIP, fingerprint)
				fmt.Println("Incorrect answer. Access denied.")
				return 1
			}
+16 −0
Original line number Diff line number Diff line
@@ -61,6 +61,20 @@ func InitSandbox() error {
		}
	}

	// Syslog socket paths (varies by distro/init system).
	potentialSyslogSockets := []string{
		"/dev/log",                     // traditional / symlink on systemd
		"/run/systemd/journal/dev-log", // systemd journal socket
		"/var/run/syslog",              // macOS / some BSDs
		"/var/run/log",                 // FreeBSD
	}
	var syslogSockets []string
	for _, s := range potentialSyslogSockets {
		if _, err := os.Stat(s); err == nil {
			syslogSockets = append(syslogSockets, s)
		}
	}

	err := landlock.V5.BestEffort().Restrict(
		// BULLETIN data directory (DB, WAL, SHM files).
		landlock.RWDirs(bulldir),
@@ -86,6 +100,8 @@ func InitSandbox() error {
		// TLS: CA certificate bundles and directory for HTTPS verification.
		landlock.ROFiles(caFiles...),
		landlock.RODirs(caDirs...),
		// Syslog socket for auth event logging (onboarding failures).
		landlock.RWFiles(syslogSockets...),
	)
	if err != nil {
		return fmt.Errorf("failed to apply landlock sandbox: %w", err)
+3 −1
Original line number Diff line number Diff line
@@ -50,6 +50,8 @@ func InitSandbox() error {
		{"/etc/hosts", "r"},
		// TLS: CA certificate bundle for HTTPS verification.
		{"/etc/ssl/cert.pem", "r"},
		// Syslog socket for auth event logging (onboarding failures).
		{"/dev/log", "rw"},
	}

	for _, u := range unveils {
@@ -74,7 +76,7 @@ func InitSandbox() error {
	//   tty    - terminal operations (editor, REPL)
	//   inet   - outbound network (SSH FETCH from forges)
	//   dns    - DNS resolution (for SSH FETCH)
	if err := unix.PledgePromises("stdio rpath wpath cpath flock tty inet dns"); err != nil {
	if err := unix.PledgePromises("stdio rpath wpath cpath flock tty inet dns unix"); err != nil {
		return fmt.Errorf("pledge: %w", err)
	}