mirror of
				https://github.com/spring-projects/spring-security.git
				synced 2025-10-24 19:28:45 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			474 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			474 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| [[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
 | |
| https://www.rackspace.com/knowledge_center/article/installing-active-directory-on-windows-server-2012[Rackspace] and
 | |
| https://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.
 | |
| 
 |