Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:

permissions:
contents: write
packages: write

jobs:
release-linux:
Expand Down Expand Up @@ -48,3 +49,78 @@ jobs:
args: release --clean --config .goreleaser-darwin.yaml --skip=validate
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

release-containers:
runs-on: ubuntu-latest
strategy:
matrix:
variant: [alpine, debian, ubuntu, scratch]
include:
- variant: alpine
is_default: true
- variant: debian
is_default: false
- variant: ubuntu
is_default: false
- variant: scratch
is_default: false
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: DeterminateSystems/nix-installer-action@v17

- uses: nix-community/cache-nix-action@v6
with:
primary-key: nix-${{ runner.os }}-${{ hashFiles('flake.lock') }}
restore-prefixes-first-match: nix-${{ runner.os }}-

- name: login to ghcr
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: build container
run: nix build ".#snitch-${{ matrix.variant }}" --print-out-paths

- name: load and push container
env:
VERSION: ${{ github.ref_name }}
REPO: ghcr.io/${{ github.repository_owner }}/snitch
VARIANT: ${{ matrix.variant }}
IS_DEFAULT: ${{ matrix.is_default }}
run: |
VERSION="${VERSION#v}"

docker load < result

IMAGE_TAG=$(docker images --format '{{.Repository}}:{{.Tag}}' | grep "snitch:.*-${VARIANT}" | head -1)

if [ -z "$IMAGE_TAG" ]; then
echo "error: could not find loaded image for ${VARIANT}"
exit 1
fi

docker tag "$IMAGE_TAG" "${REPO}:${VERSION}-${VARIANT}"
docker tag "$IMAGE_TAG" "${REPO}:latest-${VARIANT}"
docker push "${REPO}:${VERSION}-${VARIANT}"
docker push "${REPO}:latest-${VARIANT}"

if [ "$IS_DEFAULT" = "true" ]; then
docker tag "$IMAGE_TAG" "${REPO}:${VERSION}"
docker tag "$IMAGE_TAG" "${REPO}:latest"
docker push "${REPO}:${VERSION}"
docker push "${REPO}:latest"
fi

- name: summary
env:
VERSION: ${{ github.ref_name }}
REPO: ghcr.io/${{ github.repository_owner }}/snitch
VARIANT: ${{ matrix.variant }}
run: |
VERSION="${VERSION#v}"
echo "pushed ${REPO}:${VERSION}-${VARIANT}" >> $GITHUB_STEP_SUMMARY
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,47 @@ curl -sSL https://raw.githubusercontent.com/karol-broda/snitch/master/install.sh

> **macos:** the install script automatically removes the quarantine attribute (`com.apple.quarantine`) from the binary to allow it to run without gatekeeper warnings. to disable this, set `KEEP_QUARANTINE=1`.

### docker

pre-built oci images available from github container registry:

```bash
# pull from ghcr.io
docker pull ghcr.io/karol-broda/snitch:latest # alpine (default)
docker pull ghcr.io/karol-broda/snitch:latest-alpine # alpine (~17MB)
docker pull ghcr.io/karol-broda/snitch:latest-scratch # minimal, binary only (~9MB)
docker pull ghcr.io/karol-broda/snitch:latest-debian # debian trixie
docker pull ghcr.io/karol-broda/snitch:latest-ubuntu # ubuntu 24.04

# or use a specific version
docker pull ghcr.io/karol-broda/snitch:0.2.0-alpine
```

alternatively, build locally via nix flake:

```bash
nix build github:karol-broda/snitch#snitch-alpine
docker load < result
```

**running the container:**

```bash
# basic usage - sees host sockets but not process names
docker run --rm --net=host snitch:latest ls

# full info - includes PID, process name, user
docker run --rm --net=host --pid=host --cap-add=SYS_PTRACE snitch:latest ls
```

| flag | purpose |
|------|---------|
| `--net=host` | share host network namespace (required to see host connections) |
| `--pid=host` | share host pid namespace (needed for process info) |
| `--cap-add=SYS_PTRACE` | read process details from `/proc/<pid>` |

> **note:** `CAP_NET_ADMIN` and `CAP_NET_RAW` are not required. snitch reads from `/proc/net/*` which doesn't need special network capabilities.

### binary

download from [releases](https://github.com/karol-broda/snitch/releases):
Expand Down
17 changes: 12 additions & 5 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,18 @@
in
{
packages = eachSystem (system:
let pkgs = pkgsFor system; in
{
default = mkSnitch pkgs;
let
pkgs = pkgsFor system;
snitch = mkSnitch pkgs;
}
# containers only available on linux
containers = if pkgs.stdenv.isLinux
then import ./nix/containers.nix { inherit pkgs snitch; }
else { };
in
{
default = snitch;
inherit snitch;
} // containers
);

devShells = eachSystem (system:
Expand All @@ -94,7 +101,7 @@
in
{
default = pkgs.mkShell {
packages = [ go pkgs.git pkgs.vhs ];
packages = [ go pkgs.git pkgs.vhs pkgs.nix-prefetch-docker ];
env.GOTOOLCHAIN = "local";
shellHook = ''
echo "go toolchain: $(go version)"
Expand Down
121 changes: 121 additions & 0 deletions nix/containers.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# oci container definitions for snitch
# builds containers based on different base images: alpine, debian trixie, ubuntu
#
# base images are pinned by imageDigest (immutable content hash), not by tag.
# even if the upstream tag gets a new image, builds remain reproducible.
#
# to update base image hashes, run:
# nix-prefetch-docker --image-name alpine --image-tag 3.21
# nix-prefetch-docker --image-name debian --image-tag trixie-slim
# nix-prefetch-docker --image-name ubuntu --image-tag 24.04
#
# this outputs both imageDigest and sha256 values needed below
{ pkgs, snitch }:
let
commonConfig = {
name = "snitch";
tag = snitch.version;
config = {
Entrypoint = [ "${snitch}/bin/snitch" ];
Env = [ "PATH=/bin" ];
Labels = {
"org.opencontainers.image.title" = "snitch";
"org.opencontainers.image.description" = "a friendlier ss/netstat for humans";
"org.opencontainers.image.source" = "https://github.com/karol-broda/snitch";
"org.opencontainers.image.licenses" = "MIT";
};
};
};

# alpine-based container
alpine = pkgs.dockerTools.pullImage {
imageName = "alpine";
imageDigest = "sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c";
sha256 = "sha256-WNbRh44zld3lZtKARhdeWFte9JKgD2bgCuKzETWgGr8=";
finalImageName = "alpine";
finalImageTag = "3.21";
};

# debian trixie (testing) based container
debianTrixie = pkgs.dockerTools.pullImage {
imageName = "debian";
imageDigest = "sha256:e711a7b30ec1261130d0a121050b4ed81d7fb28aeabcf4ea0c7876d4e9f5aca2";
sha256 = "sha256-W/9A7aaPXFCmmg+NTSrFYL+QylsAgfnvkLldyI18tqU=";
finalImageName = "debian";
finalImageTag = "trixie-slim";
};

# ubuntu based container
ubuntu = pkgs.dockerTools.pullImage {
imageName = "ubuntu";
imageDigest = "sha256:c35e29c9450151419d9448b0fd75374fec4fff364a27f176fb458d472dfc9e54";
sha256 = "sha256-0j8xM+mECrBBHv7ZqofiRaeSoOXFBtLYjgnKivQztS0=";
finalImageName = "ubuntu";
finalImageTag = "24.04";
};

# scratch container (minimal, just the snitch binary)
scratch = pkgs.dockerTools.buildImage {
name = "snitch";
tag = "${snitch.version}-scratch";
copyToRoot = pkgs.buildEnv {
name = "snitch-root";
paths = [ snitch ];
pathsToLink = [ "/bin" ];
};
config = commonConfig.config;
};

in
{
snitch-alpine = pkgs.dockerTools.buildImage {
name = "snitch";
tag = "${snitch.version}-alpine";
fromImage = alpine;
copyToRoot = pkgs.buildEnv {
name = "snitch-root";
paths = [ snitch ];
pathsToLink = [ "/bin" ];
};
config = commonConfig.config;
};

snitch-debian = pkgs.dockerTools.buildImage {
name = "snitch";
tag = "${snitch.version}-debian";
fromImage = debianTrixie;
copyToRoot = pkgs.buildEnv {
name = "snitch-root";
paths = [ snitch ];
pathsToLink = [ "/bin" ];
};
config = commonConfig.config;
};

snitch-ubuntu = pkgs.dockerTools.buildImage {
name = "snitch";
tag = "${snitch.version}-ubuntu";
fromImage = ubuntu;
copyToRoot = pkgs.buildEnv {
name = "snitch-root";
paths = [ snitch ];
pathsToLink = [ "/bin" ];
};
config = commonConfig.config;
};

snitch-scratch = scratch;

oci-default = pkgs.dockerTools.buildImage {
name = "snitch";
tag = snitch.version;
fromImage = alpine;
copyToRoot = pkgs.buildEnv {
name = "snitch-root";
paths = [ snitch ];
pathsToLink = [ "/bin" ];
};
config = commonConfig.config;
};
}

Loading