Open Source Kerberos Tooling
Overview
Source
KNC
Kharon
krb5_admin
krb5_keytab
k5ping
lnetd
prefork
Table of Contents

A Simple Example

In this section, 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.

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::Implementation;

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::Implementation;

use strict;
use warnings;

my $obj = Example::Implementation->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/Impementation.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::Implementation;

use Kharon::Protocol::ArrayHash;
use Kharon::Engine::Server;

use strict;
use warnings;

#
# Instantiate our object:

my $obj = Example::Implementation->new();

#
# Setup Kharon:

my $ahr = Kharon::Protocol::ArrayHash->new(banner => { version => '2.0' } );
my $pes = Kharon::Engine::Server->new(protocols => [$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:

$ krb5_keytab

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 base qw(Kharon::Class::Client);

our @KHARON_RW_SC_EXPORT = qw/inc query exception/;
our @KHARON_RW_AC_EXPORT = qw/complicated/;

use Kharon::Protocol::ArrayHash;
use Kharon::Engine::Client::Knc;

use strict;
use warnings;

sub new {
        my ($isa, @servers) = @_;
        my $self;

        my $ahr = Kharon::Protocol::ArrayHash->new(banner => {version=>'2.0'});
        my $pec = Kharon::Engine::Client::Knc->new(protocols => [$ahr]);

        $pec->SetServerDefaults({KncService=>'service1', PeerPort=>2666});
        $pec->Connect(@servers);

        $self->{pec} = $pec;

        bless($self, $isa);
}

1;

Changing our original program to be a client

We now have two objects defined in modules. The first Example/Impementation.pm provides our functionality and is used by the server side. The second Example/Client.pm has almost exactly the same interface as Example/Impementation.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:

use Example::Client;

And then modifying the instantiation of $obj from using Example::Impementation to Example::Client.

my $obj = Example::Client->new('dowdeslx');

And now we have a Kerberised, mutually authenticated, encrypted, integral client/server application.

Improving Our Simple Example

So, we note that in our simple example, we duplicate the specification of the exported methods in two places: the daemon and the client module. This is necessary to export Perl objects that have not been designed with Kharon in mind but we can get around it if we’re writing modules specifically for Kharon.

What we need to do to remove this redundancy is to define a superclass for Example::Implementation and Example::Client which we will call Example. So, we can write Example.pm as:

package Example;

our @KHARON_RW_SC_EXPORT = qw/inc query exception/;
our @KHARON_RW_AC_EXPORT = qw/complicated/;

And change Example/Client.pm and Example/Implementation.pm to inherit from it. The @KHARON_* variables can then be removed from Example/Client.pm and we can eliminate the passing of cmds to RunObj in daemon.

The new files look like this…

Example/Implementation.pm

package Example::Implementation;

use base qw(Example);

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;

Example/Client.pm

package Example::Client;

use base qw(Example Kharon::Class::Client);

use Kharon::Protocol::ArrayHash;
use Kharon::Engine::Client::Knc;

use strict;
use warnings;

sub new {
        my ($isa, @servers) = @_;
        my $self;

        my $ahr = Kharon::Protocol::ArrayHash->new(banner => {version=>'2.0'});
        my $pec = Kharon::Engine::Client::Knc->new(protocols => [$ahr]);

        $pec->SetServerDefaults({KncService=>'service1', PeerPort=>2666});
        $pec->Connect(@servers);

        $self->{pec} = $pec;

        bless($self, $isa);
}

1;

daemon

#!/usr/pkg/bin/perl

use Example::Implementation;

use Kharon::Protocol::ArrayHash;
use Kharon::Engine::Server;

use strict;
use warnings;

#
# Instantiate our object:

my $obj = Example::Implementation->new();

#
# Setup Kharon:

my $ahr = Kharon::Protocol::ArrayHash->new(banner => { version => '2.0' } );
my $pes = Kharon::Engine::Server->new(protocols => [$ahr]);
$pes->Connect();

#
# And now ``Run the Object'', exporting the methods inc, query, exception
# and complicated:

$pes->RunObj(object => $obj);