package main import ( "context" "crypto/ecdh" "crypto/sha256" "encoding/base64" "encoding/json" "errors" "fmt" "os" "os/exec" "os/user" "path/filepath" "strconv" "strings" "time" ) const ( webAllowStateDirectory = "/var/lib/oci-l4-relay/web-allow" smtpDenyStateDirectory = "/var/lib/oci-l4-relay/smtp-deny" interfaceName = "wg-oci0" logIngestTokenPath = "/etc/oci-l4-relay/log-ingest.token" telemetrySnapshotPath = "/run/oci-l4-relay-telemetry/current.json" privateKeyPath = "/etc/oci-l4-relay/private.key" logIngestGroup = "oci-l4-relay-logs" ) func bootstrap(ctx context.Context, configuration config) error { publicInterface, err := defaultInterface() if err != nil { return err } fmt.Println("bootstrap_stage=fail_closed_firewall") if err := configureHost(ctx, configuration, publicInterface); err != nil { return err } if err := restoreAdmission(ctx, configuration); err != nil { fmt.Printf("bootstrap_stage=cached_admission cache_restored=false\n") } else { fmt.Printf("bootstrap_stage=cached_admission cache_restored=true\n") } if configuration.MailRelayAddress != "" { if err := restoreSMTPDeny(ctx, configuration); err != nil { fmt.Printf("bootstrap_stage=cached_smtp_deny cache_restored=false\n") } else { fmt.Printf("bootstrap_stage=cached_smtp_deny cache_restored=true\n") } } else if configuration.mailConfigurationError != "" { fmt.Printf("bootstrap_stage=mail_configuration active=false reason=%q\n", configuration.mailConfigurationError) } if fileExists(privateKeyPath) { if err := configureTunnel(ctx, configuration); err != nil { fmt.Printf("bootstrap_stage=cached_tunnel cache_restored=false\n") } else { fmt.Printf("bootstrap_stage=cached_tunnel cache_restored=true\n") } } fmt.Println("bootstrap_stage=instance_identity") clients, err := newOCIClients() if err != nil { return err } if err := configureIdentityAndTunnel(ctx, clients, configuration); err != nil { return err } fmt.Println("bootstrap_stage=wireguard_handshake") if err := waitForHandshake(ctx, 60*time.Second); err != nil { return err } fmt.Println("bootstrap_stage=fresh_admission") if err := updateAdmissionWithClients(ctx, clients, configuration); err != nil { return err } if configuration.MailRelayAddress != "" { if err := updateSMTPDenyWithClients(ctx, clients, configuration); err != nil { fmt.Println("bootstrap_stage=smtp_deny current_available=false") } else { fmt.Println("bootstrap_stage=smtp_deny current_available=true") } } if err := updateLogCredentialWithClients(ctx, clients, configuration); err != nil { fmt.Println("bootstrap_stage=log_ingest credential_available=false") } else { fmt.Println("bootstrap_stage=log_ingest credential_available=true") } fmt.Println("bootstrap_stage=complete") return nil } func reconcile(ctx context.Context, configuration config) error { clients, err := newOCIClients() if err != nil { return err } if err := configureIdentityAndTunnel(ctx, clients, configuration); err != nil { return err } if err := waitForHandshake(ctx, 180*time.Second); err != nil { return err } if err := updateAdmissionWithClients(ctx, clients, configuration); err != nil { return err } if configuration.MailRelayAddress != "" { if err := updateSMTPDenyWithClients(ctx, clients, configuration); err != nil { fmt.Println("reconcile_stage=smtp_deny current_available=false") } else { fmt.Println("reconcile_stage=smtp_deny current_available=true") } } if err := updateLogCredentialWithClients(ctx, clients, configuration); err != nil { fmt.Println("reconcile_stage=log_ingest credential_available=false") } else { fmt.Println("reconcile_stage=log_ingest credential_available=true") } return nil } func configureHost(ctx context.Context, configuration config, publicInterface string) error { if err := os.MkdirAll("/etc/oci-l4-relay", 0700); err != nil { return err } groupID, err := logIngestGroupID() if err != nil { return err } if err := os.Chown("/etc/oci-l4-relay", 0, groupID); err != nil { return err } if err := os.Chmod("/etc/oci-l4-relay", 0710); err != nil { return err } if err := os.MkdirAll(webAllowStateDirectory, 0700); err != nil { return err } if configuration.MailRelayAddress != "" { if err := os.MkdirAll(smtpDenyStateDirectory, 0700); err != nil { return err } } if err := writeAtomic("/etc/sysctl.d/90-oci-l4-relay.conf", []byte("net.ipv4.ip_forward = 1\nnet.ipv4.tcp_syncookies = 1\nnet.netfilter.nf_conntrack_acct = 1\nnet.netfilter.nf_conntrack_max = 262144\nnet.netfilter.nf_conntrack_timestamp = 1\n"), 0644); err != nil { return err } ruleset := renderRuleset(configuration, publicInterface) if err := writeAtomic("/etc/sysconfig/nftables.conf", []byte(ruleset), 0600); err != nil { return err } settings := []string{ "net.ipv4.ip_forward=1", "net.ipv4.tcp_syncookies=1", "net.netfilter.nf_conntrack_acct=1", "net.netfilter.nf_conntrack_max=262144", "net.netfilter.nf_conntrack_timestamp=1", } for _, setting := range settings { if err := run(ctx, "/usr/sbin/sysctl", "--write", setting); err != nil { return err } } if err := run(ctx, "/usr/sbin/nft", "--check", "--file", "/etc/sysconfig/nftables.conf"); err != nil { return err } return run(ctx, "/usr/sbin/nft", "--file", "/etc/sysconfig/nftables.conf") } func configureIdentityAndTunnel(ctx context.Context, clients ociClients, configuration config) error { seed, err := clients.getSecret(ctx, configuration.WireGuardSeedSecretOCID) if err != nil { return fmt.Errorf("retrieve WireGuard seed: %w", err) } privateKey, publicKey, err := deriveWireGuardKey(seed) for index := range seed { seed[index] = 0 } if err != nil { return err } defer func() { for index := range privateKey { privateKey[index] = 0 } }() encodedPrivateKey := []byte(base64.StdEncoding.EncodeToString(privateKey) + "\n") if err := writeAtomic(privateKeyPath, encodedPrivateKey, 0600); err != nil { return err } for index := range encodedPrivateKey { encodedPrivateKey[index] = 0 } encodedPublicKey := base64.StdEncoding.EncodeToString(publicKey) if err := clients.publishSecret(ctx, configuration.PublicKeySecretOCID, []byte(encodedPublicKey)); err != nil { return fmt.Errorf("publish WireGuard public key: %w", err) } return configureTunnel(ctx, configuration) } func configureTunnel(ctx context.Context, configuration config) error { if err := ensureWireGuardInterface(ctx); err != nil { return err } if err := run(ctx, "/usr/bin/wg", "set", interfaceName, "listen-port", strconv.Itoa(configuration.WireGuardListenPort), "private-key", privateKeyPath, "peer", configuration.HomeWireGuardPublicKey, "allowed-ips", "0.0.0.0/0"); err != nil { return err } commands := [][]string{ {"/usr/sbin/ip", "address", "replace", configuration.RelayTunnelAddress, "dev", interfaceName}, {"/usr/sbin/ip", "link", "set", "dev", interfaceName, "mtu", strconv.Itoa(configuration.RelayTunnelMTU), "up"}, {"/usr/sbin/ip", "route", "replace", configuration.GatewayRelayAddress + "/32", "dev", interfaceName}, } if configuration.MailRelayAddress != "" { commands = append(commands, []string{"/usr/sbin/ip", "route", "replace", configuration.MailRelayAddress + "/32", "dev", interfaceName}) } for _, command := range commands { if err := run(ctx, command[0], command[1:]...); err != nil { return err } } clearTrafficControl(ctx) for _, command := range trafficControlCommands(configuration) { if err := run(ctx, command[0], command[1:]...); err != nil { return err } } return nil } func clearTrafficControl(ctx context.Context) { // Oracle Linux cannot replace an existing HTB root qdisc; deletion also clears partial bootstrap state. _ = exec.CommandContext(ctx, "/usr/sbin/tc", "qdisc", "delete", "dev", interfaceName, "root").Run() } func trafficControlCommands(configuration config) [][]string { rate := strconv.Itoa(configuration.RateLimitMbit) + "mbit" webRate := strconv.Itoa(max(1, configuration.RateLimitMbit*40/100)) + "mbit" smtpRate := strconv.Itoa(max(1, configuration.RateLimitMbit*10/100)) + "mbit" authRate := strconv.Itoa(max(1, configuration.RateLimitMbit*20/100)) + "mbit" defaultRate := strconv.Itoa(max(1, configuration.RateLimitMbit*10/100)) + "mbit" commands := [][]string{ {"/usr/sbin/tc", "qdisc", "add", "dev", interfaceName, "root", "handle", "1:", "htb", "default", "40"}, {"/usr/sbin/tc", "class", "replace", "dev", interfaceName, "parent", "1:", "classid", "1:1", "htb", "rate", rate, "ceil", rate}, {"/usr/sbin/tc", "class", "replace", "dev", interfaceName, "parent", "1:1", "classid", "1:10", "htb", "rate", webRate, "ceil", rate}, {"/usr/sbin/tc", "class", "replace", "dev", interfaceName, "parent", "1:1", "classid", "1:20", "htb", "rate", smtpRate, "ceil", rate}, {"/usr/sbin/tc", "class", "replace", "dev", interfaceName, "parent", "1:1", "classid", "1:30", "htb", "rate", authRate, "ceil", rate}, {"/usr/sbin/tc", "class", "replace", "dev", interfaceName, "parent", "1:1", "classid", "1:40", "htb", "rate", defaultRate, "ceil", rate}, {"/usr/sbin/tc", "qdisc", "replace", "dev", interfaceName, "parent", "1:10", "handle", "10:", "fq_codel"}, {"/usr/sbin/tc", "qdisc", "replace", "dev", interfaceName, "parent", "1:20", "handle", "20:", "fq_codel"}, {"/usr/sbin/tc", "qdisc", "replace", "dev", interfaceName, "parent", "1:30", "handle", "30:", "fq_codel"}, {"/usr/sbin/tc", "qdisc", "replace", "dev", interfaceName, "parent", "1:40", "handle", "40:", "fq_codel"}, } priority := 10 addFilter := func(address string, port int, classID string) { commands = append(commands, []string{ "/usr/sbin/tc", "filter", "replace", "dev", interfaceName, "protocol", "ip", "parent", "1:", "prio", strconv.Itoa(priority), "flower", "dst_ip", address, "ip_proto", "tcp", "dst_port", strconv.Itoa(port), "classid", classID, }) priority++ } addFilter(configuration.GatewayRelayAddress, 80, "1:10") addFilter(configuration.GatewayRelayAddress, 443, "1:10") if configuration.MailRelayAddress != "" { addFilter(configuration.MailRelayAddress, 25, "1:20") for _, port := range []int{465, 993} { addFilter(configuration.MailRelayAddress, port, "1:30") } } return commands } func ensureWireGuardInterface(ctx context.Context) error { if exec.CommandContext(ctx, "/usr/sbin/ip", "link", "show", "dev", interfaceName).Run() == nil { return nil } return run(ctx, "/usr/sbin/ip", "link", "add", "dev", interfaceName, "type", "wireguard") } func deriveWireGuardKey(seed []byte) ([]byte, []byte, error) { digest := sha256.Sum256(seed) privateKey := append([]byte(nil), digest[:]...) privateKey[0] &= 248 privateKey[31] &= 127 privateKey[31] |= 64 key, err := ecdh.X25519().NewPrivateKey(privateKey) if err != nil { return nil, nil, err } return privateKey, key.PublicKey().Bytes(), nil } func updateAdmission(ctx context.Context, configuration config) error { clients, err := newOCIClients() if err != nil { return err } return updateAdmissionWithClients(ctx, clients, configuration) } func updateSMTPDeny(ctx context.Context, configuration config) error { clients, err := newOCIClients() if err != nil { return completeSMTPDenyRefresh(err) } return updateSMTPDenyWithClients(ctx, clients, configuration) } func updateLogCredential(ctx context.Context, configuration config) error { clients, err := newOCIClients() if err != nil { return err } return updateLogCredentialWithClients(ctx, clients, configuration) } func updateLogCredentialWithClients(ctx context.Context, clients ociClients, configuration config) error { token, err := clients.getSecret(ctx, configuration.LogIngestSecretOCID) if err != nil { return fmt.Errorf("retrieve log ingestion credential: %w", err) } defer func() { for index := range token { token[index] = 0 } }() if !validLogIngestToken(token) { return errors.New("log ingestion credential is invalid") } groupID, err := logIngestGroupID() if err != nil { return err } return writeAtomicOwned(logIngestTokenPath, token, 0640, 0, groupID) } func logIngestGroupID() (int, error) { group, err := user.LookupGroup(logIngestGroup) if err != nil { return 0, fmt.Errorf("look up log ingestion group: %w", err) } groupID, err := strconv.Atoi(group.Gid) if err != nil { return 0, fmt.Errorf("parse log ingestion group ID: %w", err) } return groupID, nil } func validLogIngestToken(token []byte) bool { if len(token) < 32 || len(token) > 256 { return false } for _, value := range token { if !validLogIngestTokenByte(value) { return false } } return true } func validLogIngestTokenByte(value byte) bool { switch { case value >= 'a' && value <= 'z', value >= 'A' && value <= 'Z', value >= '0' && value <= '9': return true default: return value == '-' || value == '_' || value == '.' } } func updateAdmissionWithClients(ctx context.Context, clients ociClients, configuration config) error { path, cleanup, err := clients.downloadAdmission(ctx, configuration) if err != nil { return err } defer cleanup() publicInterface, err := defaultInterface() if err != nil { return err } return runAdmission(ctx, configuration, "apply", "--input", path, "--public-interface", publicInterface) } func restoreAdmission(ctx context.Context, configuration config) error { publicInterface, err := defaultInterface() if err != nil { return err } return runAdmission(ctx, configuration, "restore", "--public-interface", publicInterface) } func runAdmission(ctx context.Context, configuration config, arguments ...string) error { arguments = append(arguments, "--state-directory", webAllowStateDirectory, "--minimum-admitted-addresses", strconv.FormatUint(configuration.Admission.MinimumAddresses, 10), "--maximum-admitted-addresses", strconv.FormatUint(configuration.Admission.MaximumAddresses, 10), "--minimum-admitted-intervals", strconv.Itoa(configuration.Admission.MinimumIntervals), ) for _, address := range configuration.MonitoringSourceIPv4 { arguments = append(arguments, "--static-exception-ipv4", address) } return run(ctx, configuration.AdmissionHelper, arguments...) } func updateSMTPDenyWithClients(ctx context.Context, clients ociClients, configuration config) (operationErr error) { defer func() { operationErr = completeSMTPDenyRefresh(operationErr) }() path, cleanup, err := clients.downloadSMTPDeny(ctx, configuration) if err != nil { return errors.Join(err, restoreSMTPDeny(ctx, configuration)) } defer cleanup() publicInterface, err := defaultInterface() if err != nil { return err } return runSMTPDeny(ctx, configuration, "apply", "--input", path, "--public-interface", publicInterface) } func restoreSMTPDeny(ctx context.Context, configuration config) error { publicInterface, err := defaultInterface() if err != nil { return err } return runSMTPDeny(ctx, configuration, "restore", "--public-interface", publicInterface) } func runSMTPDeny(ctx context.Context, configuration config, arguments ...string) error { if configuration.SMTPDeny == nil || configuration.SMTPDenyHelper == "" { return errors.New("SMTP deny configuration is inactive") } arguments = append(arguments, "--state-directory", smtpDenyStateDirectory, "--minimum-observations", strconv.FormatUint(configuration.SMTPDeny.MinimumObservations, 10), "--maximum-addresses", strconv.FormatUint(configuration.SMTPDeny.MaximumAddresses, 10), "--maximum-intervals", strconv.Itoa(configuration.SMTPDeny.MaximumIntervals), "--maximum-lifetime", strconv.Itoa(configuration.SMTPDeny.MaximumLifetimeSeconds)+"s", "--maximum-observation-window", strconv.Itoa(configuration.SMTPDeny.MaximumObservationWindow)+"s", ) return run(ctx, configuration.SMTPDenyHelper, arguments...) } func waitForHandshake(ctx context.Context, maximumAge time.Duration) error { ticker := time.NewTicker(5 * time.Second) defer ticker.Stop() for { latest, err := latestHandshake(ctx) if err == nil && latest > 0 && time.Since(time.Unix(latest, 0)) <= maximumAge { return nil } select { case <-ctx.Done(): return fmt.Errorf("wait for WireGuard handshake: %w", ctx.Err()) case <-ticker.C: } } } func latestHandshake(ctx context.Context) (int64, error) { output, err := exec.CommandContext(ctx, "/usr/bin/wg", "show", interfaceName, "latest-handshakes").Output() if err != nil { return 0, err } fields := strings.Fields(string(output)) if len(fields) < 2 { return 0, errors.New("WireGuard peer handshake is absent") } return strconv.ParseInt(fields[1], 10, 64) } func defaultInterface() (string, error) { contents, err := os.ReadFile("/proc/net/route") if err != nil { return "", err } for _, line := range strings.Split(string(contents), "\n") { fields := strings.Fields(line) if len(fields) >= 4 && fields[1] == "00000000" { flags, err := strconv.ParseUint(fields[3], 16, 16) if err == nil && flags&1 != 0 { return fields[0], nil } } } return "", errors.New("default IPv4 interface not found") } func writeAtomic(path string, contents []byte, mode os.FileMode) error { return writeAtomicOwned(path, contents, mode, -1, -1) } func writeAtomicOwned(path string, contents []byte, mode os.FileMode, ownerID, groupID int) error { if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil { return err } file, err := os.CreateTemp(filepath.Dir(path), ".write-") if err != nil { return err } temporaryPath := file.Name() defer func() { _ = os.Remove(temporaryPath) }() if err := file.Chmod(mode); err != nil { _ = file.Close() return err } if ownerID >= 0 || groupID >= 0 { if err := file.Chown(ownerID, groupID); err != nil { _ = file.Close() return err } } if _, err := file.Write(contents); err != nil { _ = file.Close() return err } if err := file.Sync(); err != nil { _ = file.Close() return err } if err := file.Close(); err != nil { return err } return os.Rename(temporaryPath, path) } func run(ctx context.Context, path string, arguments ...string) error { command := exec.CommandContext(ctx, path, arguments...) output, err := command.CombinedOutput() if err != nil { return fmt.Errorf("%s: %w: %s", filepath.Base(path), err, strings.TrimSpace(string(output))) } return nil } type status struct { ConntrackCount int64 `json:"conntrack_count"` ConntrackMaximum int64 `json:"conntrack_maximum"` HandshakeAgeSecond int64 `json:"handshake_age_seconds"` NftablesActive bool `json:"nftables_active"` SMTPDenyActive bool `json:"smtp_deny_active"` SMTPDenyActiveSource string `json:"smtp_deny_active_source,omitempty"` SMTPDenyBundleAgeSeconds int64 `json:"smtp_deny_bundle_age_seconds"` SMTPDenyBundleRemainingSeconds int64 `json:"smtp_deny_bundle_remaining_seconds"` SMTPDenyCached bool `json:"smtp_deny_cached"` SMTPDenyPolicyValid bool `json:"smtp_deny_policy_valid"` SMTPDenyRefreshCompletedAgeSeconds int64 `json:"smtp_deny_refresh_completed_age_seconds"` SMTPDenyRefreshSuccessful bool `json:"smtp_deny_refresh_successful"` SMTPDenyRemoteCurrentAgeSeconds int64 `json:"smtp_deny_remote_current_age_seconds"` WebAllowActive bool `json:"web_allow_active"` WebAllowCached bool `json:"web_allow_cached"` WireGuardActive bool `json:"wireguard_active"` } type telemetryFile struct { GeneratedAt time.Time `json:"generated_at"` Status status `json:"status"` } func currentStatus(configuration config) status { result := status{ SMTPDenyBundleAgeSeconds: -1, SMTPDenyBundleRemainingSeconds: -1, SMTPDenyRefreshCompletedAgeSeconds: -1, SMTPDenyRemoteCurrentAgeSeconds: -1, } result.NftablesActive = exec.Command("/usr/sbin/nft", "list", "table", "inet", "oci_l4_relay").Run() == nil result.WebAllowActive = exec.Command("/usr/sbin/nft", "list", "table", "inet", "oci_l4_relay_web_allow").Run() == nil result.SMTPDenyActive = exec.Command("/usr/sbin/nft", "list", "table", "inet", "oci_l4_relay_smtp_deny").Run() == nil result.WireGuardActive = exec.Command("/usr/sbin/ip", "link", "show", "dev", interfaceName).Run() == nil result.WebAllowCached = fileExists(filepath.Join(webAllowStateDirectory, "last-good.json.gz")) if configuration.MailRelayAddress != "" { now := time.Now().UTC() if active, err := readSMTPDenyActiveState(smtpDenyStateDirectory); err == nil { result.SMTPDenyActiveSource = active.Source result.SMTPDenyPolicyValid = active.validAt(now, time.Duration(configuration.SMTPDeny.MaximumLifetimeSeconds)*time.Second) result.SMTPDenyCached = result.SMTPDenyPolicyValid && fileExists(filepath.Join(smtpDenyStateDirectory, "last-good.json.gz")) if !active.BundleGeneratedAt.IsZero() { result.SMTPDenyBundleAgeSeconds = max(0, int64(now.Sub(active.BundleGeneratedAt).Seconds())) } if !active.BundleExpiresAt.IsZero() { result.SMTPDenyBundleRemainingSeconds = int64(active.BundleExpiresAt.Sub(now).Seconds()) } if !active.LastRemoteCurrentAt.IsZero() { result.SMTPDenyRemoteCurrentAgeSeconds = max(0, int64(now.Sub(active.LastRemoteCurrentAt).Seconds())) } } if refresh, err := readSMTPDenyRefreshState(smtpDenyStateDirectory); err == nil && !refresh.CompletedAt.IsZero() && !refresh.CompletedAt.After(now.Add(5*time.Minute)) { result.SMTPDenyRefreshCompletedAgeSeconds = max(0, int64(now.Sub(refresh.CompletedAt).Seconds())) result.SMTPDenyRefreshSuccessful = refresh.Successful && refresh.ActiveSource == "current" } } result.ConntrackCount = readInteger("/proc/sys/net/netfilter/nf_conntrack_count") result.ConntrackMaximum = readInteger("/proc/sys/net/netfilter/nf_conntrack_max") if latest, err := latestHandshake(context.Background()); err == nil && latest > 0 { result.HandshakeAgeSecond = max(0, time.Now().Unix()-latest) } else { result.HandshakeAgeSecond = -1 } return result } func reportStatus(configuration config, requireHealthy bool) error { result := currentStatus(configuration) encoded, err := json.Marshal(result) if err != nil { return err } fmt.Println(string(encoded)) mailPolicyUnhealthy := configuration.MailRelayAddress != "" && (!result.SMTPDenyActive || !result.SMTPDenyPolicyValid) if requireHealthy && (!result.NftablesActive || !result.WebAllowActive || !result.WebAllowCached || !result.WireGuardActive || mailPolicyUnhealthy || result.HandshakeAgeSecond < 0 || result.HandshakeAgeSecond > 180) { return errors.New("relay is unhealthy") } return nil } func reportTelemetry(ctx context.Context, configuration config) error { result := currentStatus(configuration) generatedAt := time.Now().UTC() snapshot, err := json.Marshal(telemetryFile{GeneratedAt: generatedAt, Status: result}) if err != nil { return err } if err := writeAtomic(telemetrySnapshotPath, append(snapshot, '\n'), 0644); err != nil { return err } fmt.Printf("oci_l4_relay_telemetry timestamp=%s conntrack_count=%d conntrack_max=%d wireguard_handshake_age_seconds=%d web_allow_cached=%t smtp_deny_cached=%t smtp_deny_policy_valid=%t smtp_deny_active_source=%s smtp_deny_bundle_age_seconds=%d smtp_deny_bundle_remaining_seconds=%d smtp_deny_remote_current_age_seconds=%d smtp_deny_refresh_successful=%t smtp_deny_refresh_completed_age_seconds=%d nftables_active=%t wireguard_active=%t\n", generatedAt.Format(time.RFC3339), result.ConntrackCount, result.ConntrackMaximum, result.HandshakeAgeSecond, result.WebAllowCached, result.SMTPDenyCached, result.SMTPDenyPolicyValid, result.SMTPDenyActiveSource, result.SMTPDenyBundleAgeSeconds, result.SMTPDenyBundleRemainingSeconds, result.SMTPDenyRemoteCurrentAgeSeconds, result.SMTPDenyRefreshSuccessful, result.SMTPDenyRefreshCompletedAgeSeconds, result.NftablesActive, result.WireGuardActive) for _, command := range [][]string{ {"/usr/sbin/nft", "-j", "list", "counters"}, {"/usr/sbin/tc", "-s", "-j", "qdisc", "show", "dev", interfaceName}, {"/usr/sbin/ip", "-s", "-j", "link", "show", "dev", interfaceName}, } { output, err := exec.CommandContext(ctx, command[0], command[1:]...).Output() if err != nil { return err } fmt.Printf("oci_l4_relay_telemetry detail=%s\n", strings.TrimSpace(string(output))) } return nil } func fileExists(path string) bool { _, err := os.Stat(path) return err == nil } func readInteger(path string) int64 { contents, err := os.ReadFile(path) if err != nil { return -1 } value, err := strconv.ParseInt(strings.TrimSpace(string(contents)), 10, 64) if err != nil { return -1 } return value }