Binding to all network interfaces

Binding network interfaces beyond the service's intended access scope

Description

Binding a server to 0.0.0.0 or an empty host address allows it to receive requests on multiple network interfaces. Actual external reachability also depends on routing and firewalls. Listening on all interfaces does not itself bypass authentication, but an internal service may expose sensitive information or administrative functions if its authentication, TLS and network controls do not match that exposure.

Potential impact

  • An externally reachable administration, debugging or metrics endpoint without adequate protection may expose settings, tokens or session information.
  • External clients may send requests directly to functions intended only for an internal network. Their actual permissions still depend on authentication and authorization.
  • Unnecessarily exposed services may provide an entry point for attacks on vulnerable versions or accounts.

Remediation

  • Specify the listening address. Use loopback or a required private interface address with net.Listen or tls.Listen for internal services.
  • Manage addresses and ports per environment, and review whether listening on every interface is intentional in production.
  • Restrict ports that do not need external access to approved networks or IP addresses using firewalls or cloud security groups.
  • Behind a reverse proxy, restrict backend access to loopback in the same host and network namespace, or to the required private path for separate hosts or containers. Apply authentication and transport protection too.
  • Periodically check actual listening sockets and externally reachable ports.

Examples

Before

go
package main

import (
    "log"
    "net"
)

// Example internal administration TCP server
func startAdminServer() {
    // Bind to all interfaces (0.0.0.0); the internal port may be externally reachable
    listener, err := net.Listen("tcp", "0.0.0.0:9000")
    if err != nil {
        log.Fatal(err)
    }
    defer listener.Close()

    for {
        conn, err := listener.Accept()
        if err != nil {
            log.Println("accept error:", err)
            continue
        }
        go func(c net.Conn) {
            defer c.Close()
            c.Write([]byte("admin status: OK\n"))
        }(conn)
    }
}

func main() {
    startAdminServer()
}

After

go
package main

import (
    "log"
    "net"
    "os"
)

// Internal administration TCP server with a local or private bind address
func startAdminServer() {
    // Configure the address through the environment; default to localhost
    addr := os.Getenv("ADMIN_BIND_ADDR")
    if addr == "" {
        // Bind only to 127.0.0.1 for local connections
        addr = "127.0.0.1:9000"
    }

    listener, err := net.Listen("tcp", addr)
    if err != nil {
        log.Fatal(err)
    }
    defer listener.Close()

    log.Printf("admin server listening on %s", addr)

    for {
        conn, err := listener.Accept()
        if err != nil {
            log.Println("accept error:", err)
            continue
        }
        go func(c net.Conn) {
            defer c.Close()
            c.Write([]byte("admin status: OK\n"))
        }(conn)
    }
}

func main() {
    startAdminServer()
}

Explanation:

  • Before: The server listens on 0.0.0.0:9000. If the external connection path is allowed, clients can read its unauthenticated status response.
  • After: The default address is 127.0.0.1:9000. A configured ADMIN_BIND_ADDR is used as supplied, so trusted administrators must control deployment settings and verify the actual access scope.

References