Skip to content

Host 2 — Core host (RHEL 9)

Runs ironshep-core: the gRPC/mTLS ingest server that writes to Host 1's database. Also the certificate authority for the whole POC — client certificates for every edge (including Host 3) are minted here.

Substitute: <DB_HOST_IP> (Host 1), <DB_PASSWORD> (from Host 1's setup), <CORE_HOSTNAME> / <THIS_HOST_IP> (this host, however edges will reach it), <EDGE_ID> (Host 3's id, e.g. lab1). Do Host 1 first — this host needs to reach it.


1. Toolchain

No protoc needed — the gRPC contract compiles with a pure-Rust build step.

sudo dnf install -y gcc git
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source "$HOME/.cargo/env"

2. Build

git clone https://gitlab.com/ironshep/ironshep-core.git
cd ironshep-core
cargo test --locked            # 9 unit tests + 2 mTLS integration tests
cargo build --release --locked

--locked builds the exact dependency versions recorded in the committed Cargo.lock, so this host gets what was tested rather than whatever is newest today. If the lockfile and Cargo.toml ever disagree the build fails loudly instead of silently upgrading — which is what you want on a host you are about to deploy from. Versions are inventoried in Code/TECHNOLOGY.md.

3. Generate the POC certificate authority + this host's server cert

List every hostname/IP edges will use to reach this host as SANs:

bash scripts/gen_certs.sh <CORE_HOSTNAME> <THIS_HOST_IP>

Produces certs/ca.key (guard this — it's the whole PKI), certs/ca.crt, certs/core.crt, certs/core.key.

4. Generate a client certificate for each edge

One call per edge, e.g. for Host 3:

bash scripts/gen_certs.sh --edge <EDGE_ID>

Produces certs/edge-<EDGE_ID>.crt and .key. You'll scp ca.crt + these two files to that edge host (Host 3's doc covers the receiving end). Repeat this step for every additional edge you bring online later — the CA from step 3 only needs to run once, ever.

5. Configure

mkdir -p /etc/ironshep/certs
sudo cp certs/ca.crt certs/core.crt certs/core.key /etc/ironshep/certs/
sudo chmod 600 /etc/ironshep/certs/core.key
sudo cp config/core.toml /etc/ironshep/core.toml

Edit /etc/ironshep/core.toml:

[server]
bind = "0.0.0.0:50051"
tls_cert = "/etc/ironshep/certs/core.crt"
tls_key = "/etc/ironshep/certs/core.key"
client_ca = "/etc/ironshep/certs/ca.crt"

[db]
url = "postgres://ironshep:<DB_PASSWORD>@<DB_HOST_IP>:5432/ironshep"
batch_size = 200
flush_ms = 500
queue_capacity = 20000

Prefer keeping the password out of the file? Leave url pointing at 127.0.0.1 (it's overridden anyway) and instead set it via the systemd unit in step 7, using IRONSHEP_DB_URL.

6. Apply the schema (if Host 1 hasn't already) and smoke-test connectivity

sudo useradd --system --home /opt/ironshep-core --shell /sbin/nologin ironshep
sudo mkdir -p /opt/ironshep-core
sudo cp target/release/ironshep-core /opt/ironshep-core/
sudo chown -R ironshep:ironshep /opt/ironshep-core /etc/ironshep

sudo -u ironshep /opt/ironshep-core/ironshep-core --config /etc/ironshep/core.toml --init-db

Expect Database schema created/updated. — this also proves the network path to Host 1 (firewall + pg_hba.conf) works.

7. Install as a service

sudo cp systemd/ironshep-core.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now ironshep-core
sudo systemctl status ironshep-core --no-pager
journalctl -u ironshep-core -f    # Ctrl-C to stop watching

Expect: ironshep-core listening on 0.0.0.0:50051 (gRPC, mTLS required).

If you chose to keep the DB password out of core.toml, uncomment and set this line in /etc/systemd/system/ironshep-core.service before the daemon-reload above:

Environment=IRONSHEP_DB_URL=postgres://ironshep:<DB_PASSWORD>@<DB_HOST_IP>:5432/ironshep

8. Web console — create accounts

Core serves its built-in web UI on port 8080 (config [ui] section). Accounts have one of two roles: admin (full access, including notification setup) and user (everything except notification setup).

# admin (default role):
sudo -u ironshep /opt/ironshep-core/ironshep-core --config /etc/ironshep/core.toml --add-user admin
# a read-mostly operator (can't touch notification settings):
sudo -u ironshep /opt/ironshep-core/ironshep-core --config /etc/ironshep/core.toml --add-user operator --role user

Re-running --add-user for an existing name updates the password and role. Then browse to http://<THIS_HOST_IP>:8080 and sign in.

POC posture: the console is plain HTTP — keep 8080 on a management network, or front it with a TLS reverse proxy, and open the port only to operator workstations:

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="<OPERATOR_IP>/32" port port="8080" protocol="tcp" accept'
sudo firewall-cmd --reload

Notifications (optional). In Settings → Notifications an admin can wire up email (SMTP) and Slack/Teams webhooks, with triggers for new signals, high-severity events, and edge outages. These make outbound connections from this host — SMTP to your mail server (typically tcp/587 or /465) and HTTPS to Slack/Teams — so allow that egress if your firewall restricts outbound. Secrets are stored in the core DB (plaintext at rest, like the DB password) and never shown back in the browser.

9. Firewall — open gRPC to edges

Restrict to known edge IPs where you can; otherwise open it to your site's subnet:

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="<EDGE_HOST_IP>/32" port port="50051" protocol="tcp" accept'
sudo firewall-cmd --reload

Repeat the rich-rule line per edge host as you add them. SELinux stays Enforcing.

Done

Copy these three files to Host 3 (the edge host) via scp, not chat/email:

scp certs/ca.crt certs/edge-<EDGE_ID>.crt certs/edge-<EDGE_ID>.key \
    <edge-host-user>@<EDGE_HOST_IP>:~/

Troubleshooting

Symptom Likely cause
--init-db hangs/fails to connect Host 1 firewall or pg_hba.conf not yet configured for this host's IP
service fails to start: cert read error paths in core.toml don't match /etc/ironshep/certs/, or wrong permissions (ironshep user can't read .key)
edge later reports TLS handshake failure the edge's endpoint name/IP wasn't included as a SAN in step 3 — regenerate the server cert with gen_certs.sh, or set tls_server_name on the edge side to a SAN that was included