This little NAS that could

Super useful NAS configuration of Pomerium

Pros / Cons

What’s great about doing things this way?

Most of all, this setup is convenient. I already had a NAS for backup purposes, being able to run very real software on the device is a nice plus. It’s a teeny-tiny little server that does almost everything I want in a self-hosted fashion. As much as I love all things Kubernetes, I wanted to keep the footprint small and single server and docker-compose is stupid simple to use, especially with Synology which natively supports intel based containers.

What are the drawbacks?

It’s not Kubernetes. I mean, I don’t need Kubernetes. But friends, who amongst us doesn’t love totally overbuilding their home labs? And, while this is all running on the same machine and the threat model isn’t really applicable to warrant it, I would love to be using something like Istio.

Services

  • Grafana - Shows all the metrics about my little network. Though, these days I use Pomerium Enterprise for almost anything but app specific metrics.
  • Prometheus - Prometheus collects and stores its metrics as time series data. I use it to pull metrics from Pomerium and my other cloud native tools and use Grafana to view that.
  • Loki - Loki is a log aggregation system designed to store and query logs from all your applications and infrastructure. I use it for lightweight logging in grafana.
  • Airdcpp - AirDC++ is a web client for Advanced Direct Connect and Direct Connect networks. Yes I am that old.
  • Zipkin - Zipkin is a distributed tracing system. It helps gather timing data needed to troubleshoot latency problems. I mostly use it to trace and investigate long requests.
  • archivebox - Archivebox lets me collect, save, and view sites offline. I like using it to read when I don’t have internet connections, or if I want to back up a copy of a site.
  • code-server - I’ve used visual studio code as my daily driver for a while now. Code-server lets me hack from anywhere on Pomerium, behind Pomerium, in the browser. Doesn’t get cooler than that.
  • Gitlab - Is the 900 pound gorilla (…and feels like it compared to the other containers) in the self-hosted source control management space. I mostly use it to backup my github repos, and work locally.

Tech Stack

  • Docker
  • Docker-compose
  • Synology DSM
  • DS918+ / INTEL Celeron J3455 / 8192 MB memory

Diagram

Soon ™️

Configs

docker-compose.yaml:

version: "3"
services:
  pomerium:
    image: pomerium/pomerium:main
    volumes:
      - ./pomerium:/pomerium:ro
      - ./pomerium:/data:rw
    ports:
      - "32443:443" # routers --> my nas
      - "32080:80" # router --> my nas
    expose:
      - 5443
      - 443
    restart: always

  verify:
    image: pomerium/verify:latest
    expose:
      - 8000
    restart: always

  prometheus:
    volumes:
      - ./prometheus:/prometheus
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
    image: prom/prometheus:latest
    expose:
      - 9090
    restart: always

  loki:
    container_name: loki
    image: grafana/loki:latest
    expose:
      - 3100
    command: -config.file=/etc/loki/local-config.yaml
    volumes:
      - ./loki:/etc/loki
    restart: always

  grafana:
    user: "104"
    image: grafana/grafana
    volumes:
      - ./grafana:/var/lib/grafana
    expose:
      - 3000
    restart: always
    environment:
      - "GF_AUTH_ANONYMOUS_ENABLED=true"
      - "GF_AUTH_ANONYMOUS_ORG_ROLE=Admin"
      - "GF_AUTH_DISABLE_LOGIN_FORM=true"

  airdcpp:
    image: gangefors/airdcpp-webclient:latest
    volumes:
      - /storage/tv:/tv:rw
      - /storage/movies:/movies:rw
      - ./.airdcpp:/.airdcpp:rw
    user: root
    expose:
      - 5600
      - 5601
    ports:
      - ${TCP_PORT:-21248}:${TCP_PORT:-21248}
      - ${UDP_PORT:-21248}:${UDP_PORT:-21248}/udp
      - ${TLS_PORT:-21249}:${TLS_PORT:-21249}
    restart: always

  codeserver:
    image: codercom/code-server:latest
    restart: always
    expose:
      - 8080
    volumes:
      - ".:/home/coder/project:rw"
      - "$HOME/.config:/home/coder/.config"
    command: --auth none --disable-telemetry

  zipkin:
    environment:
      - STORAGE_TYPE=mem
      - MYSQL_HOST=mysql
    image: ghcr.io/openzipkin/zipkin-slim:${TAG:-latest}
    expose:
      - 9411

  archivebox:
    image: ${DOCKER_IMAGE:-archivebox/archivebox:master}
    command: server --quick-init 0.0.0.0:8000
    ports:
      - 8000:8000
    environment:
      - ALLOWED_HOSTS=*
      - MEDIA_MAX_SIZE=1g
    volumes:
      - /volume1/misc/archivebox:/data

  gitlab:
    container_name: gitlab
    image: gitlab/gitlab-ee:latest
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'https://gitlab.int.nas.redacted.io'
        registry_external_url 'https://gitlab.int.nas.redacted.io'
        letsencrypt['enable'] = false
        nginx['listen_port'] = 80
        nginx['listen_https'] = false
        registry_nginx['listen_port'] = 80
        registry_nginx['listen_https'] = false
        pages_nginx['listen_port'] = 80
        pages_nginx['listen_https'] = false
        mattermost_nginx['listen_port'] = 80
        mattermost_nginx['listen_https'] = false
        gitlab_rails['smtp_enable'] = true
        gitlab_rails['smtp_address'] = "smtp.sendgrid.net"
        gitlab_rails['smtp_port'] = 587
        gitlab_rails['smtp_user_name'] = "apikey"
        gitlab_rails['smtp_password'] = "REDACTED"
        gitlab_rails['smtp_domain'] = "smtp.sendgrid.net"
        gitlab_rails['smtp_authentication'] = "plain"
        gitlab_rails['smtp_enable_starttls_auto'] = true
        gitlab_rails['smtp_tls'] = false
        gitlab_rails['gitlab_email_from'] = 'no-reply@redacted.io'
        gitlab_rails['gitlab_email_reply_to'] = 'no-reply@redacted.io'
    volumes:
      - "./gitlab/config:/etc/gitlab"
      - "./gitlab/logs:/var/log/gitlab"
      - "./gitlab/data:/var/opt/gitlab"
    expose:
      - 80
      - 443
      - 22
    restart: always
    shm_size: "256m"

Pomerium

config.yaml:

authenticate_service_url: https://authenticate.int.nas.redacted.io
signing_key: "REDACTED="
http_redirect_addr: ":80"
autocert: true

shared_secret: REDACTED=
cookie_secret: REDACTED=

idp_provider: google
idp_client_id: REDACTED.apps.googleusercontent.com
idp_client_secret: "REDACTED"
idp_service_account: REDACTED

# notice, I use the enterprise version of pomerium. Nearly all the route configurations are very standard with the exception of several (airdcpp, prometheus, grafana) require websockets. 

policy:
  - from: https://admin.int.nas.redacted.io
    to: http://pomerium-enterprise:8701
    pass_identity_headers: true
    allowed_users:
      - me
    allowed_groups:
      - my_family@redacted.io
      - my_friends@redacted.io

Loki

config.yaml:

auth_enabled: false

server:
  http_listen_port: 3100
  grpc_listen_port: 9096

ingester:
  wal:
    enabled: true
    dir: /tmp/wal
  lifecycler:
    address: 127.0.0.1
    ring:
      kvstore:
        store: inmemory
      replication_factor: 1
    final_sleep: 0s
  chunk_idle_period: 1h       # Any chunk not receiving new logs in this time will be flushed
  max_chunk_age: 1h           # All chunks will be flushed when they hit this age, default is 1h
  chunk_target_size: 1048576  # Loki will attempt to build chunks up to 1.5MB, flushing first if chunk_idle_period or max_chunk_age is reached first
  chunk_retain_period: 30s    # Must be greater than index read cache TTL if using an index cache (Default index read cache TTL is 5m)
  max_transfer_retries: 0     # Chunk transfers disabled

schema_config:
  configs:
    - from: 2020-10-24
      store: boltdb-shipper
      object_store: filesystem
      schema: v11
      index:
        prefix: index_
        period: 24h

storage_config:
  boltdb_shipper:
    active_index_directory: /tmp/loki/boltdb-shipper-active
    cache_location: /tmp/loki/boltdb-shipper-cache
    cache_ttl: 24h         # Can be increased for faster performance over longer query periods, uses more disk space
    shared_store: filesystem
  filesystem:
    directory: /tmp/loki/chunks

compactor:
  working_directory: /tmp/loki/boltdb-shipper-compactor
  shared_store: filesystem

limits_config:
  reject_old_samples: true
  reject_old_samples_max_age: 168h

chunk_store_config:
  max_look_back_period: 0s

table_manager:
  retention_deletes_enabled: false
  retention_period: 0s

ruler:
  storage:
    type: local
    local:
      directory: /tmp/loki/rules
  rule_path: /tmp/loki/rules-temp
  alertmanager_url: http://localhost:9093
  ring:
    kvstore:
      store: inmemory
  enable_api: true

Grafana

grafana.ini:

[auth.jwt]
enabled = true
header_name = X-Pomerium-Jwt-Assertion
email_claim = email
jwk_set_url = https://authenticate.int.nas.redacted.io/.well-known/pomerium/jwks.json
cache_ttl = 60m

Screenshots

Pomerium and these apps are super lightweight. I have a few dozen more than covered here, but CPU / Memory is still very low.

Developing Pomerium in the browser, while behind Pomerium is about as inception-y as it gets.

Links and References

Many of these are – unsurprisingly – guides in our docs. Check them out.

Happy to answer any questions about my setup, or what trading files in the early 90s felt like.

2 Likes

Neat! I also run Pomerium on a Synology NAS, but I use the DSM web GUI to configure my containers. Unfortunately that means I can’t share my config as easily, since it would just be a bunch of screenshots like this: