|
@@ -0,0 +1,473 @@
|
|
|
+[[appendices]]
|
|
|
+= Appendices
|
|
|
+:figures: servlet/authentication/kerberos
|
|
|
+:numbered!:
|
|
|
+
|
|
|
+[appendix]
|
|
|
+== Material Used in this Document
|
|
|
+Dummy UserDetailsService used in samples because we don't have a real
|
|
|
+user source.
|
|
|
+
|
|
|
+[source,java,indent=0]
|
|
|
+----
|
|
|
+include::example$kerberos/DummyUserDetailsService.java[tags=snippetA]
|
|
|
+----
|
|
|
+
|
|
|
+[appendix]
|
|
|
+== Crash Course to Kerberos
|
|
|
+In any authentication process there are usually a three parties
|
|
|
+involved.
|
|
|
+
|
|
|
+image::{figures}/drawio-kerb-cc1.png[]
|
|
|
+
|
|
|
+First is a `client` which sometimes is a client computer but in most
|
|
|
+of the scenarios it is the actual user sitting on a computer and
|
|
|
+trying to access resources. Then there is the `resource` user is trying
|
|
|
+to access. In this example it is a web server.
|
|
|
+
|
|
|
+Then there is a `Key Distribution Center` or `KDC`. In a case of
|
|
|
+Windows environment this would be a `Domain Controller`. `KDC` is the
|
|
|
+one which really brings everything together and thus is the most
|
|
|
+critical component in your environment. Because of this it is also
|
|
|
+considered as a single point of failure.
|
|
|
+
|
|
|
+Initially when `Kerberos` environment is setup and domain user
|
|
|
+principals created into a database, encryption keys are also
|
|
|
+created. These encryption keys are based on shared secrets(i.e. user
|
|
|
+password) and actual passwords are never kept in a clear text.
|
|
|
+Effectively `KDC` has its own key and other keys for domain users.
|
|
|
+
|
|
|
+Interestingly there is no communication between a `resource` and a
|
|
|
+`KDC` during the authentication process.
|
|
|
+
|
|
|
+image::{figures}/drawio-kerb-cc2.png[]
|
|
|
+
|
|
|
+When client wants to authenticate itself with a `resource` it first
|
|
|
+needs to communicate with a `KDC`. `Client` will craft a special package
|
|
|
+which contains encrypted and unencrypted parts. Unencrypted part
|
|
|
+contains i.e. information about a user and encrypted part other
|
|
|
+information which is part of a protocol. `Client` will encrypt package
|
|
|
+data with its own key.
|
|
|
+
|
|
|
+When `KDC` receives this authentication package from a client it
|
|
|
+checks who this `client` claims to be from an unencrypted part and based
|
|
|
+on that information it uses `client` decryption key it already have in
|
|
|
+its database. If this decryption is succesfull `KDC` knows that this
|
|
|
+`client` is the one it claims to be.
|
|
|
+
|
|
|
+What KDC returns to a client is a ticket called `Ticket Granting
|
|
|
+Ticket` which is signed by a KDC's own private key. Later when
|
|
|
+`client` sends back this ticket it can try to decrypt it and if that
|
|
|
+operation is succesfull it knows that it was a ticket it itself
|
|
|
+originally signed and gave to a `client`.
|
|
|
+
|
|
|
+image::{figures}/drawio-kerb-cc3.png[]
|
|
|
+
|
|
|
+When client wants to get a ticket which it can use to authenticate
|
|
|
+with a service, `TGT` is sent to `KDC` which then signs a service ticket
|
|
|
+with service's own key. This a moment when a trust between
|
|
|
+`client` and `service` is created. This service ticket contains data
|
|
|
+which only `service` itself is able to decrypt.
|
|
|
+
|
|
|
+image::{figures}/drawio-kerb-cc4.png[]
|
|
|
+
|
|
|
+When `client` is authenticating with a service it sends previously
|
|
|
+received service ticket to a service which then thinks that I don't
|
|
|
+know anything about this guy but he gave me an authentication ticket.
|
|
|
+What `service` can do next is try to decrypt that ticket and if that
|
|
|
+operation is succesfull it knows that only other party who knows my
|
|
|
+credentials is the `KDC` and because I trust him I can also trust that
|
|
|
+this client is a one he claims to be.
|
|
|
+
|
|
|
+[appendix]
|
|
|
+== Setup Kerberos Environments
|
|
|
+Doing a production setup of Kerberos environment is out of scope of
|
|
|
+this document but this appendix provides some help to get you
|
|
|
+started for setting up needed components for development.
|
|
|
+
|
|
|
+[[setupmitkerberos]]
|
|
|
+=== Setup MIT Kerberos
|
|
|
+First action is to setup a new realm and a database.
|
|
|
+
|
|
|
+[source,text,indent=0]
|
|
|
+----
|
|
|
+# kdb5_util create -s -r EXAMPLE.ORG
|
|
|
+Loading random data
|
|
|
+Initializing database '/var/lib/krb5kdc/principal' for realm 'EXAMPLE.ORG',
|
|
|
+master key name 'K/M@EXAMPLE.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:
|
|
|
+----
|
|
|
+
|
|
|
+`kadmin` command can be used to administer Kerberos environment but
|
|
|
+you can't yet use it because there are no admin users in a database.
|
|
|
+
|
|
|
+[source,text,indent=0]
|
|
|
+----
|
|
|
+root@neo:/etc/krb5kdc# kadmin
|
|
|
+Authenticating as principal root/admin@EXAMPLE.ORG with password.
|
|
|
+kadmin: Client not found in Kerberos database while initializing
|
|
|
+kadmin interface
|
|
|
+----
|
|
|
+
|
|
|
+Lets use `kadmin.local` command to create one.
|
|
|
+
|
|
|
+[source,text,indent=0]
|
|
|
+----
|
|
|
+root@neo:/etc/krb5kdc# kadmin.local
|
|
|
+Authenticating as principal root/admin@EXAMPLE.ORG with password.
|
|
|
+
|
|
|
+kadmin.local: listprincs
|
|
|
+K/M@EXAMPLE.ORG
|
|
|
+kadmin/admin@EXAMPLE.ORG
|
|
|
+kadmin/changepw@EXAMPLE.ORG
|
|
|
+kadmin/cypher@EXAMPLE.ORG
|
|
|
+krbtgt/EXAMPLE.ORG@EXAMPLE.ORG
|
|
|
+
|
|
|
+kadmin.local: addprinc root/admin@EXAMPLE.ORG
|
|
|
+WARNING: no policy specified for root/admin@EXAMPLE.ORG; defaulting to
|
|
|
+no policy
|
|
|
+Enter password for principal "root/admin@EXAMPLE.ORG":
|
|
|
+Re-enter password for principal "root/admin@EXAMPLE.ORG":
|
|
|
+Principal "root/admin@EXAMPLE.ORG" created.
|
|
|
+----
|
|
|
+
|
|
|
+Then enable admins by modifying `kadm5.acl` file and restart Kerberos
|
|
|
+services.
|
|
|
+
|
|
|
+[source,text,indent=0]
|
|
|
+----
|
|
|
+# cat /etc/krb5kdc/kadm5.acl
|
|
|
+# This file Is the access control list for krb5 administration.
|
|
|
+*/admin *
|
|
|
+----
|
|
|
+
|
|
|
+Now you can use `kadmin` with previously created `root/admin`
|
|
|
+principal. Lets create our first user `user1`.
|
|
|
+
|
|
|
+[source,text,indent=0]
|
|
|
+----
|
|
|
+kadmin: addprinc user1
|
|
|
+WARNING: no policy specified for user1@EXAMPLE.ORG; defaulting to no
|
|
|
+policy
|
|
|
+Enter password for principal "user1@EXAMPLE.ORG":
|
|
|
+Re-enter password for principal "user1@EXAMPLE.ORG":
|
|
|
+Principal "user1@EXAMPLE.ORG" created.
|
|
|
+----
|
|
|
+
|
|
|
+Lets create our second user `user2` and export a keytab file.
|
|
|
+
|
|
|
+[source,text,indent=0]
|
|
|
+----
|
|
|
+kadmin: addprinc user2
|
|
|
+WARNING: no policy specified for user2@EXAMPLE.ORG; defaulting to no
|
|
|
+policy
|
|
|
+Enter password for principal "user2@EXAMPLE.ORG":
|
|
|
+Re-enter password for principal "user2@EXAMPLE.ORG":
|
|
|
+Principal "user2@EXAMPLE.ORG" created.
|
|
|
+
|
|
|
+kadmin: ktadd -k /tmp/user2.keytab user2@EXAMPLE.ORG
|
|
|
+Entry for principal user2@EXAMPLE.ORG with kvno 2, encryption type aes256-cts-hmac-sha1-96 added to keytab WRFILE:/tmp/user2.keytab.
|
|
|
+Entry for principal user2@EXAMPLE.ORG with kvno 2, encryption type arcfour-hmac added to keytab WRFILE:/tmp/user2.keytab.
|
|
|
+Entry for principal user2@EXAMPLE.ORG with kvno 2, encryption type des3-cbc-sha1 added to keytab WRFILE:/tmp/user2.keytab.
|
|
|
+Entry for principal user2@EXAMPLE.ORG with kvno 2, encryption type des-cbc-crc added to keytab WRFILE:/tmp/user2.keytab.
|
|
|
+----
|
|
|
+
|
|
|
+Lets create a service ticket for tomcat and export credentials to a
|
|
|
+keytab file named `tomcat.keytab`.
|
|
|
+
|
|
|
+[source,text,indent=0]
|
|
|
+----
|
|
|
+kadmin: addprinc -randkey HTTP/neo.example.org@EXAMPLE.ORG
|
|
|
+WARNING: no policy specified for HTTP/neo.example.org@EXAMPLE.ORG;
|
|
|
+defaulting to no policy
|
|
|
+Principal "HTTP/neo.example.org@EXAMPLE.ORG" created.
|
|
|
+
|
|
|
+kadmin: ktadd -k /tmp/tomcat.keytab HTTP/neo.example.org@EXAMPLE.ORG
|
|
|
+Entry for principal HTTP/neo.example.org@EXAMPLE.ORG with kvno 2, encryption type aes256-cts-hmac-sha1-96 added to keytab WRFILE:/tmp/tomcat2.keytab.
|
|
|
+Entry for principal HTTP/neo.example.org@EXAMPLE.ORG with kvno 2, encryption type arcfour-hmac added to keytab WRFILE:/tmp/tomcat2.keytab.
|
|
|
+Entry for principal HTTP/neo.example.org@EXAMPLE.ORG with kvno 2, encryption type des3-cbc-sha1 added to keytab WRFILE:/tmp/tomcat2.keytab.
|
|
|
+Entry for principal HTTP/neo.example.org@EXAMPLE.ORG with kvno 2, encryption type des-cbc-crc added to keytab WRFILE:/tmp/tomcat2.keytab.
|
|
|
+----
|
|
|
+
|
|
|
+[[setupwinkerberos]]
|
|
|
+=== Setup Windows Domain Controller
|
|
|
+
|
|
|
+This was tested using `Windows Server 2012 R2`
|
|
|
+
|
|
|
+[TIP]
|
|
|
+====
|
|
|
+Internet is full of good articles and videos how to setup Windows AD
|
|
|
+but these two are quite usefull
|
|
|
+http://www.rackspace.com/knowledge_center/article/installing-active-directory-on-windows-server-2012[Rackspace] and
|
|
|
+http://social.technet.microsoft.com/wiki/contents/articles/12370.windows-server-2012-set-up-your-first-domain-controller-step-by-step.aspx[Microsoft
|
|
|
+Technet].
|
|
|
+====
|
|
|
+
|
|
|
+- Normal domain controller and active directory setup was done.
|
|
|
+- Used dns domain `example.org` and windows domain `EXAMPLE`.
|
|
|
+- I created various domain users like `user1`, `user2`, `user3`,
|
|
|
+ `tomcat` and set passwords to `Password#`.
|
|
|
+
|
|
|
+I eventually also added all ip's of my vm's to AD's dns server for
|
|
|
+that not to cause any trouble.
|
|
|
+
|
|
|
+[source,text]
|
|
|
+----
|
|
|
+Name: WIN-EKBO0EQ7TS7.example.org
|
|
|
+Address: 172.16.101.135
|
|
|
+
|
|
|
+Name: win8vm.example.org
|
|
|
+Address: 172.16.101.136
|
|
|
+
|
|
|
+Name: neo.example.org
|
|
|
+Address: 172.16.101.1
|
|
|
+----
|
|
|
+
|
|
|
+Service Principal Name(SPN) needs to be setup with `HTTP` and a
|
|
|
+server name `neo.example.org` where tomcat servlet container is run. This
|
|
|
+is used with `tomcat` domain user and its `keytab` is then used as a
|
|
|
+service credential.
|
|
|
+
|
|
|
+[source,text]
|
|
|
+----
|
|
|
+PS C:\> setspn -A HTTP/neo.example.org tomcat
|
|
|
+----
|
|
|
+
|
|
|
+I exported keytab file which is copied to linux server running tomcat.
|
|
|
+
|
|
|
+[source,text]
|
|
|
+----
|
|
|
+PS C:\> ktpass /out c:\tomcat.keytab /mapuser tomcat@EXAMPLE.ORG /princ HTTP/neo.example.org@EXAMPLE.ORG /pass Password# /ptype KRB5_NT_PRINCIPAL /crypto All
|
|
|
+ Targeting domain controller: WIN-EKBO0EQ7TS7.example.org
|
|
|
+ Using legacy password setting method
|
|
|
+ Successfully mapped HTTP/neo.example.org to tomcat.
|
|
|
+----
|
|
|
+
|
|
|
+[appendix]
|
|
|
+== Troubleshooting
|
|
|
+This appendix provides generic information about troubleshooting
|
|
|
+errors and problems.
|
|
|
+
|
|
|
+[IMPORTANT]
|
|
|
+====
|
|
|
+If you think environment and configuration is correctly setup, do
|
|
|
+double check and ask other person to check possible obvious mistakes
|
|
|
+or typos. Kerberos setup is generally very brittle and it is not
|
|
|
+always very easy to debug where the problem lies.
|
|
|
+====
|
|
|
+
|
|
|
+.Cannot find key of appropriate type to decrypt
|
|
|
+
|
|
|
+[source,text]
|
|
|
+----
|
|
|
+GSSException: Failure unspecified at GSS-API level (Mechanism level:
|
|
|
+Invalid argument (400) - Cannot find key of appropriate type to
|
|
|
+decrypt AP REP - RC4 with HMAC)
|
|
|
+----
|
|
|
+
|
|
|
+If you see abore error indicating missing key type, this will happen
|
|
|
+with two different use cases. Firstly your JVM may not support
|
|
|
+appropriate encryption type or it is disabled in your `krb5.conf`
|
|
|
+file.
|
|
|
+
|
|
|
+[source,text]
|
|
|
+----
|
|
|
+default_tkt_enctypes = rc4-hmac
|
|
|
+default_tgs_enctypes = rc4-hmac
|
|
|
+----
|
|
|
+
|
|
|
+Second case is less obvious and hard to track because it will lead
|
|
|
+into same error. This specific `GSSException` is throws also if you
|
|
|
+simply don't have a required encryption key which then may be caused
|
|
|
+by a misconfiguration in your kerberos server or a simply typo in your
|
|
|
+principal.
|
|
|
+
|
|
|
+.Using wrong kerberos configuration
|
|
|
+
|
|
|
+{zwsp} +
|
|
|
+
|
|
|
+In most system all commands and libraries will search kerberos
|
|
|
+configuration either from a default locations or special locations
|
|
|
+like JDKs. It's easy to get mixed up especially if working from unix
|
|
|
+systems, which already may have default settings to work with MIT
|
|
|
+kerberos, towards Windows domains.
|
|
|
+
|
|
|
+This is a specific example what happens with `ldapsearch` trying to
|
|
|
+query Windows AD using kerberos authentication.
|
|
|
+
|
|
|
+[source,text]
|
|
|
+----
|
|
|
+$ ldapsearch -H ldap://WIN-EKBO0EQ7TS7.example.org -b "dc=example,dc=org"
|
|
|
+SASL/GSSAPI authentication started
|
|
|
+ldap_sasl_interactive_bind_s: Local error (-2)
|
|
|
+ additional info: SASL(-1): generic failure: GSSAPI Error:
|
|
|
+ Unspecified GSS failure. Minor code may provide more information
|
|
|
+ (No Kerberos credentials available)
|
|
|
+----
|
|
|
+
|
|
|
+Well that doesn't look good and is a simple indication that I don't
|
|
|
+have a valid kerberos tickets as shown below.
|
|
|
+
|
|
|
+[source,text]
|
|
|
+----
|
|
|
+$ klist
|
|
|
+klist: Credentials cache file '/tmp/krb5cc_1000' not found
|
|
|
+----
|
|
|
+
|
|
|
+We already have a keytab file we exported from Windows AD to be used
|
|
|
+with tomcat running on Linux. Lets try to use that to authenticate
|
|
|
+with Windows AD.
|
|
|
+
|
|
|
+You can have a dedicated config file which usually can be used with
|
|
|
+native Linux commands and JVMs via system propertys.
|
|
|
+
|
|
|
+[source,text]
|
|
|
+----
|
|
|
+$ cat krb5.ini
|
|
|
+[libdefaults]
|
|
|
+default_realm = EXAMPLE.ORG
|
|
|
+default_keytab_name = /tmp/tomcat.keytab
|
|
|
+forwardable=true
|
|
|
+
|
|
|
+[realms]
|
|
|
+EXAMPLE.ORG = {
|
|
|
+ kdc = WIN-EKBO0EQ7TS7.example.org:88
|
|
|
+}
|
|
|
+
|
|
|
+[domain_realm]
|
|
|
+example.org=EXAMPLE.ORG
|
|
|
+.example.org=EXAMPLE.ORG
|
|
|
+----
|
|
|
+
|
|
|
+Lets use that config and a keytab to get initial credentials.
|
|
|
+
|
|
|
+[source,text]
|
|
|
+----
|
|
|
+$ env KRB5_CONFIG=/path/to/krb5.ini kinit -kt tomcat.keytab HTTP/neo.example.org@EXAMPLE.ORG
|
|
|
+
|
|
|
+$ klist
|
|
|
+Ticket cache: FILE:/tmp/krb5cc_1000
|
|
|
+Default principal: HTTP/neo.example.org@EXAMPLE.ORG
|
|
|
+
|
|
|
+Valid starting Expires Service principal
|
|
|
+26/03/15 09:04:37 26/03/15 19:04:37 krbtgt/EXAMPLE.ORG@EXAMPLE.ORG
|
|
|
+ renew until 27/03/15 09:04:37
|
|
|
+----
|
|
|
+
|
|
|
+Lets see what happens if we now try to do a simple query against
|
|
|
+Windows AD.
|
|
|
+
|
|
|
+[source,text]
|
|
|
+----
|
|
|
+$ ldapsearch -H ldap://WIN-EKBO0EQ7TS7.example.org -b "dc=example,dc=org"
|
|
|
+SASL/GSSAPI authentication started
|
|
|
+ldap_sasl_interactive_bind_s: Local error (-2)
|
|
|
+ additional info: SASL(-1): generic failure: GSSAPI Error:
|
|
|
+ Unspecified GSS failure. Minor code may provide more information
|
|
|
+ (KDC returned error string: PROCESS_TGS)
|
|
|
+----
|
|
|
+
|
|
|
+This may be simply because `ldapsearch` is getting confused and simply
|
|
|
+using wrong configuration. You can tell `ldapsearch` to use a
|
|
|
+different configuration via `KRB5_CONFIG` env variable just like we
|
|
|
+did with `kinit`. You can also use `KRB5_TRACE=/dev/stderr` to get
|
|
|
+more verbose output of what native libraries are doing.
|
|
|
+
|
|
|
+[source,text]
|
|
|
+----
|
|
|
+$ env KRB5_CONFIG=/path/to/krb5.ini ldapsearch -H ldap://WIN-EKBO0EQ7TS7.example.org -b "dc=example,dc=org"
|
|
|
+
|
|
|
+$ klist
|
|
|
+Ticket cache: FILE:/tmp/krb5cc_1000
|
|
|
+Default principal: HTTP/neo.example.org@EXAMPLE.ORG
|
|
|
+
|
|
|
+Valid starting Expires Service principal
|
|
|
+26/03/15 09:11:03 26/03/15 19:11:03 krbtgt/EXAMPLE.ORG@EXAMPLE.ORG
|
|
|
+ renew until 27/03/15 09:11:03
|
|
|
+ 26/03/15 09:11:44 26/03/15 19:11:03
|
|
|
+ ldap/win-ekbo0eq7ts7.example.org@EXAMPLE.ORG
|
|
|
+ renew until 27/03/15 09:11:03
|
|
|
+----
|
|
|
+
|
|
|
+Above you can see what happened if query was successful by looking
|
|
|
+kerberos tickets. Now you can experiment with further query commands
|
|
|
+i.e. if you working with `KerberosLdapContextSource`.
|
|
|
+
|
|
|
+[source,text]
|
|
|
+----
|
|
|
+$ ldapsearch -H ldap://WIN-EKBO0EQ7TS7.example.org \
|
|
|
+-b "dc=example,dc=org" \
|
|
|
+"(| (userPrincipalName=user2@EXAMPLE.ORG)
|
|
|
+(sAMAccountName=user2@EXAMPLE.ORG))" \
|
|
|
+dn
|
|
|
+
|
|
|
+...
|
|
|
+# test user, example.org
|
|
|
+dn: CN=test user,DC=example,DC=org
|
|
|
+----
|
|
|
+
|
|
|
+[appendix]
|
|
|
+[[browserspnegoconfig]]
|
|
|
+== Configure Browsers for Spnego Negotiation
|
|
|
+
|
|
|
+=== Firefox
|
|
|
+Complete following steps to ensure that your Firefox browser is
|
|
|
+enabled to perform Spnego authentication.
|
|
|
+
|
|
|
+- Open Firefox.
|
|
|
+- At address field, type *about:config*.
|
|
|
+- In filter/search, type *negotiate*.
|
|
|
+- Parameter *network.negotiate-auth.trusted-uris* may be set to
|
|
|
+ default *https://* which doesn't work for you. Generally speaking
|
|
|
+ this parameter has to replaced with the server address if Kerberos
|
|
|
+ delegation is required.
|
|
|
+- It is recommended to use `https` for all communication.
|
|
|
+
|
|
|
+=== Chrome
|
|
|
+
|
|
|
+With Google Chrome you generally need to set command-line parameters
|
|
|
+order to white list servers with Chrome will negotiate.
|
|
|
+
|
|
|
+- on Windows machines (clients): Chrome shares the configuration with
|
|
|
+ Internet Explorer so if all changes were applied to IE (as described
|
|
|
+ in E.3), nothing has to be passed via command-line parameters.
|
|
|
+- on Linux/Mac OS machines (clients): the command-line parameter
|
|
|
+ `--auth-negotiate-delegate-whitelist` should only used if Kerberos
|
|
|
+ delegation is required (otherwise do not set this parameter).
|
|
|
+- It is recommended to use `https` for all communication.
|
|
|
+
|
|
|
+[source,text]
|
|
|
+----
|
|
|
+--auth-server-whitelist="*.example.com"
|
|
|
+--auth-negotiate-delegate-whitelist="*.example.com"
|
|
|
+----
|
|
|
+
|
|
|
+You can see which policies are enable by typing *chrome://policy/*
|
|
|
+into Chrome's address bar.
|
|
|
+
|
|
|
+With Linux Chrome will also read policy files from
|
|
|
+`/etc/opt/chrome/policies/managed` directory.
|
|
|
+
|
|
|
+.mypolicy.json
|
|
|
+[source,json]
|
|
|
+----
|
|
|
+{
|
|
|
+ "AuthServerWhitelist" : "*.example.org",
|
|
|
+ "AuthNegotiateDelegateWhitelist" : "*.example.org",
|
|
|
+ "DisableAuthNegotiateCnameLookup" : true,
|
|
|
+ "EnableAuthNegotiatePort" : true
|
|
|
+}
|
|
|
+----
|
|
|
+
|
|
|
+=== Internet Explorer
|
|
|
+Complete following steps to ensure that your Internet Explorer browser
|
|
|
+is enabled to perform Spnego authentication.
|
|
|
+
|
|
|
+- Open Internet Explorer.
|
|
|
+- Click *Tools > Intenet Options > Security* tab.
|
|
|
+- In *Local intranet* section make sure your server is trusted by i.e.
|
|
|
+ adding it into a list.
|
|
|
+
|