Overview
Kharon is a simple and lightweight Perl RPC library that provides
tight integration with KNC as well as UNIX
domain sockets and forked programs. It provides an OO model which
can be conceptualised as remotely calling an object. It is mainly
intended at the moment for relatively small client/server applications
written to automate basic procedures. It is used as the basis of
the OSKT Kerberos administrative tools krb5_admin and krb5_keytab.
-
automatic failover,
-
server side redirection,
-
marshalling and unmarshalling of relatively complex
Perl data structures containing an arbitrarily nested
list of scalars, undef, array refs and hash refs, and
-
exception propagation.
In this document, we shall construct a simple example of using
Kharon with KNC to write a very simple Kerberised client/server
application in just a few minutes.
A Simple Example
The Object
In order to start with Kharon programming, the first thing that one wants
to do is construct an object which will perform the functionality that is
expected to live on the server. For our example, let’s consider a simple
service based on the canonical OO tutorial object, the incrementer.
We will add a couple of methods “complicated” and “exception” to
demonstrate slightly more complicated data structures and exception
handling. The use of the object should be relatively self-explanatory.
package Example::Eg1;
use strict;
use warnings;
sub new {
my $self;
$self->{num} = 1;
bless($self);
}
sub inc {
my ($self) = @_;
$self->{num}++;
undef;
}
sub query {
my ($self) = @_;
return $self->{num};
}
sub complicated {
return ("foo", ["bar", "baz", {a=>'b', c=>'d', wow=>undef} ]);
}
sub exception {
die "alkjsnckjsanclkjac";
}
1;
The Program
We can now write a simple program that uses our object:
#!/usr/pkg/bin/perl
use Example::Eg1;
use strict;
use warnings;
my $obj = Example::Eg1->new();
my $ret = $obj->query();
print "$ret\n";
$obj->inc();
$obj->inc();
$obj->inc();
$ret = $obj->query();
print "$ret\n";
$obj->complicated();
$obj->exception();
This program will output:
1
4
alkjsnckjsanclkjac at Example/Eg1.pm line 34.
Making our object into a daemon
So, far, though, we have not yet made a client/server application.
All we have is an object and a program that speaks to it. What we’ll
need to do now is make the object into a server. Here’s how we do it:
#!/usr/pkg/bin/perl
use Example::Eg1;
use Kharon::Protocol::ArrayHash;
use Kharon::ProtocolEngineServer;
use strict;
use warnings;
#
# Instantiate our object:
my $obj = Example::Eg1->new();
#
# Setup Kharon:
my $ahr = Kharon::Protocol::ArrayHash->new(banner => { version => '2.0' } );
my $pes = Kharon::ProtocolEngineServer->new($ahr);
$pes->Connect();
#
# And now ``Run the Object'', exporting the methods inc, query, exception
# and complicated:
$pes->RunObj(object => $obj, cmds => [ qw/inc query exception complicated/ ]);
This is a program that will interact on stdin/stdout in the following
way:
dowdeslx $ ./daemon
220 . {version=2.0}
query
250 . 1
inc
250 . !
inc
250 . !
inc
250 . !
query
250 . 4
complicated
250 - foo
250 . [bar,baz,{wow=!,c=d,a=b}]
quit
220 . bye
As you can see, the protocol looks roughly like SMTP.
Running the daemon via KNC
If you do not have a keytab, you’ll need to get one:
And now that you have a keytab, you are good to go. You can use
KNC to run the daemon. For this example, let’s use port 2666.
$ KRB5_KTNAME=/var/spool/keytabs/elric knc -l 2666 ./daemon
Now, you have a daemon that speaks the protocol that we discussed in
the previous section.
Connecting to the daemon remotely via knc (interactively)
You can further test your daemon by connecting to it remotely:
$ knc elric@stormbringer.imrryr.org 2666
This is a useful thing to remember, because it can be useful when
debugging to connect directly to your server and issue commands to
it and see how it responds.
Making a client module
Now, we need to write a client module, let’s call it Example/Client.pm:
package Example::Client;
use Kharon::Protocol::ArrayHash;
use Kharon::ProtocolEngineClientKnc;
use Kharon::utils qw/mk_array_methods mk_scalar_methods/;
use Example::Eg1;
use strict;
use warnings;
sub new {
my ($isa, @servers) = @_;
my $self;
my $ahr = Kharon::Protocol::ArrayHash->new(banner => {version=>'2.0'});
my $pec = Kharon::ProtocolEngineClientKnc->new($ahr);
$pec->SetServerDefaults({KncService=>'service1', PeerPort=>2666});
$pec->Connect(@servers);
$self->{pec} = $pec;
bless($self, $isa);
}
eval mk_scalar_methods('Example::Eg1', qw/inc query complicated exception/);
1;
Changing our original program to be a client
We now have two objects defined in modules. The first Example/Eg1.pm
provides our functionality and is used by the server side. The second
Example/Client.pm has almost exactly the same interface as Example/Eg1.pm
but when its methods are called it will marshall up the arguments and
send them over the wire to the server which will execute the method and
return the results.
So, we can rewrite our original program just adding:
And then modifying the instantiation of $obj from using Example::Eg1
to Example::Client:
my $obj = Example::Client->new('dowdeslx');
And now we have a Kerberised, mutually authenticated, encrypted,
integral client/server application.
Caveats: What wasn’t properly covered
-
talk about the banner.
-
talk about future extensions.
-
talk more about Client.pm.
-
talk about ACLs, etc.
-
talk about “Kharon::Protocol::foo”
|