servicemap

module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 7, 2026 License: MIT

README

servicemap

English · 한국어

Map running services, listening ports, and their proxy/connection relationships on Linux.

sudo servicemap

Output (tree format, default):

mariadb.service
  └─ listens :3306

myapp.service
  └─ listens :3000
  └─ connects to mariadb.service (:3306)

nginx.service
  └─ listens :80
  └─ proxies to myapp.service (:3000)

abcdef123456 [docker]
  └─ listens :8080

Install

From source
go install github.com/LeeSeokBln/servicemap/cmd/servicemap@latest
From release tarball

Download the latest release from GitHub Releases, extract, and add to your PATH:

tar xzf servicemap_*_linux_amd64.tar.gz
sudo mv servicemap /usr/local/bin/

Usage

servicemap [flags]

Flags:
  -f, --format FORMAT      output format: tree, mermaid, md, json (default "tree")
  -o, --output PATH        write output to file (infers format from extension)
  --all                    show all nodes (by default, plain processes that
                           neither listen nor are systemd/docker services are hidden)
  --version                print version
  --proc-root PATH         root of /proc for testing (default "/")

Exit codes:

  • 0: success
  • 1: error (bad flags, permission denied, etc.)

Output formats

Tree (default)

Tree view of services and their relationships:

mariadb.service
  └─ listens :3306

myapp.service
  └─ listens :3000
  └─ connects to mariadb.service (:3306)

nginx.service
  └─ listens :80, :443
  └─ proxies to myapp.service (:3000)

webapp [docker]
  └─ listens 127.0.0.1:8080
Mermaid

Flowchart for visualization in documentation or Slack:

flowchart LR
    n0["mariadb.service<br/>:3306"]
    n1["myapp.service<br/>:3000"]
    n2["nginx.service<br/>:80, :443"]
    n3["webapp [docker]<br/>127.0.0.1:8080"]
    n1 -.->|connects :3306| n0
    n2 -->|proxies :3000| n1
Markdown

Structured output for reports:

# Service Map

| Service | Kind | Listens | Connections |
|---|---|---|---|
| mariadb.service | systemd | :3306 |  |
| myapp.service | systemd | :3000 | connects to mariadb.service (:3306) |
| nginx.service | systemd | :80, :443 | proxies to myapp.service (:3000) |
| webapp | docker | 127.0.0.1:8080 |  |

\`\`\`mermaid
flowchart LR
    n1 -.->|connects :3306| n0
    n2 -->|proxies :3000| n1
\`\`\`
JSON

Complete graph in JSON for programmatic access:

{
  "nodes": [
    {"id": "unit:mariadb.service", "name": "mariadb.service", "kind": "systemd", "listens": [":3306"], "pids": [300]},
    {"id": "unit:myapp.service", "name": "myapp.service", "kind": "systemd", "listens": [":3000"], "pids": [200]},
    {"id": "unit:nginx.service", "name": "nginx.service", "kind": "systemd", "listens": [":80", ":443"], "pids": [100, 101]}
  ],
  "edges": [
    {"from": "unit:myapp.service", "to": "unit:mariadb.service", "kind": "connects", "ports": [3306]},
    {"from": "unit:nginx.service", "to": "unit:myapp.service", "kind": "proxies", "ports": [3000]}
  ]
}

How it works

servicemap has no daemon and requires no setup. It reads /proc socket tables (/proc/net/tcp, /proc/net/udp) to find listening ports, then matches them to running processes via inodes. For containers with separate network namespaces, it reads their /proc/[pid]/net socket tables too. Process-to-service mapping comes from systemd cgroups (/proc/[pid]/cgroup; unit names for systemd services, container IDs for Docker), and container names are resolved via the Docker socket. nginx proxy routes are extracted by parsing nginx config files (found via the nginx process's -c flag or defaulting to /etc/nginx/nginx.conf), matching proxy_pass directives to upstream server blocks and remote targets. UDP listens are shown but no UDP connection tracking is performed. By default, plain processes that neither listen nor are systemd/docker services are hidden (one-off clients, background tools); systemd/docker services are always shown. Use --all to display everything.

Permissions

Running without sudo can read all listening ports from /proc/net, but cannot read other users' /proc/[pid]/fd directories — so services owned by other users lose their socket attribution and edges. The tool continues and prints a stderr warning with the count of uninspectable processes. For a complete map with all users' services:

sudo servicemap

Limitations

  • nginx config parsing only — other proxies (HAProxy, Envoy, Apache) are not yet parsed; their routes appear as external TCP connects only.
  • UDP connections not tracked — UDP listening ports are shown, but no edge information about which process sends to which UDP service.
  • Docker published ports via DNAT — ports exposed through Docker's port publishing (e.g. -p 80:8080) may show the docker-proxy process's bind instead of the container's internal port.
  • Hostname resolution — nginx proxy targets given as hostnames that resolve to local IPs are shown as external nodes (not yet resolved to local services).

License

MIT — see LICENSE.


한국어

Linux에서 실행 중인 서비스, 리스닝 포트, 그리고 프록시/연결 관계를 지도화합니다.

sudo servicemap

출력 (기본값: tree 포맷):

mariadb.service
  └─ listens :3306

myapp.service
  └─ listens :3000
  └─ connects to mariadb.service (:3306)

nginx.service
  └─ listens :80
  └─ proxies to myapp.service (:3000)

abcdef123456 [docker]
  └─ listens :8080
설치
소스에서 빌드
go install github.com/LeeSeokBln/servicemap/cmd/servicemap@latest
릴리즈 타르볼

GitHub Releases에서 최신 버전을 받아서 PATH에 추가합니다:

tar xzf servicemap_*_linux_amd64.tar.gz
sudo mv servicemap /usr/local/bin/
사용법
servicemap [플래그]

플래그:
  -f, --format FORMAT      출력 포맷: tree, mermaid, md, json (기본값 "tree")
  -o, --output PATH        출력을 파일에 저장 (확장자에서 포맷 자동 감지)
  --all                    모든 노드 표시 (기본값: systemd/docker 서비스를 제외한
                           순수 프로세스 노드는 숨김)
  --version                버전 출력
  --proc-root PATH         테스트용 /proc 루트 (기본값 "/")

종료 코드:

  • 0: 성공
  • 1: 에러 (잘못된 플래그, 권한 거부 등)
출력 포맷
Tree (기본값)

서비스와 관계를 트리로 표시:

mariadb.service
  └─ listens :3306

myapp.service
  └─ listens :3000
  └─ connects to mariadb.service (:3306)

nginx.service
  └─ listens :80, :443
  └─ proxies to myapp.service (:3000)

webapp [docker]
  └─ listens 127.0.0.1:8080
Mermaid

문서나 Slack에서 시각화할 수 있는 플로우차트:

flowchart LR
    n0["mariadb.service<br/>:3306"]
    n1["myapp.service<br/>:3000"]
    n2["nginx.service<br/>:80, :443"]
    n3["webapp [docker]<br/>127.0.0.1:8080"]
    n1 -.->|connects :3306| n0
    n2 -->|proxies :3000| n1
Markdown

리포트용 구조화된 출력:

# Service Map

| Service | Kind | Listens | Connections |
|---|---|---|---|
| mariadb.service | systemd | :3306 |  |
| myapp.service | systemd | :3000 | connects to mariadb.service (:3306) |
| nginx.service | systemd | :80, :443 | proxies to myapp.service (:3000) |
| webapp | docker | 127.0.0.1:8080 |  |

\`\`\`mermaid
flowchart LR
    n1 -.->|connects :3306| n0
    n2 -->|proxies :3000| n1
\`\`\`
JSON

프로그래매틱 접근용 완전한 그래프:

{
  "nodes": [
    {"id": "unit:mariadb.service", "name": "mariadb.service", "kind": "systemd", "listens": [":3306"], "pids": [300]},
    {"id": "unit:myapp.service", "name": "myapp.service", "kind": "systemd", "listens": [":3000"], "pids": [200]},
    {"id": "unit:nginx.service", "name": "nginx.service", "kind": "systemd", "listens": [":80", ":443"], "pids": [100, 101]}
  ],
  "edges": [
    {"from": "unit:myapp.service", "to": "unit:mariadb.service", "kind": "connects", "ports": [3306]},
    {"from": "unit:nginx.service", "to": "unit:myapp.service", "kind": "proxies", "ports": [3000]}
  ]
}
작동 원리

servicemap는 데몬이 없고 사전 설정이 필요 없습니다. /proc 소켓 테이블(/proc/net/tcp, /proc/net/udp)을 읽어서 리스닝 포트를 찾고, inode를 통해 실행 중인 프로세스와 매칭합니다. 컨테이너가 별도의 네트워크 네임스페이스를 사용하면 /proc/[pid]/net 소켓 테이블도 읽습니다. 프로세스-서비스 매핑은 systemd cgroup(/proc/[pid]/cgroup에서 systemd 서비스 이름이나 Docker 컨테이너 ID)으로 결정되며, 컨테이너 이름은 Docker 소켓을 통해 조회됩니다. nginx 프록시 경로는 nginx 설정 파일을 파싱해서 추출합니다 (nginx 프로세스의 -c 플래그로 찾거나 기본값 /etc/nginx/nginx.conf). proxy_pass 지시문을 upstream server 블록 및 원격 대상과 매칭합니다. UDP 리스닝은 표시되지만 UDP 연결 추적은 하지 않습니다. 기본적으로 리스닝도 하지 않고 systemd/docker 서비스도 아닌 순수 프로세스 노드(한 번 실행되는 클라이언트, 백그라운드 도구)는 숨겨집니다. systemd/docker 서비스는 나가는 연결만 있어도 항상 표시됩니다. --all을 사용하면 모든 노드를 표시합니다.

권한

sudo 없이 실행하면 /proc/net에서 모든 리스닝 포트는 읽을 수 있지만, 다른 사용자의 /proc/[pid]/fd 디렉토리는 읽을 수 없습니다. 따라서 다른 사용자가 소유한 서비스는 소켓 속성을 잃고 엣지 정보가 사라집니다. 도구는 계속 실행되며 읽을 수 없는 프로세스 개수를 stderr에 경고로 출력합니다. 모든 사용자의 서비스를 포함한 완전한 지도를 보려면:

sudo servicemap
제한사항
  • nginx 설정만 파싱 — HAProxy, Envoy, Apache 등 다른 프록시는 아직 지원하지 않습니다. 이들의 경로는 외부 TCP 연결로만 표시됩니다.
  • UDP 연결 추적 안 함 — UDP 리스닝 포트는 표시되지만, 어느 프로세스가 어느 UDP 서비스로 보내는지에 대한 엣지 정보는 없습니다.
  • Docker 포트 포워딩(DNAT) — Docker 포트 포워딩(예: -p 80:8080)으로 노출된 포트는 컨테이너의 내부 포트가 아닌 docker-proxy 프로세스의 바인드를 표시할 수 있습니다.
  • 호스트명 해석 — nginx 프록시 타겟이 로컬 IP로 해석되는 호스트명인 경우, 로컬 서비스로 해석되지 않고 외부 노드로 표시됩니다.
라이선스

MIT — LICENSE 참조.

Directories

Path Synopsis
cmd
servicemap command
internal
collect
Package collect reads /proc, the docker socket, and nginx configs into a Snapshot of the host's processes, sockets, and service configuration.
Package collect reads /proc, the docker socket, and nginx configs into a Snapshot of the host's processes, sockets, and service configuration.
graph
Package graph turns a collect.Snapshot into a service graph: nodes (services) with listen addresses, and edges (proxies/connects).
Package graph turns a collect.Snapshot into a service graph: nodes (services) with listen addresses, and edges (proxies/connects).
render
Package render formats a graph.Graph as tree, mermaid, markdown, or JSON.
Package render formats a graph.Graph as tree, mermaid, markdown, or JSON.
testfix
Package testfix builds a fake filesystem root mimicking a small Linux server: nginx (idle, proxy configured) -> myapp -> mariadb, one docker container in its own netns, and a plain client process.
Package testfix builds a fake filesystem root mimicking a small Linux server: nginx (idle, proxy configured) -> myapp -> mariadb, one docker container in its own netns, and a plain client process.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL