Net::Remctl(3pm)
π NAME π
okay so bestie, welcome to the Net::Remctl(3pm) manual β it's coming at you from User Contributed Perl Documentation. Net::Remctl is literally Perl bindings for remctl, aka Kerberos remote command execution. no cap, it's the Perl bestie for talking to remctl. π
β¨ SYNOPSIS β¨
there are two whole vibes here: the simplified form first, and then the full interface for when you want every little bit of control. both are right here, fr fr.
# Simplified form.
use Net::Remctl;
my $result = remctl("hostname", undef, undef, "test", "echo", "Hi");
if ($result->error) {
die "test echo failed with error ", $result->error, "\n";
} else {
warn $result->stderr;
print $result->stdout;
exit $result->status;
}
and now the full interface, because sometimes you want the whole menu, not just the snack:
# Full interface.
use Net::Remctl ();
my $remctl = Net::Remctl->new;
$remctl->open("hostname")
or die "Cannot connect to hostname: ", $remctl->error, "\n";
$remctl->command("test", "echo", "Hi there")
or die "Cannot send command: ", $remctl->error, "\n";
my $output;
do {
$output = $remctl->output;
if ($output->type eq 'output') {
if ($output->stream == 1) {
print $output->data;
} elsif ($output->stream == 2) {
warn $output->data;
}
} elsif ($output->type eq 'error') {
warn $output->error, "\n";
} elsif ($output->type eq 'status') {
exit $output->status;
} else {
die "Unknown output token from library: ", $output->type, "\n";
}
} while ($output->type eq 'output');
$remctl->noop or die "Cannot send NOOP: ", $remctl->error, "\n";
okay bestie, buckle up, because Net::Remctl is the Perl glow-up for the libremctl client library π
remctl is a protocol for remote command execution that uses GSS-API authentication. The specific commands you're actually allowed to run have to be listed in a config file on the remote system, and β this is the iconic part β the remote system can map remctl command names to literally any local command it wants without exposing that mapping to the client. This module implements a remctl client. No cap. π¦
β¨ Simplified Interface β¨
If you just wanna run one single command on a remote system and get the output and exit status back, use the exported remctl() function. So simple, so clean, main character energy fr fr.
remctl(HOSTNAME, PORT, PRINCIPAL, COMMAND, [ARGS, ...])
Runs a command on the remote system and hands you back a Net::Remctl::Result object (more on that below, keep reading bestie π).
HOSTNAMEβ the remote host you're contacting.PORTβ the port of the remoteremctldserver, and it may be0to tell the library to just use the default. The default is: first try4373, the registered remctl port, and then fall back to the legacy4444port if that fails. Lowkey iconic behaviour.PRINCIPALβ the principal of the server to use for authentication. Pass in the empty string to use the default ofhost/HOSTNAME, with the realm determined by domain-realm mapping.- The remaining arguments are the remctl command and the arguments passed to the remote server.
As far as this module is concerned, undef may be passed as PORT and PRINCIPAL and it's the same as 0 and the empty string respectively. But heads up: Perl will warn about passing undef explicitly as a function argument, so like... don't do that unless you want the noise π
The return value is a Net::Remctl::Result object which supports the following methods:
error()β returns the error message from either the remote host or from the local client library (if, for instance, contacting the remote host failed). Returnsundefif there was no error. Checking whethererror()returnsundefis the supported way of determining whether theremctl()call succeeded. That's the vibe check you need, periodt.stdout()β returns the command's standard output, orundefif there was none.stderr()β returns the command's standard error, orundefif there was none.status()β returns the command's exit status.
Each call to remctl() will open a new connection to the remote host and close it after retrieving the results of the command. To maintain a persistent connection, use the full interface described below. It's giving "one and done" vs "let's stay in touch" π
πΈ Full Interface πΈ
The full remctl library interface requires that you do more bookkeeping, ngl, but it provides more flexibility and lets you issue multiple commands on the same persistent connection β provided that the remote server supports protocol version two. If not, the library will transparently fall back to opening a new connection for each command. It's got your back π«Ά
To use the full interface: first create a Net::Remctl object with new(), and then connect() to a remote server. Then, issue a command() and call output() to retrieve output tokens (as Net::Remctl::Output objects) until a status token is received. Destroying the Net::Remctl object will close the connection. Cleanup queen β¨
Methods below are annotated with the version at which they were added. Note that this is the version of the remctl distribution, which only matches the Net::Remctl module version in 3.05 and later. Earlier versions of the module used an independent versioning system that is not documented here, and versions earlier than 3.05 are therefore not suitable for use with "use". Not me warning you about version chaos π
The supported object methods are:
new()β[2.8]Create a newNet::Remctlobject. This doesn't attempt to connect to a host, and hence will only fail (by throwing an exception) if the library cannot allocate memory.error()β[2.8]Retrieves the error message from the last failing operation and returns it as a string.set_ccache(CCACHE)β[3.00]Sets the GSS-API credential cache for outgoing connections toCCACHE, which is normally the path to a Kerberos ticket cache but may have other valid forms depending on the underlying Kerberos implementation in use by GSS-API. This method will affect all subsequentopen()calls on at least the same object, but will have no effect on connections that are already open. Returns true on success and false on failure.
If the remctl client library was built against a Kerberos library and the GSS-API library supported gss_krb5_import_cred, this call affects only this Net::Remctl object. Otherwise, this will affect not only all subsequent open() calls for the same object, but all subsequent remctl connections of any kind from the same process, and even other GSS-API connections from the same process unrelated to remctl. Yeah. Scope creep, literally.
Not all GSS-API implementations support setting the credential cache. If this is not supported, false will be returned.
set_source_ip(SOURCE)β[3.00]Sets the source IP for outgoing connections toSOURCE, which can be either an IPv4 or an IPv6 address (if IPv6 is supported). It must be an IP address, not a host name. This will affect all subsequentopen()calls on the same object, but will have no effect on connections that are already open. Returns true on success and false on failure.set_timeout(TIMEOUT)β[3.01]Sets the timeout for connections and commands toTIMEOUT, which should be an integer number of seconds.TIMEOUTmay be0to clear a timeout that was previously set. All subsequent operations on thisNet::Remctlobject will be subject to this timeout, includingopen()if called prior to callingopen(). Returns true on success and false on failure. Failure is only possible ifTIMEOUTis malformed.
The timeout is a timeout on network activity from the server, not on a complete operation. So, for example, a timeout of ten seconds just requires that the server send some data every ten seconds. If the server sends only tiny amounts of data at a time, the complete operation could take much longer than ten seconds without triggering the timeout. It's giving "still alive, still texting" not "finish already" π
open(HOSTNAME[, PORT[, PRINCIPAL]])β[2.8]Connect toHOSTNAMEon portPORTusingPRINCIPALas the remote server's principal for authentication. IfPORTis omitted or0, use the default (first try4373, the registered remctl port, and fall back to the legacy4444port if that fails). IfPRINCIPALis omitted or the empty string, use the default ofhost/HOSTNAME, with the realm determined by domain-realm mapping. Returns true on success, false on failure. On failure, callerror()to get the failure message.
As far as the module is concerned, undef may be passed as PORT and PRINCIPAL and is the same as 0 and the empty string respectively. However, Perl will warn about passing undef explicitly as a function argument. (Yes, same warning as before. It's consistent. We stan consistency π
)
command(COMMAND[, ARGS, ...])β[2.8]Send the command and arguments to the remote host. The command and the arguments may, under the remctl protocol, contain any character, but be aware that most remctl servers will reject commands or arguments containing ASCII0(NUL), so currently this cannot be used for upload of arbitrary unencoded binary data. Returns true on success (meaning success in sending the command, and implying nothing about the result of the command), false on failure. On failure, callerror()to get the failure message.
Sending it β it worked. "Success" here is just "the send went through", bestie. Don't confuse the two π
output()β[2.8]Returns the next output token from the remote host. The token is returned as aNet::Remctl::Outputobject, which supports the following methods:type()β Returns the type of the output token, which will be one of"output","error","status", or"done". A command will result in either one"error"token or zero or more"output"tokens followed by a"status"token. After either a"error"or"status"token is seen, another command can be issued. If the caller tries to retrieve another output token when it has already consumed all of them for that command, the library will return a"done"token.data()β Returns the contents of the token. This method only makes sense for"output"and"error"tokens; otherwise, it will returnundef. Note that the returned value may contain any character, including ASCII0(NUL). π«£length()β Returns the length of the data in the token. As withdata(), this method only makes sense for the"output"and"error"tokens. It will return0if there is no data or if the data is zero-length.stream()β For an"output"token, returns the stream with which the data is associated. Currently, only two stream values will be used:1, meaning standard output; and2, meaning standard error. The value is undefined for all other output token types.status()β For a"status"token, returns the exit status of the remote command. The value is undefined for all other token types.error()β For an"error"token, returns the remctl error code for the protocol error. The text message will be returned bydata(). The value is undefined for all other token types.
noop()β[3.00]Send a NOOP message to the server and read the reply. This is primarily used to keep a connection to a remctl server alive, such as through a firewall with a session timeout, while waiting to issue further commands. Returns true on success, false on failure. On failure, callerror()to get the failure message.
The NOOP message requires protocol version 3 support in the server, so the caller should be prepared for this function to fail, indicating that the connection could not be kept alive and possibly that it was closed by the server. In this case, the client will need to explicitly reopen the connection with open(). Keep-alive failed, bestie β you're gonna have to re-open it yourself. π§
Note that, due to internal implementation details in the library, theNet::Remctl::Outputobject returned byoutput()will be invalidated by the next call tocommand()oroutput()or by destroying the producingNet::Remctlobject. Therefore, any data in the output token should be processed and stored if needed before making any furtherNet::Remctlmethod calls on the same object.
So like... copy that data out immediately. It's giving limited-edition drop, it's gone the second you look away ππΈ
β¨ COMPATIBILITY β¨
okay so the main object methods up above all got little annotations next to them telling you which version added that interface, right? Well β every method that does not have an annotation has been there since the very first release of this module. OG status. π
Support for the gss_krb5_import_cred method β the one that isolates the changed ticket cache to only this remctl client object β was added in version 3.5.
The default port got switched to the IANA-registered port of 4373 in version 2.11.
And this module? First released with version 2.8. Baby pics. πΌ
π CAVEATS π
bestie listen. this one matters.
If the principal argument to remctl() or remctl_open() is NULL, most GSS-API libraries will canonicalize the host using DNS before deriving the principal name from it. Which means: when you're connecting to a remctl server via a CNAME, remctl() and remctl_open() will normally authenticate using a principal based on the canonical name of the host instead of the host parameter you actually specified. π
And that can cause problems if two consecutive DNS lookups of host return two different results β like, say, with some DNS-based load-balancing systems. Not the vibe.
The canonicalization behavior is controlled by the GSS-API library. With the MIT Kerberos GSS-API library, you can turn canonicalization off by setting "rdns" to false in the [libdefaults] section of krb5.conf. You can also disable it by passing an explicit Kerberos principal name via the principal argument β which will then be used without changes. And if you do want canonicalization, you might wanna canonicalize host yourself before calling remctl() or remctl_open(), so you dodge the whole "multiple DNS calls returning different results" mess. π«
Also, heads up: the default behavior β when the port is not specified β of trying 4373 and falling back to 4444 will be removed in a future version of this module. It's getting replaced by using the"remctl"service in/etc/servicesif set, and then falling back on only 4373. Because 4444 was the poorly-chosen original remctl port and it should be phased out. periodt. π
π NOTES π
The remctl port number, 4373, was derived by tracing the diagonals of a QWERTY keyboard up from the letters "remc" to the number row.
I KNOW. iconic. that's the kind of lore you can't make up. πβ¨
π AUTHOR π
Russ Allbery <eagle@eyrie.org>
π COPYRIGHT AND LICENSE π
Copyright 2020, 2022 Russ Allbery <eagle@eyrie.org>
Copyright 2007-2008, 2011-2014 The Board of Trustees of the Leland Stanford Junior University
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
(yes the all-caps parts are legally required screaming. not me yelling at you fr fr, that's just how warranties work π)
πΈ SEE ALSO πΈ
The current version of this module is available from its web page at <https://www.eyrie.org/~eagle/software/remctl/>. go get it, bestie π¦
and that's a wrap on the whole page!! this one's from perl v5.40.1, dated 2025-04-12, and it lives at Net::Remctl(3pm). mwah ππ β¨
π¦ where this page comes from
- debian trixie: libnet-remctl-perl 3.18-3 Β· MIT
- ubuntu questing: libnet-remctl-perl 3.18-3
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms