Skip to content
Back to skills

Rex

BSecurity

Use when writing or debugging a Rexfile or Rex task — connection types, Rex::Commands, which commands need SFTP, or the LibSSH backend.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 4, 2026
developmentgoshellnodedebuggingbackend

Security analysis

B75/100
  • criticalAccesses sensitive system or user directories
  • criticalReads or references SSH private keys

Pro shows the line behind each finding and how to fix it

Scanned October 4, 2026

npx -y skills add Getty/skills --skill rex --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Rex?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Rex
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/getty-rex/badge)](https://www.skillsdirectory.com/skills/getty-rex)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: rex
description: Use when writing or debugging a Rexfile or Rex task — connection types, Rex::Commands, which commands need SFTP, or the LibSSH backend.
user-invocable: false
allowed-tools: Read, Grep, Glob
model: sonnet
---

## Rex Basics

Rex is a Perl automation framework. Entry point is a `Rexfile` (or any `.pm`/`.pl` via `-f`).

```perl
use Rex -feature => ['1.4'];   # ALWAYS include feature flag
use Rex::Commands::Run;        # explicit imports
use Rex::Commands::File;

desc "Short description shown in rex --tasks";
task "taskname", sub {
  run "uname -r";
};
```

Run with: `rex -f Rexfile -H host taskname`

## Connection Types

```perl
# Default (Net::SSH2 / libssh2) — supports SFTP
set connection => 'SSH';

# System OpenSSH binary (ControlMaster) — NO SFTP unless server has subsystem
set connection => 'OpenSSH';

# LibSSH (Net::LibSSH / libssh) — no SFTP required
set connection => 'LibSSH';   # use this for Hetzner dedicated and SFTP-less hosts

# Local — no SSH at all
set connection => 'Local';
```

**Critical:** `set connection => 'OpenSSH'` causes `Rex::Interface::Fs::OpenSSH` to call
`Rex::get_sftp()` for EVERY file operation. If the server has no SFTP subsystem,
`get_sftp()` returns undef and `$sftp->stat(...)` crashes:
```
Can't call method "stat" on an undefined value at Rex/Interface/Fs/OpenSSH.pm line 82
```
**Solution:** use `set connection => 'LibSSH'` (from the `Rex-LibSSH` distribution).

## Rex::Commands — What Needs SFTP

| Command | SFTP required? |
|---------|---------------|
| `run "cmd"` | No — exec channel |
| `file "/path", content => "..."` | SSH/OpenSSH only — LibSSH works without SFTP |
| `file "/dir", ensure => 'directory'` | SSH/OpenSSH only — LibSSH works without SFTP |
| `delete_lines_matching "/file", matching => qr/x/` | SSH/OpenSSH only |
| `host_entry ...` | SSH/OpenSSH only |
| `pkg ["curl"], ensure => "present"` | No |
| `can_run("cmd")` | No |
| `operating_system()` | No |
| `is_debian()`, `is_redhat()`, `is_suse()` | No |

## Rex::Commands::Run

```perl
use Rex::Commands::Run;

my $out = run "uname -r";               # returns stdout
my $out = run "cmd", auto_die => 1;     # croaks on non-zero exit
my $out = run "cmd", auto_die => 0;     # never croaks
# Check exit code:
run "cmd", auto_die => 0;
if ($? != 0) { ... }

# Array form (no shell injection):
run 'sh', ['-c', 'echo hi'], auto_die => 0;

can_run("nvidia-smi");   # returns path if found, undef if not
```

## Rex::Commands::Gather

```perl
use Rex::Commands::Gather;

my $os      = operating_system();         # 'Debian', 'Ubuntu', 'CentOS', ...
my $version = operating_system_version(); # '12', '22.04', '8', ...
is_debian();    # true for Debian + Ubuntu
is_redhat();    # true for RHEL/Rocky/Alma/CentOS
is_suse();      # true for openSUSE/SLES
```

## Rex::Interface Architecture

```
Rex::Interface::Connection::LibSSH  — wraps Net::LibSSH (libssh) — no SFTP needed
Rex::Interface::Connection::OpenSSH — wraps system ssh binary (ControlMaster)
Rex::Interface::Connection::SSH     — wraps Net::SSH2 / libssh2
Rex::Interface::Fs::LibSSH          — file ops via exec channels — no SFTP
Rex::Interface::Fs::OpenSSH         — file ops via get_sftp() → CRASHES if undef
Rex::Interface::Fs::SSH             — same problem as OpenSSH
```

## LibSSH Backend (Rex::LibSSH)

From the `Rex-LibSSH` distribution. Use for any host without SFTP subsystem.

```perl
use Rex -feature => ['1.4'];
use Rex::LibSSH;

set connection => 'LibSSH';

# All Rex file operations now work without SFTP:
file '/etc/hostname', content => "myhost\n";
delete_lines_matching '/etc/fstab', matching => qr/\sswap\s/;
host_entry 'myhost.internal', ip => '127.0.1.1', aliases => ['myhost'];
```

Authentication:
```perl
Rex::Config->set_private_key('/root/.ssh/id_ed25519');
Rex::Config->set_public_key('/root/.ssh/id_ed25519.pub');
```

Host key checking is disabled by default (`strict_hostkeycheck => 0`).

## Getty's Rex distributions (CPAN)

### Rex::GPU (`Rex-GPU`)

```perl
use Rex::GPU;

my $gpus = gpu_detect();
# { nvidia => [{name => "RTX 4090", compute => 1}], amd => [...] }

gpu_setup(containerd_config => 'rke2');  # detect + install + configure
# containerd_config: 'rke2', 'k3s', 'containerd', 'none'
```

Sub-modules:
- `Rex::GPU::Detect` — PCI class code based detection
- `Rex::GPU::NVIDIA` — driver install (Debian/Ubuntu/RHEL/SUSE), container toolkit, containerd config

Requires `Rex::LibSSH` connection for SFTP-less hosts. Dies with a helpful message if
neither LibSSH nor a working SFTP connection is present.

### Rex::Rancher (`Rex-Rancher`)

```perl
use Rex::Rancher::Node;
prepare_node(hostname => 'h', domain => 'd', timezone => 'UTC');

use Rex::Rancher::Server;
install_server(distribution => 'rke2', token => '...', tls_san => ['ip']);

use Rex::Rancher::Cilium;
install_cilium(distribution => 'rke2');
```

## Common Gotchas

1. **SFTP-less hosts** — use `set connection => 'LibSSH'` (from `Rex-LibSSH`).
   Never use `set connection => 'OpenSSH'` for hosts without SFTP.

2. **`<> line N` in error messages** is Perl's `$.` tracker from `<ARGV>`, not a
   source line number. Misleading — the actual crash is in the C stack.

3. **`$?` after `run`** — Rex sets `$?` to the remote exit code (shifted left 8).
   Check with `$? != 0` or use `auto_die => 1`.

4. **`use Rex -feature => ['1.4']`** — without this, many modern Rex behaviors
   are disabled. Always include it.

5. **`Rex::Exporter` not `Exporter`** — Rex modules use `require Rex::Exporter;
   use base qw(Rex::Exporter); use vars qw(@EXPORT);` pattern, not standard
   `Exporter`.

6. **OpenSSH ControlMaster** — `set connection => 'OpenSSH'` uses SSH
   multiplexing. The master process stays alive between task calls. Clean up
   with `ssh -O exit` if connection gets stuck.

7. **`operating_system_version()`** returns a string like `'22.04'` or `'12'`.
   Use `int(operating_system_version())` for major version integer.

8. **`pkg \@array, ensure => "present"`** — pass arrayref, not list.
   `pkg ["curl", "wget"], ensure => "present"` is correct.

9. **`auto_die => 0` is not the default** — `run "cmd"` without `auto_die`
   uses Rex's global `set_fail_flag` setting. Always be explicit in library
   code.

10. **Rex task names must not clash with imported functions** — `task 'foo'`
    overwrites imported `foo()` in the namespace. Use distinct task names.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…