Server : Apache System : Linux server1.cgrithy.com 3.10.0-1160.95.1.el7.x86_64 #1 SMP Mon Jul 24 13:59:37 UTC 2023 x86_64 User : nobody ( 99) PHP Version : 8.1.23 Disable Function : NONE Directory : /scripts/ |
#!/usr/local/cpanel/3rdparty/bin/perl # cpanel - bin/rsync-user-homedir.pl Copyright 2022 cPanel, L.L.C. # All rights reserved. # copyright@cpanel.net http://cpanel.net # This code is subject to the cPanel license. Unauthorized copying is prohibited package scripts::rsync_user_homedir; use strict; use warnings; use Cpanel::BinCheck::Lite (); use autodie; =encoding utf-8 =head1 DESCRIPTION This script exemplifies the client-side functionality of end classes of L<Cpanel::Server::CpXfer::Base::acctxferrsync>. =head1 USAGE (INTERNAL TO L<rsync(1)>) B<IMPORTANT:> You aren’t meant to call this script directly; L<rsync(1)> calls it via its C<--rsh> parameter. For the sake of completeness, though, this script’s arguments are: For cPanel: rsync-user-homedir.pl --cpanel [--insecure] <--apitoken-fd=# | --apitoken=...> <hostname> <username> <rsyncargs..> For WHM: rsync-user-homedir.pl --whm [--insecure] <--apitoken-fd=# | --apitoken=...> --homedir-user=<username> <hostname> <token_username> <rsyncargs..> Note that the API token, for security purposes, B<MUST> be passed via file descriptor in production code. Also note that, in both cases, all positional parameters other than C<E<lt>hostnameE<gt>> will come from rsync. =head1 REAL-USAGE EXAMPLES Below are examples of how you’ll actually give this script to L<rsync(1)>. The following … rsync --archive --rsh '/usr/local/cpanel/scripts/rsync-user-homedir.pl --cpanel --apitoken=OF24KGWQS9Q8SWI6Y5PNJJHLMBY3UX6Z localhost' theusername: /where/to … will back up C<theusername>’s home directory contents to F</where/to>. It will authenticate to C<localhost> via the given cPanel API token. Here is an example of WHM usage: rsync --archive --rsh '/usr/local/cpanel/scripts/rsync-user-homedir.pl --whm --homedir-user=theuser --apitoken-fd=5 example.com' superman: /where/to Note the additional C<--homedir-user>; this is the user whose home directory will be backed up. C<superman>’s given WHM API token is how we will authenticate to WHM on the remote server C<example.com>. The C<--insecure> flag tolerates TLS handshake errors (e.g., from self-signed or invalid certificates). As in L<curl(1)>, you may alias this flag as C<-k>. =head1 SEE ALSO F<t/support/rsync_cpsrvd_client.pl> exemplifies how to do this via the WHM endpoint exclusively and assumes that there is a root WHM access hash. (WHM access hashes were a forerunner to API tokens.) =cut #---------------------------------------------------------------------- use parent qw( Cpanel::HelpfulScript ); use IO::Socket::SSL (); use Cpanel::JSON::XS (); use Cpanel::HTTP::QueryString (); use Cpanel::Services::Ports (); # It’s curious that CPAN doesn’t seem to have anything to do this. use Cpanel::Interconnect (); use constant _OPTIONS => ( 'cpanel', 'whm|whostmgr', 'insecure|k', 'homedir-user=s', 'apitoken-fd=i', 'apitoken=s', ); use constant _ACCEPT_UNNAMED => 1; Cpanel::BinCheck::Lite::check_argv(); __PACKAGE__->new(@ARGV)->run() if !caller; sub run { my ($self) = @_; my ( $hostname, $username, @rsync_cmd ) = $self->getopt_unnamed(); if ( !$hostname ) { die $self->help('Need a hostname and a username!'); } elsif ( !$username ) { die $self->help('Need a username for the API token!'); } elsif ( !@rsync_cmd ) { die $self->help('Need an rsync command/arguments!'); } my ( $app, $port, $remote_user ); if ( $self->getopt('cpanel') ) { if ( $self->getopt('whm') ) { die $self->help('Give either --cpanel or --whm, not both.'); } $app = 'cpanel'; $port = $Cpanel::Services::Ports::SERVICE{'cpanels'}; } elsif ( $self->getopt('whm') ) { $app = 'whm'; $port = $Cpanel::Services::Ports::SERVICE{'whostmgrs'}; $remote_user = $self->getopt('homedir-user') // do { die $self->help('WHM requires a --homedir-user.'); }; } else { die $self->help('Give --cpanel or --whm.'); } my $token; if ( my $fd = $self->getopt('apitoken-fd') ) { if ( length $self->getopt('apitoken') ) { die $self->help('Do not give --apitoken with --apitoken-fd.'); } open my $rfh, '<&=', $fd or die "open(<&=$fd): $!"; local $/; $token = readline $rfh; } else { $token = $self->getopt('apitoken'); if ( length $token ) { warn "XXX SECURITY ALERT: Using API token from the command line.\n"; } } if ( !length $token ) { die $self->help('Give an API token.'); } my $query = Cpanel::HTTP::QueryString::make_query_string( ( $remote_user ? ( username => $remote_user ) : () ), rsync_arguments => Cpanel::JSON::XS::encode_json( \@rsync_cmd ), ); my $path_query = "/cpxfer/acctxferrsync?$query"; my $req = join( "\r\n", "GET $path_query HTTP/1.0", "Authorization: $app $username:$token", q<>, q<>, ); my $cl = _get_socket( $hostname, $port, ( $self->getopt('insecure') ? ( SSL_verify_mode => 0 ) : () ), ); syswrite( $cl, $req ); my $response = do { local $/ = "\r\n\r\n"; <$cl> }; if ( $response !~ m{^HTTP.*? 2} ) { print STDERR "*** ERROR ***:\n$response"; local $/; die readline($cl); } Cpanel::Interconnect->new( 'handles' => [ $cl, [ \*STDIN, \*STDOUT ] ] )->connect(); return; } sub _get_socket { my ( $hostname, $port, @extra_args ) = @_; return IO::Socket::SSL->new( PeerHost => $hostname, PeerPort => $port, @extra_args, ) || die "cannot connect: $IO::Socket::SSL::SSL_ERROR: $! ($@)"; } 1;