I was reading
System Administration Guide: Security Services: Kerberos Service book,
The Kerberos Network Authentication Service (V5) RFC and several web resources like
Kerberos: The Network Authentication Protocol and
Wikipedia: Kerberos protocol. Well, general docs are too general, specific ones too detailed, so couple of experiments are actually point out "How it works" and "How to configure it on Solaris OS" things, which may be useful for quick start with Kerberos ...
1. How it works
Conceptually, the scenario is following:
- A client sends a request for Ticket Granting Ticket (TGT) to the Authentication Server (AS).
TGT is a ticket for further requests of particular Service tickets from Ticket Granting Server (TGS).
Client -- [ Client principal ] --> AS
- AS sends back a TGT encrypted by TGS key (in most cases AS and TGS is one service, so AS knows TGS key), and some Session key generated and encrypted by a Client's key (AS has Client's key in principal's database).
Client <-- [ TGT (encrypted by TGS key) ] -- AS
[ | +------------------+ ]
[ +--| Session key | ]
[ | Client principal | ]
[ +------------------+ ]
[ Session key (encrypted by ]
[ Client's key) ]
TGT contains "Client principal", so TGT issued for one client is not valid for the other. TGT encryption ensures its integrity.
- Client then caches encrypted TGT and a Session key.
A fact that client can decrypt a Session key proves its identity to TGS.
Now request a Service ticket from TGS ...
Client -- [ TGT (encrypted by TGS key) ] --> TGS
Cache: [ | +------------------+ ]
[ encrypted TGT ] [ +--| Session key | ]
[ Session key ] [ | Client principal | ]
[ +------------------+ ]
[ Service principal ]
[ Authenticator (encrypted by ]
[ Session key) ]
Authenticator in other words is an identification record, which can be verified by a message receiver.
- TGS opens TGT, gets Session key and verifies Authenticator.
If everything is ok, TGS respond with a Service ticket encrypted by Service's key (TGS has Service's key in principal's database), and some new Session key generated and encrypted by a Session key from TGT.
Client <-- [ Service ticket (encrypted by ] -- TGS
Cache: [ | Service's key) ]
[ encrypted TGT ] [ | +----------------------+ ]
[ Session key ] [ +--| Service Session key | ]
[ | Client principal | ]
[ +----------------------+ ]
[ Service Session key (encrypted by ]
[ Session key) ]
- Now client can cache Service ticket together with Service Session key and actually talk to a service.
Client -- [ Service ticket (encrypted by ] --> Service
Cache: [ | Service's key) ]
[ encrypted TGT ] [ | +---------------------+ ]
[ Session key ] [ +--| Service Session key | ]
[ encrypted Service ] [ | Client principal | ]
[ ticket ] [ +---------------------+ ]
[ Service Session key ] [ Authenticator (encrypted by a ]
[ Service Session key) ]
- Service like TSG may examine Authenticator and decide to permit or deny.
The summary is:
- Once client specifies the password, he gets TGT from AS and can use it rather than password later.
(whether AS is real or impersonated not known yet, but anyway client doesn't provide any passwords to it)
- Once client has TGT, he can prove own identity to TGS and get a number of various Service tickets.
(whether TGS is real or impersonated not known yet as well)
- Once client has a Service ticket, he can prove own identity to a Service,
and both can be sure that nobody between them due to message integrity provided by the encryption.
(conversely, a Service can be impersonated to client, it can either somehow dig up a Service's key or impersonate AS/TGS as well -- both ways are quite difficult ...)
2. How to configure it on Solaris OS
- First of all, you need a machine which will be a Key Distribution Center (KDC), more precisely a Master KDC.
KDC basically issues various Kerberos tickets (TGT and Service tickets) and runs the following services:
-
krb5kdc - KDC service - krb5kdc(1M)
-
kadmin - Kerberos administration service - kadmind(1M)
-
ktkt_warn - Kerberos warning messages service - ktkt_warnd(1M)
So on this machine:
- Edit
/etc/krb5/krb5.conf, refer to krb5.conf(4)
Every machine in Kerberos environment must have this config, it tells what is default realm for Kerberos libraries, and for each realm it tells where is KDC (master and its slaves) and administration server (admin server typically is on the same machine as master KDC).
Also it tells how to map hosts and domains (domain names are precede with a dot ".") to realms.
For example:
[libdefaults]
default_realm = TEST.ORG
[realms]
TEST.ORG = {
kdc = ironus.test.org
admin_server = ironus.test.org
}
[domain_realm]
.test.org = TEST.ORG
[logging]
default = FILE:/var/krb5/kdc.log
kdc = FILE:/var/krb5/kdc.log
kdc_rotate = {
period = 1d
versions = 10
}
[appdefaults]
kinit = {
renewable = true
forwardable = true
}
IMPORTANT:
Of course, all machines must resolve all hostnames declared,
but additionally in order to get kadmin and other services working, the very FIRST entry of "hosts" database's record, which corresponds to a hostname from admin_server option, must be the same on all machines (independently from a naming service used, whether it's ldap or nis or dns or just files).
I.e. kerberized services use canonical representation of hostname provided by a naming service.
In other words, if you have this on KDC:
# getent hosts ironus.test.org
129.157.18.54 ironus.test.org ironus
but that on a client machine:
# getent hosts ironus.test.org
129.157.18.54 ironus ironus.test.org
then most likely you'll get SERVER_NOT_FOUND on KDC and the following on a client:
Required KADM5 principal missing while initializing kadmin interface
It's because principals used by Kerberos admin are kadmin/<admin host>@<REALM> and changepw/<admin host>@<REALM>
where <admin host> comes from the first entry queried from "hosts" database by the key taken from admin_server option of krb5.conf file (experimentally confirmed).
- Edit
/etc/krb5/kdc.conf, refer to kdc.conf(4)
That's KDC configuration (pretty self-explanatory), which defines realms to be served by this KDC, and for each realm defines where the principal's database is.
Options admin_keytab, acl_file and kadmind_port are related to the administration service, will consider them later (at steps 6 and 7).
[kdcdefaults]
kdc_ports = 88,750
[realms]
TEST.ORG = {
profile = /etc/krb5/krb5.conf
database_name = /var/krb5/principal
admin_keytab = /etc/krb5/kadm5.keytab
acl_file = /etc/krb5/kadm5.acl
kadmind_port = 749
max_life = 8h 0m 0s
max_renewable_life = 7d 0h 0m 0s
default_principal_flags = +preauth
}
- Make principal's database by using
kdb5_util(1M):
# kdb5_util create -s
Initializing database '/var/krb5/principal' for realm 'TEST.ORG',
master key name 'K/M@TEST.ORG'
You will be prompted for the database Master Password.
It is important that you NOT FORGET this password.
Enter KDC database master key::
Re-enter KDC database master key to verify::
Basically kdb5_util above creates several files in /var/krb5/, including a "stash" file with a master key of /var/krb5/principal database (in our example it is /var/krb5/.k5.TEST.ORG). Master key is obviously needed for KDC in order to access the database.
Also, by default kdb5_util creates several principals:
# kadmin.local
Authenticating as principal root/admin@TEST.ORG with password.
kadmin.local: listprincs
K/M@TEST.ORG
changepw/ironus.test.org@TEST.ORG
kadmin/admin@TEST.ORG
kadmin/changepw@TEST.ORG
kadmin/history@TEST.ORG
kadmin/ironus.test.org@TEST.ORG
krbtgt/TEST.ORG@TEST.ORG
kadmin.local(1M) doesn't need KDC service running, it just edit principal's database directly.
Double-check admin principal's (kadmin and changepw), they should correspond to KDC host name.
Command "kdb5_util destroy" removes principal's database.
- Ok, we're ready to start KDC service.
# svcadm -v enable -s krb5kdc
svc:/network/security/krb5kdc:default enabled.
IMPORTANT:
krb5kdc depends on dns/client, which in turn requires presence of /etc/resolv.conf file,
well dns/client doesn't spawn any processes, just checks existence of /etc/resolv.conf file:
# svccfg -s krb5kdc listprop | grep ^dns
dns dependency
dns/entities fmri svc:/network/dns/client
dns/grouping astring require_all
# svccfg -s dns/client listprop | grep ^config_data
config_data dependency
config_data/entities fmri file://localhost/etc/resolv.conf
config_data/grouping astring require_all
# svccfg -s dns/client listprop | grep ^start/exec
start/exec astring :true
Anyway, even if you're not using DNS, you still need to sort out this dependence, so if you get your krb5kdc offline because of resolv.conf, you can simply create an empty file:
# svcadm -v enable -s krb5kdc
svc:/network/security/krb5kdc:default enabled.
svcadm: Instance "svc:/network/security/krb5kdc:default"
has unsatisfied dependencies.
# svcs -x krb5kdc
svc:/network/security/krb5kdc:default
State: offline
Reason: Service svc:/network/dns/client:default is disabled.
# svcadm -v enable -s dns/client
svc:/network/dns/client:default enabled.
svcadm: Instance "svc:/network/dns/client:default"
has unsatisfied dependencies.
# svcs -x dns/client
svc:/network/dns/client:default (DNS resolver)
State: offline
Reason: Dependency file://localhost/etc/resolv.conf is absent.
# touch /etc/resolv.conf
# svcadm -v disable -s dns/client
# svcadm -v enable -s dns/client
Finally, krb5kdc should be running ...
# svcs -p krb5kdc
STATE STIME FMRI
online 11:40:57 svc:/network/security/krb5kdc:default
11:40:57 10701 krb5kdc
Also, check ktkt_warn service, which is by the way handled by inetd(1M).
If ktkt_warn is not enabled, kinit may get the following warning:
RPC: Program not registered
kinit: no ktkt_warnd warning possible
- A quick test of KDC ...
Add "admin" principal to the database and kinit(1), i.e. obtain and cache Kerberos ticket-granting ticket, for this principal:
# kadmin.local
kadmin.local: addprinc admin
WARNING: no policy specified for admin@TEST.ORG; defaulting to no policy
Enter password for principal "admin@TEST.ORG": :
Re-enter password for principal "admin@TEST.ORG": :
Principal "admin@TEST.ORG" created.
kadmin.local: q
# kinit admin
Password for admin@TEST.ORG:
# klist
Ticket cache: FILE:/tmp/krb5cc_0
Default principal: admin@TEST.ORG
Valid starting Expires Service principal
08/08/06 12:15:02 08/08/06 20:15:02 krbtgt/TEST.ORG@TEST.ORG
renew until 08/15/06 12:15:02
Looks good. Note that kdestroy(1) clears ticket cache.
"WARNING: no policy specified" is ok for now, a "policy" there means just a password policy.
- Now you probably want to start the administration server, so you need to create a "keytab" for it.
Well, kadmind(1M) is like a normal Kerberos service, therefore:
- it must have one or more service principals,
- and besides, log-term Service's keys, corresponding to these principals, somehow must be shared with TGS, which is
krb5kdc(1M) in our case, refer to How it works section.
Such long-term key sharing is implemented in the way that on KDC side keys are stored in principal's database, and on Service's side keys are in keytab files. According to man of kadmind(1M) it uses kadmin/<admin host> and changepw/<admin host> principals. Basically kdb5_util generates them, so we can "export" these principals to a admin server's keytab. A keytab name is specified by admin_keytab option in /etc/krb5/kdc.conf file, in our case it is /etc/krb5/kadm5.keytab, so:
# kadmin.local
Authenticating as principal root/admin@TEST.ORG with password.
kadmin.local: ktadd -k /etc/krb5/kadm5.keytab
kadmin/ironus.test.org changepw/ironus.test.org
Note that ktadd not just exports keys, it generates new keys.
If you do getprinc in kadmin.local before and after ktadd, you'll see that keys have different version numbers (vno).
You may use klist(1) or ktutil(1) to inspect a keytab:
# klist -k /etc/krb5/kadm5.keytab
Keytab name: FILE:/etc/krb5/kadm5.keytab
KVNO Principal
---- ---------------------------------
3 changepw/ironus.test.org@TEST.ORG
3 changepw/ironus.test.org@TEST.ORG
3 changepw/ironus.test.org@TEST.ORG
3 changepw/ironus.test.org@TEST.ORG
3 kadmin/ironus.test.org@TEST.ORG
3 kadmin/ironus.test.org@TEST.ORG
3 kadmin/ironus.test.org@TEST.ORG
3 kadmin/ironus.test.org@TEST.ORG
Obviously key version numbers must be the same in KDC's principal database and in Service's keytab.
- Set Access Control Level (ACL) for admin server.
Edit /etc/krb5/kadm5.acl (we specified this name in kdc.conf file, acl_file option),
for instance give all permissions to "admin" principal (read more about it in kadm5.acl(4) manual):
admin@TEST.ORG *
- Start administration service ...
# svcadm -v enable -s kadmin
svc:/network/security/kadmin:default enabled.
IMPORTANT:
The /var/krb5/rcache and /var/krb5/rcache/root directories MUST exist !
kadmind creates some files e.g. kadmin_0, changepw_0 there ...
(I guess if KDC stuff will run not under root it may change accordingly)
# ls -lR /var/krb5/rcache/
/var/krb5/rcache/:
total 2
drwx------ 2 root sys 512 Aug 8 20:25 root
/var/krb5/rcache/root:
total 4
-rw------- 1 root sys 71 Aug 9 16:09 changepw_0
-rw------- 1 root sys 1005 Aug 9 15:32 kadmin_0
Without even /var/krb5/rcache/root the kadmind fails with:
kadmind[11197](Error): Unable to set RPCSEC_GSS service names
and so kadmin service does:
Restarting too quickly, changing state to maintenance
a client in turn has:
GSS-API (or Kerberos) error while initializing kadmin interface
just nothing works ...
- A quick test of admin ...
# kadmin -p admin
Authenticating as principal admin@TEST.ORG with password.
Password for admin@TEST.ORG:
kadmin: listprincs
K/M@TEST.ORG
...
# kpasswd admin
kpasswd: Changing password for admin.
Old password::
New password::
New password (again)::
Kerberos password changed.
Here we go, master KDC is up.
Who likes GUI may run gkadmin application ;-)
- Kerberos client machines.
Before going any further, make sure ALL machines in kerberos environment have:
- Proper
/etc/krb5/krb5.conf file, it should be the same as one on KDC, see krb5.conf description.
- Valid host names specified in
kdc and admin_server options, see important note.
- Clock is close to KDC's clock (usually difference should not be bigger than 5 minutes).
Actually you can start time service on KDC:
[KDC]# svcadm -v enable -s time:stream time:dgram
svc:/network/time:stream enabled.
svc:/network/time:dgram enabled.
and then sync other's clock by rdate(1M), e.g.
[kclient]# rdate ironus.test.org
So, if points above are true, then at least kinit, kadmin, and etc. should work on client.
- Some "kerberized" services setup.
- telnet
We're on telnet server machine, which is presumably kerberos configured,
so need to create host/<telnet server hostname> principal on KDC,
and put its keys to a local keytab (by default it's /etc/krb5/krb5.keytab)
as far as we want to share long-term Service's key between in.telnetd(1M) and KDC:
[telnetd]# kadmin -p admin
Authenticating as principal admin@TEST.ORG with password.
Password for admin@TEST.ORG:
kadmin: addprinc -randkey host/frostina.test.org
Principal "host/frostina.test.org@TEST.ORG" created.
kadmin: ktadd host/frostina.test.org
kadmin: Bad encryption type while changing host/frostina.test.org's key
Hmmm, looks like we're requesting encryption type which is not supported by KDC.
Try to explicitly generate just DES and Triple DES keys, basically used by a telnet:
kadmin: ktadd -e des3-hmac-sha1:normal,des-cbc-md5:normal
host/frostina.test.org
Entry for principal host/frostina.test.org with kvno 5,
encryption type Triple DES cbc mode with HMAC/sha1 added to
keytab WRFILE:/etc/krb5/krb5.keytab.
Entry for principal host/frostina.test.org with kvno 5,
encryption type DES cbc mode with RSA-MD5 added to
keytab WRFILE:/etc/krb5/krb5.keytab.
Refer to kadmin(1M) for the ktadd subcommand description,
and for a list of permitted encryption types see krb5.conf(4): permitted_enctypes section.
Double-check local keytab, particularly its keys:
# klist -ke
Keytab name: FILE:/etc/krb5/krb5.keytab
KVNO Principal
---- --------------------------------------------------------------------
5 host/frostina.test.org@TEST.ORG (Triple DES cbc mode with HMAC/sha1)
5 host/frostina.test.org@TEST.ORG (DES cbc mode with RSA-MD5)
Keys look fine, now start/restart telnet service ...
# svcadm -v enable -s telnet
Basically, in.telnetd(1M) allocates pseudo-terminal and creates a login(1) process, which in turn uses pam(3PAM) - Pluggable Authentication Module for the authentication, account, session and password management,
therefore if we want to authenticate by kerberos, we should plug pam_krb5(5) library.
Normally /etc/pam.conf, see pam.conf(4), has the following definitions:
ktelnet auth required pam_unix_cred.so.1
ktelnet auth binding pam_krb5.so.1
ktelnet auth required pam_unix_auth.so.1
other account requisite pam_roles.so.1
other account required pam_unix_account.so.1
other session required pam_unix_session.so.1
other password required pam_dhkeys.so.1
other password requisite pam_authtok_get.so.1
other password requisite pam_authtok_check.so.1
other password required pam_authtok_store.so.1
It means that authentication for kerberized telnet (ktelnet) service goes through pam_krb5, while account, session and password management are UNIX default.
Eventually, for our example, assume:
-
user1@TEST.ORG principal exists in our realm
-
user1 is a valid user on the telnet server, otherwise
Krb5 principal lacks permission to access local account for user1
-
user1 is not locked, otherwise
pam_unix_account: ktelnet attempting to validate locked account
actually /etc/shadow may contain the following:
user1:NP:::::::
So, let's try to kinit and telnet (forwarding TGT) from some kerberized machine now ...
$ kinit user1
Password for user1@TEST.ORG:
$ telnet -a -F -l user1 frostina.test.org
Connected to frostina.test.org.
Escape character is '^]'.
[ Kerberos V5 accepts you as ''user1@TEST.ORG'' ]
[ Kerberos V5 accepted forwarded credentials ]
Sun Microsystems Inc. SunOS 5.11
[user1@frostina]$
Isn't it's cool.
Trackback URL: http://blogs.sun.com/vl/entry/start_playing_with_kerberos
Posted by Jeff on January 25, 2007 at 10:05 PM CET #
dam fine blog, helped me loads - big thanks :)
Posted by rob on August 15, 2008 at 06:17 PM CEST #
Thanks for the tip to create /var/krb5/rcache/root
Where did you find that?
Posted by Douglas on September 16, 2008 at 01:13 PM CEST #