Skip to content

nshan651/porto

Repository files navigation

Porto

My portable, private home server infrastructure.

./docs/arch.svg

Diagram Source
digraph G {
    fontname="Helvetica,Arial,sans-serif";
    node [fontname="Helvetica,Arial,sans-serif", shape=rect, style="rounded,filled", fillcolor="#2d2d2d", fontcolor=white, color=white];
    edge [fontname="Helvetica,Arial,sans-serif", color=white, fontcolor=white];
    bgcolor="#1f1f1f";
    rankdir=TB;
    nodesep=0.7;
    ranksep=0.9;
    compound=true; // Allows edges to connect to clusters
    splines=polyline;

    internet [label="Internet", shape=none, fillcolor=none];

    subgraph cluster_external {
      bgcolor="#601000";
      label="External Compose";
      fontcolor=white;
      color=white;
      style=rounded;

      public_rp [label="External Reverse Proxy"];
      website [label="Website\n(public.duckdns.org)"];
      headscale [label="Headscale\n(hs.public.duckdns.org)"];

      // Connections
      public_rp -> website;
      public_rp -> headscale;
    }

    internet -> public_rp [lhead=cluster_external];

    // Cluster_Tailnet Cluster
    subgraph cluster_tailnet {
      bgcolor="#013312";
      label="Tailnet";
      fontcolor=white;
      color=white;
      style=rounded;

      // Tailnet nodes.
      phone [label="Phone"];
      laptop [label="Laptop"];
      vps [label="VPS"];

      phone -> laptop [dir=none, constraint=false];
      laptop -> vps [dir=none, constraint=false];

      // Connect mesh to tailscale_node
      { phone; laptop; vps } -> docker_ts_node [dir=none];

      // Cluster_Tailnet Cluster
      subgraph cluster_internal {
          bgcolor="#32467a";
          label="Internal Compose";
          fontcolor=white;
          color=white;
          style=rounded;

          docker_ts_node [label="Docker Tailscale Node\n(*.private.duckdns.org)"];
          private_rp [label="Internal Reverse Proxy"];

          service1 [label="Service 1"];
          service2 [label="Service 2"];
          service3 [label="Service 3"];

          docker_ts_node -> private_rp;
          private_rp -> {service1; service2; service3};
      }
    }

    // Connect Headscale to the Cluster_Tailnet
    headscale -> docker_ts_node [lhead=cluster_internal];
  }

Usage

Usage: make [target]

Docker compose targets:
  up                Spin up the public and private compose stacks.
  down              Tear down the public and private compose stacks, removing dangling volumes.
  pull              Pull service images.

Tailscale targets:
  nodes             List existing nodes in the tailnet.
  preauth           Generate a new preauth key.
  pull              Pull service images.

Authentication:
  secret            Generate a new argon2 hashed password. Run this as `PASS='mypwd123' make secret`.

Nextcloud:
  rescan            Reload photos in nextcloud.

Default target:
  help              Show this help message.

Env

Copy the .env_template to .env, replacing the environment variables with your own information.

# Domain for public-facing services.
PUBLIC_DOMAIN=mydomain.duckdns.org

# Entrypoint for Tailscale services.
CONTROL_DOMAIN=control.duckdns.org

# ACME email and user.
EMAIL=admin@mail.com
USER=admin

# DNS token for cert mgmt.
DUCKDNS_TOKEN=

# Pre-authentication key that allows tailscale client to auto-register with headscale.
TS_AUTHKEY=

# Nextcloud DB password.
DB_PASSWORD=

Tailscale

Start by creating a headscale user in the control plane.

docker exec headscale headscale users create myuser

Next, run make preauth to create a short-lived preauthentication token. This will be used to connect nodes to the tailnet. Add this to the $TS_AUTHKEY env variable in .env and restart the compose stack. After a few minutes, run make nodes to list connected nodes. The docker tailscale node should have automatically connected!

To add additional nodes, ensure that each device has the tailscale client installed and the tailscaled daemon running. Run tailscale up to connect the machine:

sudo tailscale up \
     --login-server https://hs.${PUBLIC_DOMAIN}:443 \
     --authkey yourauthkeygoeshere \
     --accept-routes \
     --accept-dns=false

Auth

Run PASS='mypwd123' make secret to generate an argon2 hash of your password.

Copy data/authelia/users_database.template.yml to data/authelia/users_database.yml, replacing the password section with the argon2 hash we just generated.

# List of users
users:
  authelia:
    disabled: false
    displayname: "Authelia User"
    # Password is authelia
    password: "$6$rounds=50000$BpLnfgDsc2WD8F2q$Zis.ixdg9s/UOJYrs56b5QEZFiZECu0qZVNsIYxBaNJ7ucIL.nlxVCT5tqh8KHG8X4tlwCFm5r6NTOZZ5qRFN/"
    email: "authelia@authelia.com"
    groups:
      - "admins"
      - "dev"

About

A Portable, Private Cloud

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors